diff --git a/.editorconfig b/.editorconfig index f511aad460..bd318e5b8a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,16 +1,14 @@ +# In case prettier plugin or eslint with autofix is not enabled in IDE +# The fallback settings here should match with our prettierrc config +# so we get consistency! root = true + [*] -indent_style=tab -indent_size=tab -tab_width=4 +indent_style=space +indent_size=2 +tab_width=2 end_of_line=lf charset=utf-8 trim_trailing_whitespace=true max_line_length=120 insert_final_newline=true - -[*.yml] -indent_style=space -indent_size=2 -tab_width=8 -end_of_line=lf diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..8e1b6fe994 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + extends: [ + '@joystream/eslint-config' + ] +} \ No newline at end of file diff --git a/.github/workflows/joystream-cli.yml b/.github/workflows/joystream-cli.yml new file mode 100644 index 0000000000..71537074c1 --- /dev/null +++ b/.github/workflows/joystream-cli.yml @@ -0,0 +1,47 @@ +name: joystream-cli +on: [pull_request, push] + +jobs: + cli_build_ubuntu: + name: Ubuntu Checks + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: checks + run: | + yarn install --frozen-lockfile + yarn workspace @joystream/cli checks + - name: yarn pack test + run: | + yarn workspace @joystream/cli pack --filename cli-pack-test.tgz + tar zxvf ./cli/cli-pack-test.tgz -C cli + cd ./cli/package && yarn link + + cli_build_osx: + name: MacOS Checks + runs-on: macos-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: checks + run: | + yarn install --frozen-lockfile --network-timeout 120000 + yarn workspace @joystream/cli checks + - name: yarn pack test + run: | + yarn workspace @joystream/cli pack --filename cli-pack-test.tgz + tar zxvf ./cli/cli-pack-test.tgz -C cli + cd ./cli/package && yarn link diff --git a/.github/workflows/joystream-types.yml b/.github/workflows/joystream-types.yml new file mode 100644 index 0000000000..22a72c8895 --- /dev/null +++ b/.github/workflows/joystream-types.yml @@ -0,0 +1,49 @@ +name: joystream-types +on: [pull_request, push] + +jobs: + types_checks_ubuntu: + name: Ubuntu Checks + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: checks + run: | + yarn install --frozen-lockfile + yarn workspace @joystream/types checks + - name: npm pack test + run: | + cd types + npm pack | tail -1 | xargs tar xzf + cd package && npm install + node ./index.js + + types_checks_osx: + name: MacOS Checks + runs-on: macos-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: checks + run: | + yarn install --frozen-lockfile --network-timeout 120000 + yarn workspace @joystream/types checks + - name: npm pack test + run: | + cd types + npm pack | tail -1 | xargs tar xzf + cd package && npm install + node ./index.js diff --git a/.github/workflows/network-tests.yml b/.github/workflows/network-tests.yml new file mode 100644 index 0000000000..af0c69bfd4 --- /dev/null +++ b/.github/workflows/network-tests.yml @@ -0,0 +1,37 @@ +name: network-tests +on: [pull_request, push] + +jobs: + network_build_ubuntu: + name: Ubuntu Checks + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: checks + run: | + yarn install --frozen-lockfile + yarn workspace joystream-testing checks + + network_build_osx: + name: MacOS Checks + runs-on: macos-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: checks + run: | + yarn install --frozen-lockfile --network-timeout 120000 + yarn workspace joystream-testing checks diff --git a/.github/workflows/pioneer.yml b/.github/workflows/pioneer.yml new file mode 100644 index 0000000000..bca36c79cf --- /dev/null +++ b/.github/workflows/pioneer.yml @@ -0,0 +1,71 @@ +name: Pioneer +on: [pull_request, push] + +jobs: + pioneer_build_ubuntu: + name: Ubuntu Build + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: build + run: | + yarn install --frozen-lockfile + yarn workspace pioneer build + + pioneer_build_osx: + name: MacOS Build + runs-on: macos-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: build + run: | + yarn install --frozen-lockfile --network-timeout 120000 + yarn workspace pioneer build + + pioneer_lint_ubuntu: + name: Ubuntu Linting + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: lint + run: | + yarn install --frozen-lockfile + yarn workspace pioneer lint + + pioneer_lint_osx: + name: MacOS Linting + runs-on: macos-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: lint + run: | + yarn install --frozen-lockfile --network-timeout 120000 + yarn workspace pioneer lint diff --git a/.github/workflows/storage-node.yml b/.github/workflows/storage-node.yml new file mode 100644 index 0000000000..499deead82 --- /dev/null +++ b/.github/workflows/storage-node.yml @@ -0,0 +1,37 @@ +name: storage-node +on: [pull_request, push] + +jobs: + storage_node_build_ubuntu: + name: Ubuntu Checks + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: checks + run: | + yarn install --frozen-lockfile + yarn workspace storage-node checks + + storage_node_build_osx: + name: MacOS Checks + runs-on: macos-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: checks + run: | + yarn install --frozen-lockfile --network-timeout 120000 + yarn workspace storage-node checks diff --git a/.gitignore b/.gitignore index 22fa52c180..3c2bf49836 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ joystream_runtime.wasm # Generated by yarn yarn* +!yarn.lock # JetBrains IDEs .idea @@ -21,4 +22,13 @@ yarn* .*.sw* # Visual Studio Code -.vscode +.vscode/ + +# Compiled WASM code +*.wasm + +# Temporary files +.tmp/ + +# Istanbul report output +**.nyc_output/ diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000000..f2c1c07c33 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('@joystream/prettier-config'), +} diff --git a/.travis.yml b/.travis.yml index 8a76eb12cc..7693089247 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,90 +1,42 @@ language: rust -rust: - - 1.43.0 +# Caching of the runtime .wasm blob poses a problem. +# See: https://github.com/Joystream/joystream/issues/466 +# Always starting with a clean slate is probably better, it allows us to ensure +# the WASM runtime is always rebuilt. It also allows us to detect when certain upstream dependencies +# sometimes break the build. When cache is enabled do not use the produced WASM build. +# This also means the binary should not be used to produce the final chainspec file (because the same +# one is embedded in the binary) +# cache: cargo -matrix: - include: - - os: linux - env: TARGET=x86_64-unknown-linux-gnu - - os: linux - env: TARGET=arm-unknown-linux-gnueabihf - services: docker - - os: osx - env: TARGET=x86_64-apple-darwin +rust: stable +# Skip Rust build in a pull request if no rust project files were modified before_install: - - rustup component add rustfmt - - cargo fmt --all -- --check - - rustup component add clippy - - BUILD_DUMMY_WASM_BINARY=1 cargo clippy -- -D warnings - - rustup default stable - - rustup update nightly - - rustup target add wasm32-unknown-unknown --toolchain nightly - - cargo test --verbose --all - -install: - | - if [ "$TARGET" = "arm-unknown-linux-gnueabihf" ] - then - docker pull joystream/rust-raspberry + if [ "$TRAVIS_PULL_REQUEST" != "false" ] + then + if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qE "(.rs|Cargo.(lock|toml))$" + then + echo "No changes to Rust or Cargo Files, CI not running." + travis_terminate 0 + fi fi -script: - - | - if [ "$TARGET" = "arm-unknown-linux-gnueabihf" ] - then - docker run -u root \ - --volume ${TRAVIS_BUILD_DIR}:/home/cross/project \ - joystream/rust-raspberry \ - build --release - sudo chmod a+r ${TRAVIS_BUILD_DIR}/target/${TARGET}/release/joystream-node - else - cargo build --release --target=${TARGET} - fi +install: + - rustup install nightly-2020-05-23 + - rustup target add wasm32-unknown-unknown --toolchain nightly-2020-05-23 + # travis installs rust using rustup with the "minimal" profile so these tools are not installed by default + - rustup component add rustfmt + - rustup component add clippy -before_deploy: - - cp ./target/${TARGET}/release/joystream-node . - - | - if [ "$TARGET" = "arm-unknown-linux-gnueabihf" ] - then - export FILENAME="joystream-node-armv7-linux-gnueabihf" - else - export FILENAME=`./joystream-node --version | sed -e "s/ /-/g"` - fi - - tar -cf ${FILENAME}.tar ./joystream-node - - gzip ${FILENAME}.tar +before_script: + - cargo fmt --all -- --check -deploy: - - provider: releases - api_key: - secure: QTna4XzKmPrXNA5KnYfLsH8cAKxESLdFbQ5HJ6nvB9reE10SVtg8lZ+ShL+no7TACNBUNt09Qv9HNgs6JcNRJ9QMHEJHKIbMyjplhBtZ+W3l0k+6TL0yeKHZ/OvddDF+vDbpN+y4xBfOf0xqZcNH3lZJTms/NPBn/KT5DpQ3JZ8bibdMP2HSCazfvHLwj38OuLX6VWbFcmN2RAmUR9AXYvk5wWYVw8g1VDzTCxjH1G+dGWH1L9+ZDgFfv7BNSNhPc6V9GghgLVZ+37r/STzTTAQ/gPv+yruglEWUhSAngFfVYUegvTmIeQLi/V+g0tKUS+l7eNX08xz6eZcn0+/32V7P+oEN/dhU84E0kgmiOsiUEGI/KFM+qw9TyX3GtD67UmG5TZrD7OUMIu1qCuPSetsTOK2kvpwlYAn+j5iFB30Uz4hXhOH5dib2zz2I7cYHi1kvzeNQqQOPNDCmaO48bcbRIaeqMAHdsb6scGzh/+CD2V2HOmHlhd+4o1PpX6hAMwmOXAu3bMDi4zlB9Hb1cSZnsYNBHawkD6y45QGepFKpGW/6u5VRPeMK62Gm9wu815C36B4mVg6CVqtZMbk0WYPIk6zfoTft3i04YthKbRO96a5VD9LssVbiSYnudXuZJjSllSZVCi9AKS8JVIS2jC2z+tWkquAesSrwztriRcs= - file: ${FILENAME}.tar.gz - on: - tags: true - repo: Joystream/substrate-node-joystream - draft: true - overwrite: true - skip_cleanup: true - - provider: releases - api_key: - secure: QTna4XzKmPrXNA5KnYfLsH8cAKxESLdFbQ5HJ6nvB9reE10SVtg8lZ+ShL+no7TACNBUNt09Qv9HNgs6JcNRJ9QMHEJHKIbMyjplhBtZ+W3l0k+6TL0yeKHZ/OvddDF+vDbpN+y4xBfOf0xqZcNH3lZJTms/NPBn/KT5DpQ3JZ8bibdMP2HSCazfvHLwj38OuLX6VWbFcmN2RAmUR9AXYvk5wWYVw8g1VDzTCxjH1G+dGWH1L9+ZDgFfv7BNSNhPc6V9GghgLVZ+37r/STzTTAQ/gPv+yruglEWUhSAngFfVYUegvTmIeQLi/V+g0tKUS+l7eNX08xz6eZcn0+/32V7P+oEN/dhU84E0kgmiOsiUEGI/KFM+qw9TyX3GtD67UmG5TZrD7OUMIu1qCuPSetsTOK2kvpwlYAn+j5iFB30Uz4hXhOH5dib2zz2I7cYHi1kvzeNQqQOPNDCmaO48bcbRIaeqMAHdsb6scGzh/+CD2V2HOmHlhd+4o1PpX6hAMwmOXAu3bMDi4zlB9Hb1cSZnsYNBHawkD6y45QGepFKpGW/6u5VRPeMK62Gm9wu815C36B4mVg6CVqtZMbk0WYPIk6zfoTft3i04YthKbRO96a5VD9LssVbiSYnudXuZJjSllSZVCi9AKS8JVIS2jC2z+tWkquAesSrwztriRcs= - file: ${FILENAME}.tar.gz - on: - branch: development - repo: Joystream/substrate-node-joystream - draft: true - prerelease: true - overwrite: true - skip_cleanup: true - - provider: releases - api_key: - secure: ZoEXp8g+yZOEG8JZ1Fg6tWnW3aYDfupFbZflEejYaAdXhj1nw7G9N10ZX5VDdb/O1iFx8BhfFymQxk0ynxNC8c52LzOjKIhXEporxgvEPdnoPS/N1JhfsOUV0ragwZDLv2tFVi2AT0K4w8WJFJDzrK4qHOMMQgVbVQZtFmDL1whHdfBD5FyFyKmMdZdWBtTGy4s7X0LnmxjNB4/3AMa540T3LowZ5H66MYZkQmAbtg8ib93WomVanhS23vbjNaH9x1Kmzxd2B3pCSgI8uaxBrpmzINvAeSusYVJQt0EF/cAPXmq0+JmGoocvcS1ecg/SNZoKUNmeElB4ns/obg/QAyE+fyQtyl+iDYBilhFLm5xRMUnqkpyeUUD3u824i/Z+/tfLvtm5Egg1QAiXtIIJMeAj1nN8OIeSlHR4phnSTA3jl2PZw9QYidtV9WCqHC0qxtpkYSKkC8ItaefScPB1AuvOvVx8xvnIxfR/tXvL8Y3Y2BvhiLgpky9JkbdMln1b0m0E5c4vyGCEVqHqpbxM63VJkpct8sVx0atGvipWEelVjz5XpkxW2PYbgg4EKUzl3FiYcXwf5Y/ykxaZNZt7I4gv9nz2KkVwUCCPqdwWF7ww1shFWW5tCoCmJuUESOdPFx0jQ7LVWz7SDLDsqvvaW2c2OPxG6DIx9BiTeAE4qIQ= - file: "${FILENAME}.tar.gz" - skip_cleanup: true - draft: true - prerelease: true - overwrite: true - on: - repo: mnaamani/substrate-node-joystream - branch: deploy \ No newline at end of file +script: + # we set release as build type for all steps to benefit from already compiled packages in prior steps + - BUILD_DUMMY_WASM_BINARY=1 cargo clippy --release -- -D warnings + - BUILD_DUMMY_WASM_BINARY=1 cargo test --release --verbose --all + - TRIGGER_WASM_BUILD=1 WASM_BUILD_TOOLCHAIN=nightly-2020-05-23 cargo build --release -p joystream-node + - ls -l ./target/release/wbuild/joystream-node-runtime/ + - ./target/release/joystream-node --version diff --git a/.yarnclean b/.yarnclean new file mode 100644 index 0000000000..03fdc2d9d9 --- /dev/null +++ b/.yarnclean @@ -0,0 +1,2 @@ +@types/react-native +@polkadot/ts/node_modules diff --git a/Cargo.lock b/Cargo.lock index 066d9f0fba..66a6cba6b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,44 +10,70 @@ dependencies = [ "regex", ] +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" + [[package]] name = "adler32" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d" [[package]] -name = "aes-ctr" -version = "0.3.0" +name = "aead" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.3", +] + +[[package]] +name = "aes" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7001367fde4c768a19d1029f0a8be5abd9308e1119846d5bd9ad26297b8faf5" dependencies = [ "aes-soft", "aesni", - "ctr", - "stream-cipher", + "block-cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f5007801316299f922a6198d1d09a0bae95786815d066d5880d13f7c45ead1" +dependencies = [ + "aead", + "aes", + "block-cipher", + "ghash", + "subtle 2.2.2", ] [[package]] name = "aes-soft" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" +checksum = "4925647ee64e5056cf231608957ce7c81e12d6d6e316b9ce1404778cc1d35fa7" dependencies = [ - "block-cipher-trait", - "byteorder 1.3.4", - "opaque-debug", + "block-cipher", + "byteorder", + "opaque-debug 0.2.3", ] [[package]] name = "aesni" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" +checksum = "d050d39b0b7688b3a3254394c3e30a9d66c41dcf9b05b0e2dbdc623f6505d264" dependencies = [ - "block-cipher-trait", - "opaque-debug", - "stream-cipher", + "block-cipher", + "opaque-debug 0.2.3", ] [[package]] @@ -68,6 +94,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "alga" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f823d037a7ec6ea2197046bafd4ae150e6bc36f9ca347404f46a46823fa84f2" +dependencies = [ + "approx", + "num-complex", + "num-traits 0.2.11", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -87,22 +124,25 @@ dependencies = [ ] [[package]] -name = "app_dirs" -version = "1.2.1" +name = "anyhow" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" + +[[package]] +name = "approx" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" +checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" dependencies = [ - "ole32-sys", - "shell32-sys", - "winapi 0.2.8", - "xdg", + "num-traits 0.2.11", ] [[package]] name = "arc-swap" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" [[package]] name = "arrayref" @@ -140,10 +180,64 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d0864d84b8e07b145449be9a8537db86bf9de5ce03b913214694643b4743502" dependencies = [ - "quote 1.0.3", + "quote 1.0.7", "syn 1.0.17", ] +[[package]] +name = "assert_matches" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" + +[[package]] +name = "async-std" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" +dependencies = [ + "async-task", + "broadcaster", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "futures-core", + "futures-io", + "futures-timer 2.0.2", + "kv-log-macro", + "log", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "async-task" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" +dependencies = [ + "libc", + "winapi 0.3.8", +] + +[[package]] +name = "async-tls" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fd83426b89b034bf4e9ceb9c533c2f2386b813fd3dcae0a425ec6f1837d78a" +dependencies = [ + "futures 0.3.4", + "rustls", + "webpki", + "webpki-roots 0.19.0", +] + [[package]] name = "atty" version = "0.2.14" @@ -197,32 +291,31 @@ checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" [[package]] name = "base64" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder 1.3.4", -] +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "bindgen" -version = "0.47.3" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df683a55b54b41d5ea8ebfaebb5aa7e6b84e3f3006a78f010dadc9ca88469260" +checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" dependencies = [ "bitflags", "cexpr", "cfg-if", "clang-sys", "clap", - "env_logger 0.6.2", - "hashbrown 0.1.8", + "env_logger", "lazy_static", + "lazycell", "log", "peeking_take_while", - "proc-macro2 0.4.30", - "quote 0.6.13", + "proc-macro2", + "quote 1.0.7", "regex", + "rustc-hash", + "shlex", "which", ] @@ -255,9 +348,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" dependencies = [ "byte-tools", - "crypto-mac", - "digest", - "opaque-debug", + "crypto-mac 0.7.0", + "digest 0.8.1", + "opaque-debug 0.2.3", +] + +[[package]] +name = "blake2" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84ce5b6108f8e154604bd4eb76a2f726066c3464d5a552a4229262a18c9bb471" +dependencies = [ + "byte-tools", + "byteorder", + "crypto-mac 0.8.0", + "digest 0.9.0", + "opaque-debug 0.2.3", ] [[package]] @@ -270,6 +376,28 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec 0.5.1", + "constant_time_eq", +] + +[[package]] +name = "blake2s_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9e07352b829279624ceb7c64adb4f585dacdb81d35cafae81139ccd617cf44" +dependencies = [ + "arrayref", + "arrayvec 0.5.1", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.7.3" @@ -278,17 +406,26 @@ checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ "block-padding", "byte-tools", - "byteorder 1.3.4", - "generic-array", + "byteorder", + "generic-array 0.12.3", ] [[package]] -name = "block-cipher-trait" -version = "0.6.2" +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.3", +] + +[[package]] +name = "block-cipher" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +checksum = "fa136449e765dc7faa244561ccae839c394048667929af599b5d931ebe7b7f10" dependencies = [ - "generic-array", + "generic-array 0.14.3", ] [[package]] @@ -301,22 +438,30 @@ dependencies = [ ] [[package]] -name = "bs58" -version = "0.2.5" +name = "broadcaster" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95ee6bba9d950218b6cc910cf62bc9e0a171d0f4537e3627b0f54d08549b188" +checksum = "d9c972e21e0d055a36cf73e4daae870941fe7a8abcd5ac3396aab9e4c126bd87" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "futures-util", + "parking_lot 0.10.2", + "slab", +] [[package]] name = "bs58" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b170cd256a3f9fa6b9edae3e44a7dfdfc77e8124dbc3e2612d75f9c3e2396dae" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" [[package]] name = "bstr" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" +checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" dependencies = [ "memchr", ] @@ -339,12 +484,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -[[package]] -name = "byteorder" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" - [[package]] name = "byteorder" version = "1.3.4" @@ -357,16 +496,16 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ - "byteorder 1.3.4", + "byteorder", "either", "iovec", ] [[package]] name = "bytes" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "c_linked_list" @@ -376,18 +515,18 @@ checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" [[package]] name = "cc" -version = "1.0.50" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" dependencies = [ "jobserver", ] [[package]] name = "cexpr" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" dependencies = [ "nom", ] @@ -398,17 +537,41 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "chacha20" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086c0f07ac275808b7bf9a39f2fd013aae1498be83632814c8c4e0bd53f2dc58" +dependencies = [ + "stream-cipher", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18b0c90556d8e3fec7cf18d84a2f53d27b21288f2fe481b830fadcf809e48205" +dependencies = [ + "aead", + "chacha20", + "poly1305", + "stream-cipher", + "zeroize", +] + [[package]] name = "chain-spec-builder" -version = "2.0.0-alpha.3" +version = "3.0.0" dependencies = [ "ansi_term 0.12.1", "joystream-node", "rand 0.7.3", + "sc-chain-spec", + "sc-keystore", + "sc-telemetry", + "sp-core", "structopt", - "substrate-keystore", - "substrate-primitives", - "substrate-telemetry", ] [[package]] @@ -417,16 +580,18 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" dependencies = [ + "js-sys", "num-integer", - "num-traits", + "num-traits 0.2.11", "time", + "wasm-bindgen", ] [[package]] name = "clang-sys" -version = "0.26.4" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef0c1bcf2e99c649104bd7a7012d8f8802684400e03db0ec0af48583c6fa0e4" +checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" dependencies = [ "glob", "libc", @@ -435,9 +600,9 @@ dependencies = [ [[package]] name = "clap" -version = "2.33.0" +version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ "ansi_term 0.11.0", "atty", @@ -466,6 +631,26 @@ dependencies = [ "bitflags", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7871d2947441b0fdd8e2bd1ce2a2f75304f896582c0d572162d48290683c48" +dependencies = [ + "log", + "web-sys", +] + [[package]] name = "const-random" version = "0.1.8" @@ -473,7 +658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a" dependencies = [ "const-random-macro", - "proc-macro-hack 0.5.15", + "proc-macro-hack", ] [[package]] @@ -483,7 +668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a" dependencies = [ "getrandom", - "proc-macro-hack 0.5.15", + "proc-macro-hack", ] [[package]] @@ -492,6 +677,28 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + [[package]] name = "crc32fast" version = "1.2.0" @@ -503,11 +710,12 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.3.9" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" +checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6" dependencies = [ - "crossbeam-utils 0.6.6", + "cfg-if", + "crossbeam-utils", ] [[package]] @@ -517,7 +725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ "crossbeam-epoch", - "crossbeam-utils 0.7.2", + "crossbeam-utils", "maybe-uninit", ] @@ -529,31 +737,22 @@ checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ "autocfg 1.0.0", "cfg-if", - "crossbeam-utils 0.7.2", + "crossbeam-utils", "lazy_static", "maybe-uninit", "memoffset", - "scopeguard 1.1.0", + "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" -dependencies = [ - "cfg-if", - "crossbeam-utils 0.7.2", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ "cfg-if", - "lazy_static", + "crossbeam-utils", + "maybe-uninit", ] [[package]] @@ -579,10 +778,20 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "generic-array", + "generic-array 0.12.3", "subtle 1.0.0", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.3", + "subtle 2.2.2", +] + [[package]] name = "ct-logs" version = "0.6.0" @@ -593,128 +802,78 @@ dependencies = [ ] [[package]] -name = "ctr" -version = "0.3.2" +name = "curve25519-dalek" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" +checksum = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" dependencies = [ - "block-cipher-trait", - "stream-cipher", + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle 2.2.2", + "zeroize", ] [[package]] -name = "ctrlc" -version = "3.1.4" +name = "data-encoding" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4ba686dff9fa4c1c9636ce1010b0cf98ceb421361b0bb3d6faeec43bd217a7" -dependencies = [ - "nix", - "winapi 0.3.8", -] +checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" [[package]] -name = "cuckoofilter" -version = "0.3.2" +name = "derive_more" +version = "0.99.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd43f7cfaffe0a386636a10baea2ee05cc50df3b77bea4a456c9572a939bf1f" +checksum = "298998b1cf6b5b2c8a7b023dfd45821825ce3ba8a8af55c921a0e734e4653f76" dependencies = [ - "byteorder 0.5.3", - "rand 0.3.23", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "curve25519-dalek" -version = "1.2.3" +name = "difference" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7dcd30ba50cdf88b55b033456138b7c0ac4afdc436d82e1b79f370f24cc66d" -dependencies = [ - "byteorder 1.3.4", - "clear_on_drop", - "digest", - "rand_core 0.3.1", - "subtle 2.2.2", -] +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" [[package]] -name = "curve25519-dalek" -version = "2.0.0" +name = "digest" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "byteorder 1.3.4", - "digest", - "rand_core 0.5.1", - "subtle 2.2.2", - "zeroize 1.1.0", + "generic-array 0.12.3", ] [[package]] -name = "data-encoding" -version = "2.2.0" +name = "digest" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c0346158a19b3627234e15596f5e465c360fcdb97d817bcb255e0510f5a788" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.3", +] [[package]] -name = "derivative" -version = "2.1.0" +name = "directories" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eae4d76b7cefedd1b4f8cc24378b2fbd1ac1b66e3bbebe8e2192d3be81cb355" +checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "cfg-if", + "dirs-sys", ] [[package]] -name = "derive_more" -version = "0.14.1" +name = "dirs-sys" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "rustc_version", - "syn 0.15.44", -] - -[[package]] -name = "derive_more" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" -dependencies = [ - "lazy_static", - "proc-macro2 0.4.30", - "quote 0.6.13", - "regex", - "rustc_version", - "syn 0.15.44", -] - -[[package]] -name = "derive_more" -version = "0.99.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298998b1cf6b5b2c8a7b023dfd45821825ce3ba8a8af55c921a0e734e4653f76" -dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", -] - -[[package]] -name = "difference" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array", + "libc", + "redox_users", + "winapi 0.3.8", ] [[package]] @@ -723,7 +882,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4d33be9473d06f75f58220f71f7a9317aca647dc061dbd3c361b0bef505fbea" dependencies = [ - "byteorder 1.3.4", + "byteorder", "quick-error", ] @@ -739,18 +898,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d" -[[package]] -name = "ed25519-dalek" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d07e8b8a8386c3b89a7a4b329fdfa4cb545de2545e9e2ebbc3dd3929253e426" -dependencies = [ - "clear_on_drop", - "curve25519-dalek 1.2.3", - "failure", - "rand 0.6.5", -] - [[package]] name = "ed25519-dalek" version = "1.0.0-pre.3" @@ -758,9 +905,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" dependencies = [ "clear_on_drop", - "curve25519-dalek 2.0.0", + "curve25519-dalek", "rand 0.7.3", - "sha2", + "sha2 0.8.1", ] [[package]] @@ -770,25 +917,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" [[package]] -name = "elastic-array" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580f3768bd6465780d063f5b8213a2ebd506e139b345e4a81eb301ceae3d61e1" -dependencies = [ - "heapsize", -] - -[[package]] -name = "env_logger" -version = "0.6.2" +name = "enum-primitive-derive" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +checksum = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd" dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "num-traits 0.1.43", + "quote 0.3.15", + "syn 0.11.11", ] [[package]] @@ -812,21 +948,20 @@ checksum = "516aa8d7a71cb00a1c4146f0798549b93d083d4f189b3ced8f3de6b8f11ee6c4" [[package]] name = "erased-serde" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88b6d1705e16a4d62e05ea61cc0496c2bd190f4fa8e5c1f11ce747be6bcf3d1" +checksum = "6ca8b296792113e1500fd935ae487be6e00ce318952a6880555554824d6ebf38" dependencies = [ "serde", ] [[package]] name = "exit-future" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8013f441e38e31c670e7f34ec8f1d5d3a2bd9d303c1ff83976ca886005e8f48" +checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" dependencies = [ - "futures 0.1.29", - "parking_lot 0.7.1", + "futures 0.3.4", ] [[package]] @@ -845,8 +980,8 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", "synstructure", ] @@ -868,38 +1003,26 @@ dependencies = [ [[package]] name = "finality-grandpa" -version = "0.9.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34754852da8d86bc509715292c73140a5b678656d0b16132acd6737bdb5fd5f8" +checksum = "8feb87a63249689640ac9c011742c33139204e3c134293d3054022276869133b" dependencies = [ - "futures 0.1.29", - "hashbrown 0.6.3", + "either", + "futures 0.3.4", + "futures-timer 2.0.2", "log", - "num-traits", + "num-traits 0.2.11", "parity-scale-codec", "parking_lot 0.9.0", ] -[[package]] -name = "fixed-hash" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" -dependencies = [ - "byteorder 1.3.4", - "libc", - "rand 0.7.3", - "rustc-hex", - "static_assertions", -] - [[package]] name = "fixed-hash" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c" dependencies = [ - "byteorder 1.3.4", + "byteorder", "rand 0.7.3", "rustc-hex", "static_assertions", @@ -907,23 +1030,21 @@ dependencies = [ [[package]] name = "fixedbitset" -version = "0.1.9" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" [[package]] name = "flate2" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" +checksum = "68c90b0fc46cf89d227cc78b40e494ff81287a92dd07631e5af0d06fe3cf885e" dependencies = [ "cfg-if", "crc32fast", - "futures 0.1.29", "libc", "libz-sys", "miniz_oxide", - "tokio-io", ] [[package]] @@ -932,28 +1053,78 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d" dependencies = [ - "num-traits", + "num-traits 0.2.11", ] [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ "parity-scale-codec", ] [[package]] name = "fragile" -version = "0.3.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9" +checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2" + +[[package]] +name = "frame-benchmarking" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "linregress", + "parity-scale-codec", + "paste", + "sp-api", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "frame-benchmarking-cli" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-benchmarking", + "parity-scale-codec", + "sc-cli", + "sc-client-db", + "sc-executor", + "sc-service", + "sp-core", + "sp-externalities", + "sp-runtime", + "sp-state-machine", + "structopt", +] + +[[package]] +name = "frame-executive" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] [[package]] name = "frame-metadata" @@ -976,7 +1147,7 @@ dependencies = [ "frame-support-procedural", "impl-trait-for-tuples", "log", - "once_cell 1.4.0", + "once_cell", "parity-scale-codec", "paste", "serde", @@ -997,8 +1168,8 @@ version = "2.0.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ "frame-support-procedural-tools", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] @@ -1009,8 +1180,8 @@ source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478 dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] @@ -1019,8 +1190,8 @@ name = "frame-support-procedural-tools-derive" version = "2.0.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] @@ -1040,6 +1211,29 @@ dependencies = [ "sp-version", ] +[[package]] +name = "frame-system-benchmarking" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-system-rpc-runtime-api" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + [[package]] name = "fs-swap" version = "0.2.4" @@ -1112,7 +1306,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" dependencies = [ "futures-core-preview", - "futures-sink-preview", ] [[package]] @@ -1137,6 +1330,22 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "futures-diagnose" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdcef58a173af8148b182684c9f2d5250875adbcaff7b5794073894f9d8634a9" +dependencies = [ + "futures 0.1.29", + "futures 0.3.4", + "lazy_static", + "log", + "parking_lot 0.9.0", + "pin-project", + "serde", + "serde_json", +] + [[package]] name = "futures-executor" version = "0.3.4" @@ -1149,67 +1358,30 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "futures-executor-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75236e88bd9fe88e5e8bfcd175b665d0528fe03ca4c5207fabc028c8f9d93e98" -dependencies = [ - "futures-core-preview", - "futures-util-preview", - "num_cpus", -] - [[package]] name = "futures-io" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" -[[package]] -name = "futures-io-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4914ae450db1921a56c91bde97a27846287d062087d4a652efc09bb3a01ebda" - [[package]] name = "futures-macro" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" dependencies = [ - "proc-macro-hack 0.5.15", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro-hack", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] -[[package]] -name = "futures-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b1dce2a0267ada5c6ff75a8ba864b4e679a9e2aa44262af7a3b5516d530d76e" -dependencies = [ - "futures-channel-preview", - "futures-core-preview", - "futures-executor-preview", - "futures-io-preview", - "futures-sink-preview", - "futures-util-preview", -] - [[package]] name = "futures-sink" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" -[[package]] -name = "futures-sink-preview" -version = "0.3.0-alpha.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" - [[package]] name = "futures-task" version = "0.3.4" @@ -1218,13 +1390,18 @@ checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" [[package]] name = "futures-timer" -version = "0.4.0" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" + +[[package]] +name = "futures-timer" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878f1d2fc31355fa02ed2372e741b0c17e58373341e6a122569b4623a14a7d33" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" dependencies = [ - "futures-core-preview", - "futures-util-preview", - "pin-utils", + "gloo-timers", + "send_wrapper 0.4.0", ] [[package]] @@ -1233,6 +1410,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" dependencies = [ + "futures 0.1.29", "futures-channel", "futures-core", "futures-io", @@ -1241,7 +1419,7 @@ dependencies = [ "futures-task", "memchr", "pin-utils", - "proc-macro-hack 0.5.15", + "proc-macro-hack", "proc-macro-nested", "slab", ] @@ -1252,16 +1430,36 @@ version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" dependencies = [ - "futures 0.1.29", "futures-channel-preview", "futures-core-preview", - "futures-io-preview", - "futures-sink-preview", - "memchr", "pin-utils", "slab", ] +[[package]] +name = "futures_codec" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a73299e4718f5452e45980fc1d6957a070abe308d3700b63b8673f47e1c2b3" +dependencies = [ + "bytes 0.5.6", + "futures 0.3.4", + "memchr", + "pin-project", +] + +[[package]] +name = "futures_codec" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe8859feb7140742ed1a2a85a07941100ad2b5f98a421b353931d718a34144d1" +dependencies = [ + "bytes 0.5.6", + "futures 0.3.4", + "memchr", + "pin-project", +] + [[package]] name = "gcc" version = "0.3.55" @@ -1277,6 +1475,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "generic-array" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60fb4bb6bba52f78a471264d9a3b7d026cc0af47b22cd2cffbc0b787ca003e63" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "get_if_addrs" version = "0.5.3" @@ -1308,13 +1516,23 @@ dependencies = [ "cfg-if", "libc", "wasi", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e27f0689a6e15944bdce7e45425efb87eaa8ab0c6e87f11d0987a9133e2531" +dependencies = [ + "polyval", ] [[package]] name = "glob" -version = "0.2.11" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" @@ -1329,17 +1547,30 @@ dependencies = [ "regex", ] +[[package]] +name = "gloo-timers" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "h2" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" dependencies = [ - "byteorder 1.3.4", + "byteorder", "bytes 0.4.12", "fnv", "futures 0.1.29", - "http", + "http 0.1.21", "indexmap", "log", "slab", @@ -1347,6 +1578,25 @@ dependencies = [ "tokio-io", ] +[[package]] +name = "h2" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.1", + "indexmap", + "slab", + "tokio 0.2.22", + "tokio-util", + "tracing", +] + [[package]] name = "hash-db" version = "0.15.2" @@ -1364,31 +1614,21 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.1.8" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" -dependencies = [ - "byteorder 1.3.4", - "scopeguard 0.3.3", -] - -[[package]] -name = "hashbrown" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" +checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ "ahash", "autocfg 0.1.7", ] [[package]] -name = "heapsize" -version = "0.4.2" +name = "hashbrown" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb" dependencies = [ - "winapi 0.3.8", + "autocfg 1.0.0", ] [[package]] @@ -1409,54 +1649,29 @@ dependencies = [ "libc", ] -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" - [[package]] name = "hex" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" -[[package]] -name = "hex-literal" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639" -dependencies = [ - "hex-literal-impl 0.1.2", - "proc-macro-hack 0.4.2", -] - [[package]] name = "hex-literal" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" dependencies = [ - "hex-literal-impl 0.2.1", - "proc-macro-hack 0.5.15", + "hex-literal-impl", + "proc-macro-hack", ] [[package]] name = "hex-literal-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda" -dependencies = [ - "proc-macro-hack 0.4.2", -] - -[[package]] -name = "hex-literal-impl" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d" +checksum = "853f769599eb31de176303197b7ba4973299c38c7a7604a6bc88c3eef05b9b46" dependencies = [ - "proc-macro-hack 0.5.15", + "proc-macro-hack", ] [[package]] @@ -1465,8 +1680,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" dependencies = [ - "crypto-mac", - "digest", + "crypto-mac 0.7.0", + "digest 0.8.1", ] [[package]] @@ -1475,8 +1690,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" dependencies = [ - "digest", - "generic-array", + "digest 0.8.1", + "generic-array 0.12.3", "hmac", ] @@ -1491,6 +1706,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" +dependencies = [ + "bytes 0.5.6", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.1.0" @@ -1499,10 +1725,20 @@ checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" dependencies = [ "bytes 0.4.12", "futures 0.1.29", - "http", + "http 0.1.21", "tokio-buf", ] +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes 0.5.6", + "http 0.2.1", +] + [[package]] name = "httparse" version = "1.3.4" @@ -1527,9 +1763,9 @@ dependencies = [ "bytes 0.4.12", "futures 0.1.29", "futures-cpupool", - "h2", - "http", - "http-body", + "h2 0.1.26", + "http 0.1.21", + "http-body 0.1.0", "httparse", "iovec", "itoa", @@ -1537,32 +1773,57 @@ dependencies = [ "net2", "rustc_version", "time", - "tokio", + "tokio 0.1.22", "tokio-buf", - "tokio-executor", + "tokio-executor 0.1.10", "tokio-io", "tokio-reactor", "tokio-tcp", "tokio-threadpool", "tokio-timer", - "want", + "want 0.2.0", +] + +[[package]] +name = "hyper" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e68a8dd9716185d9e64ea473ea6ef63529252e3e27623295a0378a19665d5eb" +dependencies = [ + "bytes 0.5.6", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.2.6", + "http 0.2.1", + "http-body 0.3.1", + "httparse", + "itoa", + "pin-project", + "socket2", + "time", + "tokio 0.2.22", + "tower-service", + "tracing", + "want 0.3.0", ] [[package]] name = "hyper-rustls" -version = "0.17.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719d85c7df4a7f309a77d145340a063ea929dcb2e025bae46a80345cffec2952" +checksum = "ac965ea399ec3a25ac7d13b8affd4b8f39325cca00858ddf5eb29b79e6b14b08" dependencies = [ - "bytes 0.4.12", + "bytes 0.5.6", "ct-logs", - "futures 0.1.29", - "hyper", + "futures-util", + "hyper 0.13.7", + "log", "rustls", - "tokio-io", + "rustls-native-certs", + "tokio 0.2.22", "tokio-rustls", "webpki", - "webpki-roots 0.17.0", ] [[package]] @@ -1620,18 +1881,19 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] [[package]] name = "indexmap" -version = "1.3.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" +checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7" dependencies = [ "autocfg 1.0.0", + "hashbrown 0.8.1", ] [[package]] @@ -1641,10 +1903,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" [[package]] -name = "interleaved-ordered" -version = "0.1.1" +name = "intervalier" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "141340095b15ed7491bd3d4ced9d20cebfb826174b6bb03386381f62b01e3d77" +checksum = "64fa110ec7b8f493f416eed552740d10e7030ad5f63b2308f82c9608ec2df275" +dependencies = [ + "futures 0.3.4", + "futures-timer 2.0.2", +] [[package]] name = "iovec" @@ -1655,6 +1921,12 @@ dependencies = [ "libc", ] +[[package]] +name = "ip_network" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee15951c035f79eddbef745611ec962f63f4558f1dadf98ab723cc603487c6f" + [[package]] name = "ipnet" version = "2.3.0" @@ -1681,9 +1953,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "jobserver" @@ -1696,105 +1968,126 @@ dependencies = [ [[package]] name = "joystream-node" -version = "2.1.6" +version = "3.0.0" dependencies = [ - "ctrlc", - "derive_more 0.14.1", - "exit-future", - "futures 0.1.29", - "hex 0.4.2", - "hex-literal 0.2.1", + "frame-benchmarking", + "frame-benchmarking-cli", + "frame-system", + "futures 0.3.4", "joystream-node-runtime", - "jsonrpc-core 13.2.0", - "libp2p", - "log", + "jsonrpc-core", + "node-inspect", + "pallet-grandpa", + "pallet-im-online", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc", "parity-scale-codec", - "parking_lot 0.9.0", - "rand 0.7.3", + "sc-authority-discovery", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-consensus", + "sc-consensus-babe", + "sc-consensus-babe-rpc", + "sc-consensus-epochs", + "sc-executor", + "sc-finality-grandpa", + "sc-finality-grandpa-rpc", + "sc-keystore", + "sc-network", + "sc-rpc-api", + "sc-service", + "sc-transaction-pool", "serde", "serde_json", - "sr-io", - "sr-primitives", - "srml-im-online", + "sp-api", + "sp-authority-discovery", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-finality-grandpa", + "sp-finality-tracker", + "sp-inherents", + "sp-keyring", + "sp-runtime", + "sp-timestamp", + "sp-transaction-pool", "structopt", - "substrate-authority-discovery", - "substrate-basic-authorship", - "substrate-cli", - "substrate-client", - "substrate-client-db", - "substrate-consensus-babe", - "substrate-consensus-babe-primitives", - "substrate-executor", - "substrate-finality-grandpa", - "substrate-finality-grandpa-primitives", - "substrate-inherents", - "substrate-network", - "substrate-offchain", - "substrate-primitives", - "substrate-rpc", - "substrate-service", - "substrate-telemetry", - "substrate-transaction-pool", - "tokio", - "vergen", + "substrate-browser-utils", + "substrate-build-script-utils", + "substrate-frame-rpc-system", + "tempfile", + "wasm-bindgen", + "wasm-bindgen-futures", ] [[package]] name = "joystream-node-runtime" -version = "6.12.2" +version = "7.0.0" dependencies = [ + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-collective", + "pallet-common", + "pallet-content-working-group", + "pallet-finality-tracker", + "pallet-forum", + "pallet-governance", + "pallet-grandpa", + "pallet-hiring", + "pallet-im-online", + "pallet-membership", + "pallet-memo", + "pallet-offences", + "pallet-offences-benchmarking", + "pallet-proposals-codex", + "pallet-proposals-discussion", + "pallet-proposals-engine", + "pallet-randomness-collective-flip", + "pallet-recurring-reward", + "pallet-service-discovery", + "pallet-session", + "pallet-session-benchmarking", + "pallet-stake", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-storage", + "pallet-sudo", + "pallet-timestamp", + "pallet-token-mint", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-versioned-store", + "pallet-versioned-store-permissions", + "pallet-working-group", "parity-scale-codec", - "safe-mix", "serde", - "sr-io", - "sr-primitives", - "sr-staking-primitives", - "sr-std", - "sr-version", - "srml-authority-discovery", - "srml-authorship", - "srml-babe", - "srml-balances", - "srml-executive", - "srml-finality-tracker", - "srml-grandpa", - "srml-im-online", - "srml-indices", - "srml-offences", - "srml-randomness-collective-flip", - "srml-session", - "srml-staking", - "srml-staking-reward-curve", - "srml-sudo", - "srml-support", - "srml-system", - "srml-system-rpc-runtime-api", - "srml-timestamp", - "srml-transaction-payment", - "substrate-authority-discovery-primitives", - "substrate-client", - "substrate-common-module", - "substrate-consensus-babe-primitives", - "substrate-content-working-group-module", - "substrate-forum-module", - "substrate-governance-module", - "substrate-hiring-module", - "substrate-membership-module", - "substrate-memo-module", - "substrate-offchain-primitives", - "substrate-primitives", - "substrate-proposals-codex-module", - "substrate-proposals-discussion-module", - "substrate-proposals-engine-module", - "substrate-recurring-reward-module", - "substrate-roles-module", - "substrate-service-discovery-module", - "substrate-session", - "substrate-stake-module", - "substrate-storage-module", - "substrate-token-mint-module", - "substrate-versioned-store", - "substrate-versioned-store-permissions-module", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-babe", + "sp-core", + "sp-io", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-transaction-pool", + "sp-version", "substrate-wasm-builder-runner", ] @@ -1809,13 +2102,13 @@ dependencies = [ [[package]] name = "jsonrpc-client-transports" -version = "14.0.5" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9ae166c4d1f702d297cd76d4b55758ace80272ffc6dbb139fdc1bf810de40b" +checksum = "ecbdaacc17243168d9d1fa6b2bd7556a27e1e60a621d8a2a6e590ae2b145d158" dependencies = [ "failure", "futures 0.1.29", - "jsonrpc-core 14.0.5", + "jsonrpc-core", "jsonrpc-pubsub", "log", "serde", @@ -1825,22 +2118,9 @@ dependencies = [ [[package]] name = "jsonrpc-core" -version = "13.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d767c183a7e58618a609499d359ce3820700b3ebb4823a18c343b4a2a41a0d" -dependencies = [ - "futures 0.1.29", - "log", - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "jsonrpc-core" -version = "14.0.5" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3b688648f1ef5d5072229e2d672ecb92cbff7d1c79bcf3fd5898f3f3df0970" +checksum = "a0747307121ffb9703afd93afbd0fb4f854c38fb873f2c8b90e0e902f27c7b62" dependencies = [ "futures 0.1.29", "log", @@ -1851,78 +2131,93 @@ dependencies = [ [[package]] name = "jsonrpc-core-client" -version = "14.0.5" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080dc110be17701097df238fad3c816d4a478a1899dfbcf8ec8957dd40ec7304" +checksum = "34221123bc79b66279a3fde2d3363553835b43092d629b34f2e760c44dc94713" dependencies = [ "jsonrpc-client-transports", ] [[package]] name = "jsonrpc-derive" -version = "14.0.5" +version = "14.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8609af8f63b626e8e211f52441fcdb6ec54f1a446606b10d5c89ae9bf8a20058" +checksum = "0fadf6945e227246825a583514534d864554e9f23d80b3c77d034b10983db5ef" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] [[package]] name = "jsonrpc-http-server" -version = "14.0.6" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816d63997ea45d3634608edbef83ddb35e661f7c0b27b5b72f237e321f0e9807" +checksum = "0da906d682799df05754480dac1b9e70ec92e12c19ebafd2662a5ea1c9fd6522" dependencies = [ - "hyper", - "jsonrpc-core 14.0.5", + "hyper 0.12.35", + "jsonrpc-core", "jsonrpc-server-utils", "log", "net2", - "parking_lot 0.10.0", + "parking_lot 0.10.2", "unicase", ] +[[package]] +name = "jsonrpc-ipc-server" +version = "14.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dedccd693325d833963b549e959137f30a7a0ea650cde92feda81dc0c1393cb5" +dependencies = [ + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "parity-tokio-ipc", + "parking_lot 0.10.2", + "tokio-service", +] + [[package]] name = "jsonrpc-pubsub" -version = "14.0.6" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b31c9b90731276fdd24d896f31bb10aecf2e5151733364ae81123186643d939" +checksum = "2d44f5602a11d657946aac09357956d2841299ed422035edf140c552cb057986" dependencies = [ - "jsonrpc-core 14.0.5", + "jsonrpc-core", "log", - "parking_lot 0.10.0", + "parking_lot 0.10.2", + "rand 0.7.3", "serde", ] [[package]] name = "jsonrpc-server-utils" -version = "14.0.5" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b7635e618a0edbbe0d2a2bbbc69874277c49383fcf6c3c0414491cfb517d22" +checksum = "56cbfb462e7f902e21121d9f0d1c2b77b2c5b642e1a4e8f4ebfa2e15b94402bb" dependencies = [ "bytes 0.4.12", "globset", - "jsonrpc-core 14.0.5", + "jsonrpc-core", "lazy_static", "log", - "tokio", + "tokio 0.1.22", "tokio-codec", "unicase", ] [[package]] name = "jsonrpc-ws-server" -version = "14.0.6" +version = "14.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94e5773b2ae66e0e02c80775ce6bbba6f15d5bb47c14ec36a36fcf94f8df851" +checksum = "903d3109fe7c4acb932b567e1e607e0f524ed04741b09fb0e61841bc40a022fc" dependencies = [ - "jsonrpc-core 14.0.5", + "jsonrpc-core", "jsonrpc-server-utils", "log", - "parking_lot 0.10.0", + "parking_lot 0.10.2", "slab", "ws", ] @@ -1943,38 +2238,69 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "kvdb" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e763b2a9b500ba47948061d1e8bc3b5f03a8a1f067dbcf822a4d2c84d2b54a3a" dependencies = [ - "elastic-array", - "parity-bytes", + "parity-util-mem", + "smallvec 1.4.1", ] [[package]] name = "kvdb-memorydb" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73027d5e228de6f503b5b7335d530404fc26230a6ae3e09b33ec6e45408509a4" dependencies = [ "kvdb", - "parking_lot 0.6.4", + "parity-util-mem", + "parking_lot 0.10.2", ] [[package]] name = "kvdb-rocksdb" -version = "0.1.4" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84384eca250c7ff67877eda5336f28a86586aaee24acb945643590671f6bfce1" dependencies = [ - "elastic-array", "fs-swap", - "interleaved-ordered", "kvdb", "log", "num_cpus", - "parking_lot 0.6.4", + "owning_ref", + "parity-util-mem", + "parking_lot 0.10.2", "regex", "rocksdb", + "smallvec 1.4.1", +] + +[[package]] +name = "kvdb-web" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c7f36acb1841d4c701d30ae1f2cfd242e805991443f75f6935479ed3de64903" +dependencies = [ + "futures 0.3.4", + "js-sys", + "kvdb", + "kvdb-memorydb", + "log", + "parity-util-mem", + "send_wrapper 0.3.0", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -1991,9 +2317,27 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" [[package]] name = "libc" -version = "0.2.68" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" + +[[package]] +name = "libflate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9bac9023e1db29c084f9f8cd9d3852e5e8fddf98fb47c4964a0ea4663d95949" +dependencies = [ + "adler32", + "crc32fast", + "libflate_lz77", + "rle-decode-fast", +] + +[[package]] +name = "libflate_lz77" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b" [[package]] name = "libloading" @@ -2005,401 +2349,299 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + [[package]] name = "libp2p" -version = "0.13.2" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4674c6738fdd8b1cf7104dd046abcef78dc932fe25f8eb40f3a8e71341717d" +checksum = "057eba5432d3e740e313c6e13c9153d0cb76b4f71bfc2e5242ae5bdb7d41af67" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", + "bytes 0.5.6", + "futures 0.3.4", "lazy_static", "libp2p-core", "libp2p-core-derive", - "libp2p-deflate", "libp2p-dns", - "libp2p-floodsub", "libp2p-identify", "libp2p-kad", "libp2p-mdns", "libp2p-mplex", "libp2p-noise", "libp2p-ping", - "libp2p-plaintext", - "libp2p-secio", "libp2p-swarm", "libp2p-tcp", - "libp2p-uds", "libp2p-wasm-ext", "libp2p-websocket", "libp2p-yamux", - "parity-multiaddr 0.6.0", - "parity-multihash 0.2.3", - "parking_lot 0.9.0", - "smallvec 0.6.13", - "tokio-codec", - "tokio-executor", - "tokio-io", + "multihash", + "parity-multiaddr 0.9.1", + "parking_lot 0.10.2", + "pin-project", + "smallvec 1.4.1", "wasm-timer", ] [[package]] name = "libp2p-core" -version = "0.13.2" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01efc769c392d0d8863a7160d266f9b9f794968554f87490c8af4aa34ccaa94f" +checksum = "3a0387b930c3d4c2533dc4893c1e0394185ddcc019846121b1b27491e45a2c08" dependencies = [ "asn1_der", - "bs58 0.3.0", - "bytes 0.4.12", - "ed25519-dalek 1.0.0-pre.3", - "failure", + "bs58", + "ed25519-dalek", + "either", "fnv", - "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.2", "lazy_static", "libsecp256k1", "log", + "multihash", "multistream-select", - "parity-multiaddr 0.6.0", - "parity-multihash 0.2.3", - "parking_lot 0.9.0", - "protobuf", - "quick-error", + "parity-multiaddr 0.9.1", + "parking_lot 0.10.2", + "pin-project", + "prost", + "prost-build", "rand 0.7.3", "ring", "rw-stream-sink", - "sha2", - "smallvec 0.6.13", - "tokio-executor", - "tokio-io", - "unsigned-varint 0.2.3", - "untrusted", + "sha2 0.8.1", + "smallvec 1.4.1", + "thiserror", + "unsigned-varint 0.4.0", "void", - "wasm-timer", - "zeroize 1.1.0", + "zeroize", ] [[package]] name = "libp2p-core-derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eeb2704ac14c60f31967e351ed928b848526a5fc6db4104520020665012826f" -dependencies = [ - "quote 0.6.13", - "syn 0.15.44", -] - -[[package]] -name = "libp2p-deflate" -version = "0.5.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b0bf5d37692ac90e2bffa436bec26c0b0def6c0cab7ea85ff67a353d58aaa" +checksum = "f09548626b737ed64080fde595e06ce1117795b8b9fc4d2629fa36561c583171" dependencies = [ - "flate2", - "futures 0.1.29", - "libp2p-core", - "tokio-io", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] name = "libp2p-dns" -version = "0.13.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3175fb0fc9016c95c8517a297bbdb5fb6bfbd5665bacd2eb23495d1cbdeb033" +checksum = "3cc186d9a941fd0207cf8f08ef225a735e2d7296258f570155e525f6ee732f87" dependencies = [ - "futures 0.1.29", + "futures 0.3.4", "libp2p-core", "log", - "tokio-dns-unofficial", -] - -[[package]] -name = "libp2p-floodsub" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b360bbaad2560d6b8a905bd63528273d933fe54475a44def47f31e23108b3683" -dependencies = [ - "bs58 0.3.0", - "bytes 0.4.12", - "cuckoofilter", - "fnv", - "futures 0.1.29", - "libp2p-core", - "libp2p-swarm", - "protobuf", - "rand 0.6.5", - "smallvec 0.6.13", - "tokio-io", ] [[package]] name = "libp2p-identify" -version = "0.13.2" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c087bcd044a6f67a994573a92a109487a902a31555e4e63bcc4ae144c45594fe" +checksum = "62f76075b170d908bae616f550ade410d9d27c013fa69042551dbfc757c7c094" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", + "futures 0.3.4", "libp2p-core", "libp2p-swarm", "log", - "parity-multiaddr 0.6.0", - "protobuf", - "smallvec 0.6.13", - "tokio-codec", - "tokio-io", - "unsigned-varint 0.2.3", + "prost", + "prost-build", + "smallvec 1.4.1", "wasm-timer", ] [[package]] name = "libp2p-kad" -version = "0.13.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaf76a5b33b6c0203e85d450ae1855cae6860dc82eb0174ac1fee8bf68f7af5" +checksum = "41d6c1d5100973527ae70d82687465b17049c1b717a7964de38b8e65000878ff" dependencies = [ "arrayvec 0.5.1", - "bytes 0.4.12", + "bytes 0.5.6", "either", "fnv", - "futures 0.1.29", + "futures 0.3.4", + "futures_codec 0.3.4", "libp2p-core", "libp2p-swarm", "log", - "parity-multiaddr 0.6.0", - "parity-multihash 0.2.3", - "protobuf", + "multihash", + "prost", + "prost-build", "rand 0.7.3", - "sha2", - "smallvec 0.6.13", - "tokio-codec", - "tokio-io", + "sha2 0.8.1", + "smallvec 1.4.1", "uint", - "unsigned-varint 0.2.3", + "unsigned-varint 0.3.3", "void", "wasm-timer", ] [[package]] name = "libp2p-mdns" -version = "0.13.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4c2e225a7dfc571c3ad77a0a5ecccc9537afe42d72289ac9f19768567cd677d" +checksum = "7f55b2d4b80986e5bf158270ab23268ec0e7f644ece5436fbaabc5155472f357" dependencies = [ + "async-std", "data-encoding", "dns-parser", - "futures 0.1.29", + "either", + "futures 0.3.4", + "lazy_static", "libp2p-core", "libp2p-swarm", "log", "net2", - "parity-multiaddr 0.6.0", - "rand 0.6.5", - "smallvec 0.6.13", - "tokio-io", - "tokio-reactor", - "tokio-udp", + "rand 0.7.3", + "smallvec 1.4.1", "void", "wasm-timer", ] [[package]] name = "libp2p-mplex" -version = "0.13.0" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2fe584816d993dc0f893396521a3c93191d78a6f28a892b150baa714a12c3e5" +checksum = "be7d913a4cd57de2013257ec73f07d77bfce390b370023e2d59083e5ca079864" dependencies = [ - "bytes 0.4.12", + "bytes 0.5.6", "fnv", - "futures 0.1.29", + "futures 0.3.4", + "futures_codec 0.4.0", "libp2p-core", "log", - "parking_lot 0.8.0", - "tokio-codec", - "tokio-io", - "unsigned-varint 0.2.3", + "parking_lot 0.10.2", + "unsigned-varint 0.4.0", ] [[package]] name = "libp2p-noise" -version = "0.11.1" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50494fcba7cdab08390d72b3cb9d2c72fcf178e6a0c1043855ab259d818b972" +checksum = "a03db664653369f46ee03fcec483a378c20195089bb43a26cb9fb0058009ac88" dependencies = [ - "bytes 0.4.12", - "curve25519-dalek 1.2.3", - "futures 0.1.29", + "curve25519-dalek", + "futures 0.3.4", "lazy_static", "libp2p-core", "log", - "protobuf", + "prost", + "prost-build", "rand 0.7.3", - "ring", + "sha2 0.8.1", "snow", - "tokio-io", + "static_assertions", "x25519-dalek", - "zeroize 1.1.0", + "zeroize", ] [[package]] name = "libp2p-ping" -version = "0.13.1" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b975ad345eb9bb29ddc64670664a50a8ab3e66e28357abb0f83cfc0a9ca2d78" +checksum = "b8dedd34e35a9728d52d59ef36a218e411359a353f9011b2574b86ee790978f6" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", + "futures 0.3.4", "libp2p-core", "libp2p-swarm", "log", - "parity-multiaddr 0.6.0", "rand 0.7.3", - "tokio-io", "void", "wasm-timer", ] -[[package]] -name = "libp2p-plaintext" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f07be6983e1c00e8f6a5676da54ed3a8cae7fb50f1fb6ea163414613ca656cc" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", - "libp2p-core", - "log", - "protobuf", - "rw-stream-sink", - "tokio-io", - "void", -] - -[[package]] -name = "libp2p-secio" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04aa6d67a5fb2b36241a1ba54037a13deb2594cf141e43b597ce379521d530a8" -dependencies = [ - "aes-ctr", - "bytes 0.4.12", - "ctr", - "futures 0.1.29", - "hmac", - "js-sys", - "lazy_static", - "libp2p-core", - "log", - "parity-send-wrapper", - "protobuf", - "rand 0.6.5", - "ring", - "rw-stream-sink", - "sha2", - "tokio-codec", - "tokio-io", - "twofish", - "untrusted", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "libp2p-swarm" -version = "0.3.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd55bc9f5f9eac2bb1ff24ca3c8a655810a566ac38c7a6ee1f30aced5a62905b" +checksum = "ce53ff4d127cf8b39adf84dbd381ca32d49bd85788cee08e6669da2495993930" dependencies = [ - "futures 0.1.29", + "futures 0.3.4", "libp2p-core", - "smallvec 0.6.13", - "tokio-io", + "log", + "rand 0.7.3", + "smallvec 1.4.1", "void", "wasm-timer", ] [[package]] name = "libp2p-tcp" -version = "0.13.0" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234a7093d05651ab5630db926a4a42ca8978a65bab8c27c2ce2b66b200c76989" +checksum = "9481500c5774c62e8c413e9535b3f33a0e3dbacf2da63b8d3056c686a9df4146" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", + "async-std", + "futures 0.3.4", + "futures-timer 3.0.2", "get_if_addrs", "ipnet", "libp2p-core", "log", - "tokio-io", - "tokio-tcp", - "tokio-timer", -] - -[[package]] -name = "libp2p-uds" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2fe0648967da3e56e4a55055c857c8c48326b66be0047d0e04c8ca60d34630" -dependencies = [ - "futures 0.1.29", - "libp2p-core", - "log", - "tokio-uds", + "socket2", ] [[package]] name = "libp2p-wasm-ext" -version = "0.6.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f7b8f2bd81fb356e81352d4513856bc21215ecf91502aa1f55b6449642a9acf" +checksum = "f59fdbb5706f2723ca108c088b1c7a37f735a8c328021f0508007162627e9885" dependencies = [ - "futures 0.1.29", + "futures 0.3.4", "js-sys", "libp2p-core", "parity-send-wrapper", - "tokio-io", "wasm-bindgen", "wasm-bindgen-futures", ] [[package]] name = "libp2p-websocket" -version = "0.13.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d74d4fc229ad7e8d1a973178786bdcd5dadbdd7b9822c4477c8687df6f82f66" +checksum = "085fbe4c05c4116c2164ab4d5a521eb6e00516c444f61b3ee9f68c7b1e53580b" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", + "async-tls", + "bytes 0.5.6", + "either", + "futures 0.3.4", "libp2p-core", "log", + "quicksink", + "rustls", "rw-stream-sink", "soketto", - "tokio-codec", - "tokio-io", - "tokio-rustls", "url 2.1.1", + "webpki", "webpki-roots 0.18.0", ] [[package]] name = "libp2p-yamux" -version = "0.13.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1913eb7dd6eb5515957b6f1770296f6921968db87bc9b985f0e974b6657e1003" +checksum = "8da33e7b5f49c75c6a8afb0b8d1e229f5fa48be9f39bd14cdbc21459a02ac6fc" dependencies = [ - "futures 0.1.29", + "futures 0.3.4", "libp2p-core", - "log", - "tokio-io", + "parking_lot 0.10.2", + "thiserror", "yamux", ] [[package]] name = "librocksdb-sys" -version = "5.18.3" +version = "6.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19778314deaa7048f2ea7d07b8aa12e1c227acebe975a37eeab6d2f8c74e41b" +checksum = "883213ae3d09bfc3d104aefe94b25ebb183b6f4d3a515b23b14817e1f4854005" dependencies = [ "bindgen", "cc", @@ -2415,10 +2657,10 @@ checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" dependencies = [ "arrayref", "crunchy", - "digest", + "digest 0.8.1", "hmac-drbg", "rand 0.7.3", - "sha2", + "sha2 0.8.1", "subtle 2.2.2", "typenum", ] @@ -2437,45 +2679,37 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "linked_hash_set" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7c91c4c7bbeb4f2f7c4e5be11e6a05bd6830bc37249c47ce1ad86ad453ff9c" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" dependencies = [ "linked-hash-map", ] [[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -dependencies = [ - "owning_ref", - "scopeguard 0.3.3", -] - -[[package]] -name = "lock_api" -version = "0.2.0" +name = "linregress" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" +checksum = "9290cf6f928576eeb9c096c6fad9d8d452a0a1a70a2bbffa6e36064eedc0aac9" dependencies = [ - "scopeguard 1.1.0", + "failure", + "nalgebra", + "statrs", ] [[package]] name = "lock_api" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ - "scopeguard 1.1.0", + "scopeguard", ] [[package]] @@ -2488,23 +2722,12 @@ dependencies = [ ] [[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "malloc_size_of_derive" -version = "0.1.1" +name = "lru" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37c5d4cd9473c5f4c9c111f033f15d4df9bd378fdf615944e360a4f55a05f0b" +checksum = "0609345ddee5badacf857d4f547e0e5a2e987db77085c24cd887f73573a04237" dependencies = [ - "proc-macro2 1.0.10", - "syn 1.0.17", - "synstructure", + "hashbrown 0.6.3", ] [[package]] @@ -2513,6 +2736,15 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "matrixmultiply" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f7ec66360130972f34830bfad9ef05c6610a43938a467bcc9ab9369ab3478f" +dependencies = [ + "rawpointer", +] + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -2526,24 +2758,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] -name = "memoffset" -version = "0.5.4" +name = "memmap" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ - "autocfg 1.0.0", + "libc", + "winapi 0.3.8", ] [[package]] -name = "memory-db" -version = "0.15.3" +name = "memoffset" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dabfe0a8c69954ae3bcfc5fc14260a85fb80e1bf9f86a155f668d10a67e93dd" +checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" dependencies = [ - "ahash", - "hash-db", - "hashbrown 0.6.3", - "parity-util-mem 0.2.1", + "autocfg 1.0.0", ] [[package]] @@ -2555,7 +2785,7 @@ dependencies = [ "ahash", "hash-db", "hashbrown 0.6.3", - "parity-util-mem 0.6.1", + "parity-util-mem", ] [[package]] @@ -2564,44 +2794,32 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -[[package]] -name = "merlin" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0942b357c1b4d0dc43ba724674ec89c3218e6ca2b3e8269e7cb53bcecd2f6e" -dependencies = [ - "byteorder 1.3.4", - "keccak", - "rand_core 0.4.2", - "zeroize 1.1.0", -] - [[package]] name = "merlin" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6feca46f4fa3443a01769d768727f10c10a20fdb65e52dc16a81f0c8269bb78" dependencies = [ - "byteorder 1.3.4", + "byteorder", "keccak", "rand_core 0.5.1", - "zeroize 1.1.0", + "zeroize", ] [[package]] name = "miniz_oxide" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" +checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" dependencies = [ - "adler32", + "adler", ] [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if", "fuchsia-zircon", @@ -2610,7 +2828,7 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow", + "miow 0.2.1", "net2", "slab", "winapi 0.2.8", @@ -2628,6 +2846,18 @@ dependencies = [ "slab", ] +[[package]] +name = "mio-named-pipes" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" +dependencies = [ + "log", + "mio", + "miow 0.3.5", + "winapi 0.3.8", +] + [[package]] name = "mio-uds" version = "0.6.7" @@ -2651,11 +2881,21 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi 0.3.8", +] + [[package]] name = "mockall" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95a7e7cfbce0e99ebbf5356a085d3b5e320a7ef300f77cd50a7148aa362e7c2" +checksum = "256489d4d106cd2bc9e98ed0337402db0044de0621745d5d9eb70a14295ff77b" dependencies = [ "cfg-if", "downcast", @@ -2668,34 +2908,66 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a615a1ad92048ad5d9633251edb7492b8abc057d7a679a9898476aef173935" +checksum = "826e14e8643cb12103b56efb963e5f9640b69b0f7bdcc460002092df4b0e959f" dependencies = [ "cfg-if", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] +[[package]] +name = "multihash" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75db05d738947aa5389863aadafbcf2e509d7ba099dc2ddcdf4fc66bf7a9e03" +dependencies = [ + "blake2b_simd", + "blake2s_simd", + "digest 0.8.1", + "sha-1", + "sha2 0.8.1", + "sha3", + "unsigned-varint 0.3.3", +] + [[package]] name = "multimap" -version = "0.4.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04b9f127583ed176e163fb9ec6f3e793b87e21deedd5734a69386a18a0151" +checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" [[package]] name = "multistream-select" -version = "0.6.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3ef54aab1b2e37e911bcb99e376dbe4c1e0710afcdb8428608e4f993b39c47" +checksum = "c9157e87afbc2ef0d84cc0345423d715f445edde00141c93721c162de35a05e5" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", + "bytes 0.5.6", + "futures 0.3.4", "log", - "smallvec 0.6.13", - "tokio-io", - "unsigned-varint 0.2.3", + "pin-project", + "smallvec 1.4.1", + "unsigned-varint 0.4.0", +] + +[[package]] +name = "nalgebra" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaa9fddbc34c8c35dd2108515587b8ce0cab396f17977b8c738568e4edb521a2" +dependencies = [ + "alga", + "approx", + "generic-array 0.12.3", + "matrixmultiply", + "num-complex", + "num-rational", + "num-traits 0.2.11", + "rand 0.6.5", + "typenum", ] [[package]] @@ -2709,15 +2981,29 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", "winapi 0.3.8", ] +[[package]] +name = "netstat2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29449d242064c48d3057a194b049a2bdcccadda16faa18a91468677b44e8d422" +dependencies = [ + "bitflags", + "byteorder", + "enum-primitive-derive", + "libc", + "num-traits 0.2.11", + "thiserror", +] + [[package]] name = "nix" version = "0.17.0" @@ -2731,6 +3017,23 @@ dependencies = [ "void", ] +[[package]] +name = "node-inspect" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "derive_more", + "log", + "parity-scale-codec", + "sc-cli", + "sc-client-api", + "sc-service", + "sp-blockchain", + "sp-core", + "sp-runtime", + "structopt", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -2739,18 +3042,18 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "nohash-hasher" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721a2bf1c26159ebf17e0a980bc4ce61f4b2fec5ec3b42d42fddd7a84a9e538f" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nom" -version = "4.2.3" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "memchr", - "version_check 0.1.5", + "version_check", ] [[package]] @@ -2759,6 +3062,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "ntapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -2767,7 +3079,17 @@ checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ "autocfg 1.0.0", "num-integer", - "num-traits", + "num-traits 0.2.11", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg 1.0.0", + "num-traits 0.2.11", ] [[package]] @@ -2777,7 +3099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ "autocfg 1.0.0", - "num-traits", + "num-traits 0.2.11", ] [[package]] @@ -2789,7 +3111,16 @@ dependencies = [ "autocfg 1.0.0", "num-bigint", "num-integer", - "num-traits", + "num-traits 0.2.11", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.11", ] [[package]] @@ -2799,97 +3130,699 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ "autocfg 1.0.0", + "libm", ] [[package]] name = "num_cpus" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ "hermit-abi", "libc", ] [[package]] -name = "num_enum" -version = "0.4.3" +name = "once_cell" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" dependencies = [ - "derivative", - "num_enum_derive", + "parking_lot 0.10.2", ] [[package]] -name = "num_enum_derive" -version = "0.4.3" +name = "opaque-debug" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" -dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", -] +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] -name = "ole32-sys" -version = "0.2.0" +name = "opaque-debug" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "owning_ref" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "stable_deref_trait", +] + +[[package]] +name = "pallet-authority-discovery" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "serde", + "sp-application-crypto", + "sp-authority-discovery", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-authorship" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-authorship", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-babe" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-application-crypto", + "sp-consensus-babe", + "sp-consensus-vrf", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "pallet-balances" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-collective" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-common" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-runtime", +] + +[[package]] +name = "pallet-content-working-group" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-hiring", + "pallet-membership", + "pallet-recurring-reward", + "pallet-stake", + "pallet-timestamp", + "pallet-token-mint", + "pallet-versioned-store", + "pallet-versioned-store-permissions", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-finality-tracker" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-finality-tracker", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-forum" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-common", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-governance" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-membership", + "pallet-recurring-reward", + "pallet-timestamp", + "pallet-token-mint", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-grandpa" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "pallet-finality-tracker", + "pallet-session", + "parity-scale-codec", + "serde", + "sp-application-crypto", + "sp-core", + "sp-finality-grandpa", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-hiring" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "mockall", + "pallet-balances", + "pallet-stake", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-im-online" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "serde", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-membership" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-memo" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-common", + "parity-scale-codec", + "sp-arithmetic", + "sp-std", +] + +[[package]] +name = "pallet-offences" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "serde", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-offences-benchmarking" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-babe", + "pallet-balances", + "pallet-grandpa", + "pallet-im-online", + "pallet-offences", + "pallet-session", + "pallet-staking", + "parity-scale-codec", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-proposals-codex" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-content-working-group", + "pallet-governance", + "pallet-hiring", + "pallet-membership", + "pallet-proposals-discussion", + "pallet-proposals-engine", + "pallet-recurring-reward", + "pallet-stake", + "pallet-staking", + "pallet-staking-reward-curve", + "pallet-timestamp", + "pallet-token-mint", + "pallet-versioned-store", + "pallet-versioned-store-permissions", + "pallet-working-group", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", +] + +[[package]] +name = "pallet-proposals-discussion" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-membership", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-proposals-engine" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "mockall", + "pallet-balances", + "pallet-common", + "pallet-membership", + "pallet-stake", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-randomness-collective-flip" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "safe-mix", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-recurring-reward" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-token-mint", + "parity-scale-codec", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", +] + +[[package]] +name = "pallet-service-discovery" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-hiring", + "pallet-membership", + "pallet-recurring-reward", + "pallet-stake", + "pallet-timestamp", + "pallet-token-mint", + "pallet-working-group", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-session" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-timestamp", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std", + "sp-trie", +] + +[[package]] +name = "pallet-session-benchmarking" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-session", + "pallet-staking", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-stake" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-timestamp", + "parity-scale-codec", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-staking" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "rand_chacha 0.2.2", + "serde", + "sp-application-crypto", + "sp-io", + "sp-npos-elections", + "sp-runtime", + "sp-staking", + "sp-std", + "static_assertions", +] + +[[package]] +name = "pallet-staking-reward-curve" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", +] + +[[package]] +name = "pallet-storage" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-hiring", + "pallet-membership", + "pallet-recurring-reward", + "pallet-stake", + "pallet-timestamp", + "pallet-token-mint", + "pallet-working-group", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-sudo" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-io", + "sp-runtime", + "sp-std", ] [[package]] -name = "once_cell" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37" +name = "pallet-timestamp" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parking_lot 0.7.1", + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "serde", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-std", + "sp-timestamp", ] [[package]] -name = "once_cell" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d584f08c2d717d5c23a6414fc2822b71c651560713e54fa7eace675f758a355e" +name = "pallet-token-mint" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", +] [[package]] -name = "once_cell" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" +name = "pallet-transaction-payment" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parking_lot 0.10.0", + "frame-support", + "frame-system", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "serde", + "smallvec 1.4.1", + "sp-runtime", + "sp-std", ] [[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +name = "pallet-transaction-payment-rpc" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "pallet-transaction-payment-rpc-runtime-api", + "parity-scale-codec", + "serde", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", +] [[package]] -name = "owning_ref" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +name = "pallet-transaction-payment-rpc-runtime-api" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "stable_deref_trait", + "frame-support", + "parity-scale-codec", + "serde", + "sp-api", + "sp-runtime", + "sp-std", ] [[package]] -name = "pallet-content-directory" +name = "pallet-versioned-store" version = "3.0.0" dependencies = [ "frame-support", "frame-system", + "pallet-timestamp", "parity-scale-codec", "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-versioned-store-permissions" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-timestamp", + "pallet-versioned-store", + "parity-scale-codec", "sp-arithmetic", "sp-core", "sp-io", @@ -2898,59 +3831,76 @@ dependencies = [ ] [[package]] -name = "parity-bytes" -version = "0.1.0" -source = "git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d#b0317f649ab2c665b7987b8475878fc4d2e1f81d" +name = "pallet-working-group" +version = "3.0.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "pallet-common", + "pallet-hiring", + "pallet-membership", + "pallet-recurring-reward", + "pallet-stake", + "pallet-timestamp", + "pallet-token-mint", + "parity-scale-codec", + "serde", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] [[package]] -name = "parity-multiaddr" -version = "0.5.0" +name = "parity-db" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045b3c7af871285146300da35b1932bb6e4639b66c7c98e85d06a32cbc4e8fa7" +checksum = "00d595e372d119261593297debbe4193811a4dc811d2a1ccbb8caaa6666ad7ab" dependencies = [ - "arrayref", - "bs58 0.2.5", - "byteorder 1.3.4", - "bytes 0.4.12", - "data-encoding", - "parity-multihash 0.1.3", - "percent-encoding 1.0.1", - "serde", - "unsigned-varint 0.2.3", - "url 1.7.2", + "blake2-rfc", + "crc32fast", + "libc", + "log", + "memmap", + "parking_lot 0.10.2", ] [[package]] name = "parity-multiaddr" -version = "0.6.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82afcb7461eae5d122543d8be1c57d306ed89af2d6ff7f8b0f5a3cc8f7e511bc" +checksum = "f77055f9e81921a8cc7bebeb6cded3d128931d51f1e3dd6251f0770a6d431477" dependencies = [ "arrayref", - "bs58 0.3.0", - "byteorder 1.3.4", - "bytes 0.4.12", + "bs58", + "byteorder", "data-encoding", - "parity-multihash 0.2.3", + "parity-multihash", "percent-encoding 2.1.0", "serde", - "unsigned-varint 0.2.3", + "static_assertions", + "unsigned-varint 0.3.3", "url 2.1.1", ] [[package]] -name = "parity-multihash" -version = "0.1.3" +name = "parity-multiaddr" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3a17dc27848fd99e4f87eb0f8c9baba6ede0a6d555400c850ca45254ef4ce3" +checksum = "cc20af3143a62c16e7c9e92ea5c6ae49f7d271d97d4d8fe73afc28f0514a3d0f" dependencies = [ - "blake2", - "bytes 0.4.12", - "rand 0.6.5", - "sha-1", - "sha2", - "sha3", - "unsigned-varint 0.2.3", + "arrayref", + "bs58", + "byteorder", + "data-encoding", + "multihash", + "percent-encoding 2.1.0", + "serde", + "static_assertions", + "unsigned-varint 0.4.0", + "url 2.1.1", ] [[package]] @@ -2959,20 +3909,20 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1cd2ba02391b81367bec529fb209019d718684fdc8ad6a712c2b536e46f775" dependencies = [ - "blake2", - "bytes 0.5.4", + "blake2 0.8.1", + "bytes 0.5.6", "rand 0.7.3", "sha-1", - "sha2", + "sha2 0.8.1", "sha3", - "unsigned-varint 0.3.2", + "unsigned-varint 0.3.3", ] [[package]] name = "parity-scale-codec" -version = "1.3.4" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d38aeaffc032ec69faa476b3caaca8d4dd7f3f798137ff30359e5c7869ceb6" +checksum = "a74f02beb35d47e0706155c9eac554b50c671e0d868fe8296bcdf44a9a4847bf" dependencies = [ "arrayvec 0.5.1", "bitvec", @@ -2983,13 +3933,13 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd20ff7e0399b274a5f5bb37b712fccb5b3a64b9128200d1c3cc40fe709cb073" +checksum = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] @@ -3000,13 +3950,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" [[package]] -name = "parity-util-mem" -version = "0.2.1" +name = "parity-tokio-ipc" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "570093f39f786beea92dcc09e45d8aae7841516ac19a50431953ac82a0e8f85c" +checksum = "1e57fea504fea33f9fbb5f49f378359030e7e026a6ab849bb9e8f0787376f1bf" dependencies = [ - "cfg-if", - "malloc_size_of_derive", + "bytes 0.4.12", + "futures 0.1.29", + "libc", + "log", + "mio-named-pipes", + "miow 0.3.5", + "rand 0.7.3", + "tokio 0.1.22", + "tokio-named-pipes", + "tokio-uds", "winapi 0.3.8", ] @@ -3019,8 +3977,9 @@ dependencies = [ "cfg-if", "impl-trait-for-tuples", "parity-util-mem-derive", - "parking_lot 0.10.0", - "primitive-types 0.7.1", + "parking_lot 0.10.2", + "primitive-types", + "smallvec 1.4.1", "winapi 0.3.8", ] @@ -3030,115 +3989,36 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "proc-macro2 1.0.10", + "proc-macro2", "syn 1.0.17", "synstructure", ] -[[package]] -name = "parity-wasm" -version = "0.40.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e39faaa292a687ea15120b1ac31899b13586446521df6c149e46f1584671e0f" - [[package]] name = "parity-wasm" version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" -[[package]] -name = "parking_lot" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" -dependencies = [ - "lock_api 0.1.5", - "parking_lot_core 0.3.1", -] - -[[package]] -name = "parking_lot" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -dependencies = [ - "lock_api 0.1.5", - "parking_lot_core 0.4.0", -] - -[[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" -dependencies = [ - "lock_api 0.2.0", - "parking_lot_core 0.5.0", - "rustc_version", -] - [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api 0.3.3", + "lock_api", "parking_lot_core 0.6.2", "rustc_version", ] [[package]] name = "parking_lot" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -dependencies = [ - "lock_api 0.3.3", - "parking_lot_core 0.7.0", -] - -[[package]] -name = "parking_lot_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" -dependencies = [ - "libc", - "rand 0.5.6", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -dependencies = [ - "libc", - "rand 0.6.5", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.5.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" dependencies = [ - "cfg-if", - "cloudabi", - "libc", - "rand 0.6.5", - "redox_syscall", - "rustc_version", - "smallvec 0.6.13", - "winapi 0.3.8", + "lock_api", + "parking_lot_core 0.7.2", ] [[package]] @@ -3158,9 +4038,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ "cfg-if", "cloudabi", @@ -3177,7 +4057,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab4fb1930692d1b6a9cfabdde3d06ea0a7d186518e2f4d67660d8970e2fa647a" dependencies = [ "paste-impl", - "proc-macro-hack 0.5.15", + "proc-macro-hack", ] [[package]] @@ -3186,9 +4066,9 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a62486e111e571b1e93b710b61e8f493c0013be39629b714cb166bdb06aa5a8a" dependencies = [ - "proc-macro-hack 0.5.15", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro-hack", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] @@ -3198,8 +4078,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" dependencies = [ - "byteorder 1.3.4", - "crypto-mac", + "byteorder", + "crypto-mac 0.7.0", ] [[package]] @@ -3228,13 +4108,40 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "petgraph" -version = "0.4.13" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" dependencies = [ "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" +dependencies = [ + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] +[[package]] +name = "pin-project-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" + [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -3243,9 +4150,34 @@ checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" [[package]] name = "pkg-config" -version = "0.3.17" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" + +[[package]] +name = "platforms" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb3b2b1033b8a60b4da6ee470325f887758c95d5320f52f9ce0df055a55940e" + +[[package]] +name = "poly1305" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b42192ab143ed7619bf888a7f9c6733a9a2153b218e2cd557cfdb52fbf9bb1" +dependencies = [ + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +checksum = "d9a50142b55ab3ed0e9f68dfb3709f1d90d29da24e91033f28b96330643107dc" +dependencies = [ + "cfg-if", + "universal-hash", +] [[package]] name = "ppv-lite86" @@ -3278,20 +4210,8 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" dependencies = [ - "predicates-core", - "treeline", -] - -[[package]] -name = "primitive-types" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" -dependencies = [ - "fixed-hash 0.5.2", - "impl-codec", - "impl-serde 0.3.0", - "uint", + "predicates-core", + "treeline", ] [[package]] @@ -3300,7 +4220,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dedac218327b6b55fff5ef05f63ce5127024e1a36342836da7e92cbfac4531" dependencies = [ - "fixed-hash 0.6.1", + "fixed-hash", "impl-codec", "impl-serde 0.3.0", "uint", @@ -3317,22 +4237,28 @@ dependencies = [ [[package]] name = "proc-macro-error" -version = "0.2.6" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" +checksum = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro-error-attr", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", + "version_check", ] [[package]] -name = "proc-macro-hack" -version = "0.4.2" +name = "proc-macro-error-attr" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b" +checksum = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50" dependencies = [ - "proc-macro-hack-impl", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", + "syn-mid", + "version_check", ] [[package]] @@ -3341,12 +4267,6 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842" - [[package]] name = "proc-macro-nested" version = "0.1.4" @@ -3355,40 +4275,59 @@ checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" [[package]] name = "proc-macro2" -version = "0.4.30" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" dependencies = [ - "unicode-xid 0.1.0", + "unicode-xid 0.2.0", ] [[package]] -name = "proc-macro2" -version = "1.0.10" +name = "procfs" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "c434e93ef69c216e68e4f417c927b4f31502c3560b72cfdb6827e2321c5c6b3e" dependencies = [ - "unicode-xid 0.2.0", + "bitflags", + "byteorder", + "chrono", + "hex", + "lazy_static", + "libc", + "libflate", +] + +[[package]] +name = "prometheus" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0575e258dab62268e7236d7307caa38848acbda7ec7ab87bd9093791e999d20" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "protobuf", + "spin", + "thiserror", ] [[package]] name = "prost" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d14b1c185652833d24aaad41c5832b0be5616a590227c1fbff57c616754b23" +checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212" dependencies = [ - "byteorder 1.3.4", - "bytes 0.4.12", + "bytes 0.5.6", "prost-derive", ] [[package]] name = "prost-build" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb788126ea840817128183f8f603dce02cb7aea25c2a0b764359d8e20010702e" +checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26" dependencies = [ - "bytes 0.4.12", + "bytes 0.5.6", "heck", "itertools 0.8.2", "log", @@ -3402,32 +4341,32 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e7dc378b94ac374644181a2247cebf59a6ec1c88b49ac77f3a94b86b79d0e11" +checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72" dependencies = [ - "failure", + "anyhow", "itertools 0.8.2", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] name = "prost-types" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de482a366941c8d56d19b650fac09ca08508f2a696119ee7513ad590c8bac6f" +checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa" dependencies = [ - "bytes 0.4.12", + "bytes 0.5.6", "prost", ] [[package]] name = "protobuf" -version = "2.8.1" +version = "2.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40361836defdd5871ff7e84096c6f6444af7fc157f8ef1789f54f147687caa20" +checksum = "d883f78645c21b7281d21305181aa1f4dd9e9363e7cf2566c93121552cff003e" [[package]] name = "quick-error" @@ -3436,21 +4375,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] -name = "quote" -version = "0.6.13" +name = "quicksink" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" dependencies = [ - "proc-macro2 0.4.30", + "futures-core", + "futures-sink", + "pin-project-lite", ] [[package]] name = "quote" -version = "1.0.3" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + +[[package]] +name = "quote" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.10", + "proc-macro2", ] [[package]] @@ -3621,6 +4568,7 @@ dependencies = [ "libc", "rand_core 0.4.2", "rdrand", + "wasm-bindgen", "winapi 0.3.8", ] @@ -3652,12 +4600,19 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080" dependencies = [ + "autocfg 1.0.0", "crossbeam-deque", "either", "rayon-core", @@ -3665,13 +4620,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" dependencies = [ "crossbeam-deque", "crossbeam-queue", - "crossbeam-utils 0.7.2", + "crossbeam-utils", "lazy_static", "num_cpus", ] @@ -3691,6 +4646,17 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + [[package]] name = "ref-cast" version = "1.0.2" @@ -3706,8 +4672,8 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d21b475ab879ef0e315ad99067fa25778c3b0377f57f1b00207448dac1a3144" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] @@ -3720,2421 +4686,2100 @@ dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local 1.0.1", -] - -[[package]] -name = "regex-syntax" -version = "0.6.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" - -[[package]] -name = "remove_dir_all" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -dependencies = [ - "winapi 0.3.8", -] - -[[package]] -name = "rental" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f" -dependencies = [ - "rental-impl", - "stable_deref_trait", -] - -[[package]] -name = "rental-impl" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de" -dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", -] - -[[package]] -name = "ring" -version = "0.16.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" -dependencies = [ - "cc", - "lazy_static", - "libc", - "spin", - "untrusted", - "web-sys", - "winapi 0.3.8", -] - -[[package]] -name = "rocksdb" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1651697fefd273bfb4fd69466cc2a9d20de557a0213b97233b22b5e95924b5e" -dependencies = [ - "libc", - "librocksdb-sys", -] - -[[package]] -name = "rpassword" -version = "4.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99371657d3c8e4d816fb6221db98fa408242b0b53bac08f8676a41f8554fe99f" -dependencies = [ - "libc", - "winapi 0.3.8", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "rustls" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" -dependencies = [ - "base64", - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rw-stream-sink" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9cbe61c20455d3015b2bb7be39e1872310283b8e5a52f5b242b0ac7581fe78" -dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", - "tokio-io", -] - -[[package]] -name = "ryu" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" - -[[package]] -name = "safe-mix" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3d055a2582e6b00ed7a31c1524040aa391092bf636328350813f3a0605215c" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "schnorrkel" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eacd8381b3c37840c9c9f40472af529e49975bdcbc24f83c31059fd6539023d3" -dependencies = [ - "curve25519-dalek 1.2.3", - "failure", - "merlin 1.3.0", - "rand 0.6.5", - "rand_core 0.4.2", - "rand_os", - "sha2", - "subtle 2.2.2", - "zeroize 0.9.3", -] - -[[package]] -name = "schnorrkel" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" -dependencies = [ - "arrayref", - "arrayvec 0.5.1", - "curve25519-dalek 2.0.0", - "getrandom", - "merlin 2.0.0", - "rand 0.7.3", - "rand_core 0.5.1", - "sha2", - "subtle 2.2.2", - "zeroize 1.1.0", -] - -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", + "thread_local", ] [[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "send_wrapper" -version = "0.2.0" +name = "regex-syntax" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" +checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" [[package]] -name = "serde" -version = "1.0.106" +name = "remove_dir_all" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ - "serde_derive", + "winapi 0.3.8", ] [[package]] -name = "serde_derive" -version = "1.0.106" +name = "rental" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +checksum = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "rental-impl", + "stable_deref_trait", ] [[package]] -name = "serde_json" -version = "1.0.51" +name = "rental-impl" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" +checksum = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de" dependencies = [ - "itoa", - "ryu", - "serde", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "sha-1" -version = "0.8.2" +name = "ring" +version = "0.16.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" dependencies = [ - "block-buffer", - "digest", - "fake-simd", - "opaque-debug", + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.8", ] [[package]] -name = "sha1" -version = "0.6.0" +name = "rle-decode-fast" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" [[package]] -name = "sha2" -version = "0.8.1" +name = "rocksdb" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" +checksum = "61aa17a99a2413cd71c1106691bf59dad7de0cd5099127f90e9d99c429c40d4a" dependencies = [ - "block-buffer", - "digest", - "fake-simd", - "opaque-debug", + "libc", + "librocksdb-sys", ] [[package]] -name = "sha3" -version = "0.8.2" +name = "rpassword" +version = "4.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" +checksum = "99371657d3c8e4d816fb6221db98fa408242b0b53bac08f8676a41f8554fe99f" dependencies = [ - "block-buffer", - "byte-tools", - "digest", - "keccak", - "opaque-debug", + "libc", + "winapi 0.3.8", ] [[package]] -name = "shell32-sys" -version = "0.1.2" +name = "rust-argon2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", ] [[package]] -name = "slab" -version = "0.4.2" +name = "rustc-demangle" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] -name = "slog" -version = "2.5.2" +name = "rustc-hash" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99" -dependencies = [ - "erased-serde", -] +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "slog-async" -version = "2.3.0" -source = "git+https://github.com/paritytech/slog-async#0329dc74feb3afe93d0cd2533a472b7ceab44aaf" -dependencies = [ - "crossbeam-channel", - "slog", - "take_mut", - "thread_local 0.3.6", -] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] -name = "slog-json" -version = "2.3.0" +name = "rustc_version" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "chrono", - "erased-serde", - "serde", - "serde_json", - "slog", + "semver", ] [[package]] -name = "slog-scope" -version = "4.3.0" +name = "rustls" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c44c89dd8b0ae4537d1ae318353eaf7840b4869c536e31c41e963d1ea523ee6" +checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" dependencies = [ - "arc-swap", - "lazy_static", - "slog", + "base64", + "log", + "ring", + "sct", + "webpki", ] [[package]] -name = "slog_derive" -version = "0.1.1" +name = "rustls-native-certs" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eff3b513cf2e0d1a60e1aba152dc72bedc5b05585722bb3cebd7bcb1e31b98f" +checksum = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "openssl-probe", + "rustls", + "schannel", + "security-framework", ] [[package]] -name = "smallvec" -version = "0.6.13" +name = "rw-stream-sink" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020" dependencies = [ - "maybe-uninit", + "futures 0.3.4", + "pin-project", + "static_assertions", ] [[package]] -name = "smallvec" -version = "1.4.1" +name = "ryu" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] -name = "snow" -version = "0.6.2" +name = "safe-mix" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb767eee7d257ba202f0b9b08673bc13b22281632ef45267b19f13100accd2f" +checksum = "6d3d055a2582e6b00ed7a31c1524040aa391092bf636328350813f3a0605215c" dependencies = [ - "arrayref", - "rand_core 0.5.1", - "ring", "rustc_version", - "subtle 2.2.2", ] [[package]] -name = "soketto" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bceb1a3a15232d013d9a3b7cac9e5ce8e2313f348f01d4bc1097e5e53aa07095" +name = "sc-authority-discovery" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "base64", - "bytes 0.4.12", - "flate2", - "futures 0.1.29", - "http", - "httparse", + "bytes 0.5.6", + "derive_more", + "futures 0.3.4", + "futures-timer 3.0.2", + "libp2p", "log", - "rand 0.6.5", - "sha1", - "smallvec 0.6.13", - "tokio-codec", - "tokio-io", + "parity-scale-codec", + "prost", + "prost-build", + "rand 0.7.3", + "sc-client-api", + "sc-keystore", + "sc-network", + "serde_json", + "sp-api", + "sp-authority-discovery", + "sp-blockchain", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", ] [[package]] -name = "sp-application-crypto" -version = "2.0.0-rc4" +name = "sc-basic-authorship" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ + "futures 0.3.4", + "futures-timer 3.0.2", + "log", "parity-scale-codec", - "serde", + "sc-block-builder", + "sc-client-api", + "sc-proposer-metrics", + "sc-telemetry", + "sp-api", + "sp-blockchain", + "sp-consensus", "sp-core", - "sp-io", - "sp-std", + "sp-inherents", + "sp-runtime", + "sp-transaction-pool", + "substrate-prometheus-endpoint", + "tokio-executor 0.2.0-alpha.6", ] [[package]] -name = "sp-arithmetic" -version = "2.0.0-rc4" +name = "sc-block-builder" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "integer-sqrt", - "num-traits", "parity-scale-codec", - "serde", - "sp-debug-derive", - "sp-std", + "sc-client-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", ] [[package]] -name = "sp-core" +name = "sc-chain-spec" version = "2.0.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "base58", - "blake2-rfc", - "byteorder 1.3.4", - "derive_more 0.99.9", - "ed25519-dalek 1.0.0-pre.3", - "futures 0.3.4", - "hash-db", - "hash256-std-hasher", - "hex 0.4.2", - "impl-serde 0.3.0", - "lazy_static", - "libsecp256k1", - "log", - "merlin 2.0.0", - "num-traits", - "parity-scale-codec", - "parity-util-mem 0.6.1", - "parking_lot 0.10.0", - "primitive-types 0.7.1", - "rand 0.7.3", - "regex", - "schnorrkel 0.9.1", + "impl-trait-for-tuples", + "sc-chain-spec-derive", + "sc-network", + "sc-telemetry", "serde", - "sha2", - "sp-debug-derive", - "sp-externalities", - "sp-runtime-interface", - "sp-std", - "sp-storage", - "substrate-bip39 0.4.1", - "tiny-bip39 0.7.3", - "tiny-keccak 2.0.2", - "twox-hash", - "wasmi 0.6.2", - "zeroize 1.1.0", + "serde_json", + "sp-chain-spec", + "sp-core", + "sp-runtime", ] [[package]] -name = "sp-debug-derive" +name = "sc-chain-spec-derive" version = "2.0.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] [[package]] -name = "sp-externalities" +name = "sc-cli" version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "environmental", - "parity-scale-codec", - "sp-std", - "sp-storage", + "ansi_term 0.12.1", + "atty", + "chrono", + "derive_more", + "env_logger", + "fdlimit", + "futures 0.3.4", + "lazy_static", + "log", + "names", + "nix", + "parity-util-mem", + "regex", + "rpassword", + "sc-client-api", + "sc-informant", + "sc-network", + "sc-service", + "sc-telemetry", + "sc-tracing", + "serde_json", + "sp-blockchain", + "sp-core", + "sp-keyring", + "sp-panic-handler", + "sp-runtime", + "sp-state-machine", + "sp-utils", + "sp-version", + "structopt", + "substrate-prometheus-endpoint", + "time", + "tokio 0.2.22", ] [[package]] -name = "sp-inherents" +name = "sc-client-api" version = "2.0.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "derive_more 0.99.9", + "derive_more", + "fnv", + "futures 0.3.4", + "hash-db", + "hex-literal", + "kvdb", + "lazy_static", + "log", "parity-scale-codec", - "parking_lot 0.10.0", + "parking_lot 0.10.2", + "sc-executor", + "sc-telemetry", + "sp-api", + "sp-blockchain", + "sp-consensus", "sp-core", + "sp-database", + "sp-externalities", + "sp-inherents", + "sp-keyring", + "sp-runtime", + "sp-state-machine", "sp-std", + "sp-storage", + "sp-transaction-pool", + "sp-trie", + "sp-utils", + "sp-version", + "substrate-prometheus-endpoint", ] [[package]] -name = "sp-io" -version = "2.0.0-rc4" +name = "sc-client-db" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "futures 0.3.4", + "blake2-rfc", "hash-db", - "libsecp256k1", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "linked-hash-map", "log", + "parity-db", "parity-scale-codec", - "parking_lot 0.10.0", + "parity-util-mem", + "parking_lot 0.10.2", + "sc-client-api", + "sc-executor", + "sc-state-db", + "sp-blockchain", + "sp-consensus", "sp-core", - "sp-externalities", - "sp-runtime-interface", + "sp-database", + "sp-runtime", "sp-state-machine", - "sp-std", - "sp-tracing", "sp-trie", - "sp-wasm-interface", + "substrate-prometheus-endpoint", ] [[package]] -name = "sp-panic-handler" -version = "2.0.0-rc4" +name = "sc-consensus" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "backtrace", - "log", + "sc-client-api", + "sp-blockchain", + "sp-consensus", + "sp-runtime", ] [[package]] -name = "sp-runtime" -version = "2.0.0-rc4" +name = "sc-consensus-babe" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", + "derive_more", + "fork-tree", + "futures 0.3.4", + "futures-timer 3.0.2", "log", + "merlin", + "num-bigint", + "num-rational", + "num-traits 0.2.11", "parity-scale-codec", - "parity-util-mem 0.6.1", - "paste", + "parking_lot 0.10.2", + "pdqselect", "rand 0.7.3", + "sc-client-api", + "sc-consensus-epochs", + "sc-consensus-slots", + "sc-consensus-uncles", + "sc-keystore", + "sc-telemetry", + "schnorrkel", "serde", + "sp-api", "sp-application-crypto", - "sp-arithmetic", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-consensus-vrf", "sp-core", "sp-inherents", "sp-io", - "sp-std", + "sp-runtime", + "sp-timestamp", + "sp-version", + "substrate-prometheus-endpoint", ] [[package]] -name = "sp-runtime-interface" -version = "2.0.0-rc4" +name = "sc-consensus-babe-rpc" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", - "primitive-types 0.7.1", - "sp-externalities", - "sp-runtime-interface-proc-macro", - "sp-std", - "sp-tracing", - "sp-wasm-interface", - "static_assertions", + "derive_more", + "futures 0.3.4", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-keystore", + "sc-rpc-api", + "serde", + "sp-api", + "sp-application-crypto", + "sp-blockchain", + "sp-consensus", + "sp-consensus-babe", + "sp-core", + "sp-runtime", ] [[package]] -name = "sp-runtime-interface-proc-macro" -version = "2.0.0-rc4" +name = "sc-consensus-epochs" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "Inflector", - "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "fork-tree", + "parity-scale-codec", + "parking_lot 0.10.2", + "sc-client-api", + "sp-blockchain", + "sp-runtime", ] [[package]] -name = "sp-state-machine" +name = "sc-consensus-slots" version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hash-db", - "itertools 0.9.0", + "futures 0.3.4", + "futures-timer 3.0.2", "log", - "num-traits", "parity-scale-codec", - "parking_lot 0.10.0", - "rand 0.7.3", - "smallvec 1.4.1", + "parking_lot 0.10.2", + "sc-client-api", + "sc-telemetry", + "sp-api", + "sp-application-crypto", + "sp-blockchain", + "sp-consensus", "sp-core", - "sp-externalities", - "sp-panic-handler", - "sp-trie", - "trie-db 0.21.0", - "trie-root 0.16.0", + "sp-inherents", + "sp-runtime", + "sp-state-machine", ] [[package]] -name = "sp-std" -version = "2.0.0-rc4" +name = "sc-consensus-uncles" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" +dependencies = [ + "log", + "sc-client-api", + "sp-authorship", + "sp-consensus", + "sp-core", + "sp-inherents", + "sp-runtime", +] [[package]] -name = "sp-storage" -version = "2.0.0-rc4" +name = "sc-executor" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "impl-serde 0.2.3", - "ref-cast", - "serde", - "sp-debug-derive", - "sp-std", + "derive_more", + "lazy_static", + "libsecp256k1", + "log", + "parity-scale-codec", + "parity-wasm", + "parking_lot 0.10.2", + "sc-executor-common", + "sc-executor-wasmi", + "sp-api", + "sp-core", + "sp-externalities", + "sp-io", + "sp-panic-handler", + "sp-runtime-interface", + "sp-serializer", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "wasmi", ] [[package]] -name = "sp-tracing" -version = "2.0.0-rc4" +name = "sc-executor-common" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ + "derive_more", "log", - "rental", - "tracing", + "parity-scale-codec", + "parity-wasm", + "sp-allocator", + "sp-core", + "sp-runtime-interface", + "sp-serializer", + "sp-wasm-interface", + "wasmi", ] [[package]] -name = "sp-trie" -version = "2.0.0-rc4" +name = "sc-executor-wasmi" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hash-db", - "memory-db 0.21.0", + "log", "parity-scale-codec", + "sc-executor-common", + "sp-allocator", "sp-core", - "sp-std", - "trie-db 0.21.0", - "trie-root 0.16.0", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmi", ] [[package]] -name = "sp-version" -version = "2.0.0-rc4" +name = "sc-finality-grandpa" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "impl-serde 0.2.3", + "assert_matches", + "derive_more", + "finality-grandpa", + "fork-tree", + "futures 0.3.4", + "futures-timer 3.0.2", + "log", "parity-scale-codec", - "serde", + "parking_lot 0.10.2", + "pin-project", + "rand 0.7.3", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-keystore", + "sc-network", + "sc-network-gossip", + "sc-telemetry", + "serde_json", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-finality-grandpa", + "sp-finality-tracker", + "sp-inherents", "sp-runtime", - "sp-std", + "sp-utils", + "substrate-prometheus-endpoint", ] [[package]] -name = "sp-wasm-interface" -version = "2.0.0-rc4" +name = "sc-finality-grandpa-rpc" +version = "0.8.0-rc4" source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "impl-trait-for-tuples", - "parity-scale-codec", - "sp-std", - "wasmi 0.6.2", + "derive_more", + "finality-grandpa", + "futures 0.3.4", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "log", + "sc-finality-grandpa", + "serde", + "serde_json", ] [[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "sr-api-macros" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-informant" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "blake2-rfc", - "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "ansi_term 0.12.1", + "futures 0.3.4", + "log", + "parity-util-mem", + "parking_lot 0.10.2", + "sc-client-api", + "sc-network", + "sp-blockchain", + "sp-runtime", + "sp-transaction-pool", + "sp-utils", + "wasm-timer", ] [[package]] -name = "sr-arithmetic" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-keystore" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "serde", - "sr-std", - "substrate-debug-derive", + "derive_more", + "hex", + "merlin", + "parking_lot 0.10.2", + "rand 0.7.3", + "serde_json", + "sp-application-crypto", + "sp-core", + "subtle 2.2.2", ] [[package]] -name = "sr-io" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-light" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ "hash-db", - "libsecp256k1", - "log", + "lazy_static", "parity-scale-codec", - "rustc_version", - "sr-std", - "substrate-externalities", - "substrate-primitives", - "substrate-state-machine", - "substrate-trie", - "tiny-keccak 1.5.0", + "parking_lot 0.10.2", + "sc-client-api", + "sc-executor", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-externalities", + "sp-runtime", + "sp-state-machine", ] [[package]] -name = "sr-primitives" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-network" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "impl-trait-for-tuples", + "bitflags", + "bs58", + "bytes 0.5.6", + "derive_more", + "either", + "erased-serde", + "fnv", + "fork-tree", + "futures 0.3.4", + "futures-timer 3.0.2", + "futures_codec 0.3.4", + "hex", + "ip_network", + "libp2p", + "linked-hash-map", + "linked_hash_set", "log", + "lru", + "nohash-hasher", "parity-scale-codec", - "paste", + "parking_lot 0.10.2", + "pin-project", + "prost", + "prost-build", "rand 0.7.3", + "sc-block-builder", + "sc-client-api", + "sc-peerset", "serde", - "sr-arithmetic", - "sr-io", - "sr-std", - "substrate-application-crypto", - "substrate-primitives", -] - -[[package]] -name = "sr-staking-primitives" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ - "parity-scale-codec", - "sr-primitives", - "sr-std", + "serde_json", + "slog", + "slog_derive", + "smallvec 0.6.13", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-utils", + "substrate-prometheus-endpoint", + "thiserror", + "unsigned-varint 0.3.3", + "void", + "wasm-timer", + "zeroize", ] [[package]] -name = "sr-std" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-network-gossip" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "rustc_version", + "futures 0.3.4", + "futures-timer 3.0.2", + "libp2p", + "log", + "lru", + "sc-network", + "sp-runtime", + "wasm-timer", ] [[package]] -name = "sr-version" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-offchain" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "impl-serde 0.2.3", + "bytes 0.5.6", + "fnv", + "futures 0.3.4", + "futures-timer 3.0.2", + "hyper 0.13.7", + "hyper-rustls", + "log", + "num_cpus", "parity-scale-codec", - "serde", - "sr-primitives", - "sr-std", + "parking_lot 0.10.2", + "rand 0.7.3", + "sc-client-api", + "sc-keystore", + "sc-network", + "sp-api", + "sp-core", + "sp-offchain", + "sp-runtime", + "sp-utils", + "threadpool", ] [[package]] -name = "srml-authority-discovery" -version = "0.1.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-peerset" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-session", - "srml-support", - "srml-system", - "substrate-application-crypto", - "substrate-primitives", + "futures 0.3.4", + "libp2p", + "log", + "serde_json", + "sp-utils", + "wasm-timer", ] [[package]] -name = "srml-authorship" -version = "0.1.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-proposer-metrics" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "impl-trait-for-tuples", - "parity-scale-codec", - "sr-io", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", - "substrate-inherents", - "substrate-primitives", + "log", + "substrate-prometheus-endpoint", ] [[package]] -name = "srml-babe" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-rpc" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hex-literal 0.2.1", + "futures 0.3.4", + "hash-db", + "jsonrpc-core", + "jsonrpc-pubsub", + "log", "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-staking-primitives", - "sr-std", - "srml-session", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-consensus-babe-primitives", - "substrate-inherents", + "parking_lot 0.10.2", + "sc-block-builder", + "sc-client-api", + "sc-executor", + "sc-keystore", + "sc-rpc-api", + "serde_json", + "sp-api", + "sp-blockchain", + "sp-chain-spec", + "sp-core", + "sp-offchain", + "sp-rpc", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-transaction-pool", + "sp-utils", + "sp-version", ] [[package]] -name = "srml-balances" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-rpc-api" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ + "derive_more", + "futures 0.3.4", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "jsonrpc-pubsub", + "log", "parity-scale-codec", - "safe-mix", + "parking_lot 0.10.2", "serde", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", - "substrate-keyring", + "serde_json", + "sp-chain-spec", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-transaction-pool", + "sp-version", ] [[package]] -name = "srml-executive" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-rpc-server" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", + "jsonrpc-core", + "jsonrpc-http-server", + "jsonrpc-ipc-server", + "jsonrpc-pubsub", + "jsonrpc-ws-server", + "log", "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", + "serde_json", + "sp-runtime", ] [[package]] -name = "srml-finality-tracker" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-service" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "impl-trait-for-tuples", + "derive_more", + "directories", + "exit-future", + "futures 0.1.29", + "futures 0.3.4", + "futures-timer 3.0.2", + "hash-db", + "jsonrpc-pubsub", + "lazy_static", + "log", + "netstat2", + "parity-multiaddr 0.7.3", "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.2", + "pin-project", + "procfs", + "rand 0.7.3", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-executor", + "sc-informant", + "sc-keystore", + "sc-light", + "sc-network", + "sc-offchain", + "sc-rpc", + "sc-rpc-server", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", "serde", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", - "substrate-inherents", + "serde_json", + "slog", + "sp-api", + "sp-application-crypto", + "sp-block-builder", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-externalities", + "sp-io", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-transaction-pool", + "sp-trie", + "sp-utils", + "sp-version", + "substrate-prometheus-endpoint", + "sysinfo", + "tempfile", + "tracing", + "wasm-timer", ] [[package]] -name = "srml-grandpa" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-state-db" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ + "log", "parity-scale-codec", - "serde", - "sr-primitives", - "sr-staking-primitives", - "sr-std", - "srml-finality-tracker", - "srml-session", - "srml-support", - "srml-system", - "substrate-finality-grandpa-primitives", - "substrate-primitives", + "parity-util-mem", + "parity-util-mem-derive", + "parking_lot 0.10.2", + "sc-client-api", + "sp-core", ] [[package]] -name = "srml-im-online" -version = "0.1.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-telemetry" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", + "bytes 0.5.6", + "futures 0.3.4", + "futures-timer 3.0.2", + "libp2p", + "log", + "parking_lot 0.10.2", + "pin-project", + "rand 0.7.3", "serde", - "sr-io", - "sr-primitives", - "sr-staking-primitives", - "sr-std", - "srml-authorship", - "srml-session", - "srml-support", - "srml-system", - "substrate-application-crypto", - "substrate-primitives", + "slog", + "slog-json", + "slog-scope", + "take_mut", + "void", + "wasm-timer", ] [[package]] -name = "srml-indices" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-tracing" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", - "safe-mix", + "erased-serde", + "log", + "parking_lot 0.10.2", + "rustc-hash", + "sc-telemetry", "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", - "substrate-keyring", - "substrate-primitives", + "serde_json", + "slog", + "sp-tracing", + "tracing-core", ] [[package]] -name = "srml-metadata" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-transaction-graph" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", + "derive_more", + "futures 0.3.4", + "linked-hash-map", + "log", + "parity-util-mem", + "parking_lot 0.10.2", "serde", - "sr-std", - "substrate-primitives", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-transaction-pool", + "sp-utils", + "wasm-timer", ] [[package]] -name = "srml-offences" -version = "1.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sc-transaction-pool" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ + "derive_more", + "futures 0.3.4", + "futures-diagnose", + "intervalier", + "log", "parity-scale-codec", - "serde", - "sr-primitives", - "sr-staking-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-system", + "parity-util-mem", + "parking_lot 0.10.2", + "sc-client-api", + "sc-transaction-graph", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-tracing", + "sp-transaction-pool", + "sp-utils", + "substrate-prometheus-endpoint", + "wasm-timer", ] [[package]] -name = "srml-randomness-collective-flip" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ - "parity-scale-codec", - "safe-mix", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", + "lazy_static", + "winapi 0.3.8", ] [[package]] -name = "srml-session" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" dependencies = [ - "impl-trait-for-tuples", - "parity-scale-codec", - "safe-mix", - "serde", - "sr-io", - "sr-primitives", - "sr-staking-primitives", - "sr-std", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-trie", + "arrayref", + "arrayvec 0.5.1", + "curve25519-dalek", + "getrandom", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.1", + "subtle 2.2.2", + "zeroize", ] [[package]] -name = "srml-staking" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ - "parity-scale-codec", - "safe-mix", - "serde", - "sr-io", - "sr-primitives", - "sr-staking-primitives", - "sr-std", - "srml-authorship", - "srml-session", - "srml-support", - "srml-system", - "substrate-keyring", - "substrate-phragmen", -] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] -name = "srml-staking-reward-curve" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "ring", + "untrusted", ] [[package]] -name = "srml-sudo" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "security-framework" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" dependencies = [ - "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", ] [[package]] -name = "srml-support" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "security-framework-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" dependencies = [ - "bitmask", - "impl-trait-for-tuples", - "log", - "once_cell 0.2.4", - "parity-scale-codec", - "paste", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-metadata", - "srml-support-procedural", - "substrate-inherents", - "substrate-primitives", + "core-foundation-sys", + "libc", ] [[package]] -name = "srml-support-procedural" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "sr-api-macros", - "srml-support-procedural-tools", - "syn 1.0.17", + "semver-parser", ] [[package]] -name = "srml-support-procedural-tools" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "send_wrapper" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" + +[[package]] +name = "send_wrapper" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686ef91cf020ad8d4aca9a7047641fd6add626b7b89e14546c2b6a76781cf822" + +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + +[[package]] +name = "serde" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", - "srml-support-procedural-tools-derive", - "syn 1.0.17", + "serde_derive", ] [[package]] -name = "srml-support-procedural-tools-derive" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "serde_derive" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] [[package]] -name = "srml-system" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "serde_json" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" dependencies = [ - "impl-trait-for-tuples", - "parity-scale-codec", - "safe-mix", + "itoa", + "ryu", "serde", - "sr-io", - "sr-primitives", - "sr-std", - "sr-version", - "srml-support", - "substrate-primitives", ] [[package]] -name = "srml-system-rpc-runtime-api" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "parity-scale-codec", - "substrate-client", + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", ] [[package]] -name = "srml-timestamp" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + +[[package]] +name = "sha2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" dependencies = [ - "impl-trait-for-tuples", - "parity-scale-codec", - "serde", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", - "substrate-inherents", + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", ] [[package]] -name = "srml-transaction-payment" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sha2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" dependencies = [ - "parity-scale-codec", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", - "srml-transaction-payment-rpc-runtime-api", + "block-buffer 0.9.0", + "cfg-if", + "cpuid-bool", + "digest 0.9.0", + "opaque-debug 0.3.0", ] [[package]] -name = "srml-transaction-payment-rpc-runtime-api" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sha3" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" dependencies = [ - "parity-scale-codec", - "serde", - "sr-primitives", - "sr-std", - "substrate-client", + "block-buffer 0.7.3", + "byte-tools", + "digest 0.8.1", + "keccak", + "opaque-debug 0.2.3", ] [[package]] -name = "stable_deref_trait" -version = "1.1.1" +name = "shlex" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] -name = "static_assertions" -version = "1.1.0" +name = "signal-hook-registry" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] [[package]] -name = "stream-cipher" -version = "0.3.2" +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "slog" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" +checksum = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99" dependencies = [ - "generic-array", + "erased-serde", ] [[package]] -name = "string" -version = "0.2.1" +name = "slog-json" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" +checksum = "ddc0d2aff1f8f325ef660d9a0eb6e6dcd20b30b3f581a5897f58bf42d061c37a" dependencies = [ - "bytes 0.4.12", + "chrono", + "erased-serde", + "serde", + "serde_json", + "slog", ] [[package]] -name = "strsim" -version = "0.8.0" +name = "slog-scope" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "7c44c89dd8b0ae4537d1ae318353eaf7840b4869c536e31c41e963d1ea523ee6" +dependencies = [ + "arc-swap", + "lazy_static", + "slog", +] [[package]] -name = "structopt" -version = "0.3.5" +name = "slog_derive" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b3a3e93f5ad553c38b3301c8a0a0cec829a36783f6a0c467fc4bf553a5f5bf" +checksum = "a945ec7f7ce853e89ffa36be1e27dce9a43e82ff9093bf3461c30d5da74ed11b" dependencies = [ - "clap", - "structopt-derive", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "structopt-derive" -version = "0.3.5" +name = "smallvec" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea692d40005b3ceba90a9fe7a78fa8d4b82b0ce627eebbffc329aab850f3410e" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "maybe-uninit", ] [[package]] -name = "strum" -version = "0.15.0" +name = "smallvec" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f" +checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" [[package]] -name = "strum_macros" -version = "0.15.0" +name = "snow" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e" +checksum = "32bf8474159a95551661246cda4976e89356999e3cbfef36f493dacc3fae1e8e" dependencies = [ - "heck", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "aes-gcm", + "blake2 0.9.0", + "chacha20poly1305", + "rand 0.7.3", + "rand_core 0.5.1", + "ring", + "rustc_version", + "sha2 0.9.1", + "subtle 2.2.2", + "x25519-dalek", ] [[package]] -name = "substrate-application-crypto" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" dependencies = [ - "parity-scale-codec", - "serde", - "sr-io", - "sr-std", - "substrate-primitives", + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.8", ] [[package]] -name = "substrate-authority-discovery" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "soketto" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c9dab3f95c9ebdf3a88268c19af668f637a3c5039c2c56ff2d40b1b2d64a25b" dependencies = [ - "bytes 0.4.12", - "derive_more 0.15.0", - "futures-preview", - "futures-timer", - "libp2p", + "base64", + "bytes 0.5.6", + "flate2", + "futures 0.3.4", + "http 0.2.1", + "httparse", "log", - "parity-scale-codec", - "prost", - "prost-build", - "serde_json", - "sr-primitives", - "substrate-authority-discovery-primitives", - "substrate-client", - "substrate-network", - "substrate-primitives", + "rand 0.7.3", + "sha1", + "smallvec 1.4.1", + "static_assertions", + "thiserror", ] [[package]] -name = "substrate-authority-discovery-primitives" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-allocator" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", - "sr-primitives", - "sr-std", - "substrate-client", + "derive_more", + "log", + "sp-core", + "sp-std", + "sp-wasm-interface", ] [[package]] -name = "substrate-basic-authorship" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-api" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "futures-preview", - "log", + "hash-db", "parity-scale-codec", - "sr-primitives", - "substrate-client", - "substrate-consensus-common", - "substrate-inherents", - "substrate-primitives", - "substrate-telemetry", - "substrate-transaction-pool", + "sp-api-proc-macro", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-version", ] [[package]] -name = "substrate-bip39" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be511be555a3633e71739a79e4ddff6a6aaa6579fa6114182a51d72c3eb93c5" +name = "sp-api-proc-macro" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hmac", - "pbkdf2", - "schnorrkel 0.8.5", - "sha2", + "blake2-rfc", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "substrate-bip39" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c004e8166d6e0aa3a9d5fa673e5b7098ff25f930de1013a21341988151e681bb" +name = "sp-application-crypto" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hmac", - "pbkdf2", - "schnorrkel 0.9.1", - "sha2", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-std", ] [[package]] -name = "substrate-chain-spec" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-arithmetic" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "impl-trait-for-tuples", + "integer-sqrt", + "num-traits 0.2.11", + "parity-scale-codec", "serde", - "serde_json", - "sr-primitives", - "substrate-chain-spec-derive", - "substrate-network", - "substrate-primitives", - "substrate-telemetry", + "sp-debug-derive", + "sp-std", ] [[package]] -name = "substrate-chain-spec-derive" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-authority-discovery" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "proc-macro-crate", - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "parity-scale-codec", + "sp-api", + "sp-application-crypto", + "sp-runtime", + "sp-std", ] [[package]] -name = "substrate-cli" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-authorship" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "ansi_term 0.12.1", - "app_dirs", - "atty", - "clap", - "derive_more 0.15.0", - "env_logger 0.7.1", - "exit-future", - "fdlimit", - "futures 0.1.29", - "futures-preview", - "lazy_static", - "log", - "names", - "regex", - "rpassword", - "serde_json", - "sr-primitives", - "structopt", - "substrate-client", - "substrate-header-metadata", - "substrate-keyring", - "substrate-network", - "substrate-panic-handler", - "substrate-primitives", - "substrate-service", - "substrate-state-machine", - "substrate-telemetry", - "time", - "tokio", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std", ] [[package]] -name = "substrate-client" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-block-builder" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "derive_more 0.15.0", - "fnv", - "futures 0.1.29", - "futures-preview", - "hash-db", - "hex-literal 0.2.1", - "kvdb", - "log", "parity-scale-codec", - "parking_lot 0.9.0", - "sr-api-macros", - "sr-primitives", - "sr-std", - "sr-version", - "substrate-consensus-common", - "substrate-executor", - "substrate-header-metadata", - "substrate-inherents", - "substrate-keyring", - "substrate-primitives", - "substrate-state-machine", - "substrate-telemetry", - "substrate-trie", -] - -[[package]] -name = "substrate-client-db" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-blockchain" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hash-db", - "kvdb", - "kvdb-memorydb", - "kvdb-rocksdb", - "linked-hash-map", + "derive_more", "log", + "lru", "parity-scale-codec", - "parking_lot 0.9.0", - "sr-primitives", - "substrate-client", - "substrate-consensus-common", - "substrate-executor", - "substrate-header-metadata", - "substrate-primitives", - "substrate-state-db", - "substrate-state-machine", - "substrate-trie", + "parking_lot 0.10.2", + "sp-block-builder", + "sp-consensus", + "sp-runtime", + "sp-state-machine", ] [[package]] -name = "substrate-common-module" -version = "1.0.0" +name = "sp-chain-spec" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "sr-primitives", - "srml-support", - "srml-system", + "serde", + "serde_json", ] [[package]] -name = "substrate-consensus-babe" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-consensus" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "derive_more 0.15.0", - "fork-tree", - "futures 0.1.29", - "futures-preview", - "futures-timer", + "derive_more", + "futures 0.3.4", + "futures-timer 3.0.2", + "libp2p", "log", - "merlin 1.3.0", - "num-bigint", - "num-rational", - "num-traits", - "parity-scale-codec", - "parking_lot 0.9.0", - "pdqselect", - "rand 0.7.3", - "schnorrkel 0.8.5", - "sr-io", - "sr-primitives", - "sr-version", - "srml-babe", - "srml-support", - "substrate-application-crypto", - "substrate-client", - "substrate-consensus-babe-primitives", - "substrate-consensus-common", - "substrate-consensus-slots", - "substrate-consensus-uncles", - "substrate-header-metadata", - "substrate-inherents", - "substrate-keystore", - "substrate-primitives", - "substrate-telemetry", -] - -[[package]] -name = "substrate-consensus-babe-primitives" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ "parity-scale-codec", - "schnorrkel 0.8.5", - "sr-primitives", - "sr-std", - "substrate-application-crypto", - "substrate-client", - "substrate-consensus-slots", + "parking_lot 0.10.2", + "serde", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std", + "sp-utils", + "sp-version", + "substrate-prometheus-endpoint", + "wasm-timer", ] [[package]] -name = "substrate-consensus-common" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-consensus-babe" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "derive_more 0.15.0", - "futures-preview", - "futures-timer", - "libp2p", - "log", + "merlin", "parity-scale-codec", - "parking_lot 0.9.0", - "sr-primitives", - "sr-std", - "sr-version", - "substrate-inherents", - "substrate-primitives", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-vrf", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", ] [[package]] -name = "substrate-consensus-slots" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-consensus-vrf" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "futures-preview", - "futures-timer", - "log", "parity-scale-codec", - "parking_lot 0.9.0", - "sr-primitives", - "substrate-client", - "substrate-consensus-common", - "substrate-inherents", - "substrate-primitives", - "substrate-telemetry", + "schnorrkel", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] -name = "substrate-consensus-uncles" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-core" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ + "base58", + "blake2-rfc", + "byteorder", + "derive_more", + "ed25519-dalek", + "futures 0.3.4", + "hash-db", + "hash256-std-hasher", + "hex", + "impl-serde 0.3.0", + "lazy_static", + "libsecp256k1", "log", - "sr-primitives", - "srml-authorship", - "substrate-client", - "substrate-consensus-common", - "substrate-inherents", - "substrate-primitives", + "merlin", + "num-traits 0.2.11", + "parity-scale-codec", + "parity-util-mem", + "parking_lot 0.10.2", + "primitive-types", + "rand 0.7.3", + "regex", + "schnorrkel", + "serde", + "sha2 0.8.1", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "substrate-bip39", + "tiny-bip39", + "tiny-keccak", + "twox-hash", + "wasmi", + "zeroize", ] [[package]] -name = "substrate-content-working-group-module" -version = "1.0.0" -dependencies = [ - "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-common-module", - "substrate-forum-module", - "substrate-hiring-module", - "substrate-membership-module", - "substrate-primitives", - "substrate-recurring-reward-module", - "substrate-stake-module", - "substrate-token-mint-module", - "substrate-versioned-store", - "substrate-versioned-store-permissions-module", -] - -[[package]] -name = "substrate-debug-derive" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-database" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", + "kvdb", + "parking_lot 0.10.2", ] [[package]] -name = "substrate-executor" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-debug-derive" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "derive_more 0.15.0", - "lazy_static", - "libsecp256k1", - "log", - "parity-scale-codec", - "parity-wasm 0.40.3", - "parking_lot 0.9.0", - "sr-io", - "sr-version", - "substrate-externalities", - "substrate-panic-handler", - "substrate-primitives", - "substrate-serializer", - "substrate-trie", - "substrate-wasm-interface", - "tiny-keccak 1.5.0", - "wasmi 0.5.1", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "substrate-externalities" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-externalities" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ "environmental", - "primitive-types 0.6.2", - "sr-std", - "substrate-primitives-storage", + "parity-scale-codec", + "sp-std", + "sp-storage", ] [[package]] -name = "substrate-finality-grandpa" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-finality-grandpa" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ "finality-grandpa", - "fork-tree", - "futures 0.1.29", - "futures-preview", "log", "parity-scale-codec", - "parking_lot 0.9.0", - "rand 0.7.3", - "serde_json", - "sr-primitives", - "srml-finality-tracker", - "substrate-client", - "substrate-consensus-common", - "substrate-finality-grandpa-primitives", - "substrate-header-metadata", - "substrate-inherents", - "substrate-keystore", - "substrate-network", - "substrate-primitives", - "substrate-telemetry", - "tokio-executor", - "tokio-timer", + "serde", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-runtime", + "sp-std", ] [[package]] -name = "substrate-finality-grandpa-primitives" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-finality-tracker" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ "parity-scale-codec", - "serde", - "sr-primitives", - "sr-std", - "substrate-application-crypto", - "substrate-client", + "sp-inherents", + "sp-std", ] [[package]] -name = "substrate-forum-module" -version = "1.1.1" +name = "sp-inherents" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hex-literal 0.1.4", + "derive_more", "parity-scale-codec", - "quote 0.6.13", - "serde", - "serde_derive", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-support-procedural", - "srml-system", - "srml-timestamp", - "substrate-primitives", + "parking_lot 0.10.2", + "sp-core", + "sp-std", ] [[package]] -name = "substrate-governance-module" -version = "1.0.0" +name = "sp-io" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ + "futures 0.3.4", + "hash-db", + "libsecp256k1", + "log", "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-common-module", - "substrate-membership-module", - "substrate-primitives", - "substrate-token-mint-module", + "parking_lot 0.10.2", + "sp-core", + "sp-externalities", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", + "sp-wasm-interface", ] [[package]] -name = "substrate-header-metadata" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-keyring" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "lru-cache", - "parking_lot 0.9.0", - "sr-primitives", + "lazy_static", + "sp-core", + "sp-runtime", + "strum", ] [[package]] -name = "substrate-hiring-module" -version = "1.0.1" +name = "sp-npos-elections" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hex-literal 0.1.4", - "mockall", "parity-scale-codec", - "quote 0.6.13", "serde", - "serde_derive", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-support-procedural", - "srml-system", - "srml-timestamp", - "substrate-primitives", - "substrate-stake-module", -] - -[[package]] -name = "substrate-inherents" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ - "parity-scale-codec", - "parking_lot 0.9.0", - "sr-primitives", - "sr-std", + "sp-arithmetic", + "sp-npos-elections-compact", + "sp-std", ] [[package]] -name = "substrate-keyring" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-npos-elections-compact" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "lazy_static", - "sr-primitives", - "strum", - "strum_macros", - "substrate-primitives", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "substrate-keystore" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-offchain" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "derive_more 0.15.0", - "hex 0.3.2", - "parking_lot 0.9.0", - "rand 0.7.3", - "serde_json", - "substrate-application-crypto", - "substrate-primitives", - "subtle 2.2.2", + "sp-api", + "sp-core", + "sp-runtime", ] [[package]] -name = "substrate-membership-module" -version = "1.0.1" +name = "sp-panic-handler" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-common-module", - "substrate-primitives", + "backtrace", + "log", ] [[package]] -name = "substrate-memo-module" -version = "1.0.0" +name = "sp-rpc" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "parity-scale-codec", "serde", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", - "substrate-common-module", + "sp-core", ] [[package]] -name = "substrate-network" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-runtime" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "bitflags", - "bytes 0.4.12", - "derive_more 0.15.0", "either", - "erased-serde", - "fnv", - "fork-tree", - "futures 0.1.29", - "futures-preview", - "futures-timer", - "libp2p", - "linked-hash-map", - "linked_hash_set", + "hash256-std-hasher", + "impl-trait-for-tuples", "log", - "lru-cache", "parity-scale-codec", - "parking_lot 0.9.0", + "parity-util-mem", + "paste", "rand 0.7.3", - "rustc-hex", "serde", - "serde_json", - "slog", - "slog_derive", - "smallvec 0.6.13", - "sr-primitives", - "substrate-client", - "substrate-consensus-babe-primitives", - "substrate-consensus-common", - "substrate-header-metadata", - "substrate-peerset", - "substrate-primitives", - "tokio-io", - "unsigned-varint 0.2.3", - "void", - "zeroize 0.10.1", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-inherents", + "sp-io", + "sp-std", ] [[package]] -name = "substrate-offchain" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-runtime-interface" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "bytes 0.4.12", - "fnv", - "futures 0.1.29", - "futures-preview", - "futures-timer", - "hyper", - "hyper-rustls", - "log", - "num_cpus", "parity-scale-codec", - "parking_lot 0.9.0", - "rand 0.7.3", - "sr-primitives", - "substrate-client", - "substrate-keystore", - "substrate-network", - "substrate-offchain-primitives", - "substrate-primitives", - "substrate-transaction-pool", - "threadpool", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", ] [[package]] -name = "substrate-offchain-primitives" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-runtime-interface-proc-macro" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "sr-primitives", - "substrate-client", + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "substrate-panic-handler" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-serializer" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "backtrace", - "log", + "serde", + "serde_json", ] [[package]] -name = "substrate-peerset" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-session" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "futures-preview", - "libp2p", - "linked-hash-map", - "log", - "lru-cache", - "serde_json", + "parity-scale-codec", + "sp-api", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std", ] [[package]] -name = "substrate-phragmen" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-staking" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "serde", - "sr-primitives", - "sr-std", + "parity-scale-codec", + "sp-runtime", + "sp-std", ] [[package]] -name = "substrate-primitives" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-state-machine" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "base58", - "blake2-rfc", - "byteorder 1.3.4", - "ed25519-dalek 0.9.1", "hash-db", - "hash256-std-hasher", - "hex 0.4.2", - "impl-serde 0.2.3", - "lazy_static", - "libsecp256k1", + "itertools 0.9.0", "log", - "num-traits", + "num-traits 0.2.11", "parity-scale-codec", - "parking_lot 0.9.0", - "primitive-types 0.6.2", + "parking_lot 0.10.2", "rand 0.7.3", - "regex", - "rustc-hex", - "schnorrkel 0.8.5", - "serde", - "sha2", - "sr-std", - "substrate-bip39 0.3.1", - "substrate-debug-derive", - "substrate-externalities", - "substrate-primitives-storage", - "tiny-bip39 0.6.2", - "tiny-keccak 1.5.0", - "twox-hash", - "wasmi 0.5.1", - "zeroize 0.10.1", + "smallvec 1.4.1", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-trie", + "trie-db", + "trie-root", ] [[package]] -name = "substrate-primitives-storage" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-std" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" + +[[package]] +name = "sp-storage" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ "impl-serde 0.2.3", + "ref-cast", "serde", - "sr-std", - "substrate-debug-derive", + "sp-debug-derive", + "sp-std", ] [[package]] -name = "substrate-proposals-codex-module" -version = "2.0.0" -dependencies = [ - "num_enum", - "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-staking-primitives", - "sr-std", - "srml-balances", - "srml-staking", - "srml-staking-reward-curve", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-common-module", - "substrate-content-working-group-module", - "substrate-governance-module", - "substrate-hiring-module", - "substrate-membership-module", - "substrate-primitives", - "substrate-proposals-discussion-module", - "substrate-proposals-engine-module", - "substrate-recurring-reward-module", - "substrate-roles-module", - "substrate-stake-module", - "substrate-token-mint-module", - "substrate-versioned-store", - "substrate-versioned-store-permissions-module", -] - -[[package]] -name = "substrate-proposals-discussion-module" -version = "2.0.0" +name = "sp-timestamp" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "num_enum", + "impl-trait-for-tuples", "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-common-module", - "substrate-membership-module", - "substrate-primitives", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std", + "wasm-timer", ] [[package]] -name = "substrate-proposals-engine-module" -version = "2.0.0" +name = "sp-tracing" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "mockall", - "num_enum", - "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-common-module", - "substrate-membership-module", - "substrate-primitives", - "substrate-stake-module", + "log", + "rental", + "tracing", ] [[package]] -name = "substrate-recurring-reward-module" -version = "1.0.1" +name = "sp-transaction-pool" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hex-literal 0.1.4", + "derive_more", + "futures 0.3.4", + "log", "parity-scale-codec", - "quote 0.6.13", "serde", - "serde_derive", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-support-procedural", - "srml-system", - "srml-timestamp", - "substrate-primitives", - "substrate-token-mint-module", + "sp-api", + "sp-blockchain", + "sp-runtime", + "sp-utils", ] [[package]] -name = "substrate-roles-module" -version = "1.0.1" +name = "sp-trie" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ + "hash-db", + "memory-db", "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-common-module", - "substrate-membership-module", - "substrate-primitives", + "sp-core", + "sp-std", + "trie-db", + "trie-root", ] [[package]] -name = "substrate-rpc" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-utils" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "futures-preview", - "hash-db", - "jsonrpc-core 14.0.5", - "jsonrpc-pubsub", - "log", - "parity-scale-codec", - "parking_lot 0.9.0", - "serde_json", - "sr-primitives", - "sr-version", - "substrate-client", - "substrate-executor", - "substrate-keystore", - "substrate-primitives", - "substrate-rpc-api", - "substrate-rpc-primitives", - "substrate-session", - "substrate-state-machine", - "substrate-transaction-pool", + "futures 0.3.4", + "futures-core", + "futures-timer 3.0.2", + "lazy_static", + "prometheus", ] [[package]] -name = "substrate-rpc-api" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-version" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "derive_more 0.15.0", - "futures-preview", - "jsonrpc-core 14.0.5", - "jsonrpc-core-client", - "jsonrpc-derive", - "jsonrpc-pubsub", - "log", + "impl-serde 0.2.3", "parity-scale-codec", - "parking_lot 0.9.0", "serde", - "serde_json", - "sr-version", - "substrate-primitives", - "substrate-rpc-primitives", - "substrate-transaction-graph", + "sp-runtime", + "sp-std", ] [[package]] -name = "substrate-rpc-primitives" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "sp-wasm-interface" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "serde", - "substrate-primitives", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-std", + "wasmi", ] [[package]] -name = "substrate-rpc-servers" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ - "jsonrpc-core 14.0.5", - "jsonrpc-http-server", - "jsonrpc-pubsub", - "jsonrpc-ws-server", - "log", - "serde", - "serde_json", - "sr-primitives", -] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "substrate-serializer" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ - "serde", - "serde_json", -] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" [[package]] -name = "substrate-service" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ - "derive_more 0.15.0", - "exit-future", - "futures 0.1.29", - "futures-preview", - "lazy_static", - "log", - "parity-multiaddr 0.5.0", - "parity-scale-codec", - "parking_lot 0.9.0", - "serde", - "serde_json", - "slog", - "sr-io", - "sr-primitives", - "substrate-application-crypto", - "substrate-chain-spec", - "substrate-client", - "substrate-client-db", - "substrate-consensus-common", - "substrate-executor", - "substrate-keystore", - "substrate-network", - "substrate-offchain", - "substrate-primitives", - "substrate-rpc", - "substrate-rpc-servers", - "substrate-session", - "substrate-telemetry", - "substrate-transaction-pool", - "sysinfo", - "target_info", - "tokio-executor", - "tokio-timer", -] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] -name = "substrate-service-discovery-module" -version = "1.0.0" +name = "statrs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10102ac8d55e35db2b3fafc26f81ba8647da2e15879ab686a67e6d19af2685e8" dependencies = [ - "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-support", - "srml-system", - "substrate-primitives", - "substrate-roles-module", + "rand 0.5.6", ] [[package]] -name = "substrate-session" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "stream-cipher" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f8ed9974042b8c3672ff3030a69fcc03b74c47c3d1ecb7755e8a3626011e88" dependencies = [ - "sr-primitives", - "sr-std", - "substrate-client", - "substrate-primitives", + "generic-array 0.14.3", ] [[package]] -name = "substrate-stake-module" -version = "2.0.0" +name = "string" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" dependencies = [ - "hex-literal 0.1.4", - "parity-scale-codec", - "quote 0.6.13", - "serde", - "serde_derive", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-support-procedural", - "srml-system", - "srml-timestamp", - "substrate-primitives", + "bytes 0.4.12", ] [[package]] -name = "substrate-state-db" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ - "log", - "parity-scale-codec", - "parking_lot 0.9.0", - "substrate-primitives", -] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] -name = "substrate-state-machine" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "structopt" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2f5e239ee807089b62adce73e48c625e0ed80df02c7ab3f068f5db5281065c" dependencies = [ - "hash-db", - "log", - "num-traits", - "parity-scale-codec", - "parking_lot 0.9.0", - "rand 0.7.3", - "substrate-externalities", - "substrate-panic-handler", - "substrate-primitives", - "substrate-trie", - "trie-db 0.15.2", - "trie-root 0.15.2", + "clap", + "lazy_static", + "structopt-derive", ] [[package]] -name = "substrate-storage-module" -version = "1.0.0" +name = "structopt-derive" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510413f9de616762a4fbeab62509bf15c729603b72d7cd71280fbca431b1c118" dependencies = [ - "parity-scale-codec", - "serde", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-system", - "srml-timestamp", - "substrate-common-module", - "substrate-membership-module", - "substrate-primitives", - "substrate-roles-module", + "heck", + "proc-macro-error", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "substrate-telemetry" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "strum" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6138f8f88a16d90134763314e3fc76fa3ed6a7db4725d6acf9a3ef95a3188d22" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", - "futures-preview", - "futures-timer", - "libp2p", - "log", - "parking_lot 0.9.0", - "rand 0.7.3", - "serde", - "slog", - "slog-async", - "slog-json", - "slog-scope", - "tokio-io", - "void", + "strum_macros", ] [[package]] -name = "substrate-token-mint-module" -version = "1.0.1" +name = "strum_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" dependencies = [ - "hex-literal 0.1.4", - "parity-scale-codec", - "quote 0.6.13", - "serde", - "serde_derive", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-support-procedural", - "srml-system", - "srml-timestamp", - "substrate-primitives", + "heck", + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] -name = "substrate-transaction-graph" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "substrate-bip39" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c004e8166d6e0aa3a9d5fa673e5b7098ff25f930de1013a21341988151e681bb" dependencies = [ - "derive_more 0.15.0", - "futures-preview", - "log", - "parking_lot 0.9.0", - "serde", - "sr-primitives", - "substrate-primitives", + "hmac", + "pbkdf2", + "schnorrkel", + "sha2 0.8.1", ] [[package]] -name = "substrate-transaction-pool" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "substrate-browser-utils" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "derive_more 0.15.0", + "chrono", + "clear_on_drop", + "console_error_panic_hook", + "console_log", + "futures 0.1.29", "futures 0.3.4", + "futures-timer 3.0.2", + "js-sys", + "kvdb-web", + "libp2p-wasm-ext", "log", - "parity-scale-codec", - "parking_lot 0.9.0", - "sr-primitives", - "substrate-client", - "substrate-primitives", - "substrate-transaction-graph", + "rand 0.6.5", + "rand 0.7.3", + "sc-chain-spec", + "sc-informant", + "sc-network", + "sc-service", + "sp-database", + "wasm-bindgen", + "wasm-bindgen-futures", ] [[package]] -name = "substrate-trie" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" +name = "substrate-build-script-utils" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hash-db", - "memory-db 0.15.3", - "parity-scale-codec", - "sr-std", - "substrate-primitives", - "trie-db 0.15.2", - "trie-root 0.15.2", + "platforms", ] [[package]] -name = "substrate-versioned-store" -version = "1.0.1" +name = "substrate-frame-rpc-system" +version = "2.0.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hex-literal 0.1.4", + "frame-system-rpc-runtime-api", + "futures 0.3.4", + "jsonrpc-core", + "jsonrpc-core-client", + "jsonrpc-derive", + "log", "parity-scale-codec", - "quote 0.6.13", + "sc-client-api", + "sc-rpc-api", "serde", - "serde_derive", - "sr-io", - "sr-primitives", - "sr-std", - "srml-balances", - "srml-support", - "srml-support-procedural", - "srml-system", - "srml-timestamp", - "substrate-primitives", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-transaction-pool", ] [[package]] -name = "substrate-versioned-store-permissions-module" -version = "1.0.1" +name = "substrate-prometheus-endpoint" +version = "0.8.0-rc4" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" dependencies = [ - "hex-literal 0.1.4", - "parity-scale-codec", - "quote 0.6.13", - "serde", - "serde_derive", - "sr-io", - "sr-primitives", - "sr-std", - "srml-support", - "srml-support-procedural", - "srml-system", - "srml-timestamp", - "substrate-primitives", - "substrate-versioned-store", + "async-std", + "derive_more", + "futures-util", + "hyper 0.13.7", + "log", + "prometheus", + "tokio 0.2.22", ] [[package]] name = "substrate-wasm-builder-runner" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30c70de7e7d5fd404fe26db1e7a4d6b553e2760b1ac490f249c04a960c483b8" - -[[package]] -name = "substrate-wasm-interface" -version = "2.0.0" -source = "git+https://github.com/paritytech/substrate.git?rev=c37bb08535c49a12320af7facfd555ce05cce2e8#c37bb08535c49a12320af7facfd555ce05cce2e8" -dependencies = [ - "wasmi 0.5.1", -] +version = "1.0.6" +source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4" [[package]] name = "subtle" @@ -6150,13 +6795,13 @@ checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" [[package]] name = "syn" -version = "0.15.44" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", + "quote 0.3.15", + "synom", + "unicode-xid 0.0.4", ] [[package]] @@ -6165,32 +6810,54 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "unicode-xid 0.2.0", ] +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +dependencies = [ + "unicode-xid 0.0.4", +] + [[package]] name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", "unicode-xid 0.2.0", ] [[package]] name = "sysinfo" -version = "0.9.6" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4b2468c629cffba39c0a4425849ab3cdb03d9dfacba69684609aea04d08ff9" +checksum = "1cac193374347e7c263c5f547524f36ff8ec6702d56c8799c8331d26dffe8c1e" dependencies = [ "cfg-if", "doc-comment", "libc", + "ntapi", + "once_cell", "rayon", "winapi 0.3.8", ] @@ -6201,12 +6868,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" -[[package]] -name = "target_info" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" - [[package]] name = "tempfile" version = "3.1.0" @@ -6240,12 +6901,23 @@ dependencies = [ ] [[package]] -name = "thread_local" -version = "0.3.6" +name = "thiserror" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" dependencies = [ - "lazy_static", + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +dependencies = [ + "proc-macro2", + "quote 1.0.7", + "syn 1.0.17", ] [[package]] @@ -6259,9 +6931,9 @@ dependencies = [ [[package]] name = "threadpool" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" dependencies = [ "num_cpus", ] @@ -6277,21 +6949,6 @@ dependencies = [ "winapi 0.3.8", ] -[[package]] -name = "tiny-bip39" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c5676413eaeb1ea35300a0224416f57abc3bd251657e0fafc12c47ff98c060" -dependencies = [ - "failure", - "hashbrown 0.1.8", - "hmac", - "once_cell 0.1.8", - "pbkdf2", - "rand 0.6.5", - "sha2", -] - [[package]] name = "tiny-bip39" version = "0.7.3" @@ -6300,23 +6957,14 @@ checksum = "b0165e045cc2ae1660270ca65e1676dbaab60feb0f91b10f7d0665e9b47e31f2" dependencies = [ "failure", "hmac", - "once_cell 1.4.0", + "once_cell", "pbkdf2", "rand 0.7.3", "rustc-hash", - "sha2", + "sha2 0.8.1", "unicode-normalization", ] -[[package]] -name = "tiny-keccak" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" -dependencies = [ - "crunchy", -] - [[package]] name = "tiny-keccak" version = "2.0.2" @@ -6338,11 +6986,11 @@ dependencies = [ "num_cpus", "tokio-codec", "tokio-current-thread", - "tokio-executor", + "tokio-executor 0.1.10", "tokio-fs", "tokio-io", "tokio-reactor", - "tokio-sync", + "tokio-sync 0.1.8", "tokio-tcp", "tokio-threadpool", "tokio-timer", @@ -6350,6 +6998,28 @@ dependencies = [ "tokio-uds", ] +[[package]] +name = "tokio" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "slab", + "winapi 0.3.8", +] + [[package]] name = "tokio-buf" version = "0.1.1" @@ -6379,29 +7049,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ "futures 0.1.29", - "tokio-executor", + "tokio-executor 0.1.10", ] [[package]] -name = "tokio-dns-unofficial" -version = "0.4.0" +name = "tokio-executor" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c65483db54eb91b4ef3a9389a3364558590faf30ce473141707c0e16fda975" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ + "crossbeam-utils", "futures 0.1.29", - "futures-cpupool", - "lazy_static", - "tokio", ] [[package]] name = "tokio-executor" -version = "0.1.10" +version = "0.2.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" +checksum = "9ee9ceecf69145923834ea73f32ba40c790fd877b74a7817dd0b089f1eb9c7c8" dependencies = [ - "crossbeam-utils 0.7.2", - "futures 0.1.29", + "futures-util-preview", + "lazy_static", + "tokio-sync 0.2.0-alpha.6", ] [[package]] @@ -6426,13 +7095,26 @@ dependencies = [ "log", ] +[[package]] +name = "tokio-named-pipes" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.29", + "mio", + "mio-named-pipes", + "tokio 0.1.22", +] + [[package]] name = "tokio-reactor" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ - "crossbeam-utils 0.7.2", + "crossbeam-utils", "futures 0.1.29", "lazy_static", "log", @@ -6440,25 +7122,32 @@ dependencies = [ "num_cpus", "parking_lot 0.9.0", "slab", - "tokio-executor", + "tokio-executor 0.1.10", "tokio-io", - "tokio-sync", + "tokio-sync 0.1.8", ] [[package]] name = "tokio-rustls" -version = "0.10.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d7cf08f990090abd6c6a73cab46fed62f85e8aef8b99e4b918a9f4a637f0676" +checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", - "iovec", + "futures-core", "rustls", - "tokio-io", + "tokio 0.2.22", "webpki", ] +[[package]] +name = "tokio-service" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +dependencies = [ + "futures 0.1.29", +] + [[package]] name = "tokio-sync" version = "0.1.8" @@ -6469,6 +7158,17 @@ dependencies = [ "futures 0.1.29", ] +[[package]] +name = "tokio-sync" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1aaeb685540f7407ea0e27f1c9757d258c7c6bf4e3eb19da6fc59b747239d2" +dependencies = [ + "fnv", + "futures-core-preview", + "futures-util-preview", +] + [[package]] name = "tokio-tcp" version = "0.1.4" @@ -6491,13 +7191,13 @@ checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ "crossbeam-deque", "crossbeam-queue", - "crossbeam-utils 0.7.2", + "crossbeam-utils", "futures 0.1.29", "lazy_static", "log", "num_cpus", "slab", - "tokio-executor", + "tokio-executor 0.1.10", ] [[package]] @@ -6506,10 +7206,10 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils 0.7.2", + "crossbeam-utils", "futures 0.1.29", "slab", - "tokio-executor", + "tokio-executor 0.1.10", ] [[package]] @@ -6529,9 +7229,9 @@ dependencies = [ [[package]] name = "tokio-uds" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5076db410d6fdc6523df7595447629099a1fdc47b3d9f896220780fa48faf798" +checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" dependencies = [ "bytes 0.4.12", "futures 0.1.29", @@ -6545,6 +7245,20 @@ dependencies = [ "tokio-reactor", ] +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio 0.2.22", +] + [[package]] name = "toml" version = "0.5.6" @@ -6554,13 +7268,20 @@ dependencies = [ "serde", ] +[[package]] +name = "tower-service" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" + [[package]] name = "tracing" -version = "0.1.17" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbdf4ccd1652592b01286a5dbe1e2a77d78afaa34beadd9872a5f7396f92aaa9" +checksum = "c2e2a2de6b0d5cbb13fc21193a2296888eaab62b6044479aafb3c54c01c29fcd" dependencies = [ "cfg-if", + "log", "tracing-attributes", "tracing-core", ] @@ -6571,8 +7292,8 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0693bf8d6f2bf22c690fc61a9d21ac69efdbb894a17ed596b9af0f01e64b84b" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", ] @@ -6591,19 +7312,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" -[[package]] -name = "trie-db" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0b62d27e8aa1c07414549ac872480ac82380bab39e730242ab08d82d7cc098a" -dependencies = [ - "elastic-array", - "hash-db", - "hashbrown 0.6.3", - "log", - "rand 0.6.5", -] - [[package]] name = "trie-db" version = "0.21.0" @@ -6617,15 +7325,6 @@ dependencies = [ "smallvec 1.4.1", ] -[[package]] -name = "trie-root" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b779f7c1c8fe9276365d9d5be5c4b5adeacf545117bb3f64c974305789c5c0b" -dependencies = [ - "hash-db", -] - [[package]] name = "trie-root" version = "0.16.0" @@ -6637,20 +7336,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" - -[[package]] -name = "twofish" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" -dependencies = [ - "block-cipher-trait", - "byteorder 1.3.4", - "opaque-debug", -] +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "twox-hash" @@ -6673,7 +7361,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "173cd16430c206dc1a430af8a89a0e9c076cf15cb42b4aedb10e8cc8fee73681" dependencies = [ - "byteorder 1.3.4", + "byteorder", "crunchy", "rustc-hex", "static_assertions", @@ -6685,7 +7373,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.1", + "version_check", ] [[package]] @@ -6714,15 +7402,15 @@ checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-width" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.1.0" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" [[package]] name = "unicode-xid" @@ -6730,27 +7418,43 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "universal-hash" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" +dependencies = [ + "generic-array 0.14.3", + "subtle 2.2.2", +] + [[package]] name = "unsigned-varint" -version = "0.2.3" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f0023a96687fe169081e8adce3f65e3874426b7886e9234d490af2dc077959" +checksum = "f67332660eb59a6f1eb24ff1220c9e8d01738a8503c6002e30bcfe4bd9f2b4a9" dependencies = [ - "bytes 0.4.12", - "tokio-codec", + "bytes 0.5.6", + "futures-io", + "futures-util", + "futures_codec 0.3.4", ] [[package]] name = "unsigned-varint" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38e01ad4b98f042e166c1bf9a13f9873a99d79eaa171ce7ca81e6dd0f895d8a" +checksum = "669d776983b692a906c881fcd0cfb34271a48e197e4d6cb8df32b05bfc3d3fa5" +dependencies = [ + "bytes 0.5.6", + "futures_codec 0.4.0", +] [[package]] name = "untrusted" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" @@ -6776,37 +7480,21 @@ dependencies = [ [[package]] name = "vcpkg" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" [[package]] name = "vec_map" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" - -[[package]] -name = "vergen" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" -dependencies = [ - "bitflags", - "chrono", -] - -[[package]] -name = "version_check" -version = "0.1.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "void" @@ -6825,6 +7513,16 @@ dependencies = [ "try-lock", ] +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -6850,20 +7548,19 @@ dependencies = [ "bumpalo", "lazy_static", "log", - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.3.27" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83420b37346c311b9ed822af41ec2e82839bfe99867ec6c54e2da43b7538771c" +checksum = "7add542ea1ac7fdaa9dc25e031a6af33b7d63376292bd24140c637d00d1c312a" dependencies = [ "cfg-if", - "futures 0.1.29", "js-sys", "wasm-bindgen", "web-sys", @@ -6875,7 +7572,7 @@ version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" dependencies = [ - "quote 1.0.3", + "quote 1.0.7", "wasm-bindgen-macro-support", ] @@ -6885,8 +7582,8 @@ version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -6900,32 +7597,20 @@ checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" [[package]] name = "wasm-timer" -version = "0.1.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa3e01d234bb71760e685cfafa5e2c96f8ad877c161a721646356651069e26ac" +checksum = "324c5e65a08699c9c4334ba136597ab22b85dccd4b65dd1e36ccf8f723a95b54" dependencies = [ - "futures 0.1.29", + "futures 0.3.4", "js-sys", - "send_wrapper", - "tokio-timer", + "parking_lot 0.9.0", + "pin-utils", + "send_wrapper 0.2.0", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] -[[package]] -name = "wasmi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31d26deb2d9a37e6cfed420edce3ed604eab49735ba89035e13c98f9a528313" -dependencies = [ - "libc", - "memory_units", - "num-rational", - "num-traits", - "parity-wasm 0.40.3", - "wasmi-validation 0.2.0", -] - [[package]] name = "wasmi" version = "0.6.2" @@ -6935,18 +7620,9 @@ dependencies = [ "libc", "memory_units", "num-rational", - "num-traits", - "parity-wasm 0.41.0", - "wasmi-validation 0.3.0", -] - -[[package]] -name = "wasmi-validation" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bc0356e3df56e639fc7f7d8a99741915531e27ed735d911ed83d7e1339c8188" -dependencies = [ - "parity-wasm 0.40.3", + "num-traits 0.2.11", + "parity-wasm", + "wasmi-validation", ] [[package]] @@ -6955,7 +7631,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea78c597064ba73596099281e2f4cfc019075122a65cdda3205af94f0b264d93" dependencies = [ - "parity-wasm 0.41.0", + "parity-wasm", ] [[package]] @@ -6970,9 +7646,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.21.2" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef" +checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" dependencies = [ "ring", "untrusted", @@ -6980,29 +7656,28 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a262ae37dd9d60f60dd473d1158f9fbebf110ba7b6a5051c8160460f6043718b" +checksum = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" dependencies = [ "webpki", ] [[package]] name = "webpki-roots" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4" +checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739" dependencies = [ "webpki", ] [[package]] name = "which" -version = "2.0.1" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" dependencies = [ - "failure", "libc", ] @@ -7036,9 +7711,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi 0.3.8", ] @@ -7055,7 +7730,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51a2c47b5798ccc774ffb93ff536aec7c4275d722fd9c740c83cdd1af1f2d94" dependencies = [ - "byteorder 1.3.4", + "byteorder", "bytes 0.4.12", "httparse", "log", @@ -7079,50 +7754,29 @@ dependencies = [ [[package]] name = "x25519-dalek" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" +checksum = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" dependencies = [ - "clear_on_drop", - "curve25519-dalek 1.2.3", - "rand_core 0.3.1", + "curve25519-dalek", + "rand_core 0.5.1", + "zeroize", ] -[[package]] -name = "xdg" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" - [[package]] name = "yamux" -version = "0.2.2" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2758f29014c1cb7a6e74c1b1160ac8c8203be342d35b73462fc6a13cc6385423" +checksum = "cd37e58a1256a0b328ce9c67d8b62ecdd02f4803ba443df478835cb1a41a637c" dependencies = [ - "bytes 0.4.12", - "futures 0.1.29", + "futures 0.3.4", "log", "nohash-hasher", - "parking_lot 0.9.0", - "quick-error", + "parking_lot 0.10.2", "rand 0.7.3", - "tokio-codec", - "tokio-io", + "static_assertions", ] -[[package]] -name = "zeroize" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" - -[[package]] -name = "zeroize" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4090487fa66630f7b166fba2bbb525e247a5449f41c468cc1d98f8ae6ac03120" - [[package]] name = "zeroize" version = "1.1.0" @@ -7138,8 +7792,8 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "proc-macro2", + "quote 1.0.7", "syn 1.0.17", "synstructure", -] +] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 7c07a42507..8ff7a63adf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,13 +12,13 @@ members = [ "runtime-modules/membership", "runtime-modules/memo", "runtime-modules/recurring-reward", - "runtime-modules/roles", "runtime-modules/service-discovery", "runtime-modules/stake", "runtime-modules/storage", "runtime-modules/token-minting", "runtime-modules/versioned-store", "runtime-modules/versioned-store-permissions", + "runtime-modules/working-group", "runtime-modules/content-directory", "node", "utils/chain-spec-builder/" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..53d1f3d018 --- /dev/null +++ b/LICENSE @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/README.md b/README.md index 1f3e32b8da..47c8a285a8 100644 --- a/README.md +++ b/README.md @@ -1,149 +1,142 @@ -# Joystream +# Joystream [![Build Status](https://travis-ci.org/Joystream/joystream.svg?branch=master)](https://travis-ci.org/Joystream/joystream) -This is the main code reposity for all joystream software. It will house the substrate chain project, the full node and runtime and all reusable substrate runtime modules that make up the joystream runtime. In addition to all front-end apps and infrastructure servers necessary for operating the network. - -The repository is currently just a cargo workspace, but eventually will also contain yarn workspaces, and possibly other project type workspaces. +This is the main code repository for all Joystream software. In this mono-repo you will find all the software required to run a Joystream network: The Joystream full node, runtime and all reusable substrate runtime modules that make up the Joystream runtime. In addition to all front-end apps and infrastructure servers necessary for operating the network. ## Overview -The joystream network builds on a pre-release version of [substrate v2.0](https://substrate.dev/) and adds additional +The Joystream network builds on a pre-release version of [substrate v2.0](https://substrate.dev/) and adds additional functionality to support the [various roles](https://www.joystream.org/roles) that can be entered into on the platform. +## Build Status -## Validator -![ Nodes for Joystream](./node/validator-node-banner.svg) - -Joystream node is the main server application that connects to the network, synchronizes the blockchain with other nodes and produces blocks if configured as a validator node. - -To setup a full node and validator review the [advanced guide from the helpdesk](https://github.com/Joystream/helpdesk/tree/master/roles/validators). - - -### Pre-built Binaries - -The latest pre-built binaries can be downloads from the [releases](https://github.com/Joystream/substrate-runtime-joystream/releases) page. +Development [![Development Branch Build Status](https://travis-ci.org/Joystream/joystream.svg?branch=development)](https://travis-ci.org/Joystream/joystream) - build history on [Travis](https://travis-ci.org/github/Joystream/joystream/builds) +## Development Tools -### Building from source +The following tools are required for building, testing and contributing to this repo: -Clone the repository and install build tools: +- [Rust](https://www.rust-lang.org/tools/install) toolchain - _required_ +- [nodejs](https://nodejs.org/) v12.x - _required_ +- [yarn classic](https://classic.yarnpkg.com/en/docs/install) package manager v1.22.x- _required_ +- [docker](https://www.docker.com/get-started) - _optional_ +- [ansible](https://www.ansible.com/) - _optional_ -```bash -git clone https://github.com/Joystream/substrate-runtime-joystream.git +If you use VSCode as your code editor we recommend using the workspace [settings](devops/vscode/settings.json) for recommend eslint plugin to function properly. -cd substrate-runtime-joystream/ +After cloning the repo run the following initialization scripts: +```sh +# Install rust toolchain ./setup.sh -``` -### Building +# Install npm package dependencies +# Also good habit to run this when switching between branches +yarn install -```bash -cargo build --release +# run some tests +yarn cargo-checks ``` -### Running a public node on the Rome testnet +## Software -Run the node and connect to the public testnet. +**Substrate blockchain** -```bash -cargo run --release -- --chain ./rome-tesnet.json -``` +- [joystream-node](./node) +- [runtime](./runtime) +- [runtime modules](./runtime-modules) -The `rome-testnet.json` chain file can be ontained from the [release page](https://github.com/Joystream/substrate-runtime-joystream/releases/tag/v6.8.0) +**Server Applications - infrastructure** +- [Storage Node](./storage-node) - Media Storage Infrastructure +- [Query Node](https://github.com/Joystream/joystream/tree/query_node/query-node) - _under development_ +- [Discovery Node](https://github.com/Joystream/joystream/tree/init_discovery_node/discovery_node) - _under development_ -### Installing a release build -This will install the executable `joystream-node` to your `~/.cargo/bin` folder, which you would normally have in your `$PATH` environment. +**Front-end Applications** -```bash -cargo install joystream-node --path node/ -``` +- [Pioneer](./pioneer) - Main UI for accessing all Joystream features +- [Atlas](https://github.com/Joystream/joystream/tree/init_atlas/atlas) - Media Player- _under development_ -Now you can run +**Tools and CLI** -```bash -joystream-node --chain rome-testnet.json -``` +- [joystream-cli](./cli) - CLI for community and governance activities -### Local development +**Testing infrastructure** -This will build and run a fresh new local development chain purging existing one: +- [Network integration](./tests/network-tests) - Joystream network integration testing framework -```bash -./scripts/run-dev-chain.sh -``` - -### Unit tests +## Exploring the network with Pioneer -```bash -cargo test -``` +Pioneer is currently the main web interface to interact with the network: -### API integration tests - -```bash -./scripts/run-dev-chain.sh -yarn test -``` +Currently hosted on: https://testnet.joystream.org -To run the integration tests with a different chain, you can omit step running the local development chain and set the node URL using `NODE_URL` environment variable. +You may have to disable some privacy/ad-blocker extensions in your browser for proper functionality, especially when browsing media. -## Joystream Runtime +You can also run a local development instance: -![Joystream Runtime](./runtime/runtime-banner.svg) +The HEAD of the master branch should always be used for the correct version of the applications to connect to the current testnet: +```sh +git checkout master +yarn install +yarn workspace pioneer start +``` -The runtime is the code that defines the consensus rules of the Joystream protocol. -It is compiled to WASM and lives on chain. -Joystream node execute the code's logic to validate transactions and blocks on the blockchain. +This runs a local development web server on port 3000. -When building joystream-node as described abot with `cargo build --release`, in addition to the joystream-node binary being built the WASM blob artifact is produced in: +Use the link below to browse the network using the publicly hosted endpoint: +http://localhost:3000/?rpc=wss://rome-rpc-endpoint.joystream.org:9944/ -`target/release/wbuild/joystream-node-runtime/joystream_node_runtime.compact.wasm` +## Running a local full node +You can also run your our own joystream-node: -### Deployment +```sh +git checkout master +cargo build --release +cargo run --release -- --pruning archive --chain testnets/rome.json +``` -Deploying the compiled runtime on a live system can be done in one of two ways: +Wait for the node to sync to the latest block, then change pioneer settings "remote node" option to "Local Node", or follow the link below: -1. Joystream runtime upgrade proposals which will be voted on by the council. When the Joystream platform is live, this will be the only way to upgrade the chain's runtime code. +http://localhost:3000/?rpc=ws://localhost:9944/ -2. During development and testnet phases, we can send an extrinsic (transaction signed with the sudo key) invoking `system::setCode()`. This can be done either from the UI/extrinsics app, or directly with an admin script. +Learn more about [joystream-node](node/README.md). -### Versioning the runtime +A step by step guide to setup a full node and validator on the Joystream testnet, can be found [here](https://github.com/Joystream/helpdesk/tree/master/roles/validators). -Versioning of the runtime is set in `runtime/src/lib.rs` -For detailed information about how to set correct version numbers when developing a new runtime, [see this](https://github.com/Joystream/substrate-runtime-joystream/issues/1) +### Contributing +We have lots of good first [issues](https://github.com/Joystream/joystream/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) open to help you get started on contributing code. If you are not a developer you can still make valuable contributions by testing our software and providing feedback and opening new issues. -## Coding style +A description of our [branching model](https://github.com/Joystream/joystream/issues/638) will help you to understand where work on different software components happens, and consequently where to direct your pull requests. -We use `cargo-fmt` to format the source code for consistency. +We rely on `eslint` for code quality of our JavaScript and TypeScript code and `prettier` for consistent formatting. For Rust we rely on `rustfmt` and `clippy`. -It should be available on your machine if you ran the `setup.sh` script, otherwise install it with rustup: +The [husky](https://www.npmjs.com/package/husky#ci-servers) npm package is used to manage the project git-hooks. This is automatically installed and setup when you run `yarn install`. -```bash -rustup component add rustfmt -``` +When you `git commit` and `git push` some scripts will run automatically to ensure committed code passes lint, tests, and code-style checks. -Applying code formatting on all source files recursing subfolders: +During a rebase/merge you may want to skip all hooks, you can use `HUSKY_SKIP_HOOKS` environment variable. ``` -cargo-fmt +HUSKY_SKIP_HOOKS=1 git rebase ... ``` -## Contributing +## RLS Extension in VScode or Atom Editors + +If you use RLS extension in your IDE, start your editor with the `BUILD_DUMMY_WASM_BINARY=1` environment set to workaround a build issue that occurs in the IDE only. -Please see our [contributing guidlines](https://github.com/Joystream/joystream#contribute) for details on our code of conduct, and the process for submitting pull requests to us. +`BUILD_DUMMY_WASM_BINARY=1 code ./joystream` ## Authors -See also the list of [CONTRIBUTORS](./CONTRIBUTORS) who participated in this project. +See the list of [contributors](https://github.com/Joystream/joystream/graphs/contributors) who participated in this project. ## License -This project is licensed under the GPLv3 License - see the [LICENSE](LICENSE) file for details +All software under this project is licensed as [GPLv3](./LICENSE) unless otherwise indicated. ## Acknowledgments -Thanks to the whole [Parity Tech](https://www.parity.io/) team for making substrate and helping on riot chat with tips, suggestions, tutorials and answering all our questions during development. +Thanks to the whole [Parity Tech](https://www.parity.io/) team for making substrate and helping in chat with tips, suggestions, tutorials and answering all our questions during development. diff --git a/cli/.editorconfig b/cli/.editorconfig deleted file mode 100644 index c3f635323a..0000000000 --- a/cli/.editorconfig +++ /dev/null @@ -1,11 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 4 -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/cli/.eslintrc b/cli/.eslintrc deleted file mode 100644 index 7b846193cc..0000000000 --- a/cli/.eslintrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": [ - "oclif", - "oclif-typescript" - ] -} diff --git a/cli/.eslintrc.js b/cli/.eslintrc.js new file mode 100644 index 0000000000..5ef4baaeed --- /dev/null +++ b/cli/.eslintrc.js @@ -0,0 +1,17 @@ +module.exports = { + env: { + mocha: true, + }, + extends: [ + // The oclif rules have some code-style/formatting rules which may conflict with + // our prettier global settings. Disabling for now + // I suggest to only add essential rules absolutely required to make the cli work with oclif + // at the end of this file. + // "oclif", + // "oclif-typescript", + ], + rules: { + "no-unused-vars": "off", // Required by the typescript rule below + "@typescript-eslint/no-unused-vars": ["error"] + } +} diff --git a/cli/.prettierignore b/cli/.prettierignore new file mode 100644 index 0000000000..90379ecbbe --- /dev/null +++ b/cli/.prettierignore @@ -0,0 +1,2 @@ +/lib/ +.nyc_output diff --git a/cli/README.md b/cli/README.md index 591a949854..974725a1dc 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,16 +1,17 @@ -joystream-cli +@joystream/cli ============= Command Line Interface for Joystream community and governance activities [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io) -[![Version](https://img.shields.io/npm/v/joystream-cli.svg)](https://npmjs.org/package/joystream-cli) -[![Downloads/week](https://img.shields.io/npm/dw/joystream-cli.svg)](https://npmjs.org/package/joystream-cli) -[![License](https://img.shields.io/npm/l/joystream-cli.svg)](https://github.com/Joystream/cli/blob/master/package.json) +[![Version](https://img.shields.io/npm/v/@joystream/cli.svg)](https://npmjs.org/package/@joystream/cli) +[![Downloads/week](https://img.shields.io/npm/dw/@joystream/cli.svg)](https://npmjs.org/package/@joystream/cli) +[![License](https://img.shields.io/npm/l/@joystream/cli.svg)](https://github.com/Joystream/joystream/blob/master/cli/package.json) * [Development](#development) * [Usage](#usage) +* [First steps](#first-steps) * [Commands](#commands) @@ -19,19 +20,17 @@ Command Line Interface for Joystream community and governance activities To run a command in developemnt environment (without installing the package): 1. Navigate into the CLI root directory -1. Either execute any command like this: +1. Execute any command like this: ``` $ ./bin/run COMMAND ``` - Or use: +Alternatively: - ``` - $ npm link - ``` - - And then execute any command like this: +1. Navigate into the CLI root directory +1. Execute `yarn link` (if that doesn't work, consider `sudo yarn link`) +1. Execute command from any location like this: ``` $ joystream-cli COMMAND @@ -41,17 +40,29 @@ To run a command in developemnt environment (without installing the package): # Usage ```sh-session -$ npm install -g joystream-cli +$ npm install -g @joystream/cli $ joystream-cli COMMAND running command... $ joystream-cli (-v|--version|version) -joystream-cli/0.0.0 linux-x64 node-v13.12.0 +@joystream/cli/0.1.0 linux-x64 node-v13.12.0 $ joystream-cli --help [COMMAND] USAGE $ joystream-cli COMMAND ... ``` + +# First steps + +When using the CLI for the first time there are a few common steps you might want to take in order to configure the CLI: + +1. Set the correct node endpoint. You can do this by executing `api:setUri` or any command that requires an api connection. To verify the current endpoint you can execute `api:getUri`. +1. In order to use the accounts/keys that you may already have access to within Pioneer, you need to dowload the backup json file(s) ([https://testnet.joystream.org/#/accounts](https://testnet.joystream.org/#/accounts)) and import them into the CLI by executing `account:import /path/to/backup.json`. +1. By executing `account:choose` you can choose one of the imported accounts, that will then serve as context for the next commands (you can check currently selected account using `account:info`). If you just want to use the development _Alice_ or _Bob_ account, you can access them without importing by providing an additional flag: `account:choose --showSpecial`. +1. The context should now be fully set up! Feel free to use the `--help` flag to investigate the available commands or take a look at the sections below. +1. You may also find it useful to get the first part of the command (before the colon) autocompleted when you press `[Tab]` while typing the name in the console. Executing `autocomplete` command will provide the instructions on how to set this up (see documentation below). + + # Commands * [`joystream-cli account:choose`](#joystream-cli-accountchoose) @@ -63,9 +74,27 @@ USAGE * [`joystream-cli account:transferTokens RECIPIENT AMOUNT`](#joystream-cli-accounttransfertokens-recipient-amount) * [`joystream-cli api:getUri`](#joystream-cli-apigeturi) * [`joystream-cli api:inspect`](#joystream-cli-apiinspect) -* [`joystream-cli api:setUri URI`](#joystream-cli-apiseturi-uri) +* [`joystream-cli api:setUri [URI]`](#joystream-cli-apiseturi-uri) +* [`joystream-cli autocomplete [SHELL]`](#joystream-cli-autocomplete-shell) * [`joystream-cli council:info`](#joystream-cli-councilinfo) * [`joystream-cli help [COMMAND]`](#joystream-cli-help-command) +* [`joystream-cli working-groups:application WGAPPLICATIONID`](#joystream-cli-working-groupsapplication-wgapplicationid) +* [`joystream-cli working-groups:createOpening`](#joystream-cli-working-groupscreateopening) +* [`joystream-cli working-groups:decreaseWorkerStake WORKERID`](#joystream-cli-working-groupsdecreaseworkerstake-workerid) +* [`joystream-cli working-groups:evictWorker WORKERID`](#joystream-cli-working-groupsevictworker-workerid) +* [`joystream-cli working-groups:fillOpening WGOPENINGID`](#joystream-cli-working-groupsfillopening-wgopeningid) +* [`joystream-cli working-groups:increaseStake`](#joystream-cli-working-groupsincreasestake) +* [`joystream-cli working-groups:leaveRole`](#joystream-cli-working-groupsleaverole) +* [`joystream-cli working-groups:opening WGOPENINGID`](#joystream-cli-working-groupsopening-wgopeningid) +* [`joystream-cli working-groups:openings`](#joystream-cli-working-groupsopenings) +* [`joystream-cli working-groups:overview`](#joystream-cli-working-groupsoverview) +* [`joystream-cli working-groups:slashWorker WORKERID`](#joystream-cli-working-groupsslashworker-workerid) +* [`joystream-cli working-groups:startAcceptingApplications WGOPENINGID`](#joystream-cli-working-groupsstartacceptingapplications-wgopeningid) +* [`joystream-cli working-groups:startReviewPeriod WGOPENINGID`](#joystream-cli-working-groupsstartreviewperiod-wgopeningid) +* [`joystream-cli working-groups:terminateApplication WGAPPLICATIONID`](#joystream-cli-working-groupsterminateapplication-wgapplicationid) +* [`joystream-cli working-groups:updateRewardAccount [ACCOUNTADDRESS]`](#joystream-cli-working-groupsupdaterewardaccount-accountaddress) +* [`joystream-cli working-groups:updateRoleAccount [ACCOUNTADDRESS]`](#joystream-cli-working-groupsupdateroleaccount-accountaddress) +* [`joystream-cli working-groups:updateWorkerReward WORKERID`](#joystream-cli-working-groupsupdateworkerreward-workerid) ## `joystream-cli account:choose` @@ -74,6 +103,9 @@ Choose default account to use in the CLI ``` USAGE $ joystream-cli account:choose + +OPTIONS + --showSpecial Whether to show special (DEV chain) accounts ``` _See code: [src/commands/account/choose.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/account/choose.ts)_ @@ -189,15 +221,15 @@ OPTIONS If no "--method" flag is provided then all methods in that module will be listed along with the descriptions. -a, --callArgs=callArgs - Specifies the arguments to use when calling a method. Multiple arguments can be separated with a comma, ie. + Specifies the arguments to use when calling a method. Multiple arguments can be separated with a comma, ie. "-a=arg1,arg2". You can omit this flag even if the method requires some aguments. In that case you will be promted to provide value for each required argument. - Ommiting this flag is recommended when input parameters are of more complex types (and it's hard to specify them as + Ommiting this flag is recommended when input parameters are of more complex types (and it's hard to specify them as just simple comma-separated strings) -e, --exec - Provide this flag if you want to execute the actual call, instead of displaying the method description (which is + Provide this flag if you want to execute the actual call, instead of displaying the method description (which is default) -m, --method=method @@ -212,27 +244,50 @@ EXAMPLES $ api:inspect $ api:inspect -t=query $ api:inspect -t=query -M=members - $ api:inspect -t=query -M=members -m=memberProfile - $ api:inspect -t=query -M=members -m=memberProfile -e - $ api:inspect -t=query -M=members -m=memberProfile -e -a=1 + $ api:inspect -t=query -M=members -m=membershipById + $ api:inspect -t=query -M=members -m=membershipById -e + $ api:inspect -t=query -M=members -m=membershipById -e -a=1 ``` _See code: [src/commands/api/inspect.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/api/inspect.ts)_ -## `joystream-cli api:setUri URI` +## `joystream-cli api:setUri [URI]` Set api WS provider uri ``` USAGE - $ joystream-cli api:setUri URI + $ joystream-cli api:setUri [URI] ARGUMENTS - URI Uri of the node api WS provider + URI Uri of the node api WS provider (if skipped, a prompt will be displayed) ``` _See code: [src/commands/api/setUri.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/api/setUri.ts)_ +## `joystream-cli autocomplete [SHELL]` + +display autocomplete installation instructions + +``` +USAGE + $ joystream-cli autocomplete [SHELL] + +ARGUMENTS + SHELL shell type + +OPTIONS + -r, --refresh-cache Refresh cache (ignores displaying instructions) + +EXAMPLES + $ joystream-cli autocomplete + $ joystream-cli autocomplete bash + $ joystream-cli autocomplete zsh + $ joystream-cli autocomplete --refresh-cache +``` + +_See code: [@oclif/plugin-autocomplete](https://github.com/oclif/plugin-autocomplete/blob/v0.2.0/src/commands/autocomplete/index.ts)_ + ## `joystream-cli council:info` Get current council and council elections information @@ -260,4 +315,322 @@ OPTIONS ``` _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v2.2.3/src/commands/help.ts)_ + +## `joystream-cli working-groups:application WGAPPLICATIONID` + +Shows an overview of given application by Working Group Application ID + +``` +USAGE + $ joystream-cli working-groups:application WGAPPLICATIONID + +ARGUMENTS + WGAPPLICATIONID Working Group Application ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/application.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/application.ts)_ + +## `joystream-cli working-groups:createOpening` + +Create working group opening (requires lead access) + +``` +USAGE + $ joystream-cli working-groups:createOpening + +OPTIONS + -c, --createDraftOnly If provided - the extrinsic will not be executed. Use this flag if you only want to create + a draft. + + -d, --useDraft Whether to create the opening from existing draft. + If provided without --draftName - the list of choices will be displayed. + + -g, --group=group (required) [default: storageProviders] The working group context in which the command + should be executed + Available values are: storageProviders. + + -n, --draftName=draftName Name of the draft to create the opening from. + + -s, --skipPrompts Whether to skip all prompts when adding from draft (will use all default values) +``` + +_See code: [src/commands/working-groups/createOpening.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/createOpening.ts)_ + +## `joystream-cli working-groups:decreaseWorkerStake WORKERID` + +Decreases given worker stake by an amount that will be returned to the worker role account. Requires lead access. + +``` +USAGE + $ joystream-cli working-groups:decreaseWorkerStake WORKERID + +ARGUMENTS + WORKERID Worker ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/decreaseWorkerStake.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/decreaseWorkerStake.ts)_ + +## `joystream-cli working-groups:evictWorker WORKERID` + +Evicts given worker. Requires lead access. + +``` +USAGE + $ joystream-cli working-groups:evictWorker WORKERID + +ARGUMENTS + WORKERID Worker ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/evictWorker.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/evictWorker.ts)_ + +## `joystream-cli working-groups:fillOpening WGOPENINGID` + +Allows filling working group opening that's currently in review. Requires lead access. + +``` +USAGE + $ joystream-cli working-groups:fillOpening WGOPENINGID + +ARGUMENTS + WGOPENINGID Working Group Opening ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/fillOpening.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/fillOpening.ts)_ + +## `joystream-cli working-groups:increaseStake` + +Increases current role (lead/worker) stake. Requires active role account to be selected. + +``` +USAGE + $ joystream-cli working-groups:increaseStake + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/increaseStake.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/increaseStake.ts)_ + +## `joystream-cli working-groups:leaveRole` + +Leave the worker or lead role associated with currently selected account. + +``` +USAGE + $ joystream-cli working-groups:leaveRole + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/leaveRole.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/leaveRole.ts)_ + +## `joystream-cli working-groups:opening WGOPENINGID` + +Shows an overview of given working group opening by Working Group Opening ID + +``` +USAGE + $ joystream-cli working-groups:opening WGOPENINGID + +ARGUMENTS + WGOPENINGID Working Group Opening ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/opening.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/opening.ts)_ + +## `joystream-cli working-groups:openings` + +Shows an overview of given working group openings + +``` +USAGE + $ joystream-cli working-groups:openings + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/openings.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/openings.ts)_ + +## `joystream-cli working-groups:overview` + +Shows an overview of given working group (current lead and workers) + +``` +USAGE + $ joystream-cli working-groups:overview + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/overview.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/overview.ts)_ + +## `joystream-cli working-groups:slashWorker WORKERID` + +Slashes given worker stake. Requires lead access. + +``` +USAGE + $ joystream-cli working-groups:slashWorker WORKERID + +ARGUMENTS + WORKERID Worker ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/slashWorker.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/slashWorker.ts)_ + +## `joystream-cli working-groups:startAcceptingApplications WGOPENINGID` + +Changes the status of pending opening to "Accepting applications". Requires lead access. + +``` +USAGE + $ joystream-cli working-groups:startAcceptingApplications WGOPENINGID + +ARGUMENTS + WGOPENINGID Working Group Opening ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/startAcceptingApplications.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/startAcceptingApplications.ts)_ + +## `joystream-cli working-groups:startReviewPeriod WGOPENINGID` + +Changes the status of active opening to "In review". Requires lead access. + +``` +USAGE + $ joystream-cli working-groups:startReviewPeriod WGOPENINGID + +ARGUMENTS + WGOPENINGID Working Group Opening ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/startReviewPeriod.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/startReviewPeriod.ts)_ + +## `joystream-cli working-groups:terminateApplication WGAPPLICATIONID` + +Terminates given working group application. Requires lead access. + +``` +USAGE + $ joystream-cli working-groups:terminateApplication WGAPPLICATIONID + +ARGUMENTS + WGAPPLICATIONID Working Group Application ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/terminateApplication.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/terminateApplication.ts)_ + +## `joystream-cli working-groups:updateRewardAccount [ACCOUNTADDRESS]` + +Updates the worker/lead reward account (requires current role account to be selected) + +``` +USAGE + $ joystream-cli working-groups:updateRewardAccount [ACCOUNTADDRESS] + +ARGUMENTS + ACCOUNTADDRESS New reward account address (if omitted, one of the existing CLI accounts can be selected) + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/updateRewardAccount.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/updateRewardAccount.ts)_ + +## `joystream-cli working-groups:updateRoleAccount [ACCOUNTADDRESS]` + +Updates the worker/lead role account. Requires member controller account to be selected + +``` +USAGE + $ joystream-cli working-groups:updateRoleAccount [ACCOUNTADDRESS] + +ARGUMENTS + ACCOUNTADDRESS New role account address (if omitted, one of the existing CLI accounts can be selected) + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/updateRoleAccount.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/updateRoleAccount.ts)_ + +## `joystream-cli working-groups:updateWorkerReward WORKERID` + +Change given worker's reward (amount only). Requires lead access. + +``` +USAGE + $ joystream-cli working-groups:updateWorkerReward WORKERID + +ARGUMENTS + WORKERID Worker ID + +OPTIONS + -g, --group=group (required) [default: storageProviders] The working group context in which the command should be + executed + Available values are: storageProviders. +``` + +_See code: [src/commands/working-groups/updateWorkerReward.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/working-groups/updateWorkerReward.ts)_ diff --git a/cli/package-lock.json b/cli/package-lock.json deleted file mode 100644 index 5ff94a14a9..0000000000 --- a/cli/package-lock.json +++ /dev/null @@ -1,4671 +0,0 @@ -{ - "name": "joystream-cli", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.8.3" - } - }, - "@babel/generator": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz", - "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==", - "dev": true, - "requires": { - "@babel/types": "^7.9.0", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", - "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", - "dev": true - }, - "@babel/runtime": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", - "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/traverse": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz", - "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.0", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.0", - "@babel/types": "^7.9.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - } - }, - "@babel/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", - "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "@joystream/types": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@joystream/types/-/types-0.6.0.tgz", - "integrity": "sha512-b+6U36GHJLlBPxVqMVQRTZzVxu7BGsjqlC/XJfl/vdx8TOy3P8TIB/3olLU64EPB3cVNadg2p9jqYSsvh9XVAQ==", - "requires": { - "@polkadot/types": "^0.96.1", - "@types/vfile": "^4.0.0", - "ajv": "^6.11.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.3", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.3", - "fastq": "^1.6.0" - } - }, - "@oclif/command": { - "version": "1.5.19", - "resolved": "https://registry.npmjs.org/@oclif/command/-/command-1.5.19.tgz", - "integrity": "sha512-6+iaCMh/JXJaB2QWikqvGE9//wLEVYYwZd5sud8aLoLKog1Q75naZh2vlGVtg5Mq/NqpqGQvdIjJb3Bm+64AUQ==", - "requires": { - "@oclif/config": "^1", - "@oclif/errors": "^1.2.2", - "@oclif/parser": "^3.8.3", - "@oclif/plugin-help": "^2", - "debug": "^4.1.1", - "semver": "^5.6.0" - } - }, - "@oclif/config": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@oclif/config/-/config-1.14.0.tgz", - "integrity": "sha512-KsOP/mx9lzTah+EtGqLUXN3PDL0J3zb9/dTneFyiUK2K6T7vFEGhV6OasmqTh4uMZHGYTGrNPV8x/Yw6qZNL6A==", - "requires": { - "@oclif/errors": "^1.0.0", - "@oclif/parser": "^3.8.0", - "debug": "^4.1.1", - "tslib": "^1.9.3" - } - }, - "@oclif/dev-cli": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/@oclif/dev-cli/-/dev-cli-1.22.2.tgz", - "integrity": "sha512-c7633R37RxrQIpwqPKxjNRm6/jb1yuG8fd16hmNz9Nw+/MUhEtQtKHSCe9ScH8n5M06l6LEo4ldk9LEGtpaWwA==", - "dev": true, - "requires": { - "@oclif/command": "^1.5.13", - "@oclif/config": "^1.12.12", - "@oclif/errors": "^1.2.2", - "@oclif/plugin-help": "^2.1.6", - "cli-ux": "^5.2.1", - "debug": "^4.1.1", - "fs-extra": "^7.0.1", - "github-slugger": "^1.2.1", - "lodash": "^4.17.11", - "normalize-package-data": "^2.5.0", - "qqjs": "^0.3.10", - "tslib": "^1.9.3" - } - }, - "@oclif/errors": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.2.2.tgz", - "integrity": "sha512-Eq8BFuJUQcbAPVofDxwdE0bL14inIiwt5EaKRVY9ZDIG11jwdXZqiQEECJx0VfnLyUZdYfRd/znDI/MytdJoKg==", - "requires": { - "clean-stack": "^1.3.0", - "fs-extra": "^7.0.0", - "indent-string": "^3.2.0", - "strip-ansi": "^5.0.0", - "wrap-ansi": "^4.0.0" - } - }, - "@oclif/linewrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oclif/linewrap/-/linewrap-1.0.0.tgz", - "integrity": "sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw==" - }, - "@oclif/parser": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/@oclif/parser/-/parser-3.8.4.tgz", - "integrity": "sha512-cyP1at3l42kQHZtqDS3KfTeyMvxITGwXwH1qk9ktBYvqgMp5h4vHT+cOD74ld3RqJUOZY/+Zi9lb4Tbza3BtuA==", - "requires": { - "@oclif/linewrap": "^1.0.0", - "chalk": "^2.4.2", - "tslib": "^1.9.3" - } - }, - "@oclif/plugin-help": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-2.2.3.tgz", - "integrity": "sha512-bGHUdo5e7DjPJ0vTeRBMIrfqTRDBfyR5w0MP41u0n3r7YG5p14lvMmiCXxi6WDaP2Hw5nqx3PnkAIntCKZZN7g==", - "requires": { - "@oclif/command": "^1.5.13", - "chalk": "^2.4.1", - "indent-string": "^4.0.0", - "lodash.template": "^4.4.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0", - "widest-line": "^2.0.1", - "wrap-ansi": "^4.0.0" - }, - "dependencies": { - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "@oclif/screen": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-1.0.4.tgz", - "integrity": "sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw==" - }, - "@oclif/test": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@oclif/test/-/test-1.2.5.tgz", - "integrity": "sha512-8Y+Ix4A3Zhm87aL0ldVonDK7vFWyLfnFHzP3goYaLyIeh/60KL37lMxfmbp/kBN6/Y0Ru17iR1pdDi/hTDClLQ==", - "dev": true, - "requires": { - "fancy-test": "^1.4.3" - } - }, - "@polkadot/api": { - "version": "0.96.1", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-0.96.1.tgz", - "integrity": "sha512-FeYyMfJL0NACJBIuG7C7mp7f9J/WOGUERF/hUP3RlIz4Ld2X0vRjEoOgiG0VIS89I4K31XaNmSjIchH244WtHg==", - "requires": { - "@babel/runtime": "^7.7.1", - "@polkadot/api-derive": "^0.96.1", - "@polkadot/api-metadata": "^0.96.1", - "@polkadot/keyring": "^1.7.0-beta.5", - "@polkadot/rpc-core": "^0.96.1", - "@polkadot/rpc-provider": "^0.96.1", - "@polkadot/types": "^0.96.1", - "@polkadot/util-crypto": "^1.7.0-beta.5" - } - }, - "@polkadot/api-derive": { - "version": "0.96.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-0.96.1.tgz", - "integrity": "sha512-PGWdUvlD2acUKOgaJcYWuMTfSuQKUpwgwjer5SomHLFn4ZPOz8iDa7mYtrgmxQctRv1zsuck2X01uhxdEdtJZw==", - "requires": { - "@babel/runtime": "^7.7.1", - "@polkadot/api": "^0.96.1", - "@polkadot/types": "^0.96.1" - } - }, - "@polkadot/api-metadata": { - "version": "0.96.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-metadata/-/api-metadata-0.96.1.tgz", - "integrity": "sha512-I9F3twpSCgx4ny25a3moGrhf2vHKFnjooO3W9NaAxIj/us4q4Gqo4+czQajqt8vaJqrNMq/PE7lzVz1NhYDrZQ==", - "requires": { - "@babel/runtime": "^7.7.1", - "@polkadot/types": "^0.96.1", - "@polkadot/util": "^1.7.0-beta.5", - "@polkadot/util-crypto": "^1.7.0-beta.5" - } - }, - "@polkadot/jsonrpc": { - "version": "0.96.1", - "resolved": "https://registry.npmjs.org/@polkadot/jsonrpc/-/jsonrpc-0.96.1.tgz", - "integrity": "sha512-UHpcUGIvkG4dJ5gUhDyfJ1xfr/VcBlJ5lIlGamGsnNacMuIVmmEsftgxtPlJLWHuoA1EBEHY4cbPSv9CUJ0IFw==", - "requires": { - "@babel/runtime": "^7.7.1" - } - }, - "@polkadot/keyring": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-1.8.1.tgz", - "integrity": "sha512-KeDbfP8biY3bXEhMv1ANp9d3kCuXj2oxseuDK0jvxRo7CehVME9UwAMGQK3Y9NCUuYWd+xTO2To0ZOqR7hdmuQ==", - "requires": { - "@babel/runtime": "^7.7.7", - "@polkadot/util": "^1.8.1", - "@polkadot/util-crypto": "^1.8.1" - } - }, - "@polkadot/rpc-core": { - "version": "0.96.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-0.96.1.tgz", - "integrity": "sha512-ygSaJpz/QPEq1p35wYRzONuP2PCtkAJ9eS8swQqUIezTo2ZPUOyBhmnJ3nxj11R8YnQClq4Id0QdsJmH1ClYgw==", - "requires": { - "@babel/runtime": "^7.7.1", - "@polkadot/jsonrpc": "^0.96.1", - "@polkadot/rpc-provider": "^0.96.1", - "@polkadot/types": "^0.96.1", - "@polkadot/util": "^1.7.0-beta.5", - "rxjs": "^6.5.3" - } - }, - "@polkadot/rpc-provider": { - "version": "0.96.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-0.96.1.tgz", - "integrity": "sha512-cUhp8FMCYHrXrBTbxZrok/hPIgtOXEUhIXn5/zrffg1Qpbzju/y/bXx7c1Kxl1JF7Bg0vSBRZEGJTn/x0irWRQ==", - "requires": { - "@babel/runtime": "^7.7.1", - "@polkadot/api-metadata": "^0.96.1", - "@polkadot/util": "^1.7.0-beta.5", - "@polkadot/util-crypto": "^1.7.0-beta.5", - "eventemitter3": "^4.0.0", - "isomorphic-fetch": "^2.2.1", - "websocket": "^1.0.30" - } - }, - "@polkadot/ts": { - "version": "0.1.91", - "resolved": "https://registry.npmjs.org/@polkadot/ts/-/ts-0.1.91.tgz", - "integrity": "sha512-UB8zOFZXb/ih03izzAQ1r1DRpiUXBofxAlXjcx4530jopfiNsiU1LZ2J/uS3dVV1QXaGRhkgm8SIJDLsSMRYIQ==", - "dev": true, - "requires": { - "@types/chrome": "^0.0.92" - } - }, - "@polkadot/types": { - "version": "0.96.1", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-0.96.1.tgz", - "integrity": "sha512-b8AZBNmMjB0+34Oxue3AYc0gIjDHYCdVGtDpel0omHkLMcEquSvrCniLm+p7g4cfArICiZPFmS9In/OWWdRUVA==", - "requires": { - "@babel/runtime": "^7.7.1", - "@polkadot/util": "^1.7.0-beta.5", - "@polkadot/util-crypto": "^1.7.0-beta.5", - "@types/memoizee": "^0.4.3", - "memoizee": "^0.4.14" - } - }, - "@polkadot/util": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-1.8.1.tgz", - "integrity": "sha512-sFpr+JLCG9d+epjboXsmJ1qcKa96r8ZYzXmVo8+aPzI/9jKKyez6Unox/dnfnpKppZB2nJuLcsxQm6nocp2Caw==", - "requires": { - "@babel/runtime": "^7.7.7", - "@types/bn.js": "^4.11.6", - "bn.js": "^4.11.8", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "ip-regex": "^4.1.0", - "moment": "^2.24.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@polkadot/util-crypto": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-1.8.1.tgz", - "integrity": "sha512-ypUs10hV1HPvYc0ZsEu+LTGSEh0rkr0as/FUh7+Z9v3Bxibn3aO+EOxJPQuDbZZ59FSMRmc9SeOSa0wn9ddrnw==", - "requires": { - "@babel/runtime": "^7.7.7", - "@polkadot/util": "^1.8.1", - "@polkadot/wasm-crypto": "^0.14.1", - "@types/bip39": "^2.4.2", - "@types/bs58": "^4.0.0", - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^3.5.0", - "@types/xxhashjs": "^0.2.1", - "base-x": "3.0.5", - "bip39": "^2.5.0", - "blakejs": "^1.1.0", - "bs58": "^4.0.1", - "js-sha3": "^0.8.0", - "secp256k1": "^3.8.0", - "tweetnacl": "^1.0.1", - "xxhashjs": "^0.2.2" - } - }, - "@polkadot/wasm-crypto": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-0.14.1.tgz", - "integrity": "sha512-Xng7L2Z8TNZa/5g6pot4O06Jf0ohQRZdvfl8eQL+E/L2mcqJYC1IjkMxJBSBuQEV7hisWzh9mHOy5WCcgPk29Q==" - }, - "@types/bip39": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/bip39/-/bip39-2.4.2.tgz", - "integrity": "sha512-Vo9lqOIRq8uoIzEVrV87ZvcIM0PN9t0K3oYZ/CS61fIYKCBdOIM7mlWzXuRvSXrDtVa1uUO2w1cdfufxTC0bzg==", - "requires": { - "@types/node": "*" - } - }, - "@types/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", - "requires": { - "@types/node": "*" - } - }, - "@types/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-yfAgiWgVLjFCmRv8zAcOIHywYATEwiTVccTLnRp6UxTNavT55M9d/uhK3T03St/+8/z/wW+CRjGKUNmEqoHHCA==", - "requires": { - "base-x": "^3.0.6" - }, - "dependencies": { - "base-x": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", - "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", - "requires": { - "safe-buffer": "^5.0.1" - } - } - } - }, - "@types/chai": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", - "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", - "dev": true - }, - "@types/chrome": { - "version": "0.0.92", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.92.tgz", - "integrity": "sha512-bTv1EljZ03bexRJwS5FwSZmrudtw+QNbzwUY2sxVtXWgtxk752G4I2owhZ+Mlzbf3VKvG+rBYSw/FnvzuZ4xOA==", - "dev": true, - "requires": { - "@types/filesystem": "*" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, - "@types/eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", - "dev": true - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "@types/filesystem": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.29.tgz", - "integrity": "sha512-85/1KfRedmfPGsbK8YzeaQUyV1FQAvMPMTuWFQ5EkLd2w7szhNO96bk3Rh/SKmOfd9co2rCLf0Voy4o7ECBOvw==", - "dev": true, - "requires": { - "@types/filewriter": "*" - } - }, - "@types/filewriter": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.28.tgz", - "integrity": "sha1-wFTor02d11205jq8dviFFocU1LM=", - "dev": true - }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", - "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/inquirer": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", - "integrity": "sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==", - "requires": { - "@types/through": "*", - "rxjs": "^6.4.0" - } - }, - "@types/json-schema": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", - "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", - "dev": true - }, - "@types/lodash": { - "version": "4.14.149", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", - "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==", - "dev": true - }, - "@types/memoizee": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@types/memoizee/-/memoizee-0.4.3.tgz", - "integrity": "sha512-N6QT0c9ZbEKl33n1wyoTxZs4cpN+YXjs0Aqy5Qim8ipd9PBNIPqOh/p5Pixc4601tqr5GErsdxUbfqviDfubNw==" - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/mocha": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", - "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", - "dev": true - }, - "@types/node": { - "version": "10.17.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.18.tgz", - "integrity": "sha512-DQ2hl/Jl3g33KuAUOcMrcAOtsbzb+y/ufakzAdeK9z/H/xsvkpbETZZbPNMIiQuk24f5ZRMCcZIViAwyFIiKmg==" - }, - "@types/pbkdf2": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.0.0.tgz", - "integrity": "sha512-6J6MHaAlBJC/eVMy9jOwj9oHaprfutukfW/Dyt0NEnpQ/6HN6YQrpvLwzWdWDeWZIdenjGHlbYDzyEODO5Z+2Q==", - "requires": { - "@types/node": "*" - } - }, - "@types/proper-lockfile": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/proper-lockfile/-/proper-lockfile-4.1.1.tgz", - "integrity": "sha512-HAjVfDa73pFgivViHyDu8HHHcds+W4MgOuZZAdyFJrHS8ngtCXmhl4hc2YXqSOwO6Bsa+iF2Sgxb2+gv874VOQ==", - "requires": { - "@types/retry": "*" - } - }, - "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "@types/secp256k1": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-3.5.3.tgz", - "integrity": "sha512-NGcsPDR0P+Q71O63e2ayshmiZGAwCOa/cLJzOIuhOiDvmbvrCIiVtEpqdCJGogG92Bnr6tw/6lqVBsRMEl15OQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/sinon": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.0.tgz", - "integrity": "sha512-v2TkYHkts4VXshMkcmot/H+ERZ2SevKa10saGaJPGCJ8vh3lKrC4u663zYEeRZxep+VbG6YRDtQ6gVqw9dYzPA==", - "dev": true, - "requires": { - "@types/sinonjs__fake-timers": "*" - } - }, - "@types/sinonjs__fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz", - "integrity": "sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA==", - "dev": true - }, - "@types/slug": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@types/slug/-/slug-0.9.1.tgz", - "integrity": "sha512-zR/u8WFQ4/6uCIikjI00a5uB084XjgEGNRAvM4a1BL39Bw9yEiDQFiPS2DgJ8lPDkR2Qd/vZ26dCR9XqlKbDqQ==" - }, - "@types/through": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz", - "integrity": "sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==", - "requires": { - "@types/node": "*" - } - }, - "@types/unist": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", - "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==" - }, - "@types/vfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/vfile/-/vfile-4.0.0.tgz", - "integrity": "sha512-eleP0/Cz8uVWxARDLi3Axq2+fDdN4ibAXoC6Pv8p6s7znXaUL7XvhgeIhjCiNMnvlLNP+tmCLd+RuCryGgmtEg==", - "requires": { - "vfile": "*" - } - }, - "@types/xxhashjs": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@types/xxhashjs/-/xxhashjs-0.2.1.tgz", - "integrity": "sha512-Akm13wkwsQylVnBokl/aiKLtSxndSjfgTjdvmSxXNehYy4NymwdfdJHwGhpV54wcYfmOByOp3ak8AGdUlvp0sA==", - "requires": { - "@types/node": "*" - } - }, - "@typescript-eslint/eslint-plugin": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz", - "integrity": "sha512-4yUnLv40bzfzsXcTAtZyTjbiGUXMrcIJcIMioI22tSOyAxpdXiZ4r7YQUU8Jj6XXrLz9d5aMHPQf5JFR7h27Nw==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "2.26.0", - "functional-red-black-tree": "^1.0.1", - "regexpp": "^3.0.0", - "tsutils": "^3.17.1" - }, - "dependencies": { - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz", - "integrity": "sha512-RELVoH5EYd+JlGprEyojUv9HeKcZqF7nZUGSblyAw1FwOGNnmQIU8kxJ69fttQvEwCsX5D6ECJT8GTozxrDKVQ==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.26.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - }, - "dependencies": { - "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - } - } - }, - "@typescript-eslint/parser": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.26.0.tgz", - "integrity": "sha512-+Xj5fucDtdKEVGSh9353wcnseMRkPpEAOY96EEenN7kJVrLqy/EVwtIh3mxcUz8lsFXW1mT5nN5vvEam/a5HiQ==", - "dev": true, - "requires": { - "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.26.0", - "@typescript-eslint/typescript-estree": "2.26.0", - "eslint-visitor-keys": "^1.1.0" - } - }, - "@typescript-eslint/typescript-estree": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz", - "integrity": "sha512-3x4SyZCLB4zsKsjuhxDLeVJN6W29VwBnYpCsZ7vIdPel9ZqLfIZJgJXO47MNUkurGpQuIBALdPQKtsSnWpE1Yg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^6.3.0", - "tsutils": "^3.17.1" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "acorn": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", - "dev": true - }, - "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base-x": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.5.tgz", - "integrity": "sha512-C3picSgzPSLE+jW3tcBzJoGwitOtazb5B+5YmAxZm2ybmTi9LNgAtDO/jjVEBZwHoXmDBZ9m/IELj3elJVRBcA==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bip39": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.6.0.tgz", - "integrity": "sha512-RrnQRG2EgEoqO24ea+Q/fftuPUZLmrEM3qNhhGsA3PbaXaCW791LTzPuVyx/VprXQcTbPJ3K3UeTna8ZnVl2sg==", - "requires": { - "create-hash": "^1.1.0", - "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1", - "safe-buffer": "^5.0.1", - "unorm": "^1.3.3" - } - }, - "bip66": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", - "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "bl": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", - "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "blakejs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", - "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=" - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "requires": { - "base-x": "^3.0.2" - } - }, - "buffer": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.5.0.tgz", - "integrity": "sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" - }, - "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", - "dev": true, - "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - } - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha1-fMEFXYItISlU0HsIXeolHMe8VQU=", - "requires": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - } - }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha1-jffHquUf02h06PjQW5GAvBGj/tc=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "clean-stack": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-1.3.0.tgz", - "integrity": "sha1-noIVAa6XmYbEax1m0tQy2y/UrjE=" - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-progress": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.6.1.tgz", - "integrity": "sha512-OVRgcyeI0viJW47MnyS10Jw/0RTpk7wwNbrCOPyXT0TVi2o3Q/u+Os8vQUFYhvkdXSbguSdFvMv1ia+UuwgIQQ==", - "requires": { - "colors": "^1.1.2", - "string-width": "^4.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "cli-ux": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/cli-ux/-/cli-ux-5.4.5.tgz", - "integrity": "sha512-5A6FuU0wPUlfCWUjtizUvNIbXElp6jN9QUJsDibs6F9cVX1kTgaMR3m6KT0R3iriEXpMrmPKV6yYS8XICNuQ6Q==", - "requires": { - "@oclif/command": "^1.5.1", - "@oclif/errors": "^1.2.1", - "@oclif/linewrap": "^1.0.0", - "@oclif/screen": "^1.0.3", - "ansi-escapes": "^3.1.0", - "ansi-styles": "^3.2.1", - "cardinal": "^2.1.1", - "chalk": "^2.4.1", - "clean-stack": "^2.0.0", - "cli-progress": "^3.4.0", - "extract-stack": "^1.0.0", - "fs-extra": "^7.0.1", - "hyperlinker": "^1.0.0", - "indent-string": "^4.0.0", - "is-wsl": "^1.1.0", - "js-yaml": "^3.13.1", - "lodash": "^4.17.11", - "natural-orderby": "^2.0.1", - "password-prompt": "^1.1.2", - "semver": "^5.6.0", - "string-width": "^3.1.0", - "strip-ansi": "^5.1.0", - "supports-color": "^5.5.0", - "supports-hyperlinks": "^1.0.1", - "treeify": "^1.1.0", - "tslib": "^1.9.3" - }, - "dependencies": { - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - } - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=" - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "detect-indent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz", - "integrity": "sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==", - "dev": true - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "drbg.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", - "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", - "requires": { - "browserify-aes": "^1.0.6", - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4" - } - }, - "elliptic": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", - "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - }, - "dependencies": { - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - } - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "requires": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "eslint-ast-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-ast-utils/-/eslint-ast-utils-1.1.0.tgz", - "integrity": "sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==", - "dev": true, - "requires": { - "lodash.get": "^4.4.2", - "lodash.zip": "^4.2.0" - } - }, - "eslint-config-oclif": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-3.1.0.tgz", - "integrity": "sha512-Tqgy43cNXsSdhTLWW4RuDYGFhV240sC4ISSv/ZiUEg/zFxExSEUpRE6J+AGnkKY9dYwIW4C9b2YSUVv8z/miMA==", - "dev": true, - "requires": { - "eslint-config-xo-space": "^0.20.0", - "eslint-plugin-mocha": "^5.2.0", - "eslint-plugin-node": "^7.0.1", - "eslint-plugin-unicorn": "^6.0.1" - } - }, - "eslint-config-oclif-typescript": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-0.1.0.tgz", - "integrity": "sha512-BjXNJcH2F02MdaSFml9vJskviUFVkLHbTPGM5tinIt98H6klFNKP7/lQ+fB/Goc2wB45usEuuw6+l/fwAv9i7g==", - "dev": true, - "requires": { - "@typescript-eslint/eslint-plugin": "^2.6.1", - "@typescript-eslint/parser": "^2.6.1", - "eslint-config-oclif": "^3.1.0", - "eslint-config-xo-space": "^0.20.0", - "eslint-plugin-mocha": "^5.2.0", - "eslint-plugin-node": "^7.0.1", - "eslint-plugin-unicorn": "^6.0.1" - } - }, - "eslint-config-xo": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.24.2.tgz", - "integrity": "sha512-ivQ7qISScW6gfBp+p31nQntz1rg34UCybd3uvlngcxt5Utsf4PMMi9QoAluLFcPUM5Tvqk4JGraR9qu3msKPKQ==", - "dev": true - }, - "eslint-config-xo-space": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/eslint-config-xo-space/-/eslint-config-xo-space-0.20.0.tgz", - "integrity": "sha512-bOsoZA8M6v1HviDUIGVq1fLVnSu3mMZzn85m2tqKb73tSzu4GKD4Jd2Py4ZKjCgvCbRRByEB5HPC3fTMnnJ1uw==", - "dev": true, - "requires": { - "eslint-config-xo": "^0.24.0" - } - }, - "eslint-plugin-es": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.1.tgz", - "integrity": "sha512-5fa/gR2yR3NxQf+UXkeLeP8FBBl6tSgdrAz1+cF84v1FMM4twGwQoqTnn+QxFLcPOrF4pdKEJKDB/q9GoyJrCA==", - "dev": true, - "requires": { - "eslint-utils": "^1.4.2", - "regexpp": "^2.0.1" - } - }, - "eslint-plugin-mocha": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-5.3.0.tgz", - "integrity": "sha512-3uwlJVLijjEmBeNyH60nzqgA1gacUWLUmcKV8PIGNvj1kwP/CTgAWQHn2ayyJVwziX+KETkr9opNwT1qD/RZ5A==", - "dev": true, - "requires": { - "ramda": "^0.26.1" - } - }, - "eslint-plugin-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", - "integrity": "sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw==", - "dev": true, - "requires": { - "eslint-plugin-es": "^1.3.1", - "eslint-utils": "^1.3.1", - "ignore": "^4.0.2", - "minimatch": "^3.0.4", - "resolve": "^1.8.1", - "semver": "^5.5.0" - }, - "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } - } - }, - "eslint-plugin-unicorn": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-6.0.1.tgz", - "integrity": "sha512-hjy9LhTdtL7pz8WTrzS0CGXRkWK3VAPLDjihofj8JC+uxQLfXm0WwZPPPB7xKmcjRyoH+jruPHOCrHNEINpG/Q==", - "dev": true, - "requires": { - "clean-regexp": "^1.0.0", - "eslint-ast-utils": "^1.0.0", - "import-modules": "^1.1.0", - "lodash.camelcase": "^4.1.1", - "lodash.kebabcase": "^4.0.1", - "lodash.snakecase": "^4.0.1", - "lodash.upperfirst": "^4.2.0", - "safe-regex": "^1.1.0" - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", - "dev": true - }, - "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esquery": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.2.0.tgz", - "integrity": "sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==", - "dev": true, - "requires": { - "estraverse": "^5.0.0" - }, - "dependencies": { - "estraverse": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz", - "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", - "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==" - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "dependencies": { - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - } - } - }, - "extract-stack": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-1.0.0.tgz", - "integrity": "sha1-uXrK+UQe6iMyUpYktzL8WhyBZfo=" - }, - "fancy-test": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/fancy-test/-/fancy-test-1.4.7.tgz", - "integrity": "sha512-drgNrpNbvXXbPAz0rn7jvzjoEihDKpm1fFF+aZ+FVLatjE3jZSc6WwfgC5x7N/+nhmentMx4TXPQ0OkS0SElVQ==", - "dev": true, - "requires": { - "@types/chai": "*", - "@types/lodash": "*", - "@types/mocha": "*", - "@types/node": "*", - "@types/sinon": "*", - "lodash": "^4.17.13", - "mock-stdin": "^0.3.1", - "stdout-stderr": "^0.1.9" - } - }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" - }, - "fast-glob": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", - "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.7.0.tgz", - "integrity": "sha512-YOadQRnHd5q6PogvAR/x62BGituF2ufiEA6s8aavQANw5YKHERI4AREboX6KotzP8oX2klxYF2wcV/7bn1clfQ==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", - "dev": true, - "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - } - } - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "github-slugger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", - "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", - "dev": true, - "requires": { - "emoji-regex": ">=6.0.0 <=6.1.1" - }, - "dependencies": { - "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=", - "dev": true - } - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", - "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", - "dev": true, - "requires": { - "is-stream": "^1.0.1" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-call": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz", - "integrity": "sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w==", - "dev": true, - "requires": { - "content-type": "^1.0.4", - "debug": "^4.1.1", - "is-retry-allowed": "^1.1.0", - "is-stream": "^2.0.0", - "parse-json": "^4.0.0", - "tunnel-agent": "^0.6.0" - }, - "dependencies": { - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - } - } - }, - "hyperlinker": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hyperlinker/-/hyperlinker-1.0.0.tgz", - "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/import-modules/-/import-modules-1.1.0.tgz", - "integrity": "sha1-dI23nFzEK7lwHvq0JPiU5yYA6dw=", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.15", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.5.3", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "requires": { - "type-fest": "^0.11.0" - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" - } - } - }, - "ip-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.1.0.tgz", - "integrity": "sha512-pKnZpbgCTfH/1NLIlOduP/V+WRXzC2MOz3Qo8xmxk8C5GudJLgK5QyLVXOSWy3ParAH7Eemurl3xjv/WXYFvMA==" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz", - "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0" - } - }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "load-json-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", - "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "parse-json": "^5.0.0", - "strip-bom": "^4.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" - } - } - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", - "dev": true - }, - "lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=", - "dev": true - }, - "lodash.template": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", - "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=", - "dev": true - }, - "lodash.zip": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", - "integrity": "sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=", - "dev": true - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", - "requires": { - "es5-ext": "~0.10.2" - } - }, - "make-dir": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", - "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "memoizee": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", - "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", - "requires": { - "d": "1", - "es5-ext": "^0.10.45", - "es6-weak-map": "^2.0.2", - "event-emitter": "^0.3.5", - "is-promise": "^2.1", - "lru-queue": "0.1", - "next-tick": "1", - "timers-ext": "^0.1.5" - } - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "merge2": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "mkdirp-classic": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz", - "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==", - "dev": true - }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "mock-stdin": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/mock-stdin/-/mock-stdin-0.3.1.tgz", - "integrity": "sha1-xlfZZC2QeGQ1xkyl6Zu9TQm9fdM=", - "dev": true - }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "natural-orderby": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-2.0.3.tgz", - "integrity": "sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q==" - }, - "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", - "dev": true - }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", - "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", - "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "password-prompt": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/password-prompt/-/password-prompt-1.1.2.tgz", - "integrity": "sha512-bpuBhROdrhuN3E7G/koAju0WjVw9/uQOG5Co5mokNj0MiOSBVZS1JTwM4zl55hu0WFmIEFvO9cU9sJQiBIYeIA==", - "requires": { - "ansi-escapes": "^3.1.0", - "cross-spawn": "^6.0.5" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "proper-lockfile": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.1.tgz", - "integrity": "sha512-1w6rxXodisVpn7QYvLk706mzprPTAPCYAqxMvctmPN3ekuRk/kuGkGc82pangZiAt4R3lwSuUzheTTn0/Yb7Zg==", - "requires": { - "graceful-fs": "^4.1.11", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qqjs": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/qqjs/-/qqjs-0.3.11.tgz", - "integrity": "sha512-pB2X5AduTl78J+xRSxQiEmga1jQV0j43jOPs/MTgTLApGFEOn6NgdE2dEjp7nvDtjkIOZbvFIojAiYUx6ep3zg==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "debug": "^4.1.1", - "execa": "^0.10.0", - "fs-extra": "^6.0.1", - "get-stream": "^5.1.0", - "glob": "^7.1.2", - "globby": "^10.0.1", - "http-call": "^5.1.2", - "load-json-file": "^6.2.0", - "pkg-dir": "^4.2.0", - "tar-fs": "^2.0.0", - "tmp": "^0.1.0", - "write-json-file": "^4.1.1" - }, - "dependencies": { - "fs-extra": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", - "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - } - } - }, - "ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "redeyed": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=", - "requires": { - "esprima": "~4.0.0" - } - }, - "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "run-async": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", - "requires": { - "is-promise": "^2.1.0" - } - }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, - "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "secp256k1": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", - "integrity": "sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw==", - "requires": { - "bindings": "^1.5.0", - "bip66": "^1.1.5", - "bn.js": "^4.11.8", - "create-hash": "^1.2.0", - "drbg.js": "^1.0.1", - "elliptic": "^6.5.2", - "nan": "^2.14.0", - "safe-buffer": "^5.1.2" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "slug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/slug/-/slug-2.1.1.tgz", - "integrity": "sha512-yNGhDdS0DR0JyxnPC84qIx/Vd01RHVY4guJeBqBNdBoOLNWnzw5zkWJvxVSmsuUb92bikdnQFnw3PfGY8uZ82g==", - "requires": { - "unicode": ">= 0.3.1" - } - }, - "sort-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.0.0.tgz", - "integrity": "sha512-hlJLzrn/VN49uyNkZ8+9b+0q9DjmmYcYOnbMQtpkLrYpPwRApDPZfmqbUfJnAA3sb/nRib+nDot7Zi/1ER1fuA==", - "dev": true, - "requires": { - "is-plain-obj": "^2.0.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", - "dev": true, - "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", - "signal-exit": "^3.0.2", - "which": "^1.3.0" - } - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "stdout-stderr": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/stdout-stderr/-/stdout-stderr-0.1.13.tgz", - "integrity": "sha512-Xnt9/HHHYfjZ7NeQLvuQDyL1LnbsbddgMFKCuaQKwGCdJm8LnstZIXop+uOY36UR1UXXoHXfMbC1KlVdVd2JLA==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-hyperlinks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", - "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", - "requires": { - "has-flag": "^2.0.0", - "supports-color": "^5.0.0" - }, - "dependencies": { - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - } - } - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "tar-fs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", - "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.0.0" - } - }, - "tar-stream": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", - "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", - "dev": true, - "requires": { - "bl": "^4.0.1", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "dev": true, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, - "tmp": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", - "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", - "dev": true, - "requires": { - "rimraf": "^2.6.3" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "treeify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", - "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==" - }, - "ts-node": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.8.2.tgz", - "integrity": "sha512-duVj6BpSpUpD/oM4MfhO98ozgkp3Gt9qIp3jGxwU2DFvl/3IRaEAvbLa8G60uS7C77457e/m5TMowjedeRxI1Q==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "3.1.1" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - } - } - }, - "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" - }, - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", - "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", - "dev": true - }, - "unicode": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/unicode/-/unicode-12.1.0.tgz", - "integrity": "sha512-Ty6+Ew21DiYTWLYtd05RF/X4c1ekOvOgANyHbBj0h3MaXpfaGr2Rdmc0hMFuGQLyPLb9cU4ArNxl0bTF5HSzXw==" - }, - "unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "requires": { - "@types/unist": "^2.0.2" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "unorm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", - "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==" - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vfile": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.1.0.tgz", - "integrity": "sha512-BaTPalregj++64xbGK6uIlsurN3BCRNM/P2Pg8HezlGzKd1O9PrwIac6bd9Pdx2uTb0QHoioZ+rXKolbVXEgJg==", - "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - } - }, - "vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - } - }, - "websocket": { - "version": "1.0.31", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.31.tgz", - "integrity": "sha512-VAouplvGKPiKFDTeCCO65vYHsyay8DqoBSlzIO3fayrfOgU94lQN5a1uWVnFrMLceTJw/+fQXR5PGbUVRaHshQ==", - "requires": { - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "nan": "^2.14.0", - "typedarray-to-buffer": "^3.1.5", - "yaeti": "^0.0.6" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "requires": { - "string-width": "^2.1.1" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-4.0.0.tgz", - "integrity": "sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "write-json-file": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz", - "integrity": "sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ==", - "dev": true, - "requires": { - "detect-indent": "^6.0.0", - "graceful-fs": "^4.1.15", - "is-plain-obj": "^2.0.0", - "make-dir": "^3.0.0", - "sort-keys": "^4.0.0", - "write-file-atomic": "^3.0.0" - } - }, - "xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "requires": { - "cuint": "^0.2.2" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } - } -} diff --git a/cli/package.json b/cli/package.json index 942dfc1414..2dbd532144 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,21 +1,25 @@ { - "name": "joystream-cli", + "name": "@joystream/cli", "description": "Command Line Interface for Joystream community and governance activities", - "version": "0.0.0", + "version": "0.1.0", "author": "Leszek Wiesner", "bin": { "joystream-cli": "./bin/run" }, - "bugs": "https://github.com/Joystream/substrate-runtime-joystream/issues", + "bugs": "https://github.com/Joystream/joystream/issues", "dependencies": { - "@joystream/types": "^0.6.0", + "@joystream/types": "^0.13.0", "@oclif/command": "^1.5.19", "@oclif/config": "^1.14.0", + "@oclif/plugin-autocomplete": "^0.2.0", "@oclif/plugin-help": "^2.2.3", + "@oclif/plugin-not-found": "^1.2.4", + "@oclif/plugin-warn-if-update-available": "^1.7.0", "@polkadot/api": "^0.96.1", "@types/inquirer": "^6.5.0", "@types/proper-lockfile": "^4.1.1", "@types/slug": "^0.9.1", + "ajv": "^6.11.0", "cli-ux": "^5.4.5", "inquirer": "^7.1.0", "moment": "^2.24.0", @@ -26,22 +30,27 @@ "devDependencies": { "@oclif/dev-cli": "^1.22.2", "@oclif/test": "^1.2.5", + "@polkadot/ts": "^0.1.56", "@types/chai": "^4.2.11", "@types/mocha": "^5.2.7", "@types/node": "^10.17.18", "chai": "^4.2.0", - "eslint": "^5.16.0", + "eslint": "^7.6.0", "eslint-config-oclif": "^3.1.0", "eslint-config-oclif-typescript": "^0.1.0", "globby": "^10.0.2", "mocha": "^5.2.0", "nyc": "^14.1.1", "ts-node": "^8.8.2", - "typescript": "^3.8.3", - "@polkadot/ts": "^0.1.56" + "typescript": "^3.8.3" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.18.0", + "yarn": "^1.22.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" }, "files": [ "/bin", @@ -49,18 +58,21 @@ "/npm-shrinkwrap.json", "/oclif.manifest.json" ], - "homepage": "https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli", + "homepage": "https://github.com/Joystream/joystream/blob/master/cli", "keywords": [ "oclif" ], - "license": "MIT", + "license": "GPL-3.0-only", "main": "lib/index.js", "oclif": { "repositoryPrefix": "<%- repo %>/blob/master/cli/<%- commandPath %>", "commands": "./lib/commands", "bin": "joystream-cli", "plugins": [ - "@oclif/plugin-help" + "@oclif/plugin-help", + "@oclif/plugin-autocomplete", + "@oclif/plugin-not-found", + "@oclif/plugin-warn-if-update-available" ], "topics": { "council": { @@ -71,20 +83,27 @@ }, "api": { "description": "Inspect the substrate node api, perform lower-level api calls or change the current api provider uri" + }, + "working-groups": { + "description": "Working group lead and worker actions" } } }, "repository": { "type": "git", - "url": "https://github.com/Joystream/substrate-runtime-joystream", + "url": "https://github.com/Joystream/joystream", "directory": "cli" }, "scripts": { "postpack": "rm -f oclif.manifest.json", - "posttest": "eslint . --ext .ts --config .eslintrc", + "posttest": "yarn lint", "prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme", "test": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"", - "version": "oclif-dev readme && git add README.md" + "build": "tsc --build tsconfig.json", + "version": "oclif-dev readme && git add README.md", + "lint": "eslint ./ --quiet --ext .ts", + "checks": "yarn lint && tsc --noEmit --pretty && prettier ./ --check", + "format": "prettier ./ --write" }, "types": "lib/index.d.ts" } diff --git a/cli/src/Api.ts b/cli/src/Api.ts index b499a50a42..e14aaac095 100644 --- a/cli/src/Api.ts +++ b/cli/src/Api.ts @@ -1,114 +1,503 @@ -import BN from 'bn.js'; -import { registerJoystreamTypes } from '@joystream/types'; -import { ApiPromise, WsProvider } from '@polkadot/api'; -import { QueryableStorageMultiArg } from '@polkadot/api/types'; -import { formatBalance } from '@polkadot/util'; -import { Hash } from '@polkadot/types/interfaces'; -import { KeyringPair } from '@polkadot/keyring/types'; -import { Codec } from '@polkadot/types/types'; -import { AccountSummary, CouncilInfoObj, CouncilInfoTuple, createCouncilInfoObj } from './Types'; -import { DerivedFees, DerivedBalances } from '@polkadot/api-derive/types'; -import { CLIError } from '@oclif/errors'; -import ExitCodes from './ExitCodes'; - -export const DEFAULT_API_URI = 'wss://rome-rpc-endpoint.joystream.org:9944/'; -export const TOKEN_SYMBOL = 'JOY'; +import BN from 'bn.js' +import { registerJoystreamTypes } from '@joystream/types/' +import { ApiPromise, WsProvider } from '@polkadot/api' +import { QueryableStorageMultiArg } from '@polkadot/api/types' +import { formatBalance } from '@polkadot/util' +import { Hash, Balance, Moment, BlockNumber } from '@polkadot/types/interfaces' +import { KeyringPair } from '@polkadot/keyring/types' +import { Codec } from '@polkadot/types/types' +import { Option, Vec } from '@polkadot/types' +import { u32 } from '@polkadot/types/primitive' +import { + AccountSummary, + CouncilInfoObj, + CouncilInfoTuple, + createCouncilInfoObj, + WorkingGroups, + Reward, + GroupMember, + OpeningStatus, + GroupOpeningStage, + GroupOpening, + GroupApplication, + openingPolicyUnstakingPeriodsKeys, + UnstakingPeriods, + StakingPolicyUnstakingPeriodKey, +} from './Types' +import { DerivedFees, DerivedBalances } from '@polkadot/api-derive/types' +import { CLIError } from '@oclif/errors' +import ExitCodes from './ExitCodes' +import { + Worker, + WorkerId, + RoleStakeProfile, + Opening as WGOpening, + Application as WGApplication, +} from '@joystream/types/working-group' +import { + Opening, + Application, + OpeningStage, + ApplicationStageKeys, + ApplicationId, + OpeningId, + StakingPolicy, +} from '@joystream/types/hiring' +import { MemberId, Membership } from '@joystream/types/members' +import { RewardRelationship, RewardRelationshipId } from '@joystream/types/recurring-rewards' +import { Stake, StakeId } from '@joystream/types/stake' +import { LinkageResult } from '@polkadot/types/codec/Linkage' -// Api wrapper for handling most common api calls and allowing easy API implementation switch in the future +import { InputValidationLengthConstraint } from '@joystream/types/common' + +export const DEFAULT_API_URI = 'ws://localhost:9944/' +const DEFAULT_DECIMALS = new u32(12) + +// Mapping of working group to api module +export const apiModuleByGroup: { [key in WorkingGroups]: string } = { + [WorkingGroups.StorageProviders]: 'storageWorkingGroup', +} +// Api wrapper for handling most common api calls and allowing easy API implementation switch in the future export default class Api { - private _api: ApiPromise; + private _api: ApiPromise + + private constructor(originalApi: ApiPromise) { + this._api = originalApi + } + + public getOriginalApi(): ApiPromise { + return this._api + } + + private static async initApi(apiUri: string = DEFAULT_API_URI): Promise { + const wsProvider: WsProvider = new WsProvider(apiUri) + registerJoystreamTypes() + const api = await ApiPromise.create({ provider: wsProvider }) + + // Initializing some api params based on pioneer/packages/react-api/Api.tsx + const [properties] = await Promise.all([api.rpc.system.properties()]) + + const tokenSymbol = properties.tokenSymbol.unwrapOr('DEV').toString() + const tokenDecimals = properties.tokenDecimals.unwrapOr(DEFAULT_DECIMALS).toNumber() + + // formatBlanace config + formatBalance.setDefaults({ + decimals: tokenDecimals, + unit: tokenSymbol, + }) + + return api + } + + static async create(apiUri: string = DEFAULT_API_URI): Promise { + const originalApi: ApiPromise = await Api.initApi(apiUri) + return new Api(originalApi) + } + + private async queryMultiOnce(queries: Parameters[0]): Promise { + let results: Codec[] = [] + + const unsub = await this._api.queryMulti(queries, (res) => { + results = res + }) + unsub() + + if (!results.length || results.length !== queries.length) { + throw new CLIError('API querying issue', { exit: ExitCodes.ApiError }) + } + + return results + } + + async getAccountsBalancesInfo(accountAddresses: string[]): Promise { + const accountsBalances: DerivedBalances[] = await this._api.derive.balances.votingBalances(accountAddresses) + + return accountsBalances + } + + // Get on-chain data related to given account. + // For now it's just account balances + async getAccountSummary(accountAddresses: string): Promise { + const balances: DerivedBalances = (await this.getAccountsBalancesInfo([accountAddresses]))[0] + // TODO: Some more information can be fetched here in the future + + return { balances } + } + + async getCouncilInfo(): Promise { + const queries: { [P in keyof CouncilInfoObj]: QueryableStorageMultiArg<'promise'> } = { + activeCouncil: this._api.query.council.activeCouncil, + termEndsAt: this._api.query.council.termEndsAt, + autoStart: this._api.query.councilElection.autoStart, + newTermDuration: this._api.query.councilElection.newTermDuration, + candidacyLimit: this._api.query.councilElection.candidacyLimit, + councilSize: this._api.query.councilElection.councilSize, + minCouncilStake: this._api.query.councilElection.minCouncilStake, + minVotingStake: this._api.query.councilElection.minVotingStake, + announcingPeriod: this._api.query.councilElection.announcingPeriod, + votingPeriod: this._api.query.councilElection.votingPeriod, + revealingPeriod: this._api.query.councilElection.revealingPeriod, + round: this._api.query.councilElection.round, + stage: this._api.query.councilElection.stage, + } + const results: CouncilInfoTuple = (await this.queryMultiOnce(Object.values(queries))) as CouncilInfoTuple + + return createCouncilInfoObj(...results) + } + + // TODO: This formula is probably not too good, so some better implementation will be required in the future + async estimateFee(account: KeyringPair, recipientAddr: string, amount: BN): Promise { + const transfer = this._api.tx.balances.transfer(recipientAddr, amount) + const signature = account.sign(transfer.toU8a()) + const transactionByteSize: BN = new BN(transfer.encodedLength + signature.length) + + const fees: DerivedFees = await this._api.derive.balances.fees() + + const estimatedFee = fees.transactionBaseFee.add(fees.transactionByteFee.mul(transactionByteSize)) + + return estimatedFee + } + + async transfer(account: KeyringPair, recipientAddr: string, amount: BN): Promise { + const txHash = await this._api.tx.balances.transfer(recipientAddr, amount).signAndSend(account) + return txHash + } + + // Working groups + // TODO: This is a lot of repeated logic from "/pioneer/joy-roles/src/transport.substrate.ts" + // (although simplified a little bit) + // Hopefully this will be refactored to "joystream-js" soon + protected singleLinkageResult(result: LinkageResult) { + return result[0] as T + } + + protected multiLinkageResult(result: LinkageResult): [Vec, Vec] { + return [result[0] as Vec, result[1] as Vec] + } + + protected async blockHash(height: number): Promise { + const blockHash = await this._api.rpc.chain.getBlockHash(height) + + return blockHash.toString() + } + + protected async blockTimestamp(height: number): Promise { + const blockTime = (await this._api.query.timestamp.now.at(await this.blockHash(height))) as Moment + + return new Date(blockTime.toNumber()) + } + + protected workingGroupApiQuery(group: WorkingGroups) { + const module = apiModuleByGroup[group] + return this._api.query[module] + } + + protected async membershipById(memberId: MemberId): Promise { + const profile = (await this._api.query.members.membershipById(memberId)) as Membership + + // Can't just use profile.isEmpty because profile.suspended is Bool (which isEmpty method always returns false) + return profile.handle.isEmpty ? null : profile + } + + async groupLead(group: WorkingGroups): Promise { + const optLeadId = (await this.workingGroupApiQuery(group).currentLead()) as Option + + if (!optLeadId.isSome) { + return null + } - private constructor(originalApi:ApiPromise) { - this._api = originalApi; + const leadWorkerId = optLeadId.unwrap() + const leadWorker = await this.workerByWorkerId(group, leadWorkerId.toNumber()) + + return await this.parseGroupMember(leadWorkerId, leadWorker) + } + + protected async stakeValue(stakeId: StakeId): Promise { + const stake = this.singleLinkageResult((await this._api.query.stake.stakes(stakeId)) as LinkageResult) + return stake.value + } + + protected async workerStake(stakeProfile: RoleStakeProfile): Promise { + return this.stakeValue(stakeProfile.stake_id) + } + + protected async workerReward(relationshipId: RewardRelationshipId): Promise { + const rewardRelationship = this.singleLinkageResult( + (await this._api.query.recurringRewards.rewardRelationships(relationshipId)) as LinkageResult + ) + + return { + totalRecieved: rewardRelationship.total_reward_received, + value: rewardRelationship.amount_per_payout, + interval: rewardRelationship.payout_interval.unwrapOr(undefined)?.toNumber(), + nextPaymentBlock: rewardRelationship.next_payment_at_block.unwrapOr(new BN(0)).toNumber(), + } + } + + protected async parseGroupMember(id: WorkerId, worker: Worker): Promise { + const roleAccount = worker.role_account_id + const memberId = worker.member_id + + const profile = await this.membershipById(memberId) + + if (!profile) { + throw new Error(`Group member profile not found! (member id: ${memberId.toNumber()})`) + } + + let stake: Balance | undefined + if (worker.role_stake_profile && worker.role_stake_profile.isSome) { + stake = await this.workerStake(worker.role_stake_profile.unwrap()) + } + + let reward: Reward | undefined + if (worker.reward_relationship && worker.reward_relationship.isSome) { + reward = await this.workerReward(worker.reward_relationship.unwrap()) + } + + return { + workerId: id, + roleAccount, + memberId, + profile, + stake, + reward, + } + } + + async workerByWorkerId(group: WorkingGroups, workerId: number): Promise { + const nextId = (await this.workingGroupApiQuery(group).nextWorkerId()) as WorkerId + + // This is chain specfic, but if next id is still 0, it means no workers have been added yet + if (workerId < 0 || workerId >= nextId.toNumber()) { + throw new CLIError('Invalid worker id!') + } + + const worker = this.singleLinkageResult( + (await this.workingGroupApiQuery(group).workerById(workerId)) as LinkageResult + ) + + if (!worker.is_active) { + throw new CLIError('This worker is not active anymore') } - public getOriginalApi(): ApiPromise { - return this._api; + return worker + } + + async groupMember(group: WorkingGroups, workerId: number) { + const worker = await this.workerByWorkerId(group, workerId) + return await this.parseGroupMember(new WorkerId(workerId), worker) + } + + async groupMembers(group: WorkingGroups): Promise { + const nextId = (await this.workingGroupApiQuery(group).nextWorkerId()) as WorkerId + + // This is chain specfic, but if next id is still 0, it means no workers have been added yet + if (nextId.eq(0)) { + return [] } - private static async initApi(apiUri: string = DEFAULT_API_URI): Promise { - formatBalance.setDefaults({ unit: TOKEN_SYMBOL }); - const wsProvider:WsProvider = new WsProvider(apiUri); - registerJoystreamTypes(); + const [workerIds, workers] = this.multiLinkageResult( + (await this.workingGroupApiQuery(group).workerById()) as LinkageResult + ) - return await ApiPromise.create({ provider: wsProvider }); + const groupMembers: GroupMember[] = [] + for (const [index, worker] of Object.entries(workers.toArray())) { + const workerId = workerIds[parseInt(index)] + if (worker.is_active) { + groupMembers.push(await this.parseGroupMember(workerId, worker)) + } } - static async create(apiUri: string = DEFAULT_API_URI): Promise { - const originalApi: ApiPromise = await Api.initApi(apiUri); - return new Api(originalApi); + return groupMembers.reverse() + } + + async openingsByGroup(group: WorkingGroups): Promise { + const openings: GroupOpening[] = [] + const nextId = (await this.workingGroupApiQuery(group).nextOpeningId()) as OpeningId + + // This is chain specfic, but if next id is still 0, it means no openings have been added yet + if (!nextId.eq(0)) { + const highestId = nextId.toNumber() - 1 + for (let i = highestId; i >= 0; i--) { + openings.push(await this.groupOpening(group, i)) + } } - private async queryMultiOnce(queries: Parameters[0]): Promise { - let results: Codec[] = []; + return openings + } + + protected async hiringOpeningById(id: number | OpeningId): Promise { + const result = (await this._api.query.hiring.openingById(id)) as LinkageResult + return this.singleLinkageResult(result) + } - const unsub = await this._api.queryMulti( - queries, - (res) => { results = res } - ); - unsub(); + protected async hiringApplicationById(id: number | ApplicationId): Promise { + const result = (await this._api.query.hiring.applicationById(id)) as LinkageResult + return this.singleLinkageResult(result) + } - if (!results.length || results.length !== queries.length) { - throw new CLIError('API querying issue', { exit: ExitCodes.ApiError }); - } + async wgApplicationById(group: WorkingGroups, wgApplicationId: number): Promise { + const nextAppId = (await this.workingGroupApiQuery(group).nextApplicationId()) as ApplicationId - return results; + if (wgApplicationId < 0 || wgApplicationId >= nextAppId.toNumber()) { + throw new CLIError('Invalid working group application ID!') } - async getAccountsBalancesInfo(accountAddresses:string[]): Promise { - let accountsBalances: DerivedBalances[] = await this._api.derive.balances.votingBalances(accountAddresses); + return this.singleLinkageResult( + (await this.workingGroupApiQuery(group).applicationById(wgApplicationId)) as LinkageResult + ) + } - return accountsBalances; + protected async parseApplication(wgApplicationId: number, wgApplication: WGApplication): Promise { + const appId = wgApplication.application_id + const application = await this.hiringApplicationById(appId) + + const { active_role_staking_id: roleStakingId, active_application_staking_id: appStakingId } = application + + return { + wgApplicationId, + applicationId: appId.toNumber(), + wgOpeningId: wgApplication.opening_id.toNumber(), + member: await this.membershipById(wgApplication.member_id), + roleAccout: wgApplication.role_account_id, + stakes: { + application: appStakingId.isSome ? (await this.stakeValue(appStakingId.unwrap())).toNumber() : 0, + role: roleStakingId.isSome ? (await this.stakeValue(roleStakingId.unwrap())).toNumber() : 0, + }, + humanReadableText: application.human_readable_text.toString(), + stage: application.stage.type as ApplicationStageKeys, } + } + + async groupApplication(group: WorkingGroups, wgApplicationId: number): Promise { + const wgApplication = await this.wgApplicationById(group, wgApplicationId) + return await this.parseApplication(wgApplicationId, wgApplication) + } - // Get on-chain data related to given account. - // For now it's just account balances - async getAccountSummary(accountAddresses:string): Promise { - const balances: DerivedBalances = (await this.getAccountsBalancesInfo([accountAddresses]))[0]; - // TODO: Some more information can be fetched here in the future + protected async groupOpeningApplications(group: WorkingGroups, wgOpeningId: number): Promise { + const applications: GroupApplication[] = [] - return { balances }; + const nextAppId = (await this.workingGroupApiQuery(group).nextApplicationId()) as ApplicationId + for (let i = 0; i < nextAppId.toNumber(); i++) { + const wgApplication = await this.wgApplicationById(group, i) + if (wgApplication.opening_id.toNumber() !== wgOpeningId) { + continue + } + applications.push(await this.parseApplication(i, wgApplication)) } - async getCouncilInfo(): Promise { - const queries: { [P in keyof CouncilInfoObj]: QueryableStorageMultiArg<"promise"> } = { - activeCouncil: this._api.query.council.activeCouncil, - termEndsAt: this._api.query.council.termEndsAt, - autoStart: this._api.query.councilElection.autoStart, - newTermDuration: this._api.query.councilElection.newTermDuration, - candidacyLimit: this._api.query.councilElection.candidacyLimit, - councilSize: this._api.query.councilElection.councilSize, - minCouncilStake: this._api.query.councilElection.minCouncilStake, - minVotingStake: this._api.query.councilElection.minVotingStake, - announcingPeriod: this._api.query.councilElection.announcingPeriod, - votingPeriod: this._api.query.councilElection.votingPeriod, - revealingPeriod: this._api.query.councilElection.revealingPeriod, - round: this._api.query.councilElection.round, - stage: this._api.query.councilElection.stage - } - const results: CouncilInfoTuple = await this.queryMultiOnce(Object.values(queries)); + return applications + } - return createCouncilInfoObj(...results); + async groupOpening(group: WorkingGroups, wgOpeningId: number): Promise { + const nextId = ((await this.workingGroupApiQuery(group).nextOpeningId()) as OpeningId).toNumber() + + if (wgOpeningId < 0 || wgOpeningId >= nextId) { + throw new CLIError('Invalid working group opening ID!') } - // TODO: This formula is probably not too good, so some better implementation will be required in the future - async estimateFee(account: KeyringPair, recipientAddr: string, amount: BN): Promise { - const transfer = this._api.tx.balances.transfer(recipientAddr, amount); - const signature = account.sign(transfer.toU8a()); - const transactionByteSize:BN = new BN(transfer.encodedLength + signature.length); + const groupOpening = this.singleLinkageResult( + (await this.workingGroupApiQuery(group).openingById(wgOpeningId)) as LinkageResult + ) - const fees: DerivedFees = await this._api.derive.balances.fees(); + const openingId = groupOpening.hiring_opening_id.toNumber() + const opening = await this.hiringOpeningById(openingId) + const applications = await this.groupOpeningApplications(group, wgOpeningId) + const stage = await this.parseOpeningStage(opening.stage) + const type = groupOpening.opening_type + const { application_staking_policy: applSP, role_staking_policy: roleSP } = opening + const stakes = { + application: applSP.unwrapOr(undefined), + role: roleSP.unwrapOr(undefined), + } - const estimatedFee = fees.transactionBaseFee.add(fees.transactionByteFee.mul(transactionByteSize)); + const unstakingPeriod = (period: Option) => period.unwrapOr(new BN(0)).toNumber() + const spUnstakingPeriod = (sp: Option, key: StakingPolicyUnstakingPeriodKey) => + sp.isSome ? unstakingPeriod(sp.unwrap()[key]) : 0 - return estimatedFee; + const unstakingPeriods: Partial = { + ['review_period_expired_application_stake_unstaking_period_length']: spUnstakingPeriod( + applSP, + 'review_period_expired_unstaking_period_length' + ), + ['crowded_out_application_stake_unstaking_period_length']: spUnstakingPeriod( + applSP, + 'crowded_out_unstaking_period_length' + ), + ['review_period_expired_role_stake_unstaking_period_length']: spUnstakingPeriod( + roleSP, + 'review_period_expired_unstaking_period_length' + ), + ['crowded_out_role_stake_unstaking_period_length']: spUnstakingPeriod( + roleSP, + 'crowded_out_unstaking_period_length' + ), } - async transfer(account: KeyringPair, recipientAddr: string, amount: BN): Promise { - const txHash = await this._api.tx.balances - .transfer(recipientAddr, amount) - .signAndSend(account); - return txHash; + openingPolicyUnstakingPeriodsKeys.forEach((key) => { + unstakingPeriods[key] = unstakingPeriod(groupOpening.policy_commitment[key]) + }) + + return { + wgOpeningId, + openingId, + opening, + stage, + stakes, + applications, + type, + unstakingPeriods: unstakingPeriods as UnstakingPeriods, + } + } + + async parseOpeningStage(stage: OpeningStage): Promise { + let status: OpeningStatus | undefined, stageBlock: number | undefined, stageDate: Date | undefined + + if (stage.isOfType('WaitingToBegin')) { + const stageData = stage.asType('WaitingToBegin') + const currentBlockNumber = (await this._api.derive.chain.bestNumber()).toNumber() + const expectedBlockTime = (this._api.consts.babe.expectedBlockTime as Moment).toNumber() + status = OpeningStatus.WaitingToBegin + stageBlock = stageData.begins_at_block.toNumber() + stageDate = new Date(Date.now() + (stageBlock - currentBlockNumber) * expectedBlockTime) + } + + if (stage.isOfType('Active')) { + const stageData = stage.asType('Active') + const substage = stageData.stage + if (substage.isOfType('AcceptingApplications')) { + status = OpeningStatus.AcceptingApplications + stageBlock = substage.asType('AcceptingApplications').started_accepting_applicants_at_block.toNumber() + } + if (substage.isOfType('ReviewPeriod')) { + status = OpeningStatus.InReview + stageBlock = substage.asType('ReviewPeriod').started_review_period_at_block.toNumber() + } + if (substage.isOfType('Deactivated')) { + status = substage.asType('Deactivated').cause.isOfType('Filled') + ? OpeningStatus.Complete + : OpeningStatus.Cancelled + stageBlock = substage.asType('Deactivated').deactivated_at_block.toNumber() + } + if (stageBlock) { + stageDate = new Date(await this.blockTimestamp(stageBlock)) + } + } + + return { + status: status || OpeningStatus.Unknown, + block: stageBlock, + date: stageDate, } + } + + async getMemberIdsByControllerAccount(address: string): Promise { + const ids = (await this._api.query.members.memberIdsByControllerAccountId(address)) as Vec + return ids.toArray() + } + + async workerExitRationaleConstraint(group: WorkingGroups): Promise { + return (await this.workingGroupApiQuery(group).workerExitRationaleText()) as InputValidationLengthConstraint + } } diff --git a/cli/src/ExitCodes.ts b/cli/src/ExitCodes.ts index 124e76965a..c7846b266d 100644 --- a/cli/src/ExitCodes.ts +++ b/cli/src/ExitCodes.ts @@ -1,14 +1,15 @@ enum ExitCodes { - OK = 0, + OK = 0, - InvalidInput = 400, - FileNotFound = 401, - InvalidFile = 402, - NoAccountFound = 403, - NoAccountSelected = 404, + InvalidInput = 400, + FileNotFound = 401, + InvalidFile = 402, + NoAccountFound = 403, + NoAccountSelected = 404, + AccessDenied = 405, - UnexpectedException = 500, - FsOperationFailed = 501, - ApiError = 502, + UnexpectedException = 500, + FsOperationFailed = 501, + ApiError = 502, } -export = ExitCodes; +export = ExitCodes diff --git a/cli/src/Types.ts b/cli/src/Types.ts index 5d38d06aab..f82174d038 100644 --- a/cli/src/Types.ts +++ b/cli/src/Types.ts @@ -1,63 +1,401 @@ -import BN from 'bn.js'; -import { ElectionStage, Seat } from '@joystream/types'; -import { Option } from '@polkadot/types'; -import { BlockNumber, Balance } from '@polkadot/types/interfaces'; -import { DerivedBalances } from '@polkadot/api-derive/types'; -import { KeyringPair } from '@polkadot/keyring/types'; +import BN from 'bn.js' +import { ElectionStage, Seat } from '@joystream/types/council' +import { Option, Text } from '@polkadot/types' +import { Constructor, Codec } from '@polkadot/types/types' +import { Struct, Vec } from '@polkadot/types/codec' +import { u32 } from '@polkadot/types/primitive' +import { BlockNumber, Balance, AccountId } from '@polkadot/types/interfaces' +import { DerivedBalances } from '@polkadot/api-derive/types' +import { KeyringPair } from '@polkadot/keyring/types' +import { WorkerId, OpeningType } from '@joystream/types/working-group' +import { Membership, MemberId } from '@joystream/types/members' +import { + GenericJoyStreamRoleSchema, + JobSpecifics, + ApplicationDetails, + QuestionSections, + QuestionSection, + QuestionsFields, + QuestionField, + EntryInMembershipModuke, + HiringProcess, + AdditionalRolehiringProcessDetails, + CreatorDetails, +} from '@joystream/types/hiring/schemas/role.schema.typings' +import ajv from 'ajv' +import { Opening, StakingPolicy, ApplicationStageKeys } from '@joystream/types/hiring' +import { Validator } from 'inquirer' // KeyringPair type extended with mandatory "meta.name" // It's used for accounts/keys management within CLI. // If not provided in the account json file, the meta.name value is set to "Unnamed Account" export type NamedKeyringPair = KeyringPair & { - meta: { - name: string - } + meta: { + name: string + } } // Summary of the account information fetched from the api for "account:current" purposes (currently just balances) export type AccountSummary = { - balances: DerivedBalances + balances: DerivedBalances } -// Object/Tuple containing council/councilElection information (council:info). -// The tuple is useful, because that's how api.queryMulti returns the results. -export type CouncilInfoTuple = Parameters; -export type CouncilInfoObj = ReturnType; // This function allows us to easily transform the tuple into the object -// and simplifies the creation of consitent Object and Tuple types (seen above). +// and simplifies the creation of consitent Object and Tuple types (seen below). export function createCouncilInfoObj( - activeCouncil: Seat[], - termEndsAt: BlockNumber, - autoStart: Boolean, - newTermDuration: BN, - candidacyLimit: BN, - councilSize: BN, - minCouncilStake: Balance, - minVotingStake: Balance, - announcingPeriod: BlockNumber, - votingPeriod: BlockNumber, - revealingPeriod: BlockNumber, - round: BN, - stage: Option + activeCouncil: Seat[], + termEndsAt: BlockNumber, + autoStart: boolean, + newTermDuration: BN, + candidacyLimit: BN, + councilSize: BN, + minCouncilStake: Balance, + minVotingStake: Balance, + announcingPeriod: BlockNumber, + votingPeriod: BlockNumber, + revealingPeriod: BlockNumber, + round: BN, + stage: Option ) { - return { - activeCouncil, - termEndsAt, - autoStart, - newTermDuration, - candidacyLimit, - councilSize, - minCouncilStake, - minVotingStake, - announcingPeriod, - votingPeriod, - revealingPeriod, - round, - stage - }; + return { + activeCouncil, + termEndsAt, + autoStart, + newTermDuration, + candidacyLimit, + councilSize, + minCouncilStake, + minVotingStake, + announcingPeriod, + votingPeriod, + revealingPeriod, + round, + stage, + } } +// Object/Tuple containing council/councilElection information (council:info). +// The tuple is useful, because that's how api.queryMulti returns the results. +export type CouncilInfoTuple = Parameters +export type CouncilInfoObj = ReturnType // Object with "name" and "value" properties, used for rendering simple CLI tables like: // Total balance: 100 JOY // Free calance: 50 JOY -export type NameValueObj = { name: string, value: string }; +export type NameValueObj = { name: string; value: string } + +// Working groups related types +export enum WorkingGroups { + StorageProviders = 'storageProviders', +} + +// In contrast to Pioneer, currently only StorageProviders group is available in CLI +export const AvailableGroups: readonly WorkingGroups[] = [WorkingGroups.StorageProviders] as const + +export type Reward = { + totalRecieved: Balance + value: Balance + interval?: number + nextPaymentBlock: number // 0 = no incoming payment +} + +// Compound working group types +export type GroupMember = { + workerId: WorkerId + memberId: MemberId + roleAccount: AccountId + profile: Membership + stake?: Balance + reward?: Reward +} + +export type GroupApplication = { + wgApplicationId: number + applicationId: number + wgOpeningId: number + member: Membership | null + roleAccout: AccountId + stakes: { + application: number + role: number + } + humanReadableText: string + stage: ApplicationStageKeys +} + +export enum OpeningStatus { + WaitingToBegin = 'WaitingToBegin', + AcceptingApplications = 'AcceptingApplications', + InReview = 'InReview', + Complete = 'Complete', + Cancelled = 'Cancelled', + Unknown = 'Unknown', +} + +export type GroupOpeningStage = { + status: OpeningStatus + block?: number + date?: Date +} + +export type GroupOpeningStakes = { + application?: StakingPolicy + role?: StakingPolicy +} + +export const stakingPolicyUnstakingPeriodKeys = [ + 'crowded_out_unstaking_period_length', + 'review_period_expired_unstaking_period_length', +] as const + +export type StakingPolicyUnstakingPeriodKey = typeof stakingPolicyUnstakingPeriodKeys[number] + +export const openingPolicyUnstakingPeriodsKeys = [ + 'fill_opening_failed_applicant_application_stake_unstaking_period', + 'fill_opening_failed_applicant_role_stake_unstaking_period', + 'fill_opening_successful_applicant_application_stake_unstaking_period', + 'terminate_application_stake_unstaking_period', + 'terminate_role_stake_unstaking_period', + 'exit_role_application_stake_unstaking_period', + 'exit_role_stake_unstaking_period', +] as const + +export type OpeningPolicyUnstakingPeriodsKey = typeof openingPolicyUnstakingPeriodsKeys[number] +export type UnstakingPeriodsKey = + | OpeningPolicyUnstakingPeriodsKey + | 'crowded_out_application_stake_unstaking_period_length' + | 'crowded_out_role_stake_unstaking_period_length' + | 'review_period_expired_application_stake_unstaking_period_length' + | 'review_period_expired_role_stake_unstaking_period_length' + +export type UnstakingPeriods = { + [k in UnstakingPeriodsKey]: number +} + +export type GroupOpening = { + wgOpeningId: number + openingId: number + stage: GroupOpeningStage + opening: Opening + stakes: GroupOpeningStakes + applications: GroupApplication[] + type: OpeningType + unstakingPeriods: UnstakingPeriods +} + +// Some helper structs for generating human_readable_text in working group opening extrinsic +// Note those types are not part of the runtime etc., we just use them to simplify prompting for values +// (since there exists functionality that handles that for substrate types like: Struct, Vec etc.) +interface WithJSONable { + toJSON: () => T +} +export class HRTJobSpecificsStruct extends Struct implements WithJSONable { + constructor(value?: JobSpecifics) { + super( + { + title: 'Text', + description: 'Text', + }, + value + ) + } + get title(): string { + return (this.get('title') as Text).toString() + } + get description(): string { + return (this.get('description') as Text).toString() + } + toJSON(): JobSpecifics { + const { title, description } = this + return { title, description } + } +} +export class HRTEntryInMembershipModukeStruct extends Struct implements WithJSONable { + constructor(value?: EntryInMembershipModuke) { + super( + { + handle: 'Text', + }, + value + ) + } + get handle(): string { + return (this.get('handle') as Text).toString() + } + toJSON(): EntryInMembershipModuke { + const { handle } = this + return { handle } + } +} +export class HRTCreatorDetailsStruct extends Struct implements WithJSONable { + constructor(value?: CreatorDetails) { + super( + { + membership: HRTEntryInMembershipModukeStruct, + }, + value + ) + } + get membership(): EntryInMembershipModuke { + return (this.get('membership') as HRTEntryInMembershipModukeStruct).toJSON() + } + toJSON(): CreatorDetails { + const { membership } = this + return { membership } + } +} +export class HRTHiringProcessStruct extends Struct implements WithJSONable { + constructor(value?: HiringProcess) { + super( + { + details: 'Vec', + }, + value + ) + } + get details(): AdditionalRolehiringProcessDetails { + return (this.get('details') as Vec).toArray().map((v) => v.toString()) + } + toJSON(): HiringProcess { + const { details } = this + return { details } + } +} +export class HRTQuestionFieldStruct extends Struct implements WithJSONable { + constructor(value?: QuestionField) { + super( + { + title: 'Text', + type: 'Text', + }, + value + ) + } + get title(): string { + return (this.get('title') as Text).toString() + } + get type(): string { + return (this.get('type') as Text).toString() + } + toJSON(): QuestionField { + const { title, type } = this + return { title, type } + } +} +class HRTQuestionsFieldsVec extends Vec.with(HRTQuestionFieldStruct) implements WithJSONable { + toJSON(): QuestionsFields { + return this.toArray().map((v) => v.toJSON()) + } +} +export class HRTQuestionSectionStruct extends Struct implements WithJSONable { + constructor(value?: QuestionSection) { + super( + { + title: 'Text', + questions: HRTQuestionsFieldsVec, + }, + value + ) + } + get title(): string { + return (this.get('title') as Text).toString() + } + get questions(): QuestionsFields { + return (this.get('questions') as HRTQuestionsFieldsVec).toJSON() + } + toJSON(): QuestionSection { + const { title, questions } = this + return { title, questions } + } +} +export class HRTQuestionSectionsVec extends Vec.with(HRTQuestionSectionStruct) + implements WithJSONable { + toJSON(): QuestionSections { + return this.toArray().map((v) => v.toJSON()) + } +} +export class HRTApplicationDetailsStruct extends Struct implements WithJSONable { + constructor(value?: ApplicationDetails) { + super( + { + sections: HRTQuestionSectionsVec, + }, + value + ) + } + get sections(): QuestionSections { + return (this.get('sections') as HRTQuestionSectionsVec).toJSON() + } + toJSON(): ApplicationDetails { + const { sections } = this + return { sections } + } +} +export class HRTStruct extends Struct implements WithJSONable { + constructor(value?: GenericJoyStreamRoleSchema) { + super( + { + version: 'u32', + headline: 'Text', + job: HRTJobSpecificsStruct, + application: HRTApplicationDetailsStruct, + reward: 'Text', + creator: HRTCreatorDetailsStruct, + process: HRTHiringProcessStruct, + }, + value + ) + } + get version(): number { + return (this.get('version') as u32).toNumber() + } + get headline(): string { + return (this.get('headline') as Text).toString() + } + get job(): JobSpecifics { + return (this.get('job') as HRTJobSpecificsStruct).toJSON() + } + get application(): ApplicationDetails { + return (this.get('application') as HRTApplicationDetailsStruct).toJSON() + } + get reward(): string { + return (this.get('reward') as Text).toString() + } + get creator(): CreatorDetails { + return (this.get('creator') as HRTCreatorDetailsStruct).toJSON() + } + get process(): HiringProcess { + return (this.get('process') as HRTHiringProcessStruct).toJSON() + } + toJSON(): GenericJoyStreamRoleSchema { + const { version, headline, job, application, reward, creator, process } = this + return { version, headline, job, application, reward, creator, process } + } +} + +// Api-related + +// Additional options that can be passed to ApiCommandBase.promptForParam in order to override +// its default behaviour, change param name, add validation etc. +export type ApiParamOptions = { + forcedName?: string + value?: { + default: ParamType + locked?: boolean + } + jsonSchema?: { + struct: Constructor + schemaValidator: ajv.ValidateFunction + } + validator?: Validator + nestedOptions?: ApiParamsOptions // For more complex params, like structs +} +export type ApiParamsOptions = { + [paramName: string]: ApiParamOptions +} + +export type ApiMethodArg = Codec +export type ApiMethodNamedArg = { + name: string + value: ApiMethodArg +} +export type ApiMethodNamedArgs = ApiMethodNamedArg[] diff --git a/cli/src/base/AccountsCommandBase.ts b/cli/src/base/AccountsCommandBase.ts index 5f676cc9e4..ad172098d5 100644 --- a/cli/src/base/AccountsCommandBase.ts +++ b/cli/src/base/AccountsCommandBase.ts @@ -1,217 +1,234 @@ -import fs from 'fs'; -import path from 'path'; -import slug from 'slug'; -import inquirer from 'inquirer'; -import ExitCodes from '../ExitCodes'; -import { CLIError } from '@oclif/errors'; -import ApiCommandBase from './ApiCommandBase'; -import { Keyring } from '@polkadot/api'; -import { formatBalance } from '@polkadot/util'; -import { NamedKeyringPair } from '../Types'; -import { DerivedBalances } from '@polkadot/api-derive/types'; -import { toFixedLength } from '../helpers/display'; - -const ACCOUNTS_DIRNAME = '/accounts'; +import fs from 'fs' +import path from 'path' +import slug from 'slug' +import inquirer from 'inquirer' +import ExitCodes from '../ExitCodes' +import { CLIError } from '@oclif/errors' +import ApiCommandBase from './ApiCommandBase' +import { Keyring } from '@polkadot/api' +import { formatBalance } from '@polkadot/util' +import { NamedKeyringPair } from '../Types' +import { DerivedBalances } from '@polkadot/api-derive/types' +import { toFixedLength } from '../helpers/display' + +const ACCOUNTS_DIRNAME = 'accounts' +const SPECIAL_ACCOUNT_POSTFIX = '__DEV' /** * Abstract base class for account-related commands. * * All the accounts available in the CLI are stored in the form of json backup files inside: - * { this.config.dataDir }/{ ACCOUNTS_DIRNAME } (ie. ~/.local/share/joystream-cli/accounts on Ubuntu) - * Where: this.config.dataDir is provided by oclif and ACCOUNTS_DIRNAME is a const (see above). + * { APP_DATA_PATH }/{ ACCOUNTS_DIRNAME } (ie. ~/.local/share/joystream-cli/accounts on Ubuntu) + * Where: APP_DATA_PATH is provided by StateAwareCommandBase and ACCOUNTS_DIRNAME is a const (see above). */ export default abstract class AccountsCommandBase extends ApiCommandBase { - getAccountsDirPath(): string { - return path.join(this.config.dataDir, ACCOUNTS_DIRNAME); - } - - getAccountFilePath(account: NamedKeyringPair): string { - return path.join(this.getAccountsDirPath(), this.generateAccountFilename(account)); - } - - generateAccountFilename(account: NamedKeyringPair): string { - return `${ slug(account.meta.name, '_') }__${ account.address }.json`; - } - - private initAccountsFs(): void { - if (!fs.existsSync(this.getAccountsDirPath())) { - fs.mkdirSync(this.getAccountsDirPath()); - } - } - - saveAccount(account: NamedKeyringPair, password: string): void { - try { - fs.writeFileSync(this.getAccountFilePath(account), JSON.stringify(account.toJson(password))); - } catch(e) { - throw this.createDataWriteError(); - } - } - - fetchAccountFromJsonFile(jsonBackupFilePath: string): NamedKeyringPair { - if (!fs.existsSync(jsonBackupFilePath)) { - throw new CLIError('Input file does not exist!', { exit: ExitCodes.FileNotFound }); - } - if (path.extname(jsonBackupFilePath) !== '.json') { - throw new CLIError('Invalid input file: File extension should be .json', { exit: ExitCodes.InvalidFile }); - } - let accountJsonObj: any; - try { - accountJsonObj = require(jsonBackupFilePath); - } catch (e) { - throw new CLIError('Provided backup file is not valid or cannot be accessed', { exit: ExitCodes.InvalidFile }); - } - if (typeof accountJsonObj !== 'object' || accountJsonObj === null) { - throw new CLIError('Provided backup file is not valid', { exit: ExitCodes.InvalidFile }); - } - - // Force some default account name if none is provided in the original backup - if (!accountJsonObj.meta) accountJsonObj.meta = {}; - if (!accountJsonObj.meta.name) accountJsonObj.meta.name = 'Unnamed Account'; - - let keyring = new Keyring(); - let account:NamedKeyringPair; - try { - // Try adding and retrieving the keys in order to validate that the backup file is correct - keyring.addFromJson(accountJsonObj); - account = keyring.getPair(accountJsonObj.address); // We can be sure it's named, because we forced it before - } catch (e) { - throw new CLIError('Provided backup file is not valid', { exit: ExitCodes.InvalidFile }); - } - - return account; - } - - private fetchAccountOrNullFromFile(jsonFilePath: string): NamedKeyringPair | null { - try { - return this.fetchAccountFromJsonFile(jsonFilePath); - } catch (e) { - // Here in case of a typical CLIError we just return null (otherwise we throw) - if (!(e instanceof CLIError)) throw e; - return null; - } - } - - fetchAccounts(): NamedKeyringPair[] { - let files: string[] = []; - const accountDir = this.getAccountsDirPath(); - try { - files = fs.readdirSync(accountDir); - } - catch(e) { - } - - // We have to assert the type, because TS is not aware that we're filtering out the nulls at the end - return files - .map(fileName => { - const filePath = path.join(accountDir, fileName); - return this.fetchAccountOrNullFromFile(filePath); - }) - .filter(accObj => accObj !== null); - } - - getSelectedAccountFilename(): string { - return this.getPreservedState().selectedAccountFilename; - } - - getSelectedAccount(): NamedKeyringPair | null { - const selectedAccountFilename = this.getSelectedAccountFilename(); - - if (!selectedAccountFilename) { - return null; - } - - const account = this.fetchAccountOrNullFromFile( - path.join(this.getAccountsDirPath(), selectedAccountFilename) - ); - - return account; - } - - // Use when account usage is required in given command - async getRequiredSelectedAccount(promptIfMissing: boolean = true): Promise { - let selectedAccount: NamedKeyringPair | null = this.getSelectedAccount(); - if (!selectedAccount) { - this.warn('No default account selected! Use account:choose to set the default account!'); - if (!promptIfMissing) this.exit(ExitCodes.NoAccountSelected); - const accounts: NamedKeyringPair[] = this.fetchAccounts(); - if (!accounts.length) { - this.error('There are no accounts available!', { exit: ExitCodes.NoAccountFound }); - } - - selectedAccount = await this.promptForAccount(accounts); - } - - return selectedAccount; - } - - async setSelectedAccount(account: NamedKeyringPair): Promise { - await this.setPreservedState({ selectedAccountFilename: this.generateAccountFilename(account) }); - } - - async promptForPassword(message:string = 'Your account\'s password') { - const { password } = await inquirer.prompt([ - { name: 'password', type: 'password', message } - ]); - - return password; - } - - async requireConfirmation(message: string = 'Are you sure you want to execute this action?'): Promise { - const { confirmed } = await inquirer.prompt([ - { type: 'confirm', name: 'confirmed', message, default: false } - ]); - if (!confirmed) this.exit(ExitCodes.OK); - } - - async promptForAccount( - accounts: NamedKeyringPair[], - defaultAccount: NamedKeyringPair | null = null, - message: string = 'Select an account', - showBalances: boolean = true - ): Promise { - let balances: DerivedBalances[]; - if (showBalances) { - balances = await this.getApi().getAccountsBalancesInfo(accounts.map(acc => acc.address)); - } - const longestAccNameLength: number = accounts.reduce((prev, curr) => Math.max(curr.meta.name.length, prev), 0); - const accNameColLength: number = Math.min(longestAccNameLength + 1, 20); - const { chosenAccountFilename } = await inquirer.prompt([{ - name: 'chosenAccountFilename', - message, - type: 'list', - choices: accounts.map((account: NamedKeyringPair, i) => ({ - name: ( - `${ toFixedLength(account.meta.name, accNameColLength) } | `+ - `${ account.address } | ` + - ((showBalances || '') && ( - `${ formatBalance(balances[i].availableBalance) } / `+ - `${ formatBalance(balances[i].votingBalance) }` - )) - ), - value: this.generateAccountFilename(account), - short: `${ account.meta.name } (${ account.address })` - })), - default: defaultAccount && this.generateAccountFilename(defaultAccount) - }]); - - return accounts.find(acc => this.generateAccountFilename(acc) === chosenAccountFilename); - } - - async requestAccountDecoding(account: NamedKeyringPair): Promise { - const password: string = await this.promptForPassword(); - try { - account.decodePkcs8(password); - } catch (e) { - this.error('Invalid password!', { exit: ExitCodes.InvalidInput }); - } - } - - async init() { - await super.init(); - try { - this.initAccountsFs(); - } catch (e) { - throw this.createDataDirInitError(); - } - } + getAccountsDirPath(): string { + return path.join(this.getAppDataPath(), ACCOUNTS_DIRNAME) + } + + getAccountFilePath(account: NamedKeyringPair, isSpecial = false): string { + return path.join(this.getAccountsDirPath(), this.generateAccountFilename(account, isSpecial)) + } + + generateAccountFilename(account: NamedKeyringPair, isSpecial = false): string { + return `${slug(account.meta.name, '_')}__${account.address}${isSpecial ? SPECIAL_ACCOUNT_POSTFIX : ''}.json` + } + + private initAccountsFs(): void { + if (!fs.existsSync(this.getAccountsDirPath())) { + fs.mkdirSync(this.getAccountsDirPath()) + } + } + + saveAccount(account: NamedKeyringPair, password: string, isSpecial = false): void { + try { + const destPath = this.getAccountFilePath(account, isSpecial) + fs.writeFileSync(destPath, JSON.stringify(account.toJson(password))) + } catch (e) { + throw this.createDataWriteError() + } + } + + // Add dev "Alice" and "Bob" accounts + initSpecialAccounts() { + const keyring = new Keyring({ type: 'sr25519' }) + keyring.addFromUri('//Alice', { name: 'Alice' }) + keyring.addFromUri('//Bob', { name: 'Bob' }) + keyring.getPairs().forEach((pair) => this.saveAccount({ ...pair, meta: { name: pair.meta.name } }, '', true)) + } + + fetchAccountFromJsonFile(jsonBackupFilePath: string): NamedKeyringPair { + if (!fs.existsSync(jsonBackupFilePath)) { + throw new CLIError('Input file does not exist!', { exit: ExitCodes.FileNotFound }) + } + if (path.extname(jsonBackupFilePath) !== '.json') { + throw new CLIError('Invalid input file: File extension should be .json', { exit: ExitCodes.InvalidFile }) + } + let accountJsonObj: any + try { + accountJsonObj = require(jsonBackupFilePath) + } catch (e) { + throw new CLIError('Provided backup file is not valid or cannot be accessed', { exit: ExitCodes.InvalidFile }) + } + if (typeof accountJsonObj !== 'object' || accountJsonObj === null) { + throw new CLIError('Provided backup file is not valid', { exit: ExitCodes.InvalidFile }) + } + + // Force some default account name if none is provided in the original backup + if (!accountJsonObj.meta) accountJsonObj.meta = {} + if (!accountJsonObj.meta.name) accountJsonObj.meta.name = 'Unnamed Account' + + const keyring = new Keyring() + let account: NamedKeyringPair + try { + // Try adding and retrieving the keys in order to validate that the backup file is correct + keyring.addFromJson(accountJsonObj) + account = keyring.getPair(accountJsonObj.address) as NamedKeyringPair // We can be sure it's named, because we forced it before + } catch (e) { + throw new CLIError('Provided backup file is not valid', { exit: ExitCodes.InvalidFile }) + } + + return account + } + + private fetchAccountOrNullFromFile(jsonFilePath: string): NamedKeyringPair | null { + try { + return this.fetchAccountFromJsonFile(jsonFilePath) + } catch (e) { + // Here in case of a typical CLIError we just return null (otherwise we throw) + if (!(e instanceof CLIError)) throw e + return null + } + } + + fetchAccounts(includeSpecial = false): NamedKeyringPair[] { + let files: string[] = [] + const accountDir = this.getAccountsDirPath() + try { + files = fs.readdirSync(accountDir) + } catch (e) { + // Do nothing + } + + // We have to assert the type, because TS is not aware that we're filtering out the nulls at the end + return files + .map((fileName) => { + const filePath = path.join(accountDir, fileName) + if (!includeSpecial && filePath.includes(SPECIAL_ACCOUNT_POSTFIX + '.')) return null + return this.fetchAccountOrNullFromFile(filePath) + }) + .filter((accObj) => accObj !== null) as NamedKeyringPair[] + } + + getSelectedAccountFilename(): string { + return this.getPreservedState().selectedAccountFilename + } + + getSelectedAccount(): NamedKeyringPair | null { + const selectedAccountFilename = this.getSelectedAccountFilename() + + if (!selectedAccountFilename) { + return null + } + + const account = this.fetchAccountOrNullFromFile(path.join(this.getAccountsDirPath(), selectedAccountFilename)) + + return account + } + + // Use when account usage is required in given command + async getRequiredSelectedAccount(promptIfMissing = true): Promise { + let selectedAccount: NamedKeyringPair | null = this.getSelectedAccount() + if (!selectedAccount) { + if (!promptIfMissing) { + this.error('No default account selected! Use account:choose to set the default account.', { + exit: ExitCodes.NoAccountSelected, + }) + } + + const accounts: NamedKeyringPair[] = this.fetchAccounts() + if (!accounts.length) { + this.error('No accounts available! Use account:import in order to import accounts into the CLI.', { + exit: ExitCodes.NoAccountFound, + }) + } + + this.warn('No default account selected!') + selectedAccount = await this.promptForAccount(accounts) + await this.setSelectedAccount(selectedAccount) + } + + return selectedAccount + } + + async setSelectedAccount(account: NamedKeyringPair): Promise { + const accountFilename = fs.existsSync(this.getAccountFilePath(account, true)) + ? this.generateAccountFilename(account, true) + : this.generateAccountFilename(account) + + await this.setPreservedState({ selectedAccountFilename: accountFilename }) + } + + async promptForPassword(message = "Your account's password") { + const { password } = await inquirer.prompt([{ name: 'password', type: 'password', message }]) + + return password + } + + async requireConfirmation(message = 'Are you sure you want to execute this action?'): Promise { + const { confirmed } = await inquirer.prompt([{ type: 'confirm', name: 'confirmed', message, default: false }]) + if (!confirmed) this.exit(ExitCodes.OK) + } + + async promptForAccount( + accounts: NamedKeyringPair[], + defaultAccount: NamedKeyringPair | null = null, + message = 'Select an account', + showBalances = true + ): Promise { + let balances: DerivedBalances[] + if (showBalances) { + balances = await this.getApi().getAccountsBalancesInfo(accounts.map((acc) => acc.address)) + } + const longestAccNameLength: number = accounts.reduce((prev, curr) => Math.max(curr.meta.name.length, prev), 0) + const accNameColLength: number = Math.min(longestAccNameLength + 1, 20) + const { chosenAccountFilename } = await inquirer.prompt([ + { + name: 'chosenAccountFilename', + message, + type: 'list', + choices: accounts.map((account: NamedKeyringPair, i) => ({ + name: + `${toFixedLength(account.meta.name, accNameColLength)} | ` + + `${account.address} | ` + + ((showBalances || '') && + `${formatBalance(balances[i].availableBalance)} / ` + `${formatBalance(balances[i].votingBalance)}`), + value: this.generateAccountFilename(account), + short: `${account.meta.name} (${account.address})`, + })), + default: defaultAccount && this.generateAccountFilename(defaultAccount), + }, + ]) + + return accounts.find((acc) => this.generateAccountFilename(acc) === chosenAccountFilename) as NamedKeyringPair + } + + async requestAccountDecoding(account: NamedKeyringPair): Promise { + const password: string = await this.promptForPassword() + try { + account.decodePkcs8(password) + } catch (e) { + this.error('Invalid password!', { exit: ExitCodes.InvalidInput }) + } + } + + async init() { + await super.init() + try { + this.initAccountsFs() + this.initSpecialAccounts() + } catch (e) { + throw this.createDataDirInitError() + } + } } diff --git a/cli/src/base/ApiCommandBase.ts b/cli/src/base/ApiCommandBase.ts index 017810b9af..0c5166c94d 100644 --- a/cli/src/base/ApiCommandBase.ts +++ b/cli/src/base/ApiCommandBase.ts @@ -1,28 +1,462 @@ -import ExitCodes from '../ExitCodes'; -import { CLIError } from '@oclif/errors'; -import StateAwareCommandBase from './StateAwareCommandBase'; -import Api from '../Api'; -import { ApiPromise } from '@polkadot/api' +import ExitCodes from '../ExitCodes' +import { CLIError } from '@oclif/errors' +import StateAwareCommandBase from './StateAwareCommandBase' +import Api from '../Api' +import { getTypeDef, createType, Option, Tuple, Bytes } from '@polkadot/types' +import { Codec, TypeDef, TypeDefInfo, Constructor } from '@polkadot/types/types' +import { Vec, Struct, Enum } from '@polkadot/types/codec' +import { ApiPromise, WsProvider } from '@polkadot/api' +import { KeyringPair } from '@polkadot/keyring/types' +import chalk from 'chalk' +import { SubmittableResultImpl } from '@polkadot/api/types' +import ajv from 'ajv' +import { ApiMethodArg, ApiMethodNamedArgs, ApiParamsOptions, ApiParamOptions } from '../Types' +import { createParamOptions } from '../helpers/promptOptions' + +class ExtrinsicFailedError extends Error {} /** * Abstract base class for commands that require access to the API. */ export default abstract class ApiCommandBase extends StateAwareCommandBase { - private api: Api | null = null; + private api: Api | null = null + forceSkipApiUriPrompt = false + + getApi(): Api { + if (!this.api) throw new CLIError('Tried to get API before initialization.', { exit: ExitCodes.ApiError }) + return this.api + } + + // Get original api for lower-level api calls + getOriginalApi(): ApiPromise { + return this.getApi().getOriginalApi() + } + + async init() { + await super.init() + let apiUri: string = this.getPreservedState().apiUri + if (!apiUri) { + this.warn("You haven't provided a node/endpoint for the CLI to connect to yet!") + apiUri = await this.promptForApiUri() + } + this.api = await Api.create(apiUri) + } + + async promptForApiUri(): Promise { + let selectedNodeUri = await this.simplePrompt({ + type: 'list', + message: 'Choose a node/endpoint:', + choices: [ + { + name: 'Local node (ws://localhost:9944)', + value: 'ws://localhost:9944', + }, + { + name: 'Current Testnet official Joystream node (wss://rome-rpc-endpoint.joystream.org:9944/)', + value: 'wss://rome-rpc-endpoint.joystream.org:9944/', + }, + { + name: 'Custom endpoint', + value: '', + }, + ], + }) + + if (!selectedNodeUri) { + do { + selectedNodeUri = await this.simplePrompt({ + type: 'input', + message: 'Provide a WS endpoint uri', + }) + if (!this.isApiUriValid(selectedNodeUri)) { + this.warn('Provided uri seems incorrect! Please try again...') + } + } while (!this.isApiUriValid(selectedNodeUri)) + } + + await this.setPreservedState({ apiUri: selectedNodeUri }) + + return selectedNodeUri + } + + isApiUriValid(uri: string) { + try { + new WsProvider(uri) + } catch (e) { + return false + } + return true + } + + // This is needed to correctly handle some structs, enums etc. + // Where the main typeDef doesn't provide enough information + protected getRawTypeDef(type: string) { + const instance = createType(type as any) + return getTypeDef(instance.toRawType()) + } + + // Prettifier for type names which are actually JSON strings + protected prettifyJsonTypeName(json: string) { + const obj = JSON.parse(json) as { [key: string]: string } + return ( + '{\n' + + Object.keys(obj) + .map((prop) => ` ${prop}${chalk.white(':' + obj[prop])}`) + .join('\n') + + '\n}' + ) + } + + // Get param name based on TypeDef object + protected paramName(typeDef: TypeDef) { + return chalk.green( + typeDef.displayName || + typeDef.name || + (typeDef.type.startsWith('{') ? this.prettifyJsonTypeName(typeDef.type) : typeDef.type) + ) + } + + // Prompt for simple/plain value (provided as string) of given type + async promptForSimple(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise { + // If no default provided - get default value resulting from providing empty string + const defaultValueString = + paramOptions?.value?.default?.toString() || createType(typeDef.type as any, '').toString() + const providedValue = await this.simplePrompt({ + message: `Provide value for ${this.paramName(typeDef)}`, + type: 'input', + // We want to avoid showing default value like '0x', because it falsely suggests + // that user needs to provide the value as hex + default: (defaultValueString === '0x' ? '' : defaultValueString) || undefined, + validate: paramOptions?.validator, + }) + return createType(typeDef.type as any, providedValue) + } - getApi(): Api { - if (!this.api) throw new CLIError('Tried to get API before initialization.', { exit: ExitCodes.ApiError }); - return this.api; + // Prompt for Option value + async promptForOption(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise> { + const subtype = typeDef.sub as TypeDef // We assume that Opion always has a single subtype + const defaultValue = paramOptions?.value?.default as Option | undefined + const confirmed = await this.simplePrompt({ + message: `Do you want to provide the optional ${this.paramName(typeDef)} parameter?`, + type: 'confirm', + default: defaultValue ? defaultValue.isSome : false, + }) + + if (confirmed) { + this.openIndentGroup() + const value = await this.promptForParam( + subtype.type, + createParamOptions(subtype.name, defaultValue?.unwrapOr(undefined)) + ) + this.closeIndentGroup() + return new Option(subtype.type as any, value) + } + + return new Option(subtype.type as any, null) + } + + // Prompt for Tuple + // TODO: Not well tested yet + async promptForTuple(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise { + console.log(chalk.grey(`Providing values for ${this.paramName(typeDef)} tuple:`)) + + this.openIndentGroup() + const result: ApiMethodArg[] = [] + // We assume that for Tuple there is always at least 1 subtype (pethaps it's even always an array?) + const subtypes: TypeDef[] = Array.isArray(typeDef.sub) ? typeDef.sub! : [typeDef.sub!] + const defaultValue = paramOptions?.value?.default as Tuple | undefined + + for (const [index, subtype] of Object.entries(subtypes)) { + const entryDefaultVal = defaultValue && defaultValue[parseInt(index)] + const inputParam = await this.promptForParam(subtype.type, createParamOptions(subtype.name, entryDefaultVal)) + result.push(inputParam) } + this.closeIndentGroup() + + return new Tuple(subtypes.map((subtype) => subtype.type) as any, result) + } + + // Prompt for Struct + async promptForStruct(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise { + console.log(chalk.grey(`Providing values for ${this.paramName(typeDef)} struct:`)) - // Get original api for lower-level api calls - getOriginalApi(): ApiPromise { - return this.getApi().getOriginalApi(); + this.openIndentGroup() + const structType = typeDef.type + const rawTypeDef = this.getRawTypeDef(structType) + // We assume struct typeDef always has array of typeDefs inside ".sub" + const structSubtypes = rawTypeDef.sub as TypeDef[] + const structDefault = paramOptions?.value?.default as Struct | undefined + + const structValues: { [key: string]: ApiMethodArg } = {} + for (const subtype of structSubtypes) { + const fieldOptions = paramOptions?.nestedOptions && paramOptions.nestedOptions[subtype.name!] + const fieldDefaultValue = fieldOptions?.value?.default || (structDefault && structDefault.get(subtype.name!)) + const finalFieldOptions: ApiParamOptions = { + forcedName: subtype.name, + ...fieldOptions, // "forcedName" above should be overriden with "fieldOptions.forcedName" if available + value: fieldDefaultValue && { ...fieldOptions?.value, default: fieldDefaultValue }, + } + structValues[subtype.name!] = await this.promptForParam(subtype.type, finalFieldOptions) } + this.closeIndentGroup() + + return createType(structType as any, structValues) + } + + // Prompt for Vec + async promptForVec(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise> { + console.log(chalk.grey(`Providing values for ${this.paramName(typeDef)} vector:`)) + + this.openIndentGroup() + // We assume Vec always has one TypeDef as ".sub" + const subtype = typeDef.sub as TypeDef + const defaultValue = paramOptions?.value?.default as Vec | undefined + const entries: Codec[] = [] + let addAnother = false + do { + addAnother = await this.simplePrompt({ + message: `Do you want to add another entry to ${this.paramName(typeDef)} vector (currently: ${ + entries.length + })?`, + type: 'confirm', + default: defaultValue ? entries.length < defaultValue.length : false, + }) + const defaultEntryValue = defaultValue && defaultValue[entries.length] + if (addAnother) { + entries.push(await this.promptForParam(subtype.type, createParamOptions(subtype.name, defaultEntryValue))) + } + } while (addAnother) + this.closeIndentGroup() + + return new Vec(subtype.type as any, entries) + } + + // Prompt for Enum + async promptForEnum(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise { + const enumType = typeDef.type + const rawTypeDef = this.getRawTypeDef(enumType) + // We assume enum always has array on TypeDefs inside ".sub" + const enumSubtypes = rawTypeDef.sub as TypeDef[] + const defaultValue = paramOptions?.value?.default as Enum | undefined + + const enumSubtypeName = await this.simplePrompt({ + message: `Choose value for ${this.paramName(typeDef)}:`, + type: 'list', + choices: enumSubtypes.map((subtype) => ({ + name: subtype.name, + value: subtype.name, + })), + default: defaultValue?.type, + }) + + const enumSubtype = enumSubtypes.find((st) => st.name === enumSubtypeName)! - async init() { - await super.init(); - const apiUri: string = this.getPreservedState().apiUri; - this.api = await Api.create(apiUri); + if (enumSubtype.type !== 'Null') { + const subtypeOptions = createParamOptions(enumSubtype.name, defaultValue?.value) + return createType(enumType as any, { + [enumSubtype.name!]: await this.promptForParam(enumSubtype.type, subtypeOptions), + }) } + + return createType(enumType as any, enumSubtype.name) + } + + // Prompt for param based on "paramType" string (ie. Option) + // TODO: This may not yet work for all possible types + async promptForParam( + paramType: string, + paramOptions?: ApiParamOptions // TODO: This is not fully implemented for all types yet + ): Promise { + const typeDef = getTypeDef(paramType) + const rawTypeDef = this.getRawTypeDef(paramType) + + if (paramOptions?.forcedName) { + typeDef.name = paramOptions.forcedName + } + + if (paramOptions?.value?.locked) { + return paramOptions.value.default + } + + if (paramOptions?.jsonSchema) { + const { struct, schemaValidator } = paramOptions.jsonSchema + return await this.promptForJsonBytes( + struct, + typeDef.name, + paramOptions.value?.default as Bytes | undefined, + schemaValidator + ) + } + + if (rawTypeDef.info === TypeDefInfo.Option) { + return await this.promptForOption(typeDef, paramOptions) + } else if (rawTypeDef.info === TypeDefInfo.Tuple) { + return await this.promptForTuple(typeDef, paramOptions) + } else if (rawTypeDef.info === TypeDefInfo.Struct) { + return await this.promptForStruct(typeDef, paramOptions) + } else if (rawTypeDef.info === TypeDefInfo.Enum) { + return await this.promptForEnum(typeDef, paramOptions) + } else if (rawTypeDef.info === TypeDefInfo.Vec) { + return await this.promptForVec(typeDef, paramOptions) + } else { + return await this.promptForSimple(typeDef, paramOptions) + } + } + + async promptForJsonBytes( + jsonStruct: Constructor, + argName?: string, + defaultValue?: Bytes, + schemaValidator?: ajv.ValidateFunction + ) { + const rawType = new jsonStruct().toRawType() + const typeDef = getTypeDef(rawType) + + const defaultStruct = + defaultValue && new jsonStruct(JSON.parse(Buffer.from(defaultValue.toHex().replace('0x', ''), 'hex').toString())) + + if (argName) { + typeDef.name = argName + } + + let isValid = true, + jsonText: string + do { + const structVal = await this.promptForStruct(typeDef, createParamOptions(typeDef.name, defaultStruct)) + jsonText = JSON.stringify(structVal.toJSON()) + if (schemaValidator) { + isValid = Boolean(schemaValidator(JSON.parse(jsonText))) + if (!isValid) { + this.log('\n') + this.warn( + 'Schema validation failed with:\n' + + schemaValidator.errors?.map((e) => chalk.red(`${chalk.bold(e.dataPath)}: ${e.message}`)).join('\n') + + '\nTry again...' + ) + this.log('\n') + } + } + } while (!isValid) + + return new Bytes('0x' + Buffer.from(jsonText, 'ascii').toString('hex')) + } + + async promptForExtrinsicParams( + module: string, + method: string, + paramsOptions?: ApiParamsOptions + ): Promise { + const extrinsicMethod = this.getOriginalApi().tx[module][method] + const values: ApiMethodArg[] = [] + + this.openIndentGroup() + for (const arg of extrinsicMethod.meta.args.toArray()) { + const argName = arg.name.toString() + const argType = arg.type.toString() + let argOptions = paramsOptions && paramsOptions[argName] + if (!argOptions?.forcedName) { + argOptions = { ...argOptions, forcedName: argName } + } + values.push(await this.promptForParam(argType, argOptions)) + } + this.closeIndentGroup() + + return values + } + + sendExtrinsic(account: KeyringPair, module: string, method: string, params: Codec[]) { + return new Promise((resolve, reject) => { + const extrinsicMethod = this.getOriginalApi().tx[module][method] + let unsubscribe: () => void + extrinsicMethod(...params) + .signAndSend(account, {}, (result: SubmittableResultImpl) => { + // Implementation loosely based on /pioneer/packages/react-signer/src/Modal.tsx + if (!result || !result.status) { + return + } + + if (result.status.isFinalized) { + unsubscribe() + result.events + .filter(({ event: { section } }): boolean => section === 'system') + .forEach(({ event: { method } }): void => { + if (method === 'ExtrinsicFailed') { + reject(new ExtrinsicFailedError('Extrinsic execution error!')) + } else if (method === 'ExtrinsicSuccess') { + resolve() + } + }) + } else if (result.isError) { + reject(new ExtrinsicFailedError('Extrinsic execution error!')) + } + }) + .then((unsubFunc) => (unsubscribe = unsubFunc)) + .catch((e) => + reject(new ExtrinsicFailedError(`Cannot send the extrinsic: ${e.message ? e.message : JSON.stringify(e)}`)) + ) + }) + } + + async sendAndFollowExtrinsic( + account: KeyringPair, + module: string, + method: string, + params: Codec[], + warnOnly = false // If specified - only warning will be displayed (instead of error beeing thrown) + ) { + try { + this.log(chalk.white(`\nSending ${module}.${method} extrinsic...`)) + await this.sendExtrinsic(account, module, method, params) + this.log(chalk.green(`Extrinsic successful!`)) + } catch (e) { + if (e instanceof ExtrinsicFailedError && warnOnly) { + this.warn(`${module}.${method} extrinsic failed! ${e.message}`) + } else if (e instanceof ExtrinsicFailedError) { + throw new CLIError(`${module}.${method} extrinsic failed! ${e.message}`, { exit: ExitCodes.ApiError }) + } else { + throw e + } + } + } + + async buildAndSendExtrinsic( + account: KeyringPair, + module: string, + method: string, + paramsOptions: ApiParamsOptions, + warnOnly = false // If specified - only warning will be displayed (instead of error beeing thrown) + ): Promise { + const params = await this.promptForExtrinsicParams(module, method, paramsOptions) + await this.sendAndFollowExtrinsic(account, module, method, params, warnOnly) + + return params + } + + extrinsicArgsFromDraft(module: string, method: string, draftFilePath: string): ApiMethodNamedArgs { + let draftJSONObj + const parsedArgs: ApiMethodNamedArgs = [] + const extrinsicMethod = this.getOriginalApi().tx[module][method] + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + draftJSONObj = require(draftFilePath) + } catch (e) { + throw new CLIError(`Could not load draft from: ${draftFilePath}`, { exit: ExitCodes.InvalidFile }) + } + if (!draftJSONObj || !Array.isArray(draftJSONObj) || draftJSONObj.length !== extrinsicMethod.meta.args.length) { + throw new CLIError(`The draft file at ${draftFilePath} is invalid!`, { exit: ExitCodes.InvalidFile }) + } + for (const [index, arg] of Object.entries(extrinsicMethod.meta.args.toArray())) { + const argName = arg.name.toString() + const argType = arg.type.toString() + try { + parsedArgs.push({ name: argName, value: createType(argType as any, draftJSONObj[parseInt(index)]) }) + } catch (e) { + throw new CLIError(`Couldn't parse ${argName} value from draft at ${draftFilePath}!`, { + exit: ExitCodes.InvalidFile, + }) + } + } + + return parsedArgs + } } diff --git a/cli/src/base/DefaultCommandBase.ts b/cli/src/base/DefaultCommandBase.ts index 24985e1446..36dfab4b7c 100644 --- a/cli/src/base/DefaultCommandBase.ts +++ b/cli/src/base/DefaultCommandBase.ts @@ -1,15 +1,106 @@ -import ExitCodes from '../ExitCodes'; -import Command from '@oclif/command'; +import ExitCodes from '../ExitCodes' +import Command from '@oclif/command' +import inquirer, { DistinctQuestion } from 'inquirer' +import chalk from 'chalk' /** * Abstract base class for pretty much all commands * (prevents console.log from hanging the process and unifies the default exit code) */ export default abstract class DefaultCommandBase extends Command { - async finally(err: any) { - // called after run and catch regardless of whether or not the command errored - // We'll force exit here, in case there is no error, to prevent console.log from hanging the process - if (!err) this.exit(ExitCodes.OK); - super.finally(err); + protected indentGroupsOpened = 0 + protected jsonPrettyIdent = '' + + openIndentGroup() { + console.group() + ++this.indentGroupsOpened + } + + closeIndentGroup() { + console.groupEnd() + --this.indentGroupsOpened + } + + async simplePrompt(question: DistinctQuestion) { + const { result } = await inquirer.prompt([ + { + ...question, + name: 'result', + // prefix = 2 spaces for each group - 1 (because 1 is always added by default) + prefix: Array.from(new Array(this.indentGroupsOpened)) + .map(() => ' ') + .join('') + .slice(1), + }, + ]) + + return result + } + + private jsonPrettyIndented(line: string) { + return `${this.jsonPrettyIdent}${line}` + } + + private jsonPrettyOpen(char: '{' | '[') { + this.jsonPrettyIdent += ' ' + return chalk.gray(char) + '\n' + } + + private jsonPrettyClose(char: '}' | ']') { + this.jsonPrettyIdent = this.jsonPrettyIdent.slice(0, -4) + return this.jsonPrettyIndented(chalk.gray(char)) + } + + private jsonPrettyKeyVal(key: string, val: any): string { + return this.jsonPrettyIndented(chalk.white(`${key}: ${this.jsonPrettyAny(val)}`)) + } + + private jsonPrettyObj(obj: { [key: string]: any }): string { + return ( + this.jsonPrettyOpen('{') + + Object.keys(obj) + .map((k) => this.jsonPrettyKeyVal(k, obj[k])) + .join(',\n') + + '\n' + + this.jsonPrettyClose('}') + ) + } + + private jsonPrettyArr(arr: any[]): string { + return ( + this.jsonPrettyOpen('[') + + arr.map((v) => this.jsonPrettyIndented(this.jsonPrettyAny(v))).join(',\n') + + '\n' + + this.jsonPrettyClose(']') + ) + } + + private jsonPrettyAny(val: any): string { + if (Array.isArray(val)) { + return this.jsonPrettyArr(val) + } else if (typeof val === 'object' && val !== null) { + return this.jsonPrettyObj(val) + } else if (typeof val === 'string') { + return chalk.green(`"${val}"`) } + + // Number, boolean etc. + return chalk.cyan(val) + } + + jsonPrettyPrint(json: string) { + try { + const parsed = JSON.parse(json) + console.log(this.jsonPrettyAny(parsed)) + } catch (e) { + console.log(this.jsonPrettyAny(json)) + } + } + + async finally(err: any) { + // called after run and catch regardless of whether or not the command errored + // We'll force exit here, in case there is no error, to prevent console.log from hanging the process + if (!err) this.exit(ExitCodes.OK) + super.finally(err) + } } diff --git a/cli/src/base/StateAwareCommandBase.ts b/cli/src/base/StateAwareCommandBase.ts index 5c5f924994..b8d18729cd 100644 --- a/cli/src/base/StateAwareCommandBase.ts +++ b/cli/src/base/StateAwareCommandBase.ts @@ -1,115 +1,131 @@ -import fs from 'fs'; -import path from 'path'; -import ExitCodes from '../ExitCodes'; -import { CLIError } from '@oclif/errors'; -import { DEFAULT_API_URI } from '../Api'; -import lockFile from 'proper-lockfile'; -import DefaultCommandBase from './DefaultCommandBase'; +import fs from 'fs' +import path from 'path' +import ExitCodes from '../ExitCodes' +import { CLIError } from '@oclif/errors' +import lockFile from 'proper-lockfile' +import DefaultCommandBase from './DefaultCommandBase' +import os from 'os' +import _ from 'lodash' // Type for the state object (which is preserved as json in the state file) type StateObject = { - selectedAccountFilename: string, - apiUri: string -}; + selectedAccountFilename: string + apiUri: string +} // State object default values const DEFAULT_STATE: StateObject = { - selectedAccountFilename: '', - apiUri: DEFAULT_API_URI + selectedAccountFilename: '', + apiUri: '', } -// State file path (relative to this.config.dataDir) -const STATE_FILE = '/state.json'; +// State file path (relative to getAppDataPath()) +const STATE_FILE = '/state.json' // Possible data directory access errors enum DataDirErrorType { - Init = 0, - Read = 1, - Write = 2, + Init = 0, + Read = 1, + Write = 2, } /** * Abstract base class for commands that need to work with the preserved state. * - * The preserved state is kept in a json file inside the data directory (this.config.dataDir, supplied by oclif). + * The preserved state is kept in a json file inside the data directory. * The state object contains all the information that needs to be preserved across sessions, ie. the default account * choosen by the user after executing account:choose command etc. (see "StateObject" type above). */ export default abstract class StateAwareCommandBase extends DefaultCommandBase { - getStateFilePath(): string { - return path.join(this.config.dataDir, STATE_FILE); + getAppDataPath(): string { + const systemAppDataPath = + process.env.APPDATA || + (process.platform === 'darwin' + ? path.join(os.homedir(), '/Library/Application Support') + : path.join(os.homedir(), '/.local/share')) + // eslint-disable-next-line @typescript-eslint/no-var-requires + const packageJson: { name?: string } = require('../../package.json') + if (!packageJson || !packageJson.name) { + throw new CLIError('Cannot get package name from package.json!') } + return path.join(systemAppDataPath, _.kebabCase(packageJson.name)) + } - private createDataDirFsError(errorType: DataDirErrorType, specificPath: string = '') { - const actionStrs: { [x in DataDirErrorType]: string } = { - [DataDirErrorType.Init]: 'initialize', - [DataDirErrorType.Read]: 'read from', - [DataDirErrorType.Write]: 'write into' - }; - - const errorMsg = - `Unexpected error while trying to ${ actionStrs[errorType] } the data directory.`+ - `(${ path.join(this.config.dataDir, specificPath) })! Permissions issue?`; + getStateFilePath(): string { + return path.join(this.getAppDataPath(), STATE_FILE) + } - return new CLIError(errorMsg, { exit: ExitCodes.FsOperationFailed }); + private createDataDirFsError(errorType: DataDirErrorType, specificPath = '') { + const actionStrs: { [x in DataDirErrorType]: string } = { + [DataDirErrorType.Init]: 'initialize', + [DataDirErrorType.Read]: 'read from', + [DataDirErrorType.Write]: 'write into', } - createDataReadError(specificPath: string = ''): CLIError { - return this.createDataDirFsError(DataDirErrorType.Read, specificPath); - } + const errorMsg = + `Unexpected error while trying to ${actionStrs[errorType]} the data directory.` + + `(${path.join(this.getAppDataPath(), specificPath)})! Permissions issue?` - createDataWriteError(specificPath: string = ''): CLIError { - return this.createDataDirFsError(DataDirErrorType.Write, specificPath); - } + return new CLIError(errorMsg, { exit: ExitCodes.FsOperationFailed }) + } - createDataDirInitError(specificPath: string = ''): CLIError { - return this.createDataDirFsError(DataDirErrorType.Init, specificPath); - } + createDataReadError(specificPath = ''): CLIError { + return this.createDataDirFsError(DataDirErrorType.Read, specificPath) + } + + createDataWriteError(specificPath = ''): CLIError { + return this.createDataDirFsError(DataDirErrorType.Write, specificPath) + } - private initStateFs(): void { - if (!fs.existsSync(this.config.dataDir)) { - fs.mkdirSync(this.config.dataDir); - } - if (!fs.existsSync(this.getStateFilePath())) { - fs.writeFileSync(this.getStateFilePath(), JSON.stringify(DEFAULT_STATE)); - } + createDataDirInitError(specificPath = ''): CLIError { + return this.createDataDirFsError(DataDirErrorType.Init, specificPath) + } + + private initStateFs(): void { + if (!fs.existsSync(this.getAppDataPath())) { + fs.mkdirSync(this.getAppDataPath()) + } + if (!fs.existsSync(this.getStateFilePath())) { + fs.writeFileSync(this.getStateFilePath(), JSON.stringify(DEFAULT_STATE)) } + } - getPreservedState(): StateObject { - let preservedState: StateObject; - try { - preservedState = require(this.getStateFilePath()); - } catch(e) { - throw this.createDataReadError(); - } - // The state preserved in a file may be missing some required values ie. - // if the user previously used the older version of the software. - // That's why we combine it with default state before returing. - return { ...DEFAULT_STATE, ...preservedState }; + getPreservedState(): StateObject { + let preservedState: StateObject + try { + // Use readFileSync instead of "require" in order to always get a "fresh" state + preservedState = JSON.parse(fs.readFileSync(this.getStateFilePath()).toString()) as StateObject + } catch (e) { + throw this.createDataReadError() } + // The state preserved in a file may be missing some required values ie. + // if the user previously used the older version of the software. + // That's why we combine it with default state before returing. + return { ...DEFAULT_STATE, ...preservedState } + } - // Modifies preserved state. Uses file lock in order to avoid updating an older state. - // (which could potentialy change between read and write operation) - async setPreservedState(modifiedState: Partial): Promise { - const stateFilePath = this.getStateFilePath(); - const unlock = await lockFile.lock(stateFilePath); - let oldState: StateObject = this.getPreservedState(); - let newState: StateObject = { ...oldState, ...modifiedState }; - try { - fs.writeFileSync(stateFilePath, JSON.stringify(newState)); - } catch(e) { - await unlock(); - throw this.createDataWriteError(); - } - await unlock(); + // Modifies preserved state. Uses file lock in order to avoid updating an older state. + // (which could potentialy change between read and write operation) + async setPreservedState(modifiedState: Partial): Promise { + const stateFilePath = this.getStateFilePath() + const unlock = await lockFile.lock(stateFilePath) + const oldState: StateObject = this.getPreservedState() + const newState: StateObject = { ...oldState, ...modifiedState } + try { + fs.writeFileSync(stateFilePath, JSON.stringify(newState)) + } catch (e) { + await unlock() + throw this.createDataWriteError() } + await unlock() + } - async init() { - await super.init(); - try { - await this.initStateFs(); - } catch (e) { - throw this.createDataDirInitError(); - } + async init() { + await super.init() + try { + await this.initStateFs() + } catch (e) { + throw this.createDataDirInitError() } + } } diff --git a/cli/src/base/WorkingGroupsCommandBase.ts b/cli/src/base/WorkingGroupsCommandBase.ts new file mode 100644 index 0000000000..8a8d992dbd --- /dev/null +++ b/cli/src/base/WorkingGroupsCommandBase.ts @@ -0,0 +1,274 @@ +import ExitCodes from '../ExitCodes' +import AccountsCommandBase from './AccountsCommandBase' +import { flags } from '@oclif/command' +import { + WorkingGroups, + AvailableGroups, + NamedKeyringPair, + GroupMember, + GroupOpening, + ApiMethodArg, + ApiMethodNamedArgs, + OpeningStatus, + GroupApplication, +} from '../Types' +import { apiModuleByGroup } from '../Api' +import { CLIError } from '@oclif/errors' +import fs from 'fs' +import path from 'path' +import _ from 'lodash' +import { ApplicationStageKeys } from '@joystream/types/hiring' +import chalk from 'chalk' + +const DEFAULT_GROUP = WorkingGroups.StorageProviders +const DRAFTS_FOLDER = 'opening-drafts' + +/** + * Abstract base class for commands related to working groups + */ +export default abstract class WorkingGroupsCommandBase extends AccountsCommandBase { + group: WorkingGroups = DEFAULT_GROUP + + static flags = { + group: flags.string({ + char: 'g', + description: + 'The working group context in which the command should be executed\n' + + `Available values are: ${AvailableGroups.join(', ')}.`, + required: true, + default: DEFAULT_GROUP, + }), + } + + // Use when lead access is required in given command + async getRequiredLead(): Promise { + const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount() + const lead = await this.getApi().groupLead(this.group) + + if (!lead || lead.roleAccount.toString() !== selectedAccount.address) { + this.error('Lead access required for this command!', { exit: ExitCodes.AccessDenied }) + } + + return lead + } + + // Use when worker access is required in given command + async getRequiredWorker(): Promise { + const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount() + const groupMembers = await this.getApi().groupMembers(this.group) + const groupMembersByAccount = groupMembers.filter((m) => m.roleAccount.toString() === selectedAccount.address) + + if (!groupMembersByAccount.length) { + this.error('Worker access required for this command!', { exit: ExitCodes.AccessDenied }) + } else if (groupMembersByAccount.length === 1) { + return groupMembersByAccount[0] + } else { + return await this.promptForWorker(groupMembersByAccount) + } + } + + // Use when member controller access is required, but one of the associated roles is expected to be selected + async getRequiredWorkerByMemberController(): Promise { + const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount() + const memberIds = await this.getApi().getMemberIdsByControllerAccount(selectedAccount.address) + const controlledWorkers = (await this.getApi().groupMembers(this.group)).filter((groupMember) => + memberIds.some((memberId) => groupMember.memberId.eq(memberId)) + ) + + if (!controlledWorkers.length) { + this.error(`Member controller account with some associated ${this.group} group roles needs to be selected!`, { + exit: ExitCodes.AccessDenied, + }) + } else if (controlledWorkers.length === 1) { + return controlledWorkers[0] + } else { + return await this.promptForWorker(controlledWorkers) + } + } + + async promptForWorker(groupMembers: GroupMember[]): Promise { + const chosenWorkerIndex = await this.simplePrompt({ + message: 'Choose the intended worker context:', + type: 'list', + choices: groupMembers.map((groupMember, index) => ({ + name: `Worker ID ${groupMember.workerId.toString()}`, + value: index, + })), + }) + + return groupMembers[chosenWorkerIndex] + } + + async promptForApplicationsToAccept(opening: GroupOpening): Promise { + const acceptableApplications = opening.applications.filter((a) => a.stage === ApplicationStageKeys.Active) + const acceptedApplications = await this.simplePrompt({ + message: 'Select succesful applicants', + type: 'checkbox', + choices: acceptableApplications.map((a) => ({ + name: ` ${a.wgApplicationId}: ${a.member?.handle.toString()}`, + value: a.wgApplicationId, + })), + }) + + return acceptedApplications + } + + async promptForNewOpeningDraftName() { + let draftName = '', + fileExists = false, + overrideConfirmed = false + + do { + draftName = await this.simplePrompt({ + type: 'input', + message: 'Provide the draft name', + validate: (val) => (typeof val === 'string' && val.length >= 1) || 'Draft name is required!', + }) + + fileExists = fs.existsSync(this.getOpeningDraftPath(draftName)) + if (fileExists) { + overrideConfirmed = await this.simplePrompt({ + type: 'confirm', + message: 'Such draft already exists. Do you wish to override it?', + default: false, + }) + } + } while (fileExists && !overrideConfirmed) + + return draftName + } + + async promptForOpeningDraft() { + let draftFiles: string[] = [] + try { + draftFiles = fs.readdirSync(this.getOpeingDraftsPath()) + } catch (e) { + throw this.createDataReadError(DRAFTS_FOLDER) + } + if (!draftFiles.length) { + throw new CLIError('No drafts available!', { exit: ExitCodes.FileNotFound }) + } + const draftNames = draftFiles.map((fileName) => _.startCase(fileName.replace('.json', ''))) + const selectedDraftName = await this.simplePrompt({ + message: 'Select a draft', + type: 'list', + choices: draftNames, + }) + + return selectedDraftName + } + + async getOpeningForLeadAction(id: number, requiredStatus?: OpeningStatus): Promise { + const opening = await this.getApi().groupOpening(this.group, id) + + if (!opening.type.isOfType('Worker')) { + this.error('A lead can only manage Worker openings!', { exit: ExitCodes.AccessDenied }) + } + + if (requiredStatus && opening.stage.status !== requiredStatus) { + this.error( + `The opening needs to be in "${_.startCase(requiredStatus)}" stage! ` + + `This one is: "${_.startCase(opening.stage.status)}"`, + { exit: ExitCodes.InvalidInput } + ) + } + + return opening + } + + // An alias for better code readibility in case we don't need the actual return value + validateOpeningForLeadAction = this.getOpeningForLeadAction + + async getApplicationForLeadAction(id: number, requiredStatus?: ApplicationStageKeys): Promise { + const application = await this.getApi().groupApplication(this.group, id) + const opening = await this.getApi().groupOpening(this.group, application.wgOpeningId) + + if (!opening.type.isOfType('Worker')) { + this.error('A lead can only manage Worker opening applications!', { exit: ExitCodes.AccessDenied }) + } + + if (requiredStatus && application.stage !== requiredStatus) { + this.error( + `The application needs to have "${_.startCase(requiredStatus)}" status! ` + + `This one has: "${_.startCase(application.stage)}"`, + { exit: ExitCodes.InvalidInput } + ) + } + + return application + } + + async getWorkerForLeadAction(id: number, requireStakeProfile = false) { + const groupMember = await this.getApi().groupMember(this.group, id) + const groupLead = await this.getApi().groupLead(this.group) + + if (groupLead?.workerId.eq(groupMember.workerId)) { + this.error('A lead cannot manage his own role this way!', { exit: ExitCodes.AccessDenied }) + } + + if (requireStakeProfile && !groupMember.stake) { + this.error('This worker has no associated role stake profile!', { exit: ExitCodes.InvalidInput }) + } + + return groupMember + } + + // Helper for better TS handling. + // We could also use some magic with conditional types instead, but those don't seem be very well supported yet. + async getWorkerWithStakeForLeadAction(id: number) { + return (await this.getWorkerForLeadAction(id, true)) as GroupMember & Required> + } + + loadOpeningDraftParams(draftName: string): ApiMethodNamedArgs { + const draftFilePath = this.getOpeningDraftPath(draftName) + const params = this.extrinsicArgsFromDraft(apiModuleByGroup[this.group], 'addOpening', draftFilePath) + + return params + } + + getOpeingDraftsPath() { + return path.join(this.getAppDataPath(), DRAFTS_FOLDER) + } + + getOpeningDraftPath(draftName: string) { + return path.join(this.getOpeingDraftsPath(), _.snakeCase(draftName) + '.json') + } + + saveOpeningDraft(draftName: string, params: ApiMethodArg[]) { + const paramsJson = JSON.stringify( + params.map((p) => p.toJSON()), + null, + 2 + ) + + try { + fs.writeFileSync(this.getOpeningDraftPath(draftName), paramsJson) + } catch (e) { + throw this.createDataWriteError(DRAFTS_FOLDER) + } + } + + private initOpeningDraftsDir(): void { + if (!fs.existsSync(this.getOpeingDraftsPath())) { + fs.mkdirSync(this.getOpeingDraftsPath()) + } + } + + async init() { + await super.init() + try { + this.initOpeningDraftsDir() + } catch (e) { + throw this.createDataDirInitError() + } + const { flags } = this.parse(this.constructor as typeof WorkingGroupsCommandBase) + if (!AvailableGroups.includes(flags.group as any)) { + throw new CLIError(`Invalid group! Available values are: ${AvailableGroups.join(', ')}`, { + exit: ExitCodes.InvalidInput, + }) + } + this.group = flags.group as WorkingGroups + + this.log(chalk.white('Group: ' + flags.group)) + } +} diff --git a/cli/src/commands/account/choose.ts b/cli/src/commands/account/choose.ts index d1db149ae3..9b8cdb97e6 100644 --- a/cli/src/commands/account/choose.ts +++ b/cli/src/commands/account/choose.ts @@ -1,25 +1,33 @@ -import AccountsCommandBase from '../../base/AccountsCommandBase'; -import chalk from 'chalk'; -import ExitCodes from '../../ExitCodes'; +import AccountsCommandBase from '../../base/AccountsCommandBase' +import chalk from 'chalk' +import ExitCodes from '../../ExitCodes' import { NamedKeyringPair } from '../../Types' +import { flags } from '@oclif/command' export default class AccountChoose extends AccountsCommandBase { - static description = 'Choose default account to use in the CLI'; + static description = 'Choose default account to use in the CLI' + static flags = { + showSpecial: flags.boolean({ + description: 'Whether to show special (DEV chain) accounts', + required: false, + }), + } - async run() { - const accounts: NamedKeyringPair[] = this.fetchAccounts(); - const selectedAccount: NamedKeyringPair | null = this.getSelectedAccount(); + async run() { + const { showSpecial } = this.parse(AccountChoose).flags + const accounts: NamedKeyringPair[] = this.fetchAccounts(showSpecial) + const selectedAccount: NamedKeyringPair | null = this.getSelectedAccount() - this.log(chalk.white(`Found ${ accounts.length } existing accounts...\n`)); + this.log(chalk.white(`Found ${accounts.length} existing accounts...\n`)) - if (accounts.length === 0) { - this.warn('No account to choose from. Add accont using account:import or account:create.'); - this.exit(ExitCodes.NoAccountFound); - } + if (accounts.length === 0) { + this.warn('No account to choose from. Add accont using account:import or account:create.') + this.exit(ExitCodes.NoAccountFound) + } - const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, selectedAccount); + const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, selectedAccount) - await this.setSelectedAccount(choosenAccount); - this.log(chalk.greenBright("\nAccount switched!")); - } + await this.setSelectedAccount(choosenAccount) + this.log(chalk.greenBright('\nAccount switched!')) } +} diff --git a/cli/src/commands/account/create.ts b/cli/src/commands/account/create.ts index eaaf4bf8a0..785f57090f 100644 --- a/cli/src/commands/account/create.ts +++ b/cli/src/commands/account/create.ts @@ -1,47 +1,47 @@ -import chalk from 'chalk'; -import ExitCodes from '../../ExitCodes'; -import AccountsCommandBase from '../../base/AccountsCommandBase'; -import { Keyring } from '@polkadot/api'; +import chalk from 'chalk' +import ExitCodes from '../../ExitCodes' +import AccountsCommandBase from '../../base/AccountsCommandBase' +import { Keyring } from '@polkadot/api' import { mnemonicGenerate } from '@polkadot/util-crypto' -import { NamedKeyringPair } from '../../Types'; +import { NamedKeyringPair } from '../../Types' type AccountCreateArgs = { - name: string -}; + name: string +} export default class AccountCreate extends AccountsCommandBase { - static description = 'Create new account'; - - static args = [ - { - name: 'name', - required: true, - description: 'Account name' - }, - ]; - - validatePass(password: string, password2: string): void { - if (password !== password2) this.error('Passwords are not the same!', { exit: ExitCodes.InvalidInput }); - if (!password) this.error('You didn\'t provide a password', { exit: ExitCodes.InvalidInput }); - } + static description = 'Create new account' + + static args = [ + { + name: 'name', + required: true, + description: 'Account name', + }, + ] + + validatePass(password: string, password2: string): void { + if (password !== password2) this.error('Passwords are not the same!', { exit: ExitCodes.InvalidInput }) + if (!password) this.error("You didn't provide a password", { exit: ExitCodes.InvalidInput }) + } - async run() { - const args: AccountCreateArgs = this.parse(AccountCreate).args; - const keyring: Keyring = new Keyring(); - const mnemonic: string = mnemonicGenerate(); + async run() { + const args: AccountCreateArgs = this.parse(AccountCreate).args as AccountCreateArgs + const keyring: Keyring = new Keyring() + const mnemonic: string = mnemonicGenerate() - keyring.addFromMnemonic(mnemonic, { name: args.name, whenCreated: Date.now() }); - const keys: NamedKeyringPair = keyring.pairs[0]; // We assigned the name above + keyring.addFromMnemonic(mnemonic, { name: args.name, whenCreated: Date.now() }) + const keys: NamedKeyringPair = keyring.pairs[0] as NamedKeyringPair // We assigned the name above - const password = await this.promptForPassword('Set your account\'s password'); - const password2 = await this.promptForPassword('Confirm your password'); + const password = await this.promptForPassword("Set your account's password") + const password2 = await this.promptForPassword('Confirm your password') - this.validatePass(password, password2); + this.validatePass(password, password2) - this.saveAccount(keys, password); + this.saveAccount(keys, password) - this.log(chalk.greenBright(`\nAccount succesfully created!`)); - this.log(chalk.white(`${chalk.bold('Name: ') }${ args.name }`)); - this.log(chalk.white(`${chalk.bold('Address: ') }${ keys.address }`)); - } + this.log(chalk.greenBright(`\nAccount succesfully created!`)) + this.log(chalk.white(`${chalk.bold('Name: ')}${args.name}`)) + this.log(chalk.white(`${chalk.bold('Address: ')}${keys.address}`)) } +} diff --git a/cli/src/commands/account/current.ts b/cli/src/commands/account/current.ts index b820502d0b..93ceac8f02 100644 --- a/cli/src/commands/account/current.ts +++ b/cli/src/commands/account/current.ts @@ -1,41 +1,41 @@ -import AccountsCommandBase from '../../base/AccountsCommandBase'; -import { AccountSummary, NameValueObj, NamedKeyringPair } from '../../Types'; -import { DerivedBalances } from '@polkadot/api-derive/types'; -import { displayHeader, displayNameValueTable } from '../../helpers/display'; -import { formatBalance } from '@polkadot/util'; -import moment from 'moment'; +import AccountsCommandBase from '../../base/AccountsCommandBase' +import { AccountSummary, NameValueObj, NamedKeyringPair } from '../../Types' +import { DerivedBalances } from '@polkadot/api-derive/types' +import { displayHeader, displayNameValueTable } from '../../helpers/display' +import { formatBalance } from '@polkadot/util' +import moment from 'moment' export default class AccountCurrent extends AccountsCommandBase { - static description = 'Display information about currently choosen default account'; - static aliases = ['account:info', 'account:default']; + static description = 'Display information about currently choosen default account' + static aliases = ['account:info', 'account:default'] - async run() { - const currentAccount: NamedKeyringPair = await this.getRequiredSelectedAccount(false); - const summary: AccountSummary = await this.getApi().getAccountSummary(currentAccount.address); + async run() { + const currentAccount: NamedKeyringPair = await this.getRequiredSelectedAccount(false) + const summary: AccountSummary = await this.getApi().getAccountSummary(currentAccount.address) - displayHeader('Account information'); - const creationDate: string = currentAccount.meta.whenCreated ? - moment(currentAccount.meta.whenCreated).format('YYYY-MM-DD HH:mm:ss') - : '?'; - const accountRows: NameValueObj[] = [ - { name: 'Account name:', value: currentAccount.meta.name }, - { name: 'Address:', value: currentAccount.address }, - { name: 'Created:', value: creationDate } - ]; - displayNameValueTable(accountRows); + displayHeader('Account information') + const creationDate: string = currentAccount.meta.whenCreated + ? moment(currentAccount.meta.whenCreated).format('YYYY-MM-DD HH:mm:ss') + : '?' + const accountRows: NameValueObj[] = [ + { name: 'Account name:', value: currentAccount.meta.name }, + { name: 'Address:', value: currentAccount.address }, + { name: 'Created:', value: creationDate }, + ] + displayNameValueTable(accountRows) - displayHeader('Balances'); - const balances: DerivedBalances = summary.balances; - let balancesRows: NameValueObj[] = [ - { name: 'Total balance:', value: formatBalance(balances.votingBalance) }, - { name: 'Transferable balance:', value: formatBalance(balances.availableBalance) } - ]; - if (balances.lockedBalance.gtn(0)) { - balancesRows.push({ name: 'Locked balance:', value: formatBalance(balances.lockedBalance) }); - } - if (balances.reservedBalance.gtn(0)) { - balancesRows.push({ name: 'Reserved balance:', value: formatBalance(balances.reservedBalance) }); - } - displayNameValueTable(balancesRows); + displayHeader('Balances') + const balances: DerivedBalances = summary.balances + const balancesRows: NameValueObj[] = [ + { name: 'Total balance:', value: formatBalance(balances.votingBalance) }, + { name: 'Transferable balance:', value: formatBalance(balances.availableBalance) }, + ] + if (balances.lockedBalance.gtn(0)) { + balancesRows.push({ name: 'Locked balance:', value: formatBalance(balances.lockedBalance) }) } + if (balances.reservedBalance.gtn(0)) { + balancesRows.push({ name: 'Reserved balance:', value: formatBalance(balances.reservedBalance) }) + } + displayNameValueTable(balancesRows) } +} diff --git a/cli/src/commands/account/export.ts b/cli/src/commands/account/export.ts index 1d71ef51e3..e5546c695d 100644 --- a/cli/src/commands/account/export.ts +++ b/cli/src/commands/account/export.ts @@ -1,73 +1,74 @@ -import fs from 'fs'; -import chalk from 'chalk'; -import path from 'path'; -import ExitCodes from '../../ExitCodes'; -import AccountsCommandBase from '../../base/AccountsCommandBase'; -import { flags } from '@oclif/command'; -import { NamedKeyringPair } from '../../Types'; +import fs from 'fs' +import chalk from 'chalk' +import path from 'path' +import ExitCodes from '../../ExitCodes' +import AccountsCommandBase from '../../base/AccountsCommandBase' +import { flags } from '@oclif/command' +import { NamedKeyringPair } from '../../Types' -type AccountExportFlags = { all: boolean }; -type AccountExportArgs = { path: string }; +type AccountExportFlags = { all: boolean } +type AccountExportArgs = { path: string } export default class AccountExport extends AccountsCommandBase { - static description = 'Export account(s) to given location'; - static MULTI_EXPORT_FOLDER_NAME = 'exported_accounts'; + static description = 'Export account(s) to given location' + static MULTI_EXPORT_FOLDER_NAME = 'exported_accounts' - static args = [ - { - name: 'path', - required: true, - description: 'Path where the exported files should be placed' - } - ]; + static args = [ + { + name: 'path', + required: true, + description: 'Path where the exported files should be placed', + }, + ] - static flags = { - all: flags.boolean({ - char: 'a', - description: `If provided, exports all existing accounts into "${ AccountExport.MULTI_EXPORT_FOLDER_NAME }" folder inside given path`, - }), - }; - - exportAccount(account: NamedKeyringPair, destPath: string): string { - const sourceFilePath: string = this.getAccountFilePath(account); - const destFilePath: string = path.join(destPath, this.generateAccountFilename(account)); - try { - fs.copyFileSync(sourceFilePath, destFilePath); - } - catch (e) { - this.error( - `Error while trying to copy into the export file: (${ destFilePath }). Permissions issue?`, - { exit: ExitCodes.FsOperationFailed } - ); - } + static flags = { + all: flags.boolean({ + char: 'a', + description: `If provided, exports all existing accounts into "${AccountExport.MULTI_EXPORT_FOLDER_NAME}" folder inside given path`, + }), + } - return destFilePath; + exportAccount(account: NamedKeyringPair, destPath: string): string { + const sourceFilePath: string = this.getAccountFilePath(account) + const destFilePath: string = path.join(destPath, this.generateAccountFilename(account)) + try { + fs.copyFileSync(sourceFilePath, destFilePath) + } catch (e) { + this.error(`Error while trying to copy into the export file: (${destFilePath}). Permissions issue?`, { + exit: ExitCodes.FsOperationFailed, + }) } - async run() { - const args: AccountExportArgs = this.parse(AccountExport).args; - const flags: AccountExportFlags = this.parse(AccountExport).flags; - const accounts: NamedKeyringPair[] = this.fetchAccounts(); + return destFilePath + } - if (!accounts.length) { - this.error('No accounts found!', { exit: ExitCodes.NoAccountFound }); - } + async run() { + const args: AccountExportArgs = this.parse(AccountExport).args as AccountExportArgs + const flags: AccountExportFlags = this.parse(AccountExport).flags as AccountExportFlags + const accounts: NamedKeyringPair[] = this.fetchAccounts() + + if (!accounts.length) { + this.error('No accounts found!', { exit: ExitCodes.NoAccountFound }) + } - if (flags.all) { - const destPath: string = path.join(args.path, AccountExport.MULTI_EXPORT_FOLDER_NAME); - try { - if (!fs.existsSync(destPath)) fs.mkdirSync(destPath); - } catch(e) { - this.error(`Failed to create the export folder (${ destPath })`, { exit: ExitCodes.FsOperationFailed }); - } - for (let account of accounts) this.exportAccount(account, destPath); - this.log(chalk.greenBright(`All accounts succesfully exported succesfully to: ${ chalk.white(destPath) }!`)); - } - else { - const destPath: string = args.path; - const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, null, 'Select an account to export'); - const exportedFilePath: string = this.exportAccount(choosenAccount, destPath); - this.log(chalk.greenBright(`Account succesfully exported to: ${ chalk.white(exportedFilePath) }`)); - } + if (flags.all) { + const destPath: string = path.join(args.path, AccountExport.MULTI_EXPORT_FOLDER_NAME) + try { + if (!fs.existsSync(destPath)) fs.mkdirSync(destPath) + } catch (e) { + this.error(`Failed to create the export folder (${destPath})`, { exit: ExitCodes.FsOperationFailed }) + } + for (const account of accounts) this.exportAccount(account, destPath) + this.log(chalk.greenBright(`All accounts succesfully exported succesfully to: ${chalk.white(destPath)}!`)) + } else { + const destPath: string = args.path + const choosenAccount: NamedKeyringPair = await this.promptForAccount( + accounts, + null, + 'Select an account to export' + ) + const exportedFilePath: string = this.exportAccount(choosenAccount, destPath) + this.log(chalk.greenBright(`Account succesfully exported to: ${chalk.white(exportedFilePath)}`)) } } +} diff --git a/cli/src/commands/account/forget.ts b/cli/src/commands/account/forget.ts index a10f8e98ab..b9e5c6aa3a 100644 --- a/cli/src/commands/account/forget.ts +++ b/cli/src/commands/account/forget.ts @@ -1,29 +1,31 @@ -import fs from 'fs'; -import chalk from 'chalk'; -import ExitCodes from '../../ExitCodes'; -import AccountsCommandBase from '../../base/AccountsCommandBase'; -import { NamedKeyringPair } from '../../Types'; +import fs from 'fs' +import chalk from 'chalk' +import ExitCodes from '../../ExitCodes' +import AccountsCommandBase from '../../base/AccountsCommandBase' +import { NamedKeyringPair } from '../../Types' export default class AccountForget extends AccountsCommandBase { - static description = 'Forget (remove) account from the list of available accounts'; + static description = 'Forget (remove) account from the list of available accounts' - async run() { - const accounts: NamedKeyringPair[] = this.fetchAccounts(); + async run() { + const accounts: NamedKeyringPair[] = this.fetchAccounts() - if (!accounts.length) { - this.error('No accounts found!', { exit: ExitCodes.NoAccountFound }); - } - - const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, null, 'Select an account to forget'); - await this.requireConfirmation('Are you sure you want this account to be forgotten?'); + if (!accounts.length) { + this.error('No accounts found!', { exit: ExitCodes.NoAccountFound }) + } - const accountFilePath: string = this.getAccountFilePath(choosenAccount); - try { - fs.unlinkSync(accountFilePath); - } catch (e) { - this.error(`Could not remove account file (${ accountFilePath }). Permissions issue?`, { exit: ExitCodes.FsOperationFailed }); - } + const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, null, 'Select an account to forget') + await this.requireConfirmation('Are you sure you want this account to be forgotten?') - this.log(chalk.greenBright(`\nAccount has been forgotten!`)) + const accountFilePath: string = this.getAccountFilePath(choosenAccount) + try { + fs.unlinkSync(accountFilePath) + } catch (e) { + this.error(`Could not remove account file (${accountFilePath}). Permissions issue?`, { + exit: ExitCodes.FsOperationFailed, + }) } + + this.log(chalk.greenBright(`\nAccount has been forgotten!`)) } +} diff --git a/cli/src/commands/account/import.ts b/cli/src/commands/account/import.ts index 623f0c5e00..f903b15d15 100644 --- a/cli/src/commands/account/import.ts +++ b/cli/src/commands/account/import.ts @@ -1,46 +1,44 @@ -import fs from 'fs'; -import chalk from 'chalk'; -import path from 'path'; -import ExitCodes from '../../ExitCodes'; -import AccountsCommandBase from '../../base/AccountsCommandBase'; -import { NamedKeyringPair } from '../../Types'; +import fs from 'fs' +import chalk from 'chalk' +import path from 'path' +import ExitCodes from '../../ExitCodes' +import AccountsCommandBase from '../../base/AccountsCommandBase' +import { NamedKeyringPair } from '../../Types' type AccountImportArgs = { - backupFilePath: string -}; + backupFilePath: string +} export default class AccountImport extends AccountsCommandBase { - static description = 'Import account using JSON backup file'; + static description = 'Import account using JSON backup file' - static args = [ - { - name: 'backupFilePath', - required: true, - description: 'Path to account backup JSON file' - }, - ]; + static args = [ + { + name: 'backupFilePath', + required: true, + description: 'Path to account backup JSON file', + }, + ] - async run() { - const args: AccountImportArgs = this.parse(AccountImport).args; - const backupAcc: NamedKeyringPair = this.fetchAccountFromJsonFile(args.backupFilePath); - const accountName: string = backupAcc.meta.name; - const accountAddress: string = backupAcc.address; + async run() { + const args: AccountImportArgs = this.parse(AccountImport).args as AccountImportArgs + const backupAcc: NamedKeyringPair = this.fetchAccountFromJsonFile(args.backupFilePath) + const accountName: string = backupAcc.meta.name + const accountAddress: string = backupAcc.address - const sourcePath: string = args.backupFilePath; - const destPath: string = path.join(this.getAccountsDirPath(), this.generateAccountFilename(backupAcc)); + const sourcePath: string = args.backupFilePath + const destPath: string = path.join(this.getAccountsDirPath(), this.generateAccountFilename(backupAcc)) - try { - fs.copyFileSync(sourcePath, destPath); - } - catch (e) { - this.error( - 'Unexpected error while trying to copy input file! Permissions issue?', - { exit: ExitCodes.FsOperationFailed } - ); - } - - this.log(chalk.bold.greenBright(`ACCOUNT IMPORTED SUCCESFULLY!`)); - this.log(chalk.bold.white(`NAME: `), accountName); - this.log(chalk.bold.white(`ADDRESS: `), accountAddress); + try { + fs.copyFileSync(sourcePath, destPath) + } catch (e) { + this.error('Unexpected error while trying to copy input file! Permissions issue?', { + exit: ExitCodes.FsOperationFailed, + }) } + + this.log(chalk.bold.greenBright(`ACCOUNT IMPORTED SUCCESFULLY!`)) + this.log(chalk.bold.white(`NAME: `), accountName) + this.log(chalk.bold.white(`ADDRESS: `), accountAddress) } +} diff --git a/cli/src/commands/account/transferTokens.ts b/cli/src/commands/account/transferTokens.ts index 953acb22d2..95231775f6 100644 --- a/cli/src/commands/account/transferTokens.ts +++ b/cli/src/commands/account/transferTokens.ts @@ -1,68 +1,67 @@ -import BN from 'bn.js'; -import AccountsCommandBase from '../../base/AccountsCommandBase'; -import chalk from 'chalk'; -import ExitCodes from '../../ExitCodes'; -import { formatBalance } from '@polkadot/util'; -import { Hash } from '@polkadot/types/interfaces'; -import { NamedKeyringPair } from '../../Types'; -import { checkBalance, validateAddress } from '../../helpers/validation'; -import { DerivedBalances } from '@polkadot/api-derive/types'; +import BN from 'bn.js' +import AccountsCommandBase from '../../base/AccountsCommandBase' +import chalk from 'chalk' +import ExitCodes from '../../ExitCodes' +import { formatBalance } from '@polkadot/util' +import { Hash } from '@polkadot/types/interfaces' +import { NamedKeyringPair } from '../../Types' +import { checkBalance, validateAddress } from '../../helpers/validation' +import { DerivedBalances } from '@polkadot/api-derive/types' type AccountTransferArgs = { - recipient: string, - amount: string -}; + recipient: string + amount: string +} export default class AccountTransferTokens extends AccountsCommandBase { - static description = 'Transfer tokens from currently choosen account'; + static description = 'Transfer tokens from currently choosen account' - static args = [ - { - name: 'recipient', - required: true, - description: 'Address of the transfer recipient' - }, - { - name: 'amount', - required: true, - description: 'Amount of tokens to transfer' - }, - ]; + static args = [ + { + name: 'recipient', + required: true, + description: 'Address of the transfer recipient', + }, + { + name: 'amount', + required: true, + description: 'Amount of tokens to transfer', + }, + ] - async run() { - const args: AccountTransferArgs = this.parse(AccountTransferTokens).args; - const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount(); - const amountBN: BN = new BN(args.amount); + async run() { + const args: AccountTransferArgs = this.parse(AccountTransferTokens).args as AccountTransferArgs + const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount() + const amountBN: BN = new BN(args.amount) - // Initial validation - validateAddress(args.recipient, 'Invalid recipient address'); - const accBalances: DerivedBalances = (await this.getApi().getAccountsBalancesInfo([ selectedAccount.address ]))[0]; - checkBalance(accBalances, amountBN); + // Initial validation + validateAddress(args.recipient, 'Invalid recipient address') + const accBalances: DerivedBalances = (await this.getApi().getAccountsBalancesInfo([selectedAccount.address]))[0] + checkBalance(accBalances, amountBN) - await this.requestAccountDecoding(selectedAccount); + await this.requestAccountDecoding(selectedAccount) - this.log(chalk.white('Estimating fee...')); - let estimatedFee: BN; - try { - estimatedFee = await this.getApi().estimateFee(selectedAccount, args.recipient, amountBN); - } - catch (e) { - this.error('Could not estimate the fee.', { exit: ExitCodes.UnexpectedException }); - } - const totalAmount: BN = amountBN.add(estimatedFee); - this.log(chalk.white('Estimated fee:', formatBalance(estimatedFee))); - this.log(chalk.white('Total transfer amount:', formatBalance(totalAmount))); + this.log(chalk.white('Estimating fee...')) + let estimatedFee: BN + try { + estimatedFee = await this.getApi().estimateFee(selectedAccount, args.recipient, amountBN) + } catch (e) { + this.error('Could not estimate the fee.', { exit: ExitCodes.UnexpectedException }) + } + const totalAmount: BN = amountBN.add(estimatedFee) + this.log(chalk.white('Estimated fee:', formatBalance(estimatedFee))) + this.log(chalk.white('Total transfer amount:', formatBalance(totalAmount))) - checkBalance(accBalances, totalAmount); + checkBalance(accBalances, totalAmount) - await this.requireConfirmation('Do you confirm the transfer?'); + await this.requireConfirmation('Do you confirm the transfer?') - try { - const txHash: Hash = await this.getApi().transfer(selectedAccount, args.recipient, amountBN); - this.log(chalk.greenBright('Transaction succesfully sent!')); - this.log(chalk.white('Hash:', txHash.toString())); - } catch (e) { - this.error('Could not send the transaction.', { exit: ExitCodes.UnexpectedException }); - } + try { + const txHash: Hash = await this.getApi().transfer(selectedAccount, args.recipient, amountBN) + this.log(chalk.greenBright('Transaction succesfully sent!')) + this.log(chalk.white('Hash:', txHash.toString())) + } catch (e) { + this.error('Could not send the transaction.', { exit: ExitCodes.UnexpectedException }) } } +} diff --git a/cli/src/commands/api/getUri.ts b/cli/src/commands/api/getUri.ts index c404799d84..beaf896649 100644 --- a/cli/src/commands/api/getUri.ts +++ b/cli/src/commands/api/getUri.ts @@ -1,12 +1,11 @@ -import StateAwareCommandBase from '../../base/StateAwareCommandBase'; -import chalk from 'chalk'; - +import StateAwareCommandBase from '../../base/StateAwareCommandBase' +import chalk from 'chalk' export default class ApiGetUri extends StateAwareCommandBase { - static description = 'Get current api WS provider uri'; + static description = 'Get current api WS provider uri' - async run() { - const currentUri:string = this.getPreservedState().apiUri; - this.log(chalk.green(currentUri)); - } + async run() { + const currentUri: string = this.getPreservedState().apiUri + this.log(chalk.green(currentUri)) } +} diff --git a/cli/src/commands/api/inspect.ts b/cli/src/commands/api/inspect.ts index cd309cba42..7ee1dcf887 100644 --- a/cli/src/commands/api/inspect.ts +++ b/cli/src/commands/api/inspect.ts @@ -1,277 +1,234 @@ -import { flags } from '@oclif/command'; -import { CLIError } from '@oclif/errors'; -import { displayNameValueTable } from '../../helpers/display'; -import { ApiPromise } from '@polkadot/api'; -import { getTypeDef } from '@polkadot/types'; -import { Codec, TypeDef, TypeDefInfo } from '@polkadot/types/types'; -import { ConstantCodec } from '@polkadot/api-metadata/consts/types'; -import ExitCodes from '../../ExitCodes'; -import chalk from 'chalk'; -import { NameValueObj } from '../../Types'; -import inquirer from 'inquirer'; -import ApiCommandBase from '../../base/ApiCommandBase'; +import { flags } from '@oclif/command' +import { CLIError } from '@oclif/errors' +import { displayNameValueTable } from '../../helpers/display' +import { ApiPromise } from '@polkadot/api' +import { Option } from '@polkadot/types' +import { Codec } from '@polkadot/types/types' +import { ConstantCodec } from '@polkadot/api-metadata/consts/types' +import ExitCodes from '../../ExitCodes' +import chalk from 'chalk' +import { NameValueObj, ApiMethodArg } from '../../Types' +import ApiCommandBase from '../../base/ApiCommandBase' // Command flags type type ApiInspectFlags = { - type: string, - module: string, - method: string, - exec: boolean, - callArgs: string -}; + type: string + module: string + method: string + exec: boolean + callArgs: string +} // Currently "inspectable" api types -const TYPES_AVAILABLE = [ - 'query', - 'consts', -] as const; +const TYPES_AVAILABLE = ['query', 'consts'] as const // String literals type based on TYPES_AVAILABLE const. // It works as if we specified: type ApiType = 'query' | 'consts'...; -type ApiType = typeof TYPES_AVAILABLE[number]; - -// Format of the api input args (as they are specified in the CLI) -type ApiMethodInputSimpleArg = string; -// This recurring type allows the correct handling of nested types like: -// ((Type1, Type2), Option) etc. -type ApiMethodInputArg = ApiMethodInputSimpleArg | ApiMethodInputArg[]; +type ApiType = typeof TYPES_AVAILABLE[number] export default class ApiInspect extends ApiCommandBase { - static description = - 'Lists available node API modules/methods and/or their description(s), '+ - 'or calls one of the API methods (depending on provided arguments and flags)'; - - static examples = [ - '$ api:inspect', - '$ api:inspect -t=query', - '$ api:inspect -t=query -M=members', - '$ api:inspect -t=query -M=members -m=memberProfile', - '$ api:inspect -t=query -M=members -m=memberProfile -e', - '$ api:inspect -t=query -M=members -m=memberProfile -e -a=1', - ]; - - static flags = { - type: flags.string({ - char: 't', - description: - 'Specifies the type/category of the inspected request (ie. "query", "consts" etc.).\n'+ - 'If no "--module" flag is provided then all available modules in that type will be listed.\n'+ - 'If this flag is not provided then all available types will be listed.', - }), - module: flags.string({ - char: 'M', - description: - 'Specifies the api module, ie. "system", "staking" etc.\n'+ - 'If no "--method" flag is provided then all methods in that module will be listed along with the descriptions.', - dependsOn: ['type'], - }), - method: flags.string({ - char: 'm', - description: 'Specifies the api method to call/describe.', - dependsOn: ['module'], - }), - exec: flags.boolean({ - char: 'e', - description: 'Provide this flag if you want to execute the actual call, instead of displaying the method description (which is default)', - dependsOn: ['method'], - }), - callArgs: flags.string({ - char: 'a', - description: - 'Specifies the arguments to use when calling a method. Multiple arguments can be separated with a comma, ie. "-a=arg1,arg2".\n'+ - 'You can omit this flag even if the method requires some aguments.\n'+ - 'In that case you will be promted to provide value for each required argument.\n' + - 'Ommiting this flag is recommended when input parameters are of more complex types (and it\'s hard to specify them as just simple comma-separated strings)', - dependsOn: ['exec'], - }) - }; - - getMethodMeta(apiType: ApiType, apiModule: string, apiMethod: string) { - if (apiType === 'query') { - return this.getOriginalApi().query[apiModule][apiMethod].creator.meta; - } - else { - // Currently the only other optoin is api.consts - const method:ConstantCodec = this.getOriginalApi().consts[apiModule][apiMethod]; - return method.meta; - } + static description = + 'Lists available node API modules/methods and/or their description(s), ' + + 'or calls one of the API methods (depending on provided arguments and flags)' + + static examples = [ + '$ api:inspect', + '$ api:inspect -t=query', + '$ api:inspect -t=query -M=members', + '$ api:inspect -t=query -M=members -m=membershipById', + '$ api:inspect -t=query -M=members -m=membershipById -e', + '$ api:inspect -t=query -M=members -m=membershipById -e -a=1', + ] + + static flags = { + type: flags.string({ + char: 't', + description: + 'Specifies the type/category of the inspected request (ie. "query", "consts" etc.).\n' + + 'If no "--module" flag is provided then all available modules in that type will be listed.\n' + + 'If this flag is not provided then all available types will be listed.', + }), + module: flags.string({ + char: 'M', + description: + 'Specifies the api module, ie. "system", "staking" etc.\n' + + 'If no "--method" flag is provided then all methods in that module will be listed along with the descriptions.', + dependsOn: ['type'], + }), + method: flags.string({ + char: 'm', + description: 'Specifies the api method to call/describe.', + dependsOn: ['module'], + }), + exec: flags.boolean({ + char: 'e', + description: + 'Provide this flag if you want to execute the actual call, instead of displaying the method description (which is default)', + dependsOn: ['method'], + }), + callArgs: flags.string({ + char: 'a', + description: + 'Specifies the arguments to use when calling a method. Multiple arguments can be separated with a comma, ie. "-a=arg1,arg2".\n' + + 'You can omit this flag even if the method requires some aguments.\n' + + 'In that case you will be promted to provide value for each required argument.\n' + + "Ommiting this flag is recommended when input parameters are of more complex types (and it's hard to specify them as just simple comma-separated strings)", + dependsOn: ['exec'], + }), + } + + getMethodMeta(apiType: ApiType, apiModule: string, apiMethod: string) { + if (apiType === 'query') { + return this.getOriginalApi().query[apiModule][apiMethod].creator.meta + } else { + // Currently the only other optoin is api.consts + const method: ConstantCodec = this.getOriginalApi().consts[apiModule][apiMethod] as ConstantCodec + return method.meta } - - getMethodDescription(apiType: ApiType, apiModule: string, apiMethod: string): string { - let description:string = this.getMethodMeta(apiType, apiModule, apiMethod).documentation.join(' '); - return description || 'No description available.'; + } + + getMethodDescription(apiType: ApiType, apiModule: string, apiMethod: string): string { + const description: string = this.getMethodMeta(apiType, apiModule, apiMethod).documentation.join(' ') + return description || 'No description available.' + } + + getQueryMethodParamsTypes(apiModule: string, apiMethod: string): string[] { + const method = this.getOriginalApi().query[apiModule][apiMethod] + const { type } = method.creator.meta + if (type.isDoubleMap) { + return [type.asDoubleMap.key1.toString(), type.asDoubleMap.key2.toString()] } - - getQueryMethodParamsTypes(apiModule: string, apiMethod: string): string[] { - const method = this.getOriginalApi().query[apiModule][apiMethod]; - const { type } = method.creator.meta; - if (type.isDoubleMap) { - return [ type.asDoubleMap.key1.toString(), type.asDoubleMap.key2.toString() ]; - } - if (type.isMap) { - return type.asMap.linked.isTrue ? [ `Option<${type.asMap.key.toString()}>` ] : [ type.asMap.key.toString() ]; - } - return []; + if (type.isMap) { + return type.asMap.linked.isTrue ? [`Option<${type.asMap.key.toString()}>`] : [type.asMap.key.toString()] } - - getMethodReturnType(apiType: ApiType, apiModule: string, apiMethod: string): string { - if (apiType === 'query') { - const method = this.getOriginalApi().query[apiModule][apiMethod]; - const { meta: { type, modifier } } = method.creator; - if (type.isDoubleMap) { - return type.asDoubleMap.value.toString(); - } - if (modifier.isOptional) { - return `Option<${type.toString()}>`; - } - } - // Fallback for "query" and default for "consts" - return this.getMethodMeta(apiType, apiModule, apiMethod).type.toString(); + return [] + } + + getMethodReturnType(apiType: ApiType, apiModule: string, apiMethod: string): string { + if (apiType === 'query') { + const method = this.getOriginalApi().query[apiModule][apiMethod] + const { + meta: { type, modifier }, + } = method.creator + if (type.isDoubleMap) { + return type.asDoubleMap.value.toString() + } + if (modifier.isOptional) { + return `Option<${type.toString()}>` + } } - - // Validate the flags - throws an error if flags.type, flags.module or flags.method is invalid / does not exist in the api. - // Returns type, module and method which validity we can be sure about (notice they may still be "undefined" if weren't provided). - validateFlags(api: ApiPromise, flags: ApiInspectFlags): { apiType: ApiType | undefined, apiModule: string | undefined, apiMethod: string | undefined } { - let apiType: ApiType | undefined = undefined; - const { module: apiModule, method: apiMethod } = flags; - - if (flags.type !== undefined) { - const availableTypes: readonly string[] = TYPES_AVAILABLE; - if (!availableTypes.includes(flags.type)) { - throw new CLIError('Such type is not available', { exit: ExitCodes.InvalidInput }); - } - apiType = flags.type; - if (apiModule !== undefined) { - if (!api[apiType][apiModule]) { - throw new CLIError('Such module was not found', { exit: ExitCodes.InvalidInput }); - } - if (apiMethod !== undefined && !api[apiType][apiModule][apiMethod]) { - throw new CLIError('Such method was not found', { exit: ExitCodes.InvalidInput }); - } - } + // Fallback for "query" and default for "consts" + return this.getMethodMeta(apiType, apiModule, apiMethod).type.toString() + } + + // Validate the flags - throws an error if flags.type, flags.module or flags.method is invalid / does not exist in the api. + // Returns type, module and method which validity we can be sure about (notice they may still be "undefined" if weren't provided). + validateFlags( + api: ApiPromise, + flags: ApiInspectFlags + ): { apiType: ApiType | undefined; apiModule: string | undefined; apiMethod: string | undefined } { + let apiType: ApiType | undefined = undefined + const { module: apiModule, method: apiMethod } = flags + + if (flags.type !== undefined) { + const availableTypes: readonly string[] = TYPES_AVAILABLE + if (!availableTypes.includes(flags.type)) { + throw new CLIError('Such type is not available', { exit: ExitCodes.InvalidInput }) + } + apiType = flags.type as ApiType + if (apiModule !== undefined) { + if (!api[apiType][apiModule]) { + throw new CLIError('Such module was not found', { exit: ExitCodes.InvalidInput }) } - - return { apiType, apiModule, apiMethod }; - } - - // Prompt for simple value (string) - async promptForSimple(typeName: string): Promise { - const userInput = await inquirer.prompt([{ - name: 'providedValue', - message: `Provide value for ${ typeName }`, - type: 'input' - } ]) - return userInput.providedValue; - } - - // Prompt for optional value (returns undefined if user refused to provide) - async promptForOption(typeDef: TypeDef): Promise { - const userInput = await inquirer.prompt([{ - name: 'confirmed', - message: `Do you want to provide the optional ${ typeDef.type } parameter?`, - type: 'confirm' - } ]); - - if (userInput.confirmed) { - const subtype = typeDef.sub; // We assume that Opion always has a single subtype - let value = await this.promptForParam(subtype.type); - return value; + if (apiMethod !== undefined && !api[apiType][apiModule][apiMethod]) { + throw new CLIError('Such method was not found', { exit: ExitCodes.InvalidInput }) } + } } - // Prompt for tuple - returns array of values - async promptForTuple(typeDef: TypeDef): Promise<(ApiMethodInputArg)[]> { - let result: ApiMethodInputArg[] = []; - - if (!typeDef.sub) return [ await this.promptForSimple(typeDef.type) ]; - - const subtypes: TypeDef[] = Array.isArray(typeDef.sub) ? typeDef.sub : [ typeDef.sub ]; + return { apiType, apiModule, apiMethod } + } + + // Request values for params using array of param types (strings) + async requestParamsValues(paramTypes: string[]): Promise { + const result: ApiMethodArg[] = [] + for (const [key, paramType] of Object.entries(paramTypes)) { + this.log(chalk.bold.white(`Parameter no. ${parseInt(key) + 1} (${paramType}):`)) + const paramValue = await this.promptForParam(paramType) + if (paramValue instanceof Option && paramValue.isSome) { + result.push(paramValue.unwrap()) + } else if (!(paramValue instanceof Option)) { + result.push(paramValue) + } + // In case of empty option we MUST NOT add anything to the array (otherwise it causes some error) + } - for (let subtype of subtypes) { - let inputParam = await this.promptForParam(subtype.type); - if (inputParam !== undefined) result.push(inputParam); + return result + } + + async run() { + const api: ApiPromise = this.getOriginalApi() + const flags: ApiInspectFlags = this.parse(ApiInspect).flags as ApiInspectFlags + const availableTypes: readonly string[] = TYPES_AVAILABLE + const { apiType, apiModule, apiMethod } = this.validateFlags(api, flags) + + // Executing a call + if (apiType && apiModule && apiMethod && flags.exec) { + let result: Codec + + if (apiType === 'query') { + // Api query - call with (or without) arguments + let args: (string | ApiMethodArg)[] = flags.callArgs ? flags.callArgs.split(',') : [] + const paramsTypes: string[] = this.getQueryMethodParamsTypes(apiModule, apiMethod) + if (args.length < paramsTypes.length) { + this.warn('Some parameters are missing! Please, provide the missing parameters:') + const missingParamsValues = await this.requestParamsValues(paramsTypes.slice(args.length)) + args = args.concat(missingParamsValues) } + result = await api.query[apiModule][apiMethod](...args) + } else { + // Api consts - just assign the value + result = api.consts[apiModule][apiMethod] + } - return result; + this.log(chalk.green(result.toString())) } - - // Prompt for param based on "paramType" string (ie. Option) - async promptForParam(paramType: string): Promise { - const typeDef: TypeDef = getTypeDef(paramType); - if (typeDef.info === TypeDefInfo.Option) return await this.promptForOption(typeDef); - else if (typeDef.info === TypeDefInfo.Tuple) return await this.promptForTuple(typeDef); - else return await this.promptForSimple(typeDef.type); + // Describing a method + else if (apiType && apiModule && apiMethod) { + this.log(chalk.bold.white(`${apiType}.${apiModule}.${apiMethod}`)) + const description: string = this.getMethodDescription(apiType, apiModule, apiMethod) + this.log(`\n${description}\n`) + const typesRows: NameValueObj[] = [] + if (apiType === 'query') { + typesRows.push({ + name: 'Params:', + value: this.getQueryMethodParamsTypes(apiModule, apiMethod).join(', ') || '-', + }) + } + typesRows.push({ name: 'Returns:', value: this.getMethodReturnType(apiType, apiModule, apiMethod) }) + displayNameValueTable(typesRows) } - - // Request values for params using array of param types (strings) - async requestParamsValues(paramTypes: string[]): Promise { - let result: ApiMethodInputArg[] = []; - for (let [key, paramType] of Object.entries(paramTypes)) { - this.log(chalk.bold.white(`Parameter no. ${ parseInt(key)+1 } (${ paramType }):`)); - let paramValue = await this.promptForParam(paramType); - if (paramValue !== undefined) result.push(paramValue); - } - - return result; + // Displaying all available methods + else if (apiType && apiModule) { + const module = api[apiType][apiModule] + const rows: NameValueObj[] = Object.keys(module).map((key: string) => { + return { name: key, value: this.getMethodDescription(apiType, apiModule, key) } + }) + displayNameValueTable(rows) } - - async run() { - const api: ApiPromise = this.getOriginalApi(); - const flags: ApiInspectFlags = this.parse(ApiInspect).flags; - const availableTypes: readonly string[] = TYPES_AVAILABLE; - const { apiType, apiModule, apiMethod } = this.validateFlags(api, flags); - - // Executing a call - if (apiType && apiModule && apiMethod && flags.exec) { - let result: Codec; - - if (apiType === 'query') { - // Api query - call with (or without) arguments - let args: ApiMethodInputArg[] = flags.callArgs ? flags.callArgs.split(',') : []; - const paramsTypes: string[] = this.getQueryMethodParamsTypes(apiModule, apiMethod); - if (args.length < paramsTypes.length) { - this.warn('Some parameters are missing! Please, provide the missing parameters:'); - let missingParamsValues = await this.requestParamsValues(paramsTypes.slice(args.length)); - args = args.concat(missingParamsValues); - } - result = await api.query[apiModule][apiMethod](...args); - } - else { - // Api consts - just assign the value - result = api.consts[apiModule][apiMethod]; - } - - this.log(chalk.green(result.toString())); - } - // Describing a method - else if (apiType && apiModule && apiMethod) { - this.log(chalk.bold.white(`${ apiType }.${ apiModule }.${ apiMethod }`)); - const description: string = this.getMethodDescription(apiType, apiModule, apiMethod); - this.log(`\n${ description }\n`); - let typesRows: NameValueObj[] = []; - if (apiType === 'query') { - typesRows.push({ name: 'Params:', value: this.getQueryMethodParamsTypes(apiModule, apiMethod).join(', ') || '-' }); - } - typesRows.push({ name: 'Returns:', value: this.getMethodReturnType(apiType, apiModule, apiMethod) }); - displayNameValueTable(typesRows); - } - // Displaying all available methods - else if (apiType && apiModule) { - const module = api[apiType][apiModule]; - const rows: NameValueObj[] = Object.keys(module).map((key: string) => { - return { name: key, value: this.getMethodDescription(apiType, apiModule, key) }; - }); - displayNameValueTable(rows); - } - // Displaying all available modules - else if (apiType) { - this.log(chalk.bold.white('Available modules:')); - this.log(Object.keys(api[apiType]).map(key => chalk.white(key)).join('\n')); - } - // Displaying all available types - else { - this.log(chalk.bold.white('Available types:')); - this.log(availableTypes.map(type => chalk.white(type)).join('\n')); - } + // Displaying all available modules + else if (apiType) { + this.log(chalk.bold.white('Available modules:')) + this.log( + Object.keys(api[apiType]) + .map((key) => chalk.white(key)) + .join('\n') + ) + } + // Displaying all available types + else { + this.log(chalk.bold.white('Available types:')) + this.log(availableTypes.map((type) => chalk.white(type)).join('\n')) } + } } diff --git a/cli/src/commands/api/setUri.ts b/cli/src/commands/api/setUri.ts index 591fc12889..b93720e398 100644 --- a/cli/src/commands/api/setUri.ts +++ b/cli/src/commands/api/setUri.ts @@ -1,28 +1,37 @@ -import StateAwareCommandBase from '../../base/StateAwareCommandBase'; -import chalk from 'chalk'; -import { WsProvider } from '@polkadot/api'; -import ExitCodes from '../../ExitCodes'; +import chalk from 'chalk' +import ApiCommandBase from '../../base/ApiCommandBase' +import ExitCodes from '../../ExitCodes' -type ApiSetUriArgs = { uri: string }; +type ApiSetUriArgs = { uri: string } -export default class ApiSetUri extends StateAwareCommandBase { - static description = 'Set api WS provider uri'; - static args = [ - { - name: 'uri', - required: true, - description: 'Uri of the node api WS provider' - } - ]; +export default class ApiSetUri extends ApiCommandBase { + static description = 'Set api WS provider uri' + static args = [ + { + name: 'uri', + required: false, + description: 'Uri of the node api WS provider (if skipped, a prompt will be displayed)', + }, + ] - async run() { - const args: ApiSetUriArgs = this.parse(ApiSetUri).args; - try { - new WsProvider(args.uri); - } catch(e) { - this.error('The WS provider uri seems to be incorrect', { exit: ExitCodes.InvalidInput }); - } - await this.setPreservedState({ apiUri: args.uri }); - this.log(chalk.greenBright('Api uri successfuly changed! New uri: ') + chalk.white(args.uri)) + async init() { + this.forceSkipApiUriPrompt = true + super.init() + } + + async run() { + const args: ApiSetUriArgs = this.parse(ApiSetUri).args as ApiSetUriArgs + let newUri = '' + if (args.uri) { + if (this.isApiUriValid(args.uri)) { + await this.setPreservedState({ apiUri: args.uri }) + newUri = args.uri + } else { + this.error('Provided uri seems to be incorrect!', { exit: ExitCodes.InvalidInput }) + } + } else { + newUri = await this.promptForApiUri() } + this.log(chalk.greenBright('Api uri successfuly changed! New uri: ') + chalk.white(newUri)) } +} diff --git a/cli/src/commands/council/info.ts b/cli/src/commands/council/info.ts index d68b0215ac..6cf0bfee76 100644 --- a/cli/src/commands/council/info.ts +++ b/cli/src/commands/council/info.ts @@ -1,57 +1,56 @@ -import { ElectionStage } from '@joystream/types'; -import { formatNumber, formatBalance } from '@polkadot/util'; -import { BlockNumber } from '@polkadot/types/interfaces'; -import { CouncilInfoObj, NameValueObj } from '../../Types'; -import { displayHeader, displayNameValueTable } from '../../helpers/display'; -import ApiCommandBase from '../../base/ApiCommandBase'; +import { ElectionStage } from '@joystream/types/council' +import { formatNumber, formatBalance } from '@polkadot/util' +import { BlockNumber } from '@polkadot/types/interfaces' +import { CouncilInfoObj, NameValueObj } from '../../Types' +import { displayHeader, displayNameValueTable } from '../../helpers/display' +import ApiCommandBase from '../../base/ApiCommandBase' export default class CouncilInfo extends ApiCommandBase { - static description = 'Get current council and council elections information'; + static description = 'Get current council and council elections information' - displayInfo(infoObj: CouncilInfoObj) { - const { activeCouncil = [], round, stage } = infoObj; + displayInfo(infoObj: CouncilInfoObj) { + const { activeCouncil = [], round, stage } = infoObj - displayHeader('Council'); - const councilRows: NameValueObj[] = [ - { name: 'Elected:', value: activeCouncil.length ? 'YES' : 'NO' }, - { name: 'Members:', value: activeCouncil.length.toString() }, - { name: 'Term ends at block:', value: `#${formatNumber(infoObj.termEndsAt) }` }, - ]; - displayNameValueTable(councilRows); + displayHeader('Council') + const councilRows: NameValueObj[] = [ + { name: 'Elected:', value: activeCouncil.length ? 'YES' : 'NO' }, + { name: 'Members:', value: activeCouncil.length.toString() }, + { name: 'Term ends at block:', value: `#${formatNumber(infoObj.termEndsAt)}` }, + ] + displayNameValueTable(councilRows) - - displayHeader('Election'); - let electionTableRows: NameValueObj[] = [ - { name: 'Running:', value: stage && stage.isSome ? 'YES' : 'NO' }, - { name: 'Election round:', value: formatNumber(round) } - ]; - if (stage && stage.isSome) { - const stageValue = stage.value; - const stageName: string = stageValue.type; - const stageEndsAt = stageValue.value; - electionTableRows.push({ name: 'Stage:', value: stageName }); - electionTableRows.push({ name: 'Stage ends at block:', value: `#${stageEndsAt}` }); - } - displayNameValueTable(electionTableRows); - - displayHeader('Configuration'); - const isAutoStart = (infoObj.autoStart || false).valueOf(); - const configTableRows: NameValueObj[] = [ - { name: 'Auto-start elections:', value: isAutoStart ? 'YES' : 'NO' }, - { name: 'New term duration:', value: formatNumber(infoObj.newTermDuration) }, - { name: 'Candidacy limit:', value: formatNumber(infoObj.candidacyLimit) }, - { name: 'Council size:', value: formatNumber(infoObj.councilSize) }, - { name: 'Min. council stake:', value: formatBalance(infoObj.minCouncilStake) }, - { name: 'Min. voting stake:', value: formatBalance(infoObj.minVotingStake) }, - { name: 'Announcing period:', value: `${ formatNumber(infoObj.announcingPeriod) } blocks` }, - { name: 'Voting period:', value: `${ formatNumber(infoObj.votingPeriod) } blocks` }, - { name: 'Revealing period:', value: `${ formatNumber(infoObj.revealingPeriod) } blocks` } - ]; - displayNameValueTable(configTableRows); + displayHeader('Election') + const electionTableRows: NameValueObj[] = [ + { name: 'Running:', value: stage && stage.isSome ? 'YES' : 'NO' }, + { name: 'Election round:', value: formatNumber(round) }, + ] + if (stage && stage.isSome) { + const stageValue = stage.value as ElectionStage + const stageName: string = stageValue.type + const stageEndsAt = stageValue.value as BlockNumber + electionTableRows.push({ name: 'Stage:', value: stageName }) + electionTableRows.push({ name: 'Stage ends at block:', value: `#${stageEndsAt}` }) } + displayNameValueTable(electionTableRows) - async run() { - const infoObj = await this.getApi().getCouncilInfo(); - this.displayInfo(infoObj); - } + displayHeader('Configuration') + const isAutoStart = (infoObj.autoStart || false).valueOf() + const configTableRows: NameValueObj[] = [ + { name: 'Auto-start elections:', value: isAutoStart ? 'YES' : 'NO' }, + { name: 'New term duration:', value: formatNumber(infoObj.newTermDuration) }, + { name: 'Candidacy limit:', value: formatNumber(infoObj.candidacyLimit) }, + { name: 'Council size:', value: formatNumber(infoObj.councilSize) }, + { name: 'Min. council stake:', value: formatBalance(infoObj.minCouncilStake) }, + { name: 'Min. voting stake:', value: formatBalance(infoObj.minVotingStake) }, + { name: 'Announcing period:', value: `${formatNumber(infoObj.announcingPeriod)} blocks` }, + { name: 'Voting period:', value: `${formatNumber(infoObj.votingPeriod)} blocks` }, + { name: 'Revealing period:', value: `${formatNumber(infoObj.revealingPeriod)} blocks` }, + ] + displayNameValueTable(configTableRows) + } + + async run() { + const infoObj = await this.getApi().getCouncilInfo() + this.displayInfo(infoObj) } +} diff --git a/cli/src/commands/working-groups/application.ts b/cli/src/commands/working-groups/application.ts new file mode 100644 index 0000000000..bff68b9eda --- /dev/null +++ b/cli/src/commands/working-groups/application.ts @@ -0,0 +1,39 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { displayCollapsedRow, displayHeader } from '../../helpers/display' +import chalk from 'chalk' + +export default class WorkingGroupsApplication extends WorkingGroupsCommandBase { + static description = 'Shows an overview of given application by Working Group Application ID' + static args = [ + { + name: 'wgApplicationId', + required: true, + description: 'Working Group Application ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsApplication) + + const application = await this.getApi().groupApplication(this.group, parseInt(args.wgApplicationId)) + + displayHeader('Human readable text') + this.jsonPrettyPrint(application.humanReadableText) + + displayHeader(`Details`) + const applicationRow = { + 'WG application ID': application.wgApplicationId, + 'Application ID': application.applicationId, + 'Member handle': application.member?.handle.toString() || chalk.red('NONE'), + 'Role account': application.roleAccout.toString(), + Stage: application.stage, + 'Application stake': application.stakes.application, + 'Role stake': application.stakes.role, + 'Total stake': Object.values(application.stakes).reduce((a, b) => a + b), + } + displayCollapsedRow(applicationRow) + } +} diff --git a/cli/src/commands/working-groups/createOpening.ts b/cli/src/commands/working-groups/createOpening.ts new file mode 100644 index 0000000000..6c11f91b0b --- /dev/null +++ b/cli/src/commands/working-groups/createOpening.ts @@ -0,0 +1,89 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { ApiMethodArg, ApiMethodNamedArgs } from '../../Types' +import chalk from 'chalk' +import { flags } from '@oclif/command' +import { apiModuleByGroup } from '../../Api' +import WorkerOpeningOptions from '../../promptOptions/addWorkerOpening' +import { setDefaults } from '../../helpers/promptOptions' + +export default class WorkingGroupsCreateOpening extends WorkingGroupsCommandBase { + static description = 'Create working group opening (requires lead access)' + static flags = { + ...WorkingGroupsCommandBase.flags, + useDraft: flags.boolean({ + char: 'd', + description: + 'Whether to create the opening from existing draft.\n' + + 'If provided without --draftName - the list of choices will be displayed.', + }), + draftName: flags.string({ + char: 'n', + description: 'Name of the draft to create the opening from.', + dependsOn: ['useDraft'], + }), + createDraftOnly: flags.boolean({ + char: 'c', + description: + 'If provided - the extrinsic will not be executed. Use this flag if you only want to create a draft.', + }), + skipPrompts: flags.boolean({ + char: 's', + description: 'Whether to skip all prompts when adding from draft (will use all default values)', + dependsOn: ['useDraft'], + exclusive: ['createDraftOnly'], + }), + } + + async run() { + const account = await this.getRequiredSelectedAccount() + // lead-only gate + await this.getRequiredLead() + + const { flags } = this.parse(WorkingGroupsCreateOpening) + + const promptOptions = new WorkerOpeningOptions() + let defaultValues: ApiMethodNamedArgs | undefined + if (flags.useDraft) { + const draftName = flags.draftName || (await this.promptForOpeningDraft()) + defaultValues = await this.loadOpeningDraftParams(draftName) + setDefaults(promptOptions, defaultValues) + } + + if (!flags.skipPrompts) { + const module = apiModuleByGroup[this.group] + const method = 'addOpening' + + let saveDraft = false, + params: ApiMethodArg[] + if (flags.createDraftOnly) { + params = await this.promptForExtrinsicParams(module, method, promptOptions) + saveDraft = true + } else { + await this.requestAccountDecoding(account) // Prompt for password + params = await this.buildAndSendExtrinsic(account, module, method, promptOptions, true) + + saveDraft = await this.simplePrompt({ + message: 'Do you wish to save this opening as draft?', + type: 'confirm', + }) + } + + if (saveDraft) { + const draftName = await this.promptForNewOpeningDraftName() + this.saveOpeningDraft(draftName, params) + + this.log(chalk.green(`Opening draft ${chalk.white(draftName)} succesfully saved!`)) + } + } else { + await this.requestAccountDecoding(account) // Prompt for password + this.log(chalk.white('Sending the extrinsic...')) + await this.sendExtrinsic( + account, + apiModuleByGroup[this.group], + 'addOpening', + defaultValues!.map((v) => v.value) + ) + this.log(chalk.green('Opening succesfully created!')) + } + } +} diff --git a/cli/src/commands/working-groups/decreaseWorkerStake.ts b/cli/src/commands/working-groups/decreaseWorkerStake.ts new file mode 100644 index 0000000000..4208bc79a2 --- /dev/null +++ b/cli/src/commands/working-groups/decreaseWorkerStake.ts @@ -0,0 +1,56 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { WorkerId } from '@joystream/types/working-group' +import { Balance } from '@polkadot/types/interfaces' +import { formatBalance } from '@polkadot/util' +import { minMaxInt } from '../../validators/common' +import chalk from 'chalk' +import { createParamOptions } from '../../helpers/promptOptions' + +export default class WorkingGroupsDecreaseWorkerStake extends WorkingGroupsCommandBase { + static description = + 'Decreases given worker stake by an amount that will be returned to the worker role account. ' + + 'Requires lead access.' + static args = [ + { + name: 'workerId', + required: true, + description: 'Worker ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsDecreaseWorkerStake) + + const account = await this.getRequiredSelectedAccount() + // Lead-only gate + await this.getRequiredLead() + + const workerId = parseInt(args.workerId) + const groupMember = await this.getWorkerWithStakeForLeadAction(workerId) + + this.log(chalk.white('Current worker stake: ', formatBalance(groupMember.stake))) + const balanceValidator = minMaxInt(1, groupMember.stake.toNumber()) + const balance = (await this.promptForParam( + 'Balance', + createParamOptions('amount', undefined, balanceValidator) + )) as Balance + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'decreaseStake', [ + new WorkerId(workerId), + balance, + ]) + + this.log( + chalk.green( + `${chalk.white(formatBalance(balance))} from worker ${chalk.white(workerId)} stake ` + + `has been returned to worker's role account (${chalk.white(groupMember.roleAccount.toString())})!` + ) + ) + } +} diff --git a/cli/src/commands/working-groups/evictWorker.ts b/cli/src/commands/working-groups/evictWorker.ts new file mode 100644 index 0000000000..961e2618e1 --- /dev/null +++ b/cli/src/commands/working-groups/evictWorker.ts @@ -0,0 +1,56 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { WorkerId } from '@joystream/types/working-group' +import { bool } from '@polkadot/types/primitive' +import { formatBalance } from '@polkadot/util' +import chalk from 'chalk' +import { createParamOptions } from '../../helpers/promptOptions' + +export default class WorkingGroupsEvictWorker extends WorkingGroupsCommandBase { + static description = 'Evicts given worker. Requires lead access.' + static args = [ + { + name: 'workerId', + required: true, + description: 'Worker ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsEvictWorker) + + const account = await this.getRequiredSelectedAccount() + // Lead-only gate + await this.getRequiredLead() + + const workerId = parseInt(args.workerId) + // This will also make sure the worker is valid + const groupMember = await this.getWorkerForLeadAction(workerId) + + // TODO: Terminate worker text limits? (minMaxStr) + const rationale = await this.promptForParam('Bytes', createParamOptions('rationale')) + const shouldSlash = groupMember.stake + ? await this.simplePrompt({ + message: `Should the worker stake (${formatBalance(groupMember.stake)}) be slashed?`, + type: 'confirm', + default: false, + }) + : false + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateRole', [ + new WorkerId(workerId), + rationale, + new bool(shouldSlash), + ]) + + this.log(chalk.green(`Worker ${chalk.white(workerId)} has been evicted!`)) + if (shouldSlash) { + this.log(chalk.green(`Worker stake totalling ${chalk.white(formatBalance(groupMember.stake))} has been slashed!`)) + } + } +} diff --git a/cli/src/commands/working-groups/fillOpening.ts b/cli/src/commands/working-groups/fillOpening.ts new file mode 100644 index 0000000000..9d3d42361c --- /dev/null +++ b/cli/src/commands/working-groups/fillOpening.ts @@ -0,0 +1,52 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { OpeningStatus } from '../../Types' +import { apiModuleByGroup } from '../../Api' +import { OpeningId } from '@joystream/types/hiring' +import { ApplicationIdSet, RewardPolicy } from '@joystream/types/working-group' +import chalk from 'chalk' +import { createParamOptions } from '../../helpers/promptOptions' + +export default class WorkingGroupsFillOpening extends WorkingGroupsCommandBase { + static description = "Allows filling working group opening that's currently in review. Requires lead access." + static args = [ + { + name: 'wgOpeningId', + required: true, + description: 'Working Group Opening ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsFillOpening) + + const account = await this.getRequiredSelectedAccount() + // Lead-only gate + await this.getRequiredLead() + + const openingId = parseInt(args.wgOpeningId) + const opening = await this.getOpeningForLeadAction(openingId, OpeningStatus.InReview) + + const applicationIds = await this.promptForApplicationsToAccept(opening) + const rewardPolicyOpt = await this.promptForParam( + `Option<${RewardPolicy.name}>`, + createParamOptions('RewardPolicy') + ) + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'fillOpening', [ + new OpeningId(openingId), + new ApplicationIdSet(applicationIds), + rewardPolicyOpt, + ]) + + this.log(chalk.green(`Opening ${chalk.white(openingId)} succesfully filled!`)) + this.log( + chalk.green('Accepted working group application IDs: ') + + chalk.white(applicationIds.length ? applicationIds.join(chalk.green(', ')) : 'NONE') + ) + } +} diff --git a/cli/src/commands/working-groups/increaseStake.ts b/cli/src/commands/working-groups/increaseStake.ts new file mode 100644 index 0000000000..e17b3c01a5 --- /dev/null +++ b/cli/src/commands/working-groups/increaseStake.ts @@ -0,0 +1,46 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { Balance } from '@polkadot/types/interfaces' +import { formatBalance } from '@polkadot/util' +import { positiveInt } from '../../validators/common' +import chalk from 'chalk' +import ExitCodes from '../../ExitCodes' +import { createParamOptions } from '../../helpers/promptOptions' + +export default class WorkingGroupsIncreaseStake extends WorkingGroupsCommandBase { + static description = 'Increases current role (lead/worker) stake. Requires active role account to be selected.' + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const account = await this.getRequiredSelectedAccount() + // Worker-only gate + const worker = await this.getRequiredWorker() + + if (!worker.stake) { + this.error('Cannot increase stake. No associated role stake profile found!', { exit: ExitCodes.InvalidInput }) + } + + this.log(chalk.white('Current stake: ', formatBalance(worker.stake))) + const balance = (await this.promptForParam( + 'Balance', + createParamOptions('amount', undefined, positiveInt()) + )) as Balance + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'increaseStake', [ + worker.workerId, + balance, + ]) + + this.log( + chalk.green( + `Worker ${chalk.white(worker.workerId.toNumber())} stake has been increased by ${chalk.white( + formatBalance(balance) + )}` + ) + ) + } +} diff --git a/cli/src/commands/working-groups/leaveRole.ts b/cli/src/commands/working-groups/leaveRole.ts new file mode 100644 index 0000000000..865b2addeb --- /dev/null +++ b/cli/src/commands/working-groups/leaveRole.ts @@ -0,0 +1,28 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { minMaxStr } from '../../validators/common' +import chalk from 'chalk' +import { createParamOptions } from '../../helpers/promptOptions' + +export default class WorkingGroupsLeaveRole extends WorkingGroupsCommandBase { + static description = 'Leave the worker or lead role associated with currently selected account.' + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const account = await this.getRequiredSelectedAccount() + // Worker-only gate + const worker = await this.getRequiredWorker() + + const constraint = await this.getApi().workerExitRationaleConstraint(this.group) + const rationaleValidator = minMaxStr(constraint.min.toNumber(), constraint.max.toNumber()) + const rationale = await this.promptForParam('Bytes', createParamOptions('rationale', undefined, rationaleValidator)) + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'leaveRole', [worker.workerId, rationale]) + + this.log(chalk.green(`Succesfully left the role! (worker id: ${chalk.white(worker.workerId.toNumber())})`)) + } +} diff --git a/cli/src/commands/working-groups/opening.ts b/cli/src/commands/working-groups/opening.ts new file mode 100644 index 0000000000..3ac2411546 --- /dev/null +++ b/cli/src/commands/working-groups/opening.ts @@ -0,0 +1,88 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { displayTable, displayCollapsedRow, displayHeader } from '../../helpers/display' +import _ from 'lodash' +import { OpeningStatus, GroupOpeningStage, GroupOpeningStakes, UnstakingPeriodsKey } from '../../Types' +import { StakingAmountLimitModeKeys, StakingPolicy } from '@joystream/types/hiring' +import { formatBalance } from '@polkadot/util' +import chalk from 'chalk' + +export default class WorkingGroupsOpening extends WorkingGroupsCommandBase { + static description = 'Shows an overview of given working group opening by Working Group Opening ID' + static args = [ + { + name: 'wgOpeningId', + required: true, + description: 'Working Group Opening ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + stageColumns(stage: GroupOpeningStage) { + const { status, date, block } = stage + const statusTimeHeader = status === OpeningStatus.WaitingToBegin ? 'Starts at' : 'Last status change' + return { + Stage: _.startCase(status), + [statusTimeHeader]: + date && block + ? `~ ${date.toLocaleTimeString()} ${date.toLocaleDateString()} (#${block})` + : (block && `#${block}`) || '?', + } + } + + formatStake(stake: StakingPolicy | undefined) { + if (!stake) return 'NONE' + const { amount, amount_mode: amountMode } = stake + return amountMode.type === StakingAmountLimitModeKeys.AtLeast + ? `>= ${formatBalance(amount)}` + : `== ${formatBalance(amount)}` + } + + stakeColumns(stakes: GroupOpeningStakes) { + const { role, application } = stakes + return { + 'Application stake': this.formatStake(application), + 'Role stake': this.formatStake(role), + } + } + + async run() { + const { args } = this.parse(WorkingGroupsOpening) + + const opening = await this.getApi().groupOpening(this.group, parseInt(args.wgOpeningId)) + + displayHeader('Human readable text') + this.jsonPrettyPrint(opening.opening.human_readable_text.toString()) + + displayHeader('Opening details') + const openingRow = { + 'WG Opening ID': opening.wgOpeningId, + 'Opening ID': opening.openingId, + Type: opening.type.type, + ...this.stageColumns(opening.stage), + ...this.stakeColumns(opening.stakes), + } + displayCollapsedRow(openingRow) + + displayHeader('Unstaking periods') + const periodsRow: { [k: string]: string } = {} + for (const key of Object.keys(opening.unstakingPeriods).sort()) { + const displayKey = _.startCase(key) + ': ' + periodsRow[displayKey] = opening.unstakingPeriods[key as UnstakingPeriodsKey].toLocaleString() + ' blocks' + } + displayCollapsedRow(periodsRow) + + displayHeader(`Applications (${opening.applications.length})`) + const applicationsRows = opening.applications.map((a) => ({ + 'WG appl. ID': a.wgApplicationId, + 'Appl. ID': a.applicationId, + Member: a.member?.handle.toString() || chalk.red('NONE'), + Stage: a.stage, + 'Appl. stake': a.stakes.application, + 'Role stake': a.stakes.role, + 'Total stake': Object.values(a.stakes).reduce((a, b) => a + b), + })) + displayTable(applicationsRows, 5) + } +} diff --git a/cli/src/commands/working-groups/openings.ts b/cli/src/commands/working-groups/openings.ts new file mode 100644 index 0000000000..ccf04960d1 --- /dev/null +++ b/cli/src/commands/working-groups/openings.ts @@ -0,0 +1,22 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { displayTable } from '../../helpers/display' +import _ from 'lodash' + +export default class WorkingGroupsOpenings extends WorkingGroupsCommandBase { + static description = 'Shows an overview of given working group openings' + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const openings = await this.getApi().openingsByGroup(this.group) + + const openingsRows = openings.map((o) => ({ + 'WG Opening ID': o.wgOpeningId, + Type: o.type.type, + Stage: `${_.startCase(o.stage.status)}${o.stage.block ? ` (#${o.stage.block})` : ''}`, + Applications: o.applications.length, + })) + displayTable(openingsRows, 5) + } +} diff --git a/cli/src/commands/working-groups/overview.ts b/cli/src/commands/working-groups/overview.ts new file mode 100644 index 0000000000..e206379c19 --- /dev/null +++ b/cli/src/commands/working-groups/overview.ts @@ -0,0 +1,49 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { displayHeader, displayNameValueTable, displayTable, shortAddress } from '../../helpers/display' +import { formatBalance } from '@polkadot/util' + +import chalk from 'chalk' + +export default class WorkingGroupsOverview extends WorkingGroupsCommandBase { + static description = 'Shows an overview of given working group (current lead and workers)' + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const lead = await this.getApi().groupLead(this.group) + const members = await this.getApi().groupMembers(this.group) + + displayHeader('Group lead') + if (lead) { + displayNameValueTable([ + { name: 'Member id:', value: lead.memberId.toString() }, + { name: 'Member handle:', value: lead.profile.handle.toString() }, + { name: 'Role account:', value: lead.roleAccount.toString() }, + ]) + } else { + this.log(chalk.yellow('No lead assigned!')) + } + + const accounts = this.fetchAccounts() + + displayHeader('Members') + const membersRows = members.map((m) => ({ + 'Worker id': m.workerId.toString(), + 'Member id': m.memberId.toString(), + 'Member handle': m.profile.handle.toString(), + Stake: formatBalance(m.stake), + Earned: formatBalance(m.reward?.totalRecieved), + 'Role account': shortAddress(m.roleAccount), + '': + (lead?.workerId.eq(m.workerId) ? '\u{2B50}' : ' ') + + ' ' + + (accounts.some((a) => a.address === m.roleAccount.toString()) ? '\u{1F511}' : ' '), + })) + displayTable(membersRows, 5) + + displayHeader('Legend') + this.log('\u{2B50} - Leader') + this.log('\u{1F511} - Role key available in CLI') + } +} diff --git a/cli/src/commands/working-groups/slashWorker.ts b/cli/src/commands/working-groups/slashWorker.ts new file mode 100644 index 0000000000..3e7b2629c9 --- /dev/null +++ b/cli/src/commands/working-groups/slashWorker.ts @@ -0,0 +1,55 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { WorkerId } from '@joystream/types/working-group' +import { Balance } from '@polkadot/types/interfaces' +import { formatBalance } from '@polkadot/util' +import { minMaxInt } from '../../validators/common' +import chalk from 'chalk' +import { createParamOptions } from '../../helpers/promptOptions' + +export default class WorkingGroupsSlashWorker extends WorkingGroupsCommandBase { + static description = 'Slashes given worker stake. Requires lead access.' + static args = [ + { + name: 'workerId', + required: true, + description: 'Worker ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsSlashWorker) + + const account = await this.getRequiredSelectedAccount() + // Lead-only gate + await this.getRequiredLead() + + const workerId = parseInt(args.workerId) + const groupMember = await this.getWorkerWithStakeForLeadAction(workerId) + + this.log(chalk.white('Current worker stake: ', formatBalance(groupMember.stake))) + const balanceValidator = minMaxInt(1, groupMember.stake.toNumber()) + const balance = (await this.promptForParam( + 'Balance', + createParamOptions('amount', undefined, balanceValidator) + )) as Balance + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'slashStake', [ + new WorkerId(workerId), + balance, + ]) + + this.log( + chalk.green( + `${chalk.white(formatBalance(balance))} from worker ${chalk.white( + workerId + )} stake has been succesfully slashed!` + ) + ) + } +} diff --git a/cli/src/commands/working-groups/startAcceptingApplications.ts b/cli/src/commands/working-groups/startAcceptingApplications.ts new file mode 100644 index 0000000000..6dad081152 --- /dev/null +++ b/cli/src/commands/working-groups/startAcceptingApplications.ts @@ -0,0 +1,40 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { OpeningStatus } from '../../Types' +import { apiModuleByGroup } from '../../Api' +import { OpeningId } from '@joystream/types/hiring' +import chalk from 'chalk' + +export default class WorkingGroupsStartAcceptingApplications extends WorkingGroupsCommandBase { + static description = 'Changes the status of pending opening to "Accepting applications". Requires lead access.' + static args = [ + { + name: 'wgOpeningId', + required: true, + description: 'Working Group Opening ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsStartAcceptingApplications) + + const account = await this.getRequiredSelectedAccount() + // Lead-only gate + await this.getRequiredLead() + + const openingId = parseInt(args.wgOpeningId) + await this.validateOpeningForLeadAction(openingId, OpeningStatus.WaitingToBegin) + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'acceptApplications', [ + new OpeningId(openingId), + ]) + + this.log( + chalk.green(`Opening ${chalk.white(openingId)} status changed to: ${chalk.white('Accepting Applications')}`) + ) + } +} diff --git a/cli/src/commands/working-groups/startReviewPeriod.ts b/cli/src/commands/working-groups/startReviewPeriod.ts new file mode 100644 index 0000000000..3245effa65 --- /dev/null +++ b/cli/src/commands/working-groups/startReviewPeriod.ts @@ -0,0 +1,38 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { OpeningStatus } from '../../Types' +import { apiModuleByGroup } from '../../Api' +import { OpeningId } from '@joystream/types/hiring' +import chalk from 'chalk' + +export default class WorkingGroupsStartReviewPeriod extends WorkingGroupsCommandBase { + static description = 'Changes the status of active opening to "In review". Requires lead access.' + static args = [ + { + name: 'wgOpeningId', + required: true, + description: 'Working Group Opening ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsStartReviewPeriod) + + const account = await this.getRequiredSelectedAccount() + // Lead-only gate + await this.getRequiredLead() + + const openingId = parseInt(args.wgOpeningId) + await this.validateOpeningForLeadAction(openingId, OpeningStatus.AcceptingApplications) + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'beginApplicantReview', [ + new OpeningId(openingId), + ]) + + this.log(chalk.green(`Opening ${chalk.white(openingId)} status changed to: ${chalk.white('In Review')}`)) + } +} diff --git a/cli/src/commands/working-groups/terminateApplication.ts b/cli/src/commands/working-groups/terminateApplication.ts new file mode 100644 index 0000000000..8d33d7128c --- /dev/null +++ b/cli/src/commands/working-groups/terminateApplication.ts @@ -0,0 +1,38 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { ApplicationStageKeys, ApplicationId } from '@joystream/types/hiring' +import chalk from 'chalk' + +export default class WorkingGroupsTerminateApplication extends WorkingGroupsCommandBase { + static description = 'Terminates given working group application. Requires lead access.' + static args = [ + { + name: 'wgApplicationId', + required: true, + description: 'Working Group Application ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsTerminateApplication) + + const account = await this.getRequiredSelectedAccount() + // Lead-only gate + await this.getRequiredLead() + + const applicationId = parseInt(args.wgApplicationId) + // We don't really need the application itself here, so this one is just for validation purposes + await this.getApplicationForLeadAction(applicationId, ApplicationStageKeys.Active) + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateApplication', [ + new ApplicationId(applicationId), + ]) + + this.log(chalk.green(`Application ${chalk.white(applicationId)} has been succesfully terminated!`)) + } +} diff --git a/cli/src/commands/working-groups/updateRewardAccount.ts b/cli/src/commands/working-groups/updateRewardAccount.ts new file mode 100644 index 0000000000..cb65fdd96c --- /dev/null +++ b/cli/src/commands/working-groups/updateRewardAccount.ts @@ -0,0 +1,48 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { validateAddress } from '../../helpers/validation' +import { GenericAccountId } from '@polkadot/types' +import chalk from 'chalk' +import ExitCodes from '../../ExitCodes' + +export default class WorkingGroupsUpdateRewardAccount extends WorkingGroupsCommandBase { + static description = 'Updates the worker/lead reward account (requires current role account to be selected)' + static args = [ + { + name: 'accountAddress', + required: false, + description: 'New reward account address (if omitted, one of the existing CLI accounts can be selected)', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsUpdateRewardAccount) + + const account = await this.getRequiredSelectedAccount() + // Worker-only gate + const worker = await this.getRequiredWorker() + + if (!worker.reward) { + this.error('There is no reward relationship associated with this role!', { exit: ExitCodes.InvalidInput }) + } + + let newRewardAccount: string = args.accountAddress + if (!newRewardAccount) { + const accounts = await this.fetchAccounts() + newRewardAccount = (await this.promptForAccount(accounts, undefined, 'Choose the new reward account')).address + } + validateAddress(newRewardAccount) + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRewardAccount', [ + worker.workerId, + new GenericAccountId(newRewardAccount), + ]) + + this.log(chalk.green(`Succesfully updated the reward account to: ${chalk.white(newRewardAccount)})`)) + } +} diff --git a/cli/src/commands/working-groups/updateRoleAccount.ts b/cli/src/commands/working-groups/updateRoleAccount.ts new file mode 100644 index 0000000000..a584d21423 --- /dev/null +++ b/cli/src/commands/working-groups/updateRoleAccount.ts @@ -0,0 +1,58 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { validateAddress } from '../../helpers/validation' +import { GenericAccountId } from '@polkadot/types' +import chalk from 'chalk' + +export default class WorkingGroupsUpdateRoleAccount extends WorkingGroupsCommandBase { + static description = 'Updates the worker/lead role account. Requires member controller account to be selected' + static args = [ + { + name: 'accountAddress', + required: false, + description: 'New role account address (if omitted, one of the existing CLI accounts can be selected)', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + async run() { + const { args } = this.parse(WorkingGroupsUpdateRoleAccount) + + const account = await this.getRequiredSelectedAccount() + const worker = await this.getRequiredWorkerByMemberController() + + const cliAccounts = await this.fetchAccounts() + let newRoleAccount: string = args.accountAddress + if (!newRoleAccount) { + newRoleAccount = (await this.promptForAccount(cliAccounts, undefined, 'Choose the new role account')).address + } + validateAddress(newRoleAccount) + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRoleAccount', [ + worker.workerId, + new GenericAccountId(newRoleAccount), + ]) + + this.log(chalk.green(`Succesfully updated the role account to: ${chalk.white(newRoleAccount)})`)) + + const matchingAccount = cliAccounts.find((account) => account.address === newRoleAccount) + if (matchingAccount) { + const switchAccount = await this.simplePrompt({ + type: 'confirm', + message: 'Do you want to switch the currenly selected CLI account to the new role account?', + default: false, + }) + if (switchAccount) { + await this.setSelectedAccount(matchingAccount) + this.log( + chalk.green('Account switched to: ') + + chalk.white(`${matchingAccount.meta.name} (${matchingAccount.address})`) + ) + } + } + } +} diff --git a/cli/src/commands/working-groups/updateWorkerReward.ts b/cli/src/commands/working-groups/updateWorkerReward.ts new file mode 100644 index 0000000000..d6421eb5bf --- /dev/null +++ b/cli/src/commands/working-groups/updateWorkerReward.ts @@ -0,0 +1,67 @@ +import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase' +import { apiModuleByGroup } from '../../Api' +import { WorkerId } from '@joystream/types/working-group' +import { formatBalance } from '@polkadot/util' +import chalk from 'chalk' +import { Reward } from '../../Types' +import { positiveInt } from '../../validators/common' +import { createParamOptions } from '../../helpers/promptOptions' +import ExitCodes from '../../ExitCodes' + +export default class WorkingGroupsUpdateWorkerReward extends WorkingGroupsCommandBase { + static description = "Change given worker's reward (amount only). Requires lead access." + static args = [ + { + name: 'workerId', + required: true, + description: 'Worker ID', + }, + ] + static flags = { + ...WorkingGroupsCommandBase.flags, + } + + formatReward(reward?: Reward) { + return reward + ? formatBalance(reward.value) + + (reward.interval && ` / ${reward.interval} block(s)`) + + (reward.nextPaymentBlock && ` (next payment: #${reward.nextPaymentBlock})`) + : 'NONE' + } + + async run() { + const { args } = this.parse(WorkingGroupsUpdateWorkerReward) + + const account = await this.getRequiredSelectedAccount() + // Lead-only gate + await this.getRequiredLead() + + const workerId = parseInt(args.workerId) + // This will also make sure the worker is valid + const groupMember = await this.getWorkerForLeadAction(workerId) + + const { reward } = groupMember + + if (!reward) { + this.error('There is no reward relationship associated with this worker!', { exit: ExitCodes.InvalidInput }) + } + + console.log(chalk.white(`Current worker reward: ${this.formatReward(reward)}`)) + + const newRewardValue = await this.promptForParam( + 'BalanceOfMint', + createParamOptions('new_amount', undefined, positiveInt()) + ) + + await this.requestAccountDecoding(account) + + await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRewardAmount', [ + new WorkerId(workerId), + newRewardValue, + ]) + + const updatedGroupMember = await this.getApi().groupMember(this.group, workerId) + this.log(chalk.green(`Worker ${chalk.white(workerId)} reward has been updated!`)) + this.log(chalk.green(`New worker reward: ${chalk.white(this.formatReward(updatedGroupMember.reward))}`)) + } +} diff --git a/cli/src/helpers/display.ts b/cli/src/helpers/display.ts index 13a189c938..2ffb87d67b 100644 --- a/cli/src/helpers/display.ts +++ b/cli/src/helpers/display.ts @@ -1,33 +1,72 @@ -import { cli } from 'cli-ux'; -import chalk from 'chalk'; -import { NameValueObj } from '../Types'; - -export function displayHeader(caption: string, placeholderSign: string = '_', size: number = 50) { - let singsPerSide: number = Math.floor((size - (caption.length + 2)) / 2); - let finalStr: string = ''; - for (let i = 0; i < singsPerSide; ++i) finalStr += placeholderSign; - finalStr += ` ${ caption} `; - while (finalStr.length < size) finalStr += placeholderSign; - - process.stdout.write("\n" + chalk.bold.blueBright(finalStr) + "\n\n"); +import { cli, Table } from 'cli-ux' +import chalk from 'chalk' +import { NameValueObj } from '../Types' +import { AccountId } from '@polkadot/types/interfaces' + +export function displayHeader(caption: string, placeholderSign = '_', size = 50) { + const singsPerSide: number = Math.floor((size - (caption.length + 2)) / 2) + let finalStr = '' + for (let i = 0; i < singsPerSide; ++i) finalStr += placeholderSign + finalStr += ` ${caption} ` + while (finalStr.length < size) finalStr += placeholderSign + + process.stdout.write('\n' + chalk.bold.blueBright(finalStr) + '\n\n') } export function displayNameValueTable(rows: NameValueObj[]) { - cli.table( - rows, - { - name: { minWidth: 30, get: row => chalk.bold.white(row.name) }, - value: { get: row => chalk.white(row.value) } - }, - { 'no-header': true } - ); + cli.table( + rows, + { + name: { minWidth: 30, get: (row) => chalk.bold.white(row.name) }, + value: { get: (row) => chalk.white(row.value) }, + }, + { 'no-header': true } + ) +} + +export function displayCollapsedRow(row: { [k: string]: string | number }) { + const collapsedRow: NameValueObj[] = Object.keys(row).map((name) => ({ + name, + value: typeof row[name] === 'string' ? (row[name] as string) : row[name].toString(), + })) + + displayNameValueTable(collapsedRow) +} + +export function displayCollapsedTable(rows: { [k: string]: string | number }[]) { + for (const row of rows) displayCollapsedRow(row) +} + +export function displayTable(rows: { [k: string]: string | number }[], cellHorizontalPadding = 0) { + if (!rows.length) { + return + } + const maxLength = (columnName: string) => + rows.reduce((maxLength, row) => { + const val = row[columnName] + const valLength = typeof val === 'string' ? val.length : val.toString().length + return Math.max(maxLength, valLength) + }, columnName.length) + const columnDef = (columnName: string) => ({ + get: (row: typeof rows[number]) => chalk.white(`${row[columnName]}`), + minWidth: maxLength(columnName) + cellHorizontalPadding, + }) + const columns: Table.table.Columns<{ [k: string]: string }> = {} + Object.keys(rows[0]).forEach((columnName) => (columns[columnName] = columnDef(columnName))) + cli.table(rows, columns) } export function toFixedLength(text: string, length: number, spacesOnLeft = false): string { - if (text.length > length && length > 3) { - return text.slice(0, length-3) + '...'; - } - while(text.length < length) { spacesOnLeft ? text = ' '+text : text += ' ' }; + if (text.length > length && length > 3) { + return text.slice(0, length - 3) + '...' + } + while (text.length < length) { + spacesOnLeft ? (text = ' ' + text) : (text += ' ') + } + + return text +} - return text; +export function shortAddress(address: AccountId | string): string { + return address.toString().substr(0, 6) + '...' + address.toString().substr(-6) } diff --git a/cli/src/helpers/promptOptions.ts b/cli/src/helpers/promptOptions.ts new file mode 100644 index 0000000000..63041f6dee --- /dev/null +++ b/cli/src/helpers/promptOptions.ts @@ -0,0 +1,30 @@ +import { ApiParamsOptions, ApiMethodNamedArgs, ApiParamOptions, ApiMethodArg } from '../Types' +import { Validator } from 'inquirer' + +export function setDefaults(promptOptions: ApiParamsOptions, defaultValues: ApiMethodNamedArgs) { + for (const defaultValue of defaultValues) { + const { name: paramName, value: paramValue } = defaultValue + const paramOptions = promptOptions[paramName] + if (paramOptions && paramOptions.value) { + paramOptions.value.default = paramValue + } else if (paramOptions) { + promptOptions[paramName].value = { default: paramValue } + } else { + promptOptions[paramName] = { value: { default: paramValue } } + } + } +} + +// Temporary(?) helper for easier creation of common ApiParamOptions +export function createParamOptions( + forcedName?: string, + defaultValue?: ApiMethodArg | undefined, + validator?: Validator +): ApiParamOptions { + const paramOptions: ApiParamOptions = { forcedName, validator } + if (defaultValue) { + paramOptions.value = { default: defaultValue } + } + + return paramOptions +} diff --git a/cli/src/helpers/validation.ts b/cli/src/helpers/validation.ts index cce907b13d..0cd9c1f67a 100644 --- a/cli/src/helpers/validation.ts +++ b/cli/src/helpers/validation.ts @@ -1,19 +1,19 @@ -import BN from 'bn.js'; -import ExitCodes from '../ExitCodes'; -import { decodeAddress } from '@polkadot/util-crypto'; -import { DerivedBalances } from '@polkadot/api-derive/types'; -import { CLIError } from '@oclif/errors'; +import BN from 'bn.js' +import ExitCodes from '../ExitCodes' +import { decodeAddress } from '@polkadot/util-crypto' +import { DerivedBalances } from '@polkadot/api-derive/types' +import { CLIError } from '@oclif/errors' -export function validateAddress(address: string, errorMessage: string = 'Invalid address'): void { - try { - decodeAddress(address); - } catch (e) { - throw new CLIError(errorMessage, { exit: ExitCodes.InvalidInput }); - } +export function validateAddress(address: string, errorMessage = 'Invalid address'): void { + try { + decodeAddress(address) + } catch (e) { + throw new CLIError(errorMessage, { exit: ExitCodes.InvalidInput }) + } } export function checkBalance(accBalances: DerivedBalances, requiredBalance: BN): void { - if (requiredBalance.gt(accBalances.availableBalance)) { - throw new CLIError('Not enough balance available', { exit: ExitCodes.InvalidInput }); - } + if (requiredBalance.gt(accBalances.availableBalance)) { + throw new CLIError('Not enough balance available', { exit: ExitCodes.InvalidInput }) + } } diff --git a/cli/src/index.ts b/cli/src/index.ts index 4caa481eee..088abcf778 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -1 +1 @@ -export {run} from '@oclif/command' +export { run } from '@oclif/command' diff --git a/cli/src/promptOptions/addWorkerOpening.ts b/cli/src/promptOptions/addWorkerOpening.ts new file mode 100644 index 0000000000..4b901a01b6 --- /dev/null +++ b/cli/src/promptOptions/addWorkerOpening.ts @@ -0,0 +1,57 @@ +import { ApiParamsOptions, ApiParamOptions, HRTStruct } from '../Types' +import { + OpeningType, + SlashingTerms, + UnslashableTerms, + OpeningType_Worker as OpeningTypeWorker, + WorkingGroupOpeningPolicyCommitment, +} from '@joystream/types/working-group' +import { Bytes } from '@polkadot/types' +import { schemaValidator } from '@joystream/types/hiring' + +class OpeningPolicyCommitmentOptions implements ApiParamsOptions { + [paramName: string]: ApiParamOptions + public role_slashing_terms: ApiParamOptions = { + value: { + default: SlashingTerms.create('Unslashable', new UnslashableTerms()), + locked: true, + }, + } + // Rename fields containing "curator" (solivg minor UI issue related to flat namespace) + public terminate_curator_application_stake_unstaking_period: ApiParamOptions = { + forcedName: 'terminate_application_stake_unstaking_period', + } + public terminate_curator_role_stake_unstaking_period: ApiParamOptions = { + forcedName: 'terminate_role_stake_unstaking_period', + } + public exit_curator_role_application_stake_unstaking_period: ApiParamOptions = { + forcedName: 'exit_role_application_stake_unstaking_period', + } + public exit_curator_role_stake_unstaking_period: ApiParamOptions = { + forcedName: 'exit_role_stake_unstaking_period', + } +} + +class AddWrokerOpeningOptions implements ApiParamsOptions { + [paramName: string]: ApiParamOptions + // Lock value for opening_type + public opening_type: ApiParamOptions = { + value: { + default: OpeningType.create('Worker', new OpeningTypeWorker()), + locked: true, + }, + } + // Json schema for human_readable_text + public human_readable_text: ApiParamOptions = { + jsonSchema: { + schemaValidator, + struct: HRTStruct, + }, + } + // Lock value for role_slashing_terms + public commitment: ApiParamOptions = { + nestedOptions: new OpeningPolicyCommitmentOptions(), + } +} + +export default AddWrokerOpeningOptions diff --git a/cli/src/validators/common.ts b/cli/src/validators/common.ts new file mode 100644 index 0000000000..acafc33edc --- /dev/null +++ b/cli/src/validators/common.ts @@ -0,0 +1,51 @@ +// +// Validators for console input +// (usable with inquirer package) +// + +type Validator = (value: any) => boolean | string + +export const isInt = (message?: string) => (value: any) => + (typeof value === 'number' && Math.floor(value) === value) || + (typeof value === 'string' && parseInt(value).toString() === value) + ? true + : message || 'The value must be an integer!' + +export const gte = (min: number, message?: string) => (value: any) => + parseFloat(value) >= min + ? true + : message?.replace('{min}', min.toString()) || `The value must be a number greater than or equal ${min}` + +export const lte = (max: number, message?: string) => (value: any) => + parseFloat(value) <= max + ? true + : message?.replace('{max}', max.toString()) || `The value must be less than or equal ${max}` + +export const minLen = (min: number, message?: string) => (value: any) => + typeof value === 'string' && value.length >= min + ? true + : message?.replace('{min}', min.toString()) || `The value should be at least ${min} character(s) long` + +export const maxLen = (max: number, message?: string) => (value: any) => + typeof value === 'string' && value.length <= max + ? true + : message?.replace('{max}', max.toString()) || `The value cannot be more than ${max} character(s) long` + +export const combined = (validators: Validator[], message?: string) => (value: any) => { + for (const validator of validators) { + const result = validator(value) + if (result !== true) { + return message || result + } + } + + return true +} + +export const positiveInt = (message?: string) => combined([isInt(), gte(0)], message) + +export const minMaxInt = (min: number, max: number, message?: string) => + combined([isInt(), gte(min), lte(max)], message?.replace('{min}', min.toString()).replace('{max}', max.toString())) + +export const minMaxStr = (min: number, max: number, message?: string) => + combined([minLen(min), maxLen(max)], message?.replace('{min}', min.toString()).replace('{max}', max.toString())) diff --git a/cli/test/commands/council/info.test.ts b/cli/test/commands/council/info.test.ts index 2d455f9eb6..70bdfe8b8b 100644 --- a/cli/test/commands/council/info.test.ts +++ b/cli/test/commands/council/info.test.ts @@ -1,11 +1,11 @@ -import {expect, test} from '@oclif/test' +import { expect, test } from '@oclif/test' describe('info', () => { test - .stdout() - .command(['council:info']) - .exit(0) - .it('displays "Council" string', ctx => { - expect(ctx.stdout).to.contain('Council') - }) + .stdout() + .command(['council:info']) + .exit(0) + .it('displays "Council" string', (ctx) => { + expect(ctx.stdout).to.contain('Council') + }) }) diff --git a/cli/test/tsconfig.json b/cli/test/tsconfig.json index 95898fcedf..460cf368f7 100644 --- a/cli/test/tsconfig.json +++ b/cli/test/tsconfig.json @@ -3,7 +3,5 @@ "compilerOptions": { "noEmit": true }, - "references": [ - {"path": ".."} - ] + "references": [{ "path": ".." }] } diff --git a/cli/tsconfig.json b/cli/tsconfig.json index c6477fa01e..0735d6c692 100644 --- a/cli/tsconfig.json +++ b/cli/tsconfig.json @@ -7,7 +7,9 @@ "rootDir": "src", "strict": true, "target": "es2017", - "esModuleInterop": true + "esModuleInterop": true, + "types" : [ "node" ], + "noUnusedLocals": true }, "include": [ "src/**/*" diff --git a/devops/.eslintrc.js b/devops/.eslintrc.js new file mode 100644 index 0000000000..99979c2841 --- /dev/null +++ b/devops/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + node: true, + }, +} diff --git a/devops/ansible/build-and-run-tests-single-node-playbook.yml b/devops/ansible/build-and-run-tests-single-node-playbook.yml new file mode 100644 index 0000000000..61f60d6e38 --- /dev/null +++ b/devops/ansible/build-and-run-tests-single-node-playbook.yml @@ -0,0 +1,17 @@ +- hosts: 127.0.0.1 + user: root + become: yes + become_method: sudo + + tasks: + - name: install dependencies + include_role: + name: install_dependencies + + - name: build node + include_role: + name: build_docker_image + + - name: run tests + include_role: + name: run_tests_single_node diff --git a/devops/ansible/build-and-run-tests-two-nodes-playbook.yml b/devops/ansible/build-and-run-tests-two-nodes-playbook.yml new file mode 100644 index 0000000000..cc90c3529a --- /dev/null +++ b/devops/ansible/build-and-run-tests-two-nodes-playbook.yml @@ -0,0 +1,17 @@ +- hosts: 127.0.0.1 + user: root + become: yes + become_method: sudo + + tasks: + - name: install dependencies + include_role: + name: install_dependencies + + - name: build node + include_role: + name: build_docker_image + + - name: run tests + include_role: + name: run_tests_two_nodes diff --git a/devops/ansible/build-image-playbook.yml b/devops/ansible/build-image-playbook.yml new file mode 100644 index 0000000000..b07a41ffe5 --- /dev/null +++ b/devops/ansible/build-image-playbook.yml @@ -0,0 +1,13 @@ +- hosts: 127.0.0.1 + user: root + become: yes + become_method: sudo + + tasks: + - name: install dependencies + include_role: + name: install_dependencies + + - name: build node + include_role: + name: build_docker_image \ No newline at end of file diff --git a/devops/ansible/docker-compose.yml b/devops/ansible/docker-compose.yml new file mode 100644 index 0000000000..7087792485 --- /dev/null +++ b/devops/ansible/docker-compose.yml @@ -0,0 +1,34 @@ +version: "3" +services: + node_alice: + image: joystream/node-testing + container_name: alice + entrypoint: ./node --chain=chainspec.json --alice --validator --ws-external --rpc-cors=all + ports: + - "30333:30333" + - "9933:9933" + - "9944:9944" + networks: + testing_net: + ipv4_address: 172.28.1.1 + + node_bob: + image: joystream/node-testing + container_name: bob + entrypoint: ./node --chain=chainspec.json --bob --ws-external --rpc-cors=all + ports: + - "30335:30333" + - "9935:9933" + - "9945:9944" + links: + - "node_alice:alice" + networks: + testing_net: + ipv4_address: 172.28.1.2 + +networks: + testing_net: + ipam: + driver: default + config: + - subnet: 172.28.0.0/16 diff --git a/devops/ansible/hosts b/devops/ansible/hosts new file mode 100644 index 0000000000..2be9cc2691 --- /dev/null +++ b/devops/ansible/hosts @@ -0,0 +1,2 @@ +[sites] +127.0.0.1 ansible_connection=local \ No newline at end of file diff --git a/devops/ansible/roles/build_docker_image/tasks/main.yml b/devops/ansible/roles/build_docker_image/tasks/main.yml new file mode 100644 index 0000000000..10d35d3d37 --- /dev/null +++ b/devops/ansible/roles/build_docker_image/tasks/main.yml @@ -0,0 +1,4 @@ +- name: create testing node docker image + shell: ./scripts/build-joystream-testing-node-docker-image.sh + args: + chdir: ../../ diff --git a/devops/ansible/roles/install_dependencies/tasks/main.yml b/devops/ansible/roles/install_dependencies/tasks/main.yml new file mode 100644 index 0000000000..28b70901e0 --- /dev/null +++ b/devops/ansible/roles/install_dependencies/tasks/main.yml @@ -0,0 +1,35 @@ +- name: install pip on Debian + block: + - name: install pip using apt + apt: name=python-pip state=present + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + +- name: install pip on Mac + block: + - name: create temporary folder + file: + path: ../../.tmp + state: directory + - name: get pip installer using curl + get_url: + url: https://bootstrap.pypa.io/get-pip.py + dest: ../../.tmp/get-pip.py + - name: install pip + shell: python ../../.tmp/get-pip.py + when: ansible_distribution == 'MacOSX' + always: + - name: remove pip installer script + file: + path: ../../.tmp/get-pip.py + state: absent + +- name: install docker + pip: name=docker + +- name: Install yarn with npm + npm: + name: yarn + global: yes + +- name: Install docker compose + pip: name=docker-compose diff --git a/devops/ansible/roles/run_tests_single_node/tasks/main.yml b/devops/ansible/roles/run_tests_single_node/tasks/main.yml new file mode 100644 index 0000000000..281d9cdcef --- /dev/null +++ b/devops/ansible/roles/run_tests_single_node/tasks/main.yml @@ -0,0 +1,22 @@ +- name: run network + block: + + - name: run docker container + docker_container: + name: "joystream-node-testing" + image: "joystream/node-testing" + ports: + - "9944:9944" + entrypoint: ./node --chain=chainspec.json --alice --validator --ws-external --rpc-cors=all + state: started + + - name: execute network tests + shell: yarn debug >> ../../.tmp/tests.log + args: + chdir: ../../tests/network-tests/ + + always: + - name: stop docker container + docker_container: + name: "joystream-node-testing" + state: absent diff --git a/devops/ansible/roles/run_tests_two_nodes/tasks/main.yml b/devops/ansible/roles/run_tests_two_nodes/tasks/main.yml new file mode 100644 index 0000000000..2d757add0b --- /dev/null +++ b/devops/ansible/roles/run_tests_two_nodes/tasks/main.yml @@ -0,0 +1,18 @@ +- name: run network + block: + + - name: run two nodes containerized network + docker_compose: + project_src: ./ + state: present + + - name: execute network tests + shell: yarn test >> ../../.tmp/tests.log + args: + chdir: ../../tests/network-tests/ + + always: + - name: stop containers + docker_compose: + project_src: ./ + state: absent diff --git a/devops/dockerfiles/ansible-node/Dockerfile b/devops/dockerfiles/ansible-node/Dockerfile new file mode 100644 index 0000000000..22074f1c88 --- /dev/null +++ b/devops/dockerfiles/ansible-node/Dockerfile @@ -0,0 +1,37 @@ +FROM joystream/rust-builder AS builder +LABEL description="Compiles all workspace artifacts" +WORKDIR /joystream +COPY . /joystream + +# Build joystream-node and its dependencies - runtime +RUN cargo build --release -p joystream-node +RUN /joystream/scripts/create-test-chainspec.sh + +FROM debian:stretch +LABEL description="Joystream node" +WORKDIR /joystream +COPY --from=builder /joystream/target/release/joystream-node /joystream/node +COPY --from=builder /joystream/target/release/wbuild/joystream-node-runtime/joystream_node_runtime.compact.wasm /joystream/runtime.compact.wasm +COPY --from=builder /joystream/.tmp/chainspec.json /joystream/chainspec.json + +# confirm it works +RUN /joystream/node --version + +# https://manpages.debian.org/stretch/coreutils/b2sum.1.en.html +# RUN apt-get install coreutils +# print the blake2 256 hash of the wasm blob +RUN b2sum -l 256 /joystream/runtime.compact.wasm +# print the blake2 512 hash of the wasm blob +RUN b2sum -l 512 /joystream/runtime.compact.wasm + +EXPOSE 30333 9933 9944 + +# Use these volumes to persits chain state and keystore, eg.: +# --base-path /data +# optionally separate keystore (otherwise it will be stored in the base path) +# --keystore-path /keystore +# if base-path isn't specified, chain state is stored inside container in ~/.local/share/joystream-node/ +# which is not ideal +VOLUME ["/data", "/keystore"] + +ENTRYPOINT ["/joystream/node"] diff --git a/devops/dockerfiles/node-and-runtime/Dockerfile b/devops/dockerfiles/node-and-runtime/Dockerfile index ed0a8c1ce3..405d46116f 100644 --- a/devops/dockerfiles/node-and-runtime/Dockerfile +++ b/devops/dockerfiles/node-and-runtime/Dockerfile @@ -3,7 +3,8 @@ LABEL description="Compiles all workspace artifacts" WORKDIR /joystream COPY . /joystream -RUN cargo build --release +# Build joystream-node and its dependencies - runtime +RUN cargo build --release -p joystream-node FROM debian:stretch LABEL description="Joystream node" @@ -14,6 +15,13 @@ COPY --from=builder /joystream/target/release/wbuild/joystream-node-runtime/joys # confirm it works RUN /joystream/node --version +# https://manpages.debian.org/stretch/coreutils/b2sum.1.en.html +# RUN apt-get install coreutils +# print the blake2 256 hash of the wasm blob +RUN b2sum -l 256 /joystream/runtime.compact.wasm +# print the blake2 512 hash of the wasm blob +RUN b2sum -l 512 /joystream/runtime.compact.wasm + EXPOSE 30333 9933 9944 # Use these volumes to persits chain state and keystore, eg.: diff --git a/devops/dockerfiles/rust-builder/Dockerfile b/devops/dockerfiles/rust-builder/Dockerfile index 26e35b69ab..c6e0d9283f 100644 --- a/devops/dockerfiles/rust-builder/Dockerfile +++ b/devops/dockerfiles/rust-builder/Dockerfile @@ -1,4 +1,4 @@ -FROM liuchong/rustup:1.42.0 AS builder +FROM liuchong/rustup:1.43.0 AS builder LABEL description="Rust and WASM build environment for joystream and substrate" WORKDIR /setup diff --git a/devops/eslint-config/index.js b/devops/eslint-config/index.js new file mode 100644 index 0000000000..cd0e0e8cb8 --- /dev/null +++ b/devops/eslint-config/index.js @@ -0,0 +1,78 @@ +// This config is used globally at the root of the repo, so it should be as thin +// as possible with rules that we absolutely require across all projects. +module.exports = { + env: { + es6: true, + }, + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly', + }, + // We are relying on version that comes with @polkadot/dev + // Newest version is breaking pioneer! + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 2019, + sourceType: 'module', + }, + extends: [ + 'standard', + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + // this is only in newer versions of eslint-plugin-react-hooks + // 'plugin:react-hooks/recommended', + 'plugin:prettier/recommended', + 'prettier/@typescript-eslint', + 'prettier/react', + 'prettier/standard', + ], + settings: { + react: { + version: 'detect', + }, + }, + rules: { + // drop these when using newer versions of eslint-plugin-react-hooks + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + // only cli projects should really have this rule, web apps + // should prefer using 'debug' package at least to allow control of + // output verbosity if logging to console. + 'no-console': 'off', + 'camelcase': 'off', + '@typescript-eslint/class-name-casing': 'off', + "@typescript-eslint/naming-convention": [ + "error", + { + selector: 'default', + format: ['camelCase'], + }, + { + selector: 'variable', + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + }, + { + selector: 'property', + format: [] // Don't force format of object properties, so they can be ie.: { "Some thing": 123 }, { some_thing: 123 } etc. + }, + { + selector: 'accessor', + format: ['camelCase', 'snake_case'] + }, + { + selector: 'enumMember', + format: ['PascalCase'] + }, + { + selector: 'typeLike', + format: [], + custom: { regex: '^([A-Z][a-z0-9]*_?)+', match: true }, // combined PascalCase and snake_case to allow ie. OpeningType_Worker + } + ], + }, + plugins: ['standard', '@typescript-eslint', 'react', 'react-hooks', 'prettier'], +} diff --git a/devops/eslint-config/package.json b/devops/eslint-config/package.json new file mode 100644 index 0000000000..6d3d79eabe --- /dev/null +++ b/devops/eslint-config/package.json @@ -0,0 +1,35 @@ +{ + "name": "@joystream/eslint-config", + "version": "1.0.0", + "description": "joystream eslint shared config", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/joystream/joystream.git" + }, + "author": "Joystream contributors", + "license": "MIT", + "bugs": { + "url": "https://github.com/joystream/joystream/issues" + }, + "homepage": "https://github.com/joystream/joystream#readme", + "peerDependencies": { + "eslint": "^7.6.0" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": "3.8.0", + "@typescript-eslint/parser": "3.8.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-prettier": "^3.1.3", + "eslint-plugin-react": "^7.20.5", + "eslint-plugin-react-hooks": "^4.0.8", + "eslint-config-standard": "^14.1.1", + "eslint-plugin-standard": "^4.0.1", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-node": "^11.1.0" + } +} diff --git a/devops/git-hooks/pre-push b/devops/git-hooks/pre-push index b9ffbcb184..553608c38a 100755 --- a/devops/git-hooks/pre-push +++ b/devops/git-hooks/pre-push @@ -1,10 +1,10 @@ #!/bin/sh set -e -export BUILD_DUMMY_WASM_BINARY=1 +echo '+cargo test --release --all' +BUILD_DUMMY_WASM_BINARY=1 cargo test --all + +echo '+cargo clippy --release --all -- -D warnings' +BUILD_DUMMY_WASM_BINARY=1 cargo clippy --all -- -D warnings -echo '+cargo test --all' -cargo test --all -echo '+cargo clippy --all -- -D warnings' -cargo clippy --all -- -D warnings \ No newline at end of file diff --git a/devops/prettier-config/index.js b/devops/prettier-config/index.js new file mode 100644 index 0000000000..2a4aa73a90 --- /dev/null +++ b/devops/prettier-config/index.js @@ -0,0 +1,9 @@ +module.exports = { + singleQuote: true, + arrowParens: 'always', + useTabs: false, + tabWidth: 2, + semi: false, + trailingComma: 'es5', + quoteProps: 'preserve' +} diff --git a/devops/prettier-config/package.json b/devops/prettier-config/package.json new file mode 100644 index 0000000000..5233dd5fb7 --- /dev/null +++ b/devops/prettier-config/package.json @@ -0,0 +1,22 @@ +{ + "name": "@joystream/prettier-config", + "version": "1.0.0", + "description": "joystream prettier shared config", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/joystream/joystream.git" + }, + "author": "Joystream contributors", + "license": "MIT", + "bugs": { + "url": "https://github.com/joystream/joystream/issues" + }, + "homepage": "https://github.com/joystream/joystream#readme", + "peerDependencies": { + "prettier": ">= 2" + } +} diff --git a/devops/vscode/settings.json b/devops/vscode/settings.json new file mode 100644 index 0000000000..38ac1f6e46 --- /dev/null +++ b/devops/vscode/settings.json @@ -0,0 +1,10 @@ +{ + "eslint.workingDirectories": [ + "./cli", + "./pioneer", + "./tests/network-tests", + "./types", + "./storage-node", + "./atlas" + ] +} diff --git a/devops/vstore/classes.json b/devops/vstore/classes.json new file mode 100644 index 0000000000..ffb54d5347 --- /dev/null +++ b/devops/vstore/classes.json @@ -0,0 +1,766 @@ +[ + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x4d65646961204f626a656374", + "0x436c61737320666f72207265736f6c76696e67206120636f6e74656e7420656e7469747920746f20616e2061637475616c206d656469612066696c65206f72206c696e6b2e", + { + "entity_permissions": { "update": [0, 1, 2], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0, 1, 2], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x4c616e6775616765", + "0x436c61737320666f722073657474696e67206c616e67756167652e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x436f6e74656e74204c6963656e7365", + "0x436c61737320666f722073706563696679696e6720746865206c6963656e736520756e64657220776869636820636f6e74656e74206973207075626c69736865642e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x5075626c69636174696f6e20537461747573", + "0x436c61737320666f722073657474696e6720746865207075626c69636174696f6e20737461747573206f66206120636f6e74656e7420656e746974792e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x4375726174696f6e20537461747573", + "0x436c61737320666f722063757261746f727320746f2073657420746865207075626c69636174696f6e20737461747573206f66206120636f6e74656e7420656e746974792e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x466561747572656420436f6e74656e74", + "0x436c61737320666f722073657474696e6720666561747572656420636f6e74656e74206f6e2074686520706c6174666f726d2e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x566964656f", + "0x436c61737320666f722067656e6572616c20766964656f73206e6f742061737369676e61626c6520746f2061206d6f726520737065636966696320766964656f20636f6e74656e7420747970652e", + { + "entity_permissions": { "update": [0, 1, 2], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0, 1, 2], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x566964656f2043617465676f7279", + "0x436c61737320666f722073657474696e67207468652063617465676f727920666f7220766964656f7320696e2074686520566964656f20636c6173732e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x4d7573696320416c62756d", + "0x416e20616c62756d206973206120636f6c6c656374696f6e206f6620747261636b73206f7220617564696f207265636f7264696e67732e20557375616c6c7920627920612073696e676c6520617274697374206f722067726f75702e", + { + "entity_permissions": { "update": [0, 1, 2], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0, 1, 2], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x4d757369632047656e7265", + "0x436c61737320666f722073657474696e67207468652067656e726520666f72206d757369632e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x4d75736963204d6f6f64", + "0x436c61737320666f722073657474696e6720746865206d6f6f647320666f72206d757369632e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x4d75736963205468656d65", + "0x436c61737320666f722073657474696e6720746865207468656d657320666f72206d757369632e", + { + "entity_permissions": { "update": [0], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "createClass", + "args": [ + "0x4d7573696320547261636b", + "0x4120747261636b20697320616e20696e646976696475616c20736f6e67206f7220696e737472756d656e74616c207265636f7264696e672e", + { + "entity_permissions": { "update": [0, 1, 2], "maintainer_has_all_permissions": true }, + "entities_can_be_created": true, + "add_schemas": [0], + "create_entities": [0, 1, 2], + "reference_constraint": { "NoConstraint": null }, + "admins": [0], + "last_permissions_update": 0 + } + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 1, + [], + [ + { + "prop_type": { "Text": 48 }, + "required": true, + "name": "Value", + "description": "ContentId of object in the data directory" + }, + { + "prop_type": { "Uint64": null }, + "required": false, + "name": "Channel Id", + "description": "Id of the channel this media object is published under." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 2, + [], + [ + { + "prop_type": { "Text": 2 }, + "required": true, + "name": "Value", + "description": "Language code following the ISO 639-1 two letter standard." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 3, + [], + [ + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Value", + "description": "The license of which the content is originally published under." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 4, + [], + [ + { + "prop_type": { "Text": 50 }, + "required": true, + "name": "Value", + "description": "The publication status of the content in the content directory." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 5, + [], + [ + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Value", + "description": "The curator publication status of the content in the content directory." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 6, + [], + [ + { + "prop_type": { "Internal": 7 }, + "required": false, + "name": "Top Video", + "description": "The video that has the most prominent position(s) on the platform." + }, + { + "prop_type": { "InternalVec": [12, 7] }, + "required": false, + "name": "Featured Videos", + "description": "Videos featured in the Video tab." + }, + { + "prop_type": { "InternalVec": [12, 9] }, + "required": false, + "name": "Featured Albums", + "description": "Music albums featured in the Music tab." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 7, + [], + [ + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Title", + "description": "The title of the video" + }, + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Thumbnail", + "description": "URL to video thumbnail: NOTE: Should be an https link to an image of ratio 16:9, ideally 1280 pixels wide by 720 pixels tall, with a minimum width of 640 pixels, in JPEG or PNG format." + }, + { + "prop_type": { "Text": 4000 }, + "required": true, + "name": "Description", + "description": "Information about the video." + }, + { + "prop_type": { "Internal": 2 }, + "required": true, + "name": "Language", + "description": "The main language used in the video." + }, + { + "prop_type": { "Int64": null }, + "required": true, + "name": "First Released", + "description": "When the video was first released" + }, + { + "prop_type": { "Internal": 8 }, + "required": false, + "name": "Category", + "description": "The category of the video." + }, + { + "prop_type": { "TextVec": [5, 255] }, + "required": false, + "name": "Links", + "description": "Links to the creators pages." + }, + { + "prop_type": { "Internal": 1 }, + "required": false, + "name": "Object", + "description": "The entityId of the object in the data directory." + }, + { + "prop_type": { "Internal": 4 }, + "required": true, + "name": "Publication Status", + "description": "The publication status of the video." + }, + { + "prop_type": { "Internal": 5 }, + "required": false, + "name": "Curation Status", + "description": "The publication status of the video set by the a content curator on the platform." + }, + { + "prop_type": { "Bool": null }, + "required": true, + "name": "Explicit", + "description": "Indicates whether the video contains explicit material." + }, + { + "prop_type": { "Internal": 3 }, + "required": true, + "name": "License", + "description": "The license of which the video is released under." + }, + { + "prop_type": { "Text": 255 }, + "required": false, + "name": "Attribution", + "description": "If the License requires attribution, add this here." + }, + { + "prop_type": { "Uint64": null }, + "required": false, + "name": "Channel Id", + "description": "Id of the channel this video is published under." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 8, + [], + [ + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Value", + "description": "Categories for videos." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 9, + [], + [ + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Title", + "description": "The title of the album" + }, + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Artist", + "description": "The artist, composer, band or group that published the album." + }, + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Thumbnail", + "description": "URL to album cover art thumbnail: NOTE: Should be an https link to a square image, between 1400x1400 and 3000x3000 pixels, in JPEG or PNG format." + }, + { + "prop_type": { "Text": 4000 }, + "required": true, + "name": "Description", + "description": "Information about the album and artist." + }, + { + "prop_type": { "Int64": null }, + "required": true, + "name": "First Released", + "description": "When the album was first released" + }, + { + "prop_type": { "InternalVec": [3, 10] }, + "required": false, + "name": "Genre", + "description": "The genre(s) of the album." + }, + { + "prop_type": { "InternalVec": [3, 11] }, + "required": false, + "name": "Mood", + "description": "The mood(s) of the album." + }, + { + "prop_type": { "InternalVec": [3, 12] }, + "required": false, + "name": "Theme", + "description": "The theme(s) of the album." + }, + { + "prop_type": { "InternalVec": [100, 13] }, + "required": false, + "name": "Tracks", + "description": "The tracks of the album." + }, + { + "prop_type": { "Internal": 2 }, + "required": false, + "name": "Language", + "description": "The language of the song lyrics in the album." + }, + { + "prop_type": { "TextVec": [5, 255] }, + "required": false, + "name": "Links", + "description": "Links to the artist or album site, or social media pages." + }, + { + "prop_type": { "Text": 255 }, + "required": false, + "name": "Lyrics", + "description": "Link to the album tracks lyrics." + }, + { + "prop_type": { "Text": 255 }, + "required": false, + "name": "Composer or songwriter", + "description": "The composer(s) and/or songwriter(s) of the album." + }, + { + "prop_type": { "TextVec": [5, 255] }, + "required": false, + "name": "Reviews", + "description": "Links to reviews of the album." + }, + { + "prop_type": { "Internal": 4 }, + "required": true, + "name": "Publication Status", + "description": "The publication status of the album." + }, + { + "prop_type": { "Internal": 5 }, + "required": false, + "name": "Curation Status", + "description": "The publication status of the album set by the a content curator on the platform." + }, + { + "prop_type": { "Bool": null }, + "required": true, + "name": "Explicit", + "description": "Indicates whether the album contains explicit material." + }, + { + "prop_type": { "Internal": 3 }, + "required": true, + "name": "License", + "description": "The license of which the album is released under." + }, + { + "prop_type": { "Text": 255 }, + "required": false, + "name": "Attribution", + "description": "If the License requires attribution, add this here." + }, + { + "prop_type": { "Uint64": null }, + "required": false, + "name": "Channel Id", + "description": "Id of the channel this album is published under." + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 10, + [], + [{ "prop_type": { "Text": 100 }, "required": true, "name": "Value", "description": "Genres for music." }] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 11, + [], + [{ "prop_type": { "Text": 100 }, "required": true, "name": "Value", "description": "Moods for music." }] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 12, + [], + [{ "prop_type": { "Text": 100 }, "required": true, "name": "Value", "description": "Themes for music." }] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "addClassSchema", + "args": [ + 0, + 13, + [], + [ + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Title", + "description": "The title of the track" + }, + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Artist", + "description": "The artist, composer, band or group that published the track." + }, + { + "prop_type": { "Text": 255 }, + "required": true, + "name": "Thumbnail", + "description": "URL to track cover art: NOTE: Should be an https link to a square image, between 1400x1400 and 3000x3000 pixels, in JPEG or PNG format." + }, + { + "prop_type": { "Text": 255 }, + "required": false, + "name": "Description", + "description": "Information about the track." + }, + { + "prop_type": { "Internal": 2 }, + "required": false, + "name": "Language", + "description": "The language of the lyrics in the track." + }, + { + "prop_type": { "Int64": null }, + "required": true, + "name": "First Released", + "description": "When the track was first released" + }, + { + "prop_type": { "Internal": 10 }, + "required": false, + "name": "Genre", + "description": "The genre of the track." + }, + { + "prop_type": { "Internal": 11 }, + "required": false, + "name": "Mood", + "description": "The mood of the track." + }, + { + "prop_type": { "Internal": 12 }, + "required": false, + "name": "Theme", + "description": "The theme of the track." + }, + { + "prop_type": { "TextVec": [5, 255] }, + "required": false, + "name": "Links", + "description": "Links to the artist site or social media pages." + }, + { + "prop_type": { "Text": 255 }, + "required": false, + "name": "Composer or songwriter", + "description": "The composer(s) and/or songwriter(s) of the track." + }, + { + "prop_type": { "Text": 255 }, + "required": false, + "name": "Lyrics", + "description": "Link to the track lyrics." + }, + { + "prop_type": { "Internal": 1 }, + "required": false, + "name": "Object", + "description": "The entityId of the object in the data directory." + }, + { + "prop_type": { "Internal": 4 }, + "required": true, + "name": "Publication Status", + "description": "The publication status of the track." + }, + { + "prop_type": { "Internal": 5 }, + "required": false, + "name": "Curation Status", + "description": "The publication status of the track set by the a content curator on the platform." + }, + { + "prop_type": { "Bool": null }, + "required": true, + "name": "Explicit", + "description": "Indicates whether the track contains explicit material." + }, + { + "prop_type": { "Internal": 3 }, + "required": true, + "name": "License", + "description": "The license of which the track is released under." + }, + { + "prop_type": { "Text": 255 }, + "required": false, + "name": "Attribution", + "description": "If the License requires attribution, add this here." + }, + { + "prop_type": { "Uint64": null }, + "required": false, + "name": "Channel Id", + "description": "Id of the channel this track is published under." + } + ] + ] + } +] diff --git a/devops/vstore/entities.json b/devops/vstore/entities.json new file mode 100644 index 0000000000..db326ca633 --- /dev/null +++ b/devops/vstore/entities.json @@ -0,0 +1,12694 @@ +[ + { + "sectionName": "versionedStorePermissions", + "methodName": "transaction", + "args": [ + [ + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 2 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 4 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 4 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 5 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 5 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 5 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 5 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 3 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 3 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 3 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 3 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 0 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "aa" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 1 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ab" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 2 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ae" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 3 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "af" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 4 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ak" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 5 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "am" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 6 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "an" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 7 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ar" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 8 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "as" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 9 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "av" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 10 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ay" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 11 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "az" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 12 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ba" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 13 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "be" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 14 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "bg" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 15 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "bh" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 16 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "bi" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 17 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "bm" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 18 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "bn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 19 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "bo" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 20 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "br" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 21 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "bs" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 22 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ca" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 23 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ce" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 24 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ch" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 25 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "co" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 26 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "cr" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 27 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "cs" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 28 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "cu" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 29 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "cv" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 30 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "cy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 31 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "da" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 32 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "de" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 33 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "dv" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 34 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "dz" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 35 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ee" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 36 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "el" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 37 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "en" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 38 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "eo" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 39 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "es" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 40 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "et" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 41 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "eu" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 42 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "fa" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 43 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ff" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 44 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "fi" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 45 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "fj" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 46 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "fo" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 47 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "fr" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 48 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "fy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 49 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ga" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 50 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "gd" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 51 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "gl" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 52 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "gn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 53 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "gu" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 54 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "gv" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 55 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ha" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 56 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "he" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 57 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "hi" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 58 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ho" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 59 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "hr" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 60 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ht" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 61 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "hu" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 62 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "hy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 63 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "hz" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 64 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ia" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 65 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "id" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 66 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ie" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 67 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ig" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 68 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ii" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 69 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ik" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 70 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "io" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 71 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "is" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 72 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "it" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 73 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "iu" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 74 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ja" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 75 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "jv" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 76 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ka" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 77 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "kg" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 78 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ki" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 79 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "kj" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 80 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "kk" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 81 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "kl" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 82 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "km" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 83 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "kn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 84 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ko" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 85 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "kr" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 86 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ks" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 87 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ku" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 88 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "kv" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 89 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "kw" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 90 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ky" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 91 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "la" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 92 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "lb" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 93 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "lg" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 94 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "li" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 95 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ln" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 96 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "lo" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 97 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "lt" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 98 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "lu" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 99 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "lv" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 100 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "mg" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 101 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "mh" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 102 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "mi" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 103 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "mk" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 104 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ml" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 105 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "mn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 106 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "mr" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 107 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ms" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 108 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "mt" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 109 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "my" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 110 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "na" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 111 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "nb" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 112 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "nd" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 113 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ne" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 114 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ng" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 115 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "nl" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 116 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "nn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 117 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "no" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 118 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "nr" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 119 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "nv" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 120 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ny" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 121 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "oc" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 122 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "oj" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 123 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "om" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 124 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "or" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 125 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "os" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 126 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "pa" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 127 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "pi" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 128 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "pl" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 129 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ps" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 130 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "pt" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 131 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "qu" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 132 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "rm" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 133 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "rn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 134 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ro" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 135 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ru" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 136 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "rw" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 137 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sa" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 138 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sc" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 139 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sd" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 140 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "se" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 141 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sg" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 142 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "si" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 143 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sk" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 144 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sl" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 145 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sm" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 146 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 147 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "so" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 148 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sq" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 149 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sr" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 150 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ss" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 151 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "st" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 152 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "su" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 153 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sv" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 154 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "sw" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 155 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ta" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 156 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "te" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 157 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "tg" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 158 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "th" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 159 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ti" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 160 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "tk" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 161 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "tl" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 162 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "tn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 163 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "to" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 164 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "tr" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 165 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ts" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 166 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "tt" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 167 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "tw" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 168 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ty" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 169 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ug" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 170 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "uk" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 171 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ur" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 172 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "uz" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 173 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "ve" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 174 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "vi" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 175 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "vo" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 176 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "wa" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 177 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "wo" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 178 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "xh" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 179 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "yi" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 180 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "yo" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 181 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "za" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 182 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "zh" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 183 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "zu" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 184 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Public" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 185 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Unlisted" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 186 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Edited" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 187 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Updated schema" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 188 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Under review" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 189 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Removed" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 190 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Public Domain" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 191 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Original content" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 192 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Creative Commons (attribution required)" } } + } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 193 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Fair Use (attribution required)" } } + } + ] + } + } + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "transaction", + "args": [ + [ + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 8 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 0 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Film & Animation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 1 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Autos & Vehicles" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 2 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Music" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 3 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pets & Animals" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 4 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sports" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 5 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Travel & Events" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 6 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gaming" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 7 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "People & Blogs" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 8 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Comedy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 9 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Entertainment" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 10 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "News & Politics" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 11 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Howto & Style" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 12 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Education" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 13 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Science & Technology" } } + } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 14 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Nonprofits & Activism" } } + } + ] + } + } + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "transaction", + "args": [ + [ + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 10 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 0 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Avant-Garde" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 1 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Blues" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 2 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Children's" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 3 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Classical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 4 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Comedy/Spoken" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 5 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Country" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 6 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Easy Listening" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 7 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Electronic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 8 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Folk" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 9 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Holiday" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 10 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "International" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 11 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Jazz" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 12 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Latin" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 13 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "New Age" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 14 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pop/Rock" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 15 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "R&B" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 16 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rap" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 17 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reggae" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 18 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Religious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 19 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Stage & Screen" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 20 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Vocal" } } } + ] + } + } + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "transaction", + "args": [ + [ + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 11 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 0 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Acerbic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 1 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Aggressive" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 2 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Agreeable" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 3 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Airy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 4 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ambitious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 5 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Amiable/Good-Natured" } } + } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 6 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Angry" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 7 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Angst-Ridden" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 8 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Anguished/Distraught" } } + } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 9 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Angular" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 10 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Animated" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 11 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Apocalyptic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 12 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Arid" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 13 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Athletic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 14 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Atmospheric" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 15 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Austere" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 16 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Autumnal" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 17 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Belligerent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 18 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Benevolent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 19 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bitter" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 20 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bittersweet" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 21 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bleak" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 22 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Boisterous" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 23 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bombastic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 24 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Brash" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 25 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Brassy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 26 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bravado" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 27 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bright" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 28 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Brittle" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 29 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Brooding" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 30 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Calm/Peaceful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 31 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Campy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 32 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Capricious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 33 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Carefree" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 34 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cartoonish" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 35 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cathartic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 36 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Celebratory" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 37 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cerebral" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 38 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cheerful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 39 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Child-like" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 40 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Circular" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 41 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Clinical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 42 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cold" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 43 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Comic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 44 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Complex" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 45 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Concise" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 46 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Confident" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 47 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Confrontational" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 48 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cosmopolitan" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 49 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Crunchy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 50 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cynical/Sarcastic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 51 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dark" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 52 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Declamatory" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 53 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Defiant" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 54 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Delicate" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 55 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Demonic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 56 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Desperate" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 57 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Detached" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 58 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Devotional" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 59 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Difficult" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 60 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dignified/Noble" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 61 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dramatic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 62 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dreamy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 63 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Driving" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 64 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Druggy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 65 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Earnest" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 66 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Earthy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 67 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ebullient" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 68 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Eccentric" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 69 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ecstatic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 70 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Eerie" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 71 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Effervescent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 72 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Elaborate" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 73 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Elegant" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 74 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Elegiac" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 75 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Energetic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 76 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Enigmatic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 77 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Epic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 78 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Erotic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 79 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ethereal" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 80 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Euphoric" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 81 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Exciting" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 82 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Exotic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 83 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Explosive" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 84 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Extroverted" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 85 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Exuberant" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 86 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Fantastic/Fantasy-like" } } + } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 87 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Feral" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 88 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Feverish" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 89 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fierce" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 90 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fiery" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 91 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Flashy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 92 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Flowing" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 93 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fractured" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 94 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Freewheeling" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 95 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fun" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 96 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Funereal" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 97 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gentle" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 98 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Giddy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 99 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gleeful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 100 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gloomy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 101 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Graceful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 102 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Greasy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 103 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Grim" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 104 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gritty" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 105 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gutsy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 106 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Happy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 107 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Harsh" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 108 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hedonistic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 109 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Heroic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 110 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hostile" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 111 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Humorous" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 112 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hungry" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 113 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hymn-like" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 114 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hyper" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 115 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hypnotic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 116 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Improvisatory" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 117 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Indulgent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 118 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Innocent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 119 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Insular" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 120 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Intense" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 121 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Intimate" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 122 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Introspective" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 123 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ironic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 124 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Irreverent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 125 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Jovial" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 126 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Joyous" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 127 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Kinetic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 128 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Knotty" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 129 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Laid-Back/Mellow" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 130 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Languid" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 131 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lazy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 132 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Light" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 133 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Literate" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 134 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lively" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 135 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lonely" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 136 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lush" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 137 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lyrical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 138 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Macabre" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 139 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Magical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 140 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Majestic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 141 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Malevolent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 142 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Manic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 143 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Marching" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 144 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Martial" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 145 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Meandering" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 146 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mechanical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 147 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Meditative" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 148 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Melancholy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 149 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Menacing" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 150 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Messy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 151 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mighty" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 152 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Monastic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 153 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Monumental" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 154 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Motoric" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 155 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mysterious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 156 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mystical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 157 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Naive" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 158 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Narcotic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 159 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Narrative" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 160 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Negative" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 161 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nervous/Jittery" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 162 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nihilistic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 163 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nocturnal" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 164 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nostalgic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 165 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ominous" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 166 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Optimistic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 167 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Opulent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 168 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Organic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 169 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ornate" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 170 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Outraged" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 171 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Outrageous" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 172 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Paranoid" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 173 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Passionate" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 174 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pastoral" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 175 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Patriotic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 176 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Perky" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 177 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Philosophical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 178 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Plain" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 179 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Plaintive" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 180 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Playful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 181 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Poignant" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 182 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Positive" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 183 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Powerful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 184 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Precious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 185 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Provocative" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 186 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pulsing" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 187 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pure" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 188 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Quirky" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 189 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rambunctious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 190 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ramshackle" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 191 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Raucous" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 192 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Reassuring/Consoling" } } + } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 193 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rebellious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 194 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reckless" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 195 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Refined" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 196 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reflective" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 197 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Regretful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 198 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Relaxed" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 199 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reserved" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 200 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Resolute" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 201 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Restrained" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 202 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reverent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 203 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rhapsodic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 204 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rollicking" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 205 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Romantic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 206 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rousing" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 207 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rowdy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 208 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rustic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 209 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sacred" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 210 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sad" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 211 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sarcastic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 212 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sardonic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 213 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Satirical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 214 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Savage" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 215 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Scary" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 216 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Scattered" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 217 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Searching" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 218 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Self-Conscious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 219 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sensual" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 220 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sentimental" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 221 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Serious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 222 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Severe" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 223 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sexual" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 224 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sexy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 225 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Shimmering" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 226 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Silly" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 227 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sleazy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 228 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Slick" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 229 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Smooth" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 230 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Snide" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 231 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Soft/Quiet" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 232 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Somber" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 233 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Soothing" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 234 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sophisticated" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 235 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spacey" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 236 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sparkling" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 237 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sparse" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 238 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spicy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 239 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spiritual" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 240 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spontaneous" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 241 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spooky" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 242 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sprawling" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 243 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sprightly" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 244 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Springlike" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 245 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Stately" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 246 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Street-Smart" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 247 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Striding" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 248 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Strong" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 249 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Stylish" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 250 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Suffocating" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 251 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sugary" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 252 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Summery" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 253 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Suspenseful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 254 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Swaggering" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 255 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sweet" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 256 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Swinging" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 257 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Technical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 258 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tender" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 259 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tense/Anxious" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 260 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Theatrical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 261 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Thoughtful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 262 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Threatening" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 263 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Thrilling" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 264 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Thuggish" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 265 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tragic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 266 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Transparent/Translucent" } } + } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 267 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Trashy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 268 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Trippy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 269 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Triumphant" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 270 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tuneful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 271 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Turbulent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 272 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Uncompromising" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 273 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Understated" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 274 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Unsettling" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 275 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Uplifting" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 276 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Urgent" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 277 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Virile" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 278 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Visceral" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 279 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Volatile" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 280 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Vulgar" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 281 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Warm" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 282 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Weary" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 283 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Whimsical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 284 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wintry" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 285 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wistful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 286 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Witty" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 287 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wry" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 288 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Yearning" } } } + ] + } + } + } + ] + ] + }, + + { + "sectionName": "versionedStorePermissions", + "methodName": "transaction", + "args": [ + [ + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { "CreateEntity": { "class_id": 12 } } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 0 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Adventure" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 1 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Affection/Fondness" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 2 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Affirmation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 3 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Anger/Hostility" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 4 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Animals" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 5 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Anniversary" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 6 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Argument" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 7 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "At the Beach" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 8 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "At the Office" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 9 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Autumn" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 10 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Award Winners" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 11 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Awareness" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 12 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Background Music" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 13 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Biographical" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 14 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Birth" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 15 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Birthday" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 16 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Breakup" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 17 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cars" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 18 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Celebration" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 19 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Celebrities" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 20 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Children" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 21 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Christmas" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 22 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Christmas Party" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 23 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "City Life" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 24 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Classy Gatherings" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 25 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Club" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 26 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Comfort" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 27 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Conflict" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 28 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cool & Cocky" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 29 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Country Life" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 30 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Crime" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 31 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "D-I-V-O-R-C-E" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 32 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dance Party" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 33 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Day Driving" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 34 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Daydreaming" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 35 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Death" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 36 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Despair" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 37 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Destiny" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 38 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dinner Ambiance" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 39 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Disappointment" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 40 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dreaming" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 41 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Drinking" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 42 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Drugs" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 43 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Early Morning" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 44 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Easter" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 45 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Empowering" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 46 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Everyday Life" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 47 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Exercise/Workout" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 48 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Family" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 49 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Family Gatherings" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 50 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fantasy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 51 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fear" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 52 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Feeling Blue" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 53 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Flying" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 54 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Food/Eating" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 55 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Forgiveness" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 56 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fourth of July" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 57 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Freedom" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 58 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Friendship" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 59 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Funeral" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 60 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Girls Night Out" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 61 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Good Times" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 62 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Goodbyes" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 63 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Graduation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 64 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Guys Night Out" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 65 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Halloween" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 66 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hanging Out" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 67 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Happiness" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 68 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Healing/Comfort" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 69 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Heartache" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 70 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Heartbreak" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 71 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "High School" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 72 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Historical Events" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 73 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Holidays" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 74 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Home" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 75 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Homecoming" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 76 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hope" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 77 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Housework" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 78 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Illness" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 79 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "In Love" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 80 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Introspection" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 81 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Jealousy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 82 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Joy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 83 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Late Night" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 84 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lifecycle" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 85 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Loneliness" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 86 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Long Walk" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 87 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Loss/Grief" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 88 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lying" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 89 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Magic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 90 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Maverick" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 91 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Meditation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 92 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Memorial" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 93 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Military" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 94 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mischievous" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 95 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Monday Morning" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 96 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Money" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 97 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Moon" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 98 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Morning" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 99 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Motivation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 100 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Music" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 101 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Myths & Legends" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 102 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nature" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 103 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "New Love" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 104 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Night Driving" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 105 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nighttime" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 106 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Open Road" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 107 }, + "schema_id": 0, + "parametrized_property_values": [ + { + "in_class_index": 0, + "value": { "PropertyValue": { "Text": "Other Times & Places" } } + } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 108 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pain" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 109 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Parenthood" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 110 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Partying" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 111 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Passion" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 112 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Patriotism" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 113 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Peace" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 114 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Picnic" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 115 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Playful" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 116 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Poetry" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 117 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Politics/Society" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 118 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pool Party" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 119 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Prom" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 120 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Promises" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 121 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Protest" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 122 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rainy Day" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 123 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reflection" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 124 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Regret" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 125 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Relationships" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 126 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Relaxation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 127 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Religion" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 128 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reminiscing" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 129 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reunion" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 130 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Revolutionary" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 131 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Road Trip" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 132 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Romance" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 133 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Romantic Evening" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 134 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Scary Music" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 135 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "School" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 136 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Science" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 137 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "SciFi" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 138 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Seduction" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 139 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Separation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 140 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sex" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 141 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Slow Dance" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 142 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Small Gathering" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 143 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Solitude" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 144 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sorrow" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 145 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sports" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 146 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spring" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 147 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Starry Sky" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 148 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Starting Out" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 149 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Stay in Bed" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 150 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Storms" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 151 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Street Life" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 152 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Summer" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 153 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sun" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 154 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sunday Afternoon" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 155 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sweet Dreams" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 156 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Teenagers" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 157 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Temptation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 158 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "TGIF" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 159 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Thanksgiving" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 160 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "The Creative Side" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 161 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "The Great Outdoors" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 162 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Value" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 163 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tragedy" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 164 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Travel" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 165 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Truth" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 166 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Vacation" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 167 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Victory" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 168 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Violence" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 169 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Visions" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 170 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "War" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 171 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Water" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 172 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Weather" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 173 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wedding" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 174 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Winter" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 175 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wisdom" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 176 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Word Play" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 177 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Work" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 178 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "World View" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 179 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Yearning" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 180 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Youth" } } } + ] + } + } + }, + { + "with_credential": 0, + "as_entity_maintainer": true, + "operation_type": { + "AddSchemaSupportToEntity": { + "entity_id": { "InternalEntityJustAdded": 181 }, + "schema_id": 0, + "parametrized_property_values": [ + { "in_class_index": 0, "value": { "PropertyValue": { "Text": "Zeitgeist" } } } + ] + } + } + } + ] + ] + } +] diff --git a/node/Cargo.toml b/node/Cargo.toml index bb6930245e..bc44e4b283 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,9 +1,9 @@ [package] -authors = ['Joystream'] +authors = ['Joystream contributors'] build = 'build.rs' edition = '2018' name = 'joystream-node' -version = '2.1.6' +version = '3.0.0' default-run = "joystream-node" [[bin]] @@ -14,143 +14,98 @@ path = 'bin/main.rs' crate-type = ["cdylib", "rlib"] [dependencies] -hex-literal = '0.2.1' -derive_more = '0.14.0' -exit-future = '0.1.4' -futures = '0.1.29' -log = '0.4.8' -parking_lot = '0.9.0' -tokio = '0.1.22' -jsonrpc-core = '13.2.0' -rand = '0.7.2' -structopt = '=0.3.5' +# third-party dependencies +serde = { version = "1.0.102", features = ["derive"] } +futures = { version = "0.3.1", features = ["compat"] } +jsonrpc-core = "14.2.0" +structopt = { version = "0.3.8", optional = true} serde_json = '1.0' -serde = '1.0' -hex = '0.4' -# https://users.rust-lang.org/t/failure-derive-compilation-error/39062 -# quote = '<=1.0.2' -[dependencies.node-runtime] -package = 'joystream-node-runtime' -path = '../runtime' - -[dependencies.substrate-basic-authorship] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-basic-authorship' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.babe] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-consensus-babe' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.babe-primitives] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-consensus-babe-primitives' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.codec] -package = 'parity-scale-codec' -version = '1.0.0' - -[dependencies.ctrlc] -features = ['termination'] -version = '3.0' - -[dependencies.inherents] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-inherents' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.network] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-network' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.primitives] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-primitives' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.sr-io] -git = 'https://github.com/paritytech/substrate.git' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.substrate-cli] -git = 'https://github.com/paritytech/substrate.git' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.substrate-client] -git = 'https://github.com/paritytech/substrate.git' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.substrate-executor] -git = 'https://github.com/paritytech/substrate.git' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.substrate-service] -git = 'https://github.com/paritytech/substrate.git' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.transaction-pool] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-transaction-pool' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.substrate-telemetry] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-telemetry' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.grandpa] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-finality-grandpa' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.grandpa-primitives] -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-finality-grandpa-primitives' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.im-online] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'srml-im-online' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.substrate-rpc] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-rpc' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.authority-discovery] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-authority-discovery' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.client-db] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-client-db' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.runtime-primitives] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'sr-primitives' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.offchain] -default_features = false -git = 'https://github.com/paritytech/substrate.git' -package = 'substrate-offchain' -rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8' - -[dependencies.libp2p] -version = '0.13.2' -default-features = false +# primitives +sp-authority-discovery = { package = 'sp-authority-discovery', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-consensus-babe = { package = 'sp-consensus-babe', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-finality-grandpa = { package = 'sp-finality-grandpa', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-core = { package = 'sp-core', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-runtime = { package = 'sp-runtime', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-finality-tracker = { package = 'sp-finality-tracker', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-inherents = { package = 'sp-inherents', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-consensus = { package = 'sp-consensus', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-transaction-pool = { package = 'sp-transaction-pool', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-api = { package = 'sp-api', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-blockchain = { package = 'sp-blockchain', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-block-builder = { package = 'sp-block-builder', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } + +# client dependencies +sc-client-api = { package = 'sc-client-api', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-chain-spec = { package = 'sc-chain-spec', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-consensus = { package = 'sc-consensus', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-transaction-pool = { package = 'sc-transaction-pool', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-network = { package = 'sc-network', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-consensus-babe = { package = 'sc-consensus-babe', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4'} +sc-finality-grandpa = { package = 'sc-finality-grandpa', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-basic-authorship = { package = 'sc-basic-authorship', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-service = { package = 'sc-service', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-authority-discovery = { package = 'sc-authority-discovery', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-consensus-epochs = { package = 'sc-consensus-epochs', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-keystore = { package = 'sc-keystore', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-consensus-babe-rpc = { package = 'sc-consensus-babe-rpc', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-finality-grandpa-rpc = { package = 'sc-finality-grandpa-rpc', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-rpc-api = { package = 'sc-rpc-api', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-executor = { package = 'sc-executor', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } + +# frame dependencies +pallet-im-online = { package = 'pallet-im-online', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +pallet-transaction-payment-rpc = { package = 'pallet-transaction-payment-rpc', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +substrate-frame-rpc-system = { package = 'substrate-frame-rpc-system', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +frame-benchmarking = { package = 'frame-benchmarking', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } + +# node-specific dependencies +node-runtime = { package= "joystream-node-runtime", path = "../runtime" } + +# CLI-specific dependencies +sc-cli = { package = 'sc-cli', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', optional = true } +frame-benchmarking-cli = { package = 'frame-benchmarking-cli', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', optional = true } +node-inspect = { package = 'node-inspect', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', optional = true } + +# WASM-specific dependencies +wasm-bindgen = { version = "0.2.57", optional = true } +wasm-bindgen-futures = { version = "0.4.7", optional = true } +browser-utils = { package = 'substrate-browser-utils', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', optional = true} + +[dev-dependencies] +tempfile = "3.1.0" +codec = { package = "parity-scale-codec", version = "1.3.1" } +sp-timestamp = { package = 'sp-timestamp', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sp-keyring = { package = 'sp-keyring', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +sc-consensus-babe = { git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', features = ["test-helpers"]} +# sc-service-test = { git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +frame-system = { package = 'frame-system', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +pallet-transaction-payment = { package = 'pallet-transaction-payment', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } +pallet-grandpa = { package = 'pallet-grandpa', git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4' } [build-dependencies] -vergen = '3' +structopt = { version = "0.3.8", optional = true } +node-inspect = { git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', optional = true} +sc-cli = { git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', optional = true} +frame-benchmarking-cli = { git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', optional = true } +substrate-build-script-utils = { git = 'https://github.com/paritytech/substrate.git', rev = '00768a1f21a579c478fe5d4f51e1fa71f7db9fd4', optional = true } + +[features] +default = [ "cli" ] +browser = [ + "browser-utils", + "wasm-bindgen", + "wasm-bindgen-futures", +] +cli = [ + "node-inspect", + "sc-cli", + "frame-benchmarking-cli", + "sc-service/db", + "structopt", + "substrate-build-script-utils", +] +runtime-benchmarks = [ + "node-runtime/runtime-benchmarks", + "frame-benchmarking-cli", +] diff --git a/node/README.md b/node/README.md new file mode 100644 index 0000000000..9d1b5ef0ce --- /dev/null +++ b/node/README.md @@ -0,0 +1,98 @@ +## Joystream-Node - full node/validator + +![ Nodes for Joystream](./validator-node-banner.svg) + +The joystream-node is the main server application that connects to the network, synchronizes the blockchain with other nodes and produces blocks if configured as a validator node. + +A step by step guide to setup a full node and validator on the Joystream testnet, can be found [here](https://github.com/Joystream/helpdesk/tree/master/roles/validators). + +### Pre-built binaries + +The latest pre-built binaries can be downloaded from the [releases](https://github.com/Joystream/joystream/releases) page. +Generally these will be built from `master` branch and will pertain to the currently active testnet. + +### Building from source + +Clone the repository and install build tools: + +```bash +git clone https://github.com/Joystream/joystream.git + +cd joystream/ + +./setup.sh +``` + +Compile the node and runtime: + +```bash +cargo build --release +``` + +This produces the binary in `./target/release/joystream-node` + +### Running local development chain + +```bash +cargo run --release -- --dev +``` + +If you repeatedly need to restart a new chain, +this script will build and run a fresh new local development chain (purging existing chain data): + +```bash +./scripts/run-dev-chain.sh +``` + +### Joystream Public Testnets + +Use the `--chain` argument, and specify the path to the genesis `chain.json` file for that public network. The JSON "chain spec" files for Joystream public networks can be found in [../testnets/](../testnets/). + +```bash +cargo run --release -- --chain testnets/rome.json +``` + +### Tests and code quality + +Running unit tests: + +```bash +cargo test --all +``` + +Running full suite of checks, tests, formatting and linting: + +```bash +yarn cargo-checks +``` + +Always format your rust code with `cargo fmt` before committing: + +```bash +cargo fmt --all +``` + +### Integration tests + +```bash +./scripts/run-test-chain.sh +yarn workspace joystream-testing test +``` + +To run the integration tests with a different chain, you can omit the step of running the local development chain and simply set the node URL using `NODE_URL` environment variable. + +### Installing a release build + +If you are building a tagged release from `master` branch and want to install the executable to your path follow the step below. + +This will install the executable `joystream-node` to your `~/.cargo/bin` folder, which you would normally have in your `$PATH` environment. + +```bash +cargo install joystream-node --path node/ +``` + +Now you can run and connect to the Rome testnet: + +```bash +joystream-node --chain testnets/rome.json +``` diff --git a/node/bin/main.rs b/node/bin/main.rs index 70006bf571..9298282e3a 100644 --- a/node/bin/main.rs +++ b/node/bin/main.rs @@ -14,27 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Joystream node. If not, see . -//! Substrate Node Template CLI library. +//! Joystream Node. #![warn(missing_docs)] -#![warn(unused_extern_crates)] -use joystream_node::cli; -pub use substrate_cli::{error, IntoExit, VersionInfo}; - -fn main() { - let version = VersionInfo { - name: "Joystream Node", - commit: env!("VERGEN_SHA_SHORT"), - version: env!("CARGO_PKG_VERSION"), - executable_name: "joystream-node", - author: "Joystream", - description: "Joystream substrate node", - support_url: "https://www.joystream.org/", - }; - - if let Err(e) = cli::run(::std::env::args(), cli::Exit, version) { - eprintln!("Fatal error: {}\n\n{:?}", e, e); - std::process::exit(1) - } +fn main() -> sc_cli::Result<()> { + joystream_node::command::run() } diff --git a/node/build.rs b/node/build.rs index 9a51c38b81..f148b56c32 100644 --- a/node/build.rs +++ b/node/build.rs @@ -1,27 +1,72 @@ -use std::{env, path::PathBuf}; +// This file is part of Substrate. -use vergen::{generate_cargo_keys, ConstantsFlags}; +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 -const ERROR_MSG: &str = "Failed to generate metadata files"; +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . fn main() { - generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG); - - let mut manifest_dir = PathBuf::from( - env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."), - ); - - while manifest_dir.parent().is_some() { - if manifest_dir.join(".git/HEAD").exists() { - println!( - "cargo:rerun-if-changed={}", - manifest_dir.join(".git/HEAD").display() - ); - return; - } + #[cfg(feature = "cli")] + cli::main(); +} + +#[cfg(feature = "cli")] +mod cli { + include!("src/cli.rs"); + + use sc_cli::structopt::clap::Shell; + use std::{env, fs, path::Path}; + use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + + pub fn main() { + build_shell_completion(); + generate_cargo_keys(); - manifest_dir.pop(); + rerun_if_git_head_changed(); } - println!("cargo:warning=Could not find `.git/HEAD` from manifest dir!"); + /// Build shell completion scripts for all known shells + /// Full list in https://github.com/kbknapp/clap-rs/blob/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9/src/app/parser.rs#L123 + fn build_shell_completion() { + for shell in &[ + Shell::Bash, + Shell::Fish, + Shell::Zsh, + Shell::Elvish, + Shell::PowerShell, + ] { + build_completion(shell); + } + } + + /// Build the shell auto-completion for a given Shell + fn build_completion(shell: &Shell) { + let outdir = match env::var_os("OUT_DIR") { + None => return, + Some(dir) => dir, + }; + let path = Path::new(&outdir) + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .join("completion-scripts"); + + fs::create_dir(&path).ok(); + + Cli::clap().gen_completions("joystream-node", *shell, &path); + } } diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index 7d663a7176..5376413019 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -15,30 +15,37 @@ // along with Joystream node. If not, see . // Clippy linter warning. -#![allow(clippy::identity_op)] // disable it because we use such syntax for a code readability - // Example: voting_period: 1 * DAY +// Disable it because we use such syntax for a code readability. +// Example: voting_period: 1 * DAY +#![allow(clippy::identity_op)] + +use pallet_im_online::sr25519::AuthorityId as ImOnlineId; +use serde_json as json; +use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_consensus_babe::AuthorityId as BabeId; +use sp_core::{sr25519, Pair, Public}; +use sp_finality_grandpa::AuthorityId as GrandpaId; +use sp_runtime::traits::{IdentifyAccount, Verify}; +use sp_runtime::Perbill; use node_runtime::{ - versioned_store::InputValidationLengthConstraint as VsInputValidation, ActorsConfig, + versioned_store::InputValidationLengthConstraint as VsInputValidation, AuthorityDiscoveryConfig, BabeConfig, Balance, BalancesConfig, ContentWorkingGroupConfig, CouncilConfig, CouncilElectionConfig, DataObjectStorageRegistryConfig, - DataObjectTypeRegistryConfig, ElectionParameters, GrandpaConfig, ImOnlineConfig, IndicesConfig, - MembersConfig, Perbill, ProposalsCodexConfig, SessionConfig, SessionKeys, Signature, - StakerStatus, StakingConfig, SudoConfig, SystemConfig, VersionedStoreConfig, DAYS, WASM_BINARY, + DataObjectTypeRegistryConfig, ElectionParameters, GrandpaConfig, ImOnlineConfig, MembersConfig, + ProposalsCodexConfig, SessionConfig, SessionKeys, Signature, StakerStatus, StakingConfig, + StorageWorkingGroupConfig, SudoConfig, SystemConfig, VersionedStoreConfig, DAYS, WASM_BINARY, }; -pub use node_runtime::{AccountId, GenesisConfig}; -use primitives::{sr25519, Pair, Public}; -use runtime_primitives::traits::{IdentifyAccount, Verify}; -use babe_primitives::AuthorityId as BabeId; -use grandpa_primitives::AuthorityId as GrandpaId; -use im_online::sr25519::AuthorityId as ImOnlineId; -use serde_json as json; +pub use node_runtime::{AccountId, GenesisConfig}; type AccountPublic = ::Signer; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. -pub type ChainSpec = substrate_service::ChainSpec; +pub type ChainSpec = sc_service::GenericChainSpec; + +use node_runtime::common::constraints::InputValidationLengthConstraint; +use sc_chain_spec::ChainType; /// The chain specification option. This is expected to come in from the CLI and /// is little more than one of a number of alternatives which can easily be converted @@ -69,21 +76,35 @@ where /// Helper function to generate stash, controller and session key from seed pub fn get_authority_keys_from_seed( seed: &str, -) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId) { +) -> ( + AccountId, + AccountId, + GrandpaId, + BabeId, + ImOnlineId, + AuthorityDiscoveryId, +) { ( get_account_id_from_seed::(&format!("{}//stash", seed)), get_account_id_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), + get_from_seed::(seed), ) } -fn session_keys(grandpa: GrandpaId, babe: BabeId, im_online: ImOnlineId) -> SessionKeys { +fn session_keys( + grandpa: GrandpaId, + babe: BabeId, + im_online: ImOnlineId, + authority_discovery: AuthorityDiscoveryId, +) -> SessionKeys { SessionKeys { grandpa, babe, im_online, + authority_discovery, } } @@ -94,6 +115,7 @@ impl Alternative { Alternative::Development => ChainSpec::from_genesis( "Development", "dev", + ChainType::Development, || { testnet_genesis( vec![get_authority_keys_from_seed("Alice")], @@ -104,9 +126,10 @@ impl Alternative { get_account_id_from_seed::("Alice//stash"), get_account_id_from_seed::("Bob//stash"), ], + crate::proposals_config::development(), ) }, - vec![], + Vec::new(), None, None, Some(chain_spec_properties()), @@ -115,6 +138,7 @@ impl Alternative { Alternative::LocalTestnet => ChainSpec::from_genesis( "Local Testnet", "local_testnet", + ChainType::Local, || { testnet_genesis( vec![ @@ -136,9 +160,10 @@ impl Alternative { get_account_id_from_seed::("Eve//stash"), get_account_id_from_seed::("Ferdie//stash"), ], + crate::proposals_config::development(), ) }, - vec![], + Vec::new(), None, None, Some(chain_spec_properties()), @@ -146,14 +171,6 @@ impl Alternative { ), }) } - - pub(crate) fn from(s: &str) -> Option { - match s { - "dev" => Some(Alternative::Development), - "local" => Some(Alternative::LocalTestnet), - _ => None, - } - } } fn new_vs_validation(min: u16, max_min_diff: u16) -> VsInputValidation { @@ -174,46 +191,39 @@ pub fn chain_spec_properties() -> json::map::Map { } pub fn testnet_genesis( - initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)>, + initial_authorities: Vec<( + AccountId, + AccountId, + GrandpaId, + BabeId, + ImOnlineId, + AuthorityDiscoveryId, + )>, root_key: AccountId, endowed_accounts: Vec, + cpcp: node_runtime::ProposalsConfigParameters, ) -> GenesisConfig { const CENTS: Balance = 1; const DOLLARS: Balance = 100 * CENTS; const STASH: Balance = 20 * DOLLARS; const ENDOWMENT: Balance = 100_000 * DOLLARS; - // default codex proposals config parameters - let cpcp = node_runtime::ProposalsConfigParameters::default(); + let default_text_constraint = node_runtime::working_group::default_text_constraint(); GenesisConfig { system: Some(SystemConfig { code: WASM_BINARY.to_vec(), changes_trie_config: Default::default(), }), - balances: Some(BalancesConfig { + pallet_balances: Some(BalancesConfig { balances: endowed_accounts .iter() .cloned() .map(|k| (k, ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) .collect(), - vesting: vec![], }), - indices: Some(IndicesConfig { ids: vec![] }), - session: Some(SessionConfig { - keys: initial_authorities - .iter() - .map(|x| { - ( - x.0.clone(), - session_keys(x.2.clone(), x.3.clone(), x.4.clone()), - ) - }) - .collect::>(), - }), - staking: Some(StakingConfig { - current_era: 0, + pallet_staking: Some(StakingConfig { validator_count: 20, minimum_validator_count: 1, stakers: initial_authorities @@ -224,15 +234,29 @@ pub fn testnet_genesis( slash_reward_fraction: Perbill::from_percent(10), ..Default::default() }), - sudo: Some(SudoConfig { key: root_key }), - babe: Some(BabeConfig { + pallet_sudo: Some(SudoConfig { + key: root_key.clone(), + }), + pallet_babe: Some(BabeConfig { authorities: vec![], }), - im_online: Some(ImOnlineConfig { keys: vec![] }), - authority_discovery: Some(AuthorityDiscoveryConfig { keys: vec![] }), - grandpa: Some(GrandpaConfig { + pallet_im_online: Some(ImOnlineConfig { keys: vec![] }), + pallet_authority_discovery: Some(AuthorityDiscoveryConfig { keys: vec![] }), + pallet_grandpa: Some(GrandpaConfig { authorities: vec![], }), + pallet_session: Some(SessionConfig { + keys: initial_authorities + .iter() + .map(|x| { + ( + x.0.clone(), + x.0.clone(), + session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone()), + ) + }) + .collect::>(), + }), council: Some(CouncilConfig { active_council: vec![], term_ends_at: 1, @@ -250,22 +274,23 @@ pub fn testnet_genesis( min_voting_stake: 1 * DOLLARS, }, }), - members: Some(MembersConfig { + membership: Some(MembersConfig { default_paid_membership_fee: 100u128, - members: crate::members_config::initial_members(), + members: vec![], }), - forum: Some(crate::forum_config::from_serialized::create( - endowed_accounts[0].clone(), - )), + forum: Some(crate::forum_config::from_serialized::create(root_key)), data_object_type_registry: Some(DataObjectTypeRegistryConfig { first_data_object_type_id: 1, }), data_object_storage_registry: Some(DataObjectStorageRegistryConfig { first_relationship_id: 1, }), - actors: Some(ActorsConfig { - enable_storage_role: true, - request_life_time: 300, + working_group_Instance2: Some(StorageWorkingGroupConfig { + phantom: Default::default(), + storage_working_group_mint_capacity: 0, + opening_human_readable_text_constraint: default_text_constraint, + worker_application_human_readable_text_constraint: default_text_constraint, + worker_exit_rationale_text_constraint: default_text_constraint, }), versioned_store: Some(VersionedStoreConfig { class_by_id: vec![], @@ -292,14 +317,14 @@ pub fn testnet_genesis( next_principal_id: 0, channel_creation_enabled: true, // there is no extrinsic to change it so enabling at genesis unstaker_by_stake_id: vec![], - channel_handle_constraint: crate::forum_config::new_validation(5, 20), - channel_description_constraint: crate::forum_config::new_validation(1, 1024), - opening_human_readable_text: crate::forum_config::new_validation(1, 2048), - curator_application_human_readable_text: crate::forum_config::new_validation(1, 2048), - curator_exit_rationale_text: crate::forum_config::new_validation(1, 2048), - channel_avatar_constraint: crate::forum_config::new_validation(5, 1024), - channel_banner_constraint: crate::forum_config::new_validation(5, 1024), - channel_title_constraint: crate::forum_config::new_validation(5, 1024), + channel_handle_constraint: InputValidationLengthConstraint::new(5, 20), + channel_description_constraint: InputValidationLengthConstraint::new(1, 1024), + opening_human_readable_text: InputValidationLengthConstraint::new(1, 2048), + curator_application_human_readable_text: InputValidationLengthConstraint::new(1, 2048), + curator_exit_rationale_text: InputValidationLengthConstraint::new(1, 2048), + channel_avatar_constraint: InputValidationLengthConstraint::new(5, 1024), + channel_banner_constraint: InputValidationLengthConstraint::new(5, 1024), + channel_title_constraint: InputValidationLengthConstraint::new(5, 1024), }), proposals_codex: Some(ProposalsCodexConfig { set_validator_count_proposal_voting_period: cpcp @@ -319,17 +344,115 @@ pub fn testnet_genesis( set_content_working_group_mint_capacity_proposal_grace_period: cpcp .set_content_working_group_mint_capacity_proposal_grace_period, set_lead_proposal_voting_period: cpcp.set_lead_proposal_voting_period, - set_lead_proposal_grace_period: cpcp.set_lead_proposal_voting_period, + set_lead_proposal_grace_period: cpcp.set_lead_proposal_grace_period, spending_proposal_voting_period: cpcp.spending_proposal_voting_period, spending_proposal_grace_period: cpcp.spending_proposal_grace_period, - evict_storage_provider_proposal_voting_period: cpcp - .evict_storage_provider_proposal_voting_period, - evict_storage_provider_proposal_grace_period: cpcp - .evict_storage_provider_proposal_grace_period, - set_storage_role_parameters_proposal_voting_period: cpcp - .set_storage_role_parameters_proposal_voting_period, - set_storage_role_parameters_proposal_grace_period: cpcp - .set_storage_role_parameters_proposal_grace_period, + add_working_group_opening_proposal_voting_period: cpcp + .add_working_group_opening_proposal_voting_period, + add_working_group_opening_proposal_grace_period: cpcp + .add_working_group_opening_proposal_grace_period, + begin_review_working_group_leader_applications_proposal_voting_period: cpcp + .begin_review_working_group_leader_applications_proposal_voting_period, + begin_review_working_group_leader_applications_proposal_grace_period: cpcp + .begin_review_working_group_leader_applications_proposal_grace_period, + fill_working_group_leader_opening_proposal_voting_period: cpcp + .fill_working_group_leader_opening_proposal_voting_period, + fill_working_group_leader_opening_proposal_grace_period: cpcp + .fill_working_group_leader_opening_proposal_grace_period, + set_working_group_mint_capacity_proposal_voting_period: cpcp + .set_content_working_group_mint_capacity_proposal_voting_period, + set_working_group_mint_capacity_proposal_grace_period: cpcp + .set_content_working_group_mint_capacity_proposal_grace_period, + decrease_working_group_leader_stake_proposal_voting_period: cpcp + .decrease_working_group_leader_stake_proposal_voting_period, + decrease_working_group_leader_stake_proposal_grace_period: cpcp + .decrease_working_group_leader_stake_proposal_grace_period, + slash_working_group_leader_stake_proposal_voting_period: cpcp + .slash_working_group_leader_stake_proposal_voting_period, + slash_working_group_leader_stake_proposal_grace_period: cpcp + .slash_working_group_leader_stake_proposal_grace_period, + set_working_group_leader_reward_proposal_voting_period: cpcp + .set_working_group_leader_reward_proposal_voting_period, + set_working_group_leader_reward_proposal_grace_period: cpcp + .set_working_group_leader_reward_proposal_grace_period, + terminate_working_group_leader_role_proposal_voting_period: cpcp + .terminate_working_group_leader_role_proposal_voting_period, + terminate_working_group_leader_role_proposal_grace_period: cpcp + .terminate_working_group_leader_role_proposal_grace_period, }), } } + +// Tests are commented out until we find a solution to why +// building dependencies for the tests are taking so long on Travis CI + +// #[cfg(test)] +// pub(crate) mod tests { +// use super::*; +// use crate::service::{new_full, new_light}; +// use sc_service_test; + +// fn local_testnet_genesis_instant_single() -> GenesisConfig { +// testnet_genesis( +// vec![get_authority_keys_from_seed("Alice")], +// get_account_id_from_seed::("Alice"), +// vec![get_authority_keys_from_seed("Alice").0], +// crate::proposals_config::development(), +// ) +// } + +// /// Local testnet config (single validator - Alice) +// pub fn integration_test_config_with_single_authority() -> ChainSpec { +// ChainSpec::from_genesis( +// "Integration Test", +// "test", +// ChainType::Development, +// local_testnet_genesis_instant_single, +// vec![], +// None, +// None, +// None, +// Default::default(), +// ) +// } + +// fn local_testnet_genesis() -> GenesisConfig { +// testnet_genesis( +// vec![ +// get_authority_keys_from_seed("Alice"), +// get_authority_keys_from_seed("Bob"), +// ], +// get_account_id_from_seed::("Alice"), +// vec![ +// get_authority_keys_from_seed("Alice").0, +// get_authority_keys_from_seed("Bob").0, +// ], +// crate::proposals_config::development(), +// ) +// } + +// /// Local testnet config (multivalidator Alice + Bob) +// pub fn integration_test_config_with_two_authorities() -> ChainSpec { +// ChainSpec::from_genesis( +// "Integration Test", +// "test", +// ChainType::Development, +// local_testnet_genesis, +// vec![], +// None, +// None, +// None, +// Default::default(), +// ) +// } + +// #[test] +// #[ignore] +// fn test_connectivity() { +// sc_service_test::connectivity( +// integration_test_config_with_two_authorities(), +// |config| new_full(config), +// |config| new_light(config), +// ); +// } +// } diff --git a/node/src/cli.rs b/node/src/cli.rs index 9c33d231aa..6acca3e920 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -1,128 +1,48 @@ -use crate::chain_spec; -use crate::new_full_start; -use crate::service; -use futures::{future, sync::oneshot, Future}; -use log::info; -use std::cell::RefCell; -pub use substrate_cli::{error, IntoExit, VersionInfo}; -use substrate_cli::{informant, parse_and_prepare, NoCustom, ParseAndPrepare}; -use substrate_service::{AbstractService, Configuration, Roles as ServiceRoles}; -use tokio::runtime::Runtime; - -/// Parse command line arguments into service configuration. -pub fn run(args: I, exit: E, version: VersionInfo) -> error::Result<()> -where - I: IntoIterator, - T: Into + Clone, - E: IntoExit, -{ - type Config = Configuration<(), T>; - match parse_and_prepare::(&version, "joystream-node", args) { - ParseAndPrepare::Run(cmd) => cmd.run( - load_spec, - exit, - |exit, _cli_args, _custom_args, config: Config<_>| { - info!("{}", version.name); - info!(" version {}", config.full_version()); - info!(" by {}, 2019", version.author); - info!("Chain specification: {}", config.chain_spec.name()); - info!("Node name: {}", config.name); - info!("Roles: {:?}", config.roles); - let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?; - match config.roles { - ServiceRoles::LIGHT => run_until_exit( - runtime, - service::new_light(config).map_err(|e| format!("{:?}", e))?, - exit, - ), - _ => run_until_exit( - runtime, - service::new_full(config).map_err(|e| format!("{:?}", e))?, - exit, - ), - } - .map_err(|e| format!("{:?}", e)) - }, - ), - ParseAndPrepare::BuildSpec(cmd) => cmd.run::(load_spec), - ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder( - |config: Config<_>| Ok(new_full_start!(config).0), - load_spec, - exit, - ), - ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder( - |config: Config<_>| Ok(new_full_start!(config).0), - load_spec, - exit, - ), - ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec), - ParseAndPrepare::RevertChain(cmd) => { - cmd.run_with_builder(|config: Config<_>| Ok(new_full_start!(config).0), load_spec) - } - ParseAndPrepare::CustomCommand(_) => Ok(()), - }?; - - Ok(()) -} - -fn load_spec(id: &str) -> Result, String> { - Ok(match chain_spec::Alternative::from(id) { - Some(spec) => Some(spec.load()?), - None => None, - }) -} - -fn run_until_exit(mut runtime: Runtime, service: T, e: E) -> error::Result<()> -where - T: AbstractService, - E: IntoExit, -{ - let (exit_send, exit) = exit_future::signal(); - - let informant = informant::build(&service); - runtime.executor().spawn(exit.until(informant).map(|_| ())); - - // we eagerly drop the service so that the internal exit future is fired, - // but we need to keep holding a reference to the global telemetry guard - let _telemetry = service.telemetry(); - - let service_res = { - let exit = e - .into_exit() - .map_err(|_| error::Error::Other("Exit future failed.".into())); - let service = service.map_err(error::Error::Service); - let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err); - runtime.block_on(select) - }; - - exit_send.fire(); - - // TODO [andre]: timeout this future #1318 - let _ = runtime.shutdown_on_idle().wait(); - - service_res +// Copyright 2019 Joystream Contributors +// This file is part of Joystream node. + +// Joystream node is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Joystream node is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Joystream node. If not, see . + +use sc_cli::RunCmd; +use structopt::StructOpt; + +/// An overarching CLI command definition. +#[derive(Debug, StructOpt)] +pub struct Cli { + /// Possible subcommand with parameters. + #[structopt(subcommand)] + pub subcommand: Option, + #[allow(missing_docs)] + #[structopt(flatten)] + pub run: RunCmd, } -// handles ctrl-c -pub struct Exit; -impl IntoExit for Exit { - type Exit = future::MapErr, fn(oneshot::Canceled) -> ()>; - fn into_exit(self) -> Self::Exit { - // can't use signal directly here because CtrlC takes only `Fn`. - let (exit_send, exit) = oneshot::channel(); - - let exit_send_cell = RefCell::new(Some(exit_send)); - ctrlc::set_handler(move || { - let exit_send = exit_send_cell - .try_borrow_mut() - .expect("signal handler not reentrant; qed") - .take(); - if let Some(exit_send) = exit_send { - exit_send.send(()).expect("Error sending exit notification"); - } - }) - .expect("Error setting Ctrl-C handler"); - - exit.map_err(drop) - } +/// Possible subcommands of the main binary. +#[derive(Debug, StructOpt)] +pub enum Subcommand { + /// A set of base subcommands handled by `sc_cli`. + #[structopt(flatten)] + Base(sc_cli::Subcommand), + + /// The custom inspect subcommmand for decoding blocks and extrinsics. + #[structopt( + name = "inspect", + about = "Decode given block or extrinsic using current native runtime." + )] + Inspect(node_inspect::cli::InspectCmd), + + /// The custom benchmark subcommmand benchmarking runtime pallets. + #[structopt(name = "benchmark", about = "Benchmark runtime pallets.")] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), } diff --git a/node/src/command.rs b/node/src/command.rs new file mode 100644 index 0000000000..5372c9c7eb --- /dev/null +++ b/node/src/command.rs @@ -0,0 +1,100 @@ +// Copyright 2019 Joystream Contributors +// This file is part of Joystream node. + +// Joystream node is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Joystream node is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Joystream node. If not, see . + +use crate::cli::{Cli, Subcommand}; +use crate::node_executor; +use crate::node_rpc; +use crate::{chain_spec, service}; + +use node_executor::Executor; +use node_runtime::{opaque::Block, RuntimeApi}; +use sc_cli::{Result, SubstrateCli}; +use sc_finality_grandpa::{self as grandpa}; + +impl SubstrateCli for Cli { + fn impl_name() -> &'static str { + "Joystream Node" + } + + fn support_url() -> &'static str { + "https://www.joystream.org/" + } + + fn copyright_start_year() -> i32 { + 2019 + } + + fn executable_name() -> &'static str { + "joystream-node" + } + + fn impl_version() -> &'static str { + env!("SUBSTRATE_CLI_IMPL_VERSION") + } + + fn description() -> &'static str { + env!("CARGO_PKG_DESCRIPTION") + } + + fn author() -> &'static str { + env!("CARGO_PKG_AUTHORS") + } + + fn load_spec(&self, id: &str) -> std::result::Result, String> { + Ok(match id { + "dev" => Box::new(chain_spec::Alternative::Development.load()?), + "local" => Box::new(chain_spec::Alternative::LocalTestnet.load()?), + path => Box::new(chain_spec::ChainSpec::from_json_file( + std::path::PathBuf::from(path), + )?), + }) + } +} + +/// Parse command line arguments into service configuration. +pub fn run() -> Result<()> { + let cli = Cli::from_args(); + + match &cli.subcommand { + None => { + let runner = cli.create_runner(&cli.run)?; + runner.run_node(service::new_light, service::new_full, node_runtime::VERSION) + } + Some(Subcommand::Inspect(cmd)) => { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| cmd.run::(config)) + } + Some(Subcommand::Benchmark(cmd)) => { + if cfg!(feature = "runtime-benchmarks") { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| cmd.run::(config)) + } else { + println!( + "Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`." + ); + Ok(()) + } + } + Some(Subcommand::Base(subcommand)) => { + let runner = cli.create_runner(subcommand)?; + + runner.run_subcommand(subcommand, |config| Ok(new_full_start!(config).0)) + } + } +} diff --git a/node/src/forum_config/from_serialized.rs b/node/src/forum_config/from_serialized.rs index 06b0ee0b4c..9c72cd7c00 100644 --- a/node/src/forum_config/from_serialized.rs +++ b/node/src/forum_config/from_serialized.rs @@ -1,7 +1,9 @@ +#![allow(clippy::type_complexity)] + use super::new_validation; use node_runtime::{ - forum::{Category, CategoryId, Post, PostId, Thread, ThreadId}, - AccountId, BlockNumber, ForumConfig, Moment, + forum::{Category, CategoryId, Post, Thread}, + AccountId, BlockNumber, ForumConfig, Moment, PostId, ThreadId, }; use serde::Deserialize; use serde_json::Result; @@ -9,12 +11,15 @@ use serde_json::Result; #[derive(Deserialize)] struct ForumData { categories: Vec<(CategoryId, Category)>, - posts: Vec<(PostId, Post)>, - threads: Vec<(ThreadId, Thread)>, + posts: Vec<( + PostId, + Post, + )>, + threads: Vec<(ThreadId, Thread)>, } fn parse_forum_json() -> Result { - let data = include_str!("../../res/forum_data_acropolis_serialized.json"); + let data = include_str!("../../res/forum_data_empty.json"); serde_json::from_str(data) } @@ -35,12 +40,12 @@ pub fn create(forum_sudo: AccountId) -> ForumConfig { next_category_id, next_thread_id, next_post_id, - forum_sudo, category_title_constraint: new_validation(10, 90), category_description_constraint: new_validation(10, 490), thread_title_constraint: new_validation(10, 90), post_text_constraint: new_validation(10, 990), thread_moderation_rationale_constraint: new_validation(10, 290), post_moderation_rationale_constraint: new_validation(10, 290), + forum_sudo, } } diff --git a/node/src/forum_config/mod.rs b/node/src/forum_config/mod.rs index c99f69b4ce..be7349ae97 100644 --- a/node/src/forum_config/mod.rs +++ b/node/src/forum_config/mod.rs @@ -3,7 +3,7 @@ pub mod from_serialized; // Not exported - only here as sample code // mod from_encoded; -use node_runtime::forum::InputValidationLengthConstraint; +use node_runtime::common::constraints::InputValidationLengthConstraint; pub fn new_validation(min: u16, max_min_diff: u16) -> InputValidationLengthConstraint { InputValidationLengthConstraint { min, max_min_diff } diff --git a/node/src/lib.rs b/node/src/lib.rs index d0b23aa71a..3715eeffdf 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -2,4 +2,9 @@ pub mod chain_spec; pub mod cli; pub mod forum_config; pub mod members_config; +pub mod proposals_config; +#[macro_use] pub mod service; +pub mod command; +pub mod node_executor; +pub mod node_rpc; diff --git a/node/src/members_config.rs b/node/src/members_config.rs index 23172e9918..7b33e21b9b 100644 --- a/node/src/members_config.rs +++ b/node/src/members_config.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use serde_json::Result; -use primitives::crypto::{AccountId32, Ss58Codec}; +use sp_core::crypto::{AccountId32, Ss58Codec}; #[derive(Deserialize)] struct Member { @@ -12,18 +12,6 @@ struct Member { about: String, } -// fn test_load_members() -> Result> { -// let data = r#" -// [{ -// "address": "5Gn9n7SDJ7VgHqHQWYzkSA4vX6DCmS5TFWdHxikTXp9b4L32", -// "handle": "mokhtar", -// "avatar_uri": "http://mokhtar.net/avatar.png", -// "about": "Mokhtar" -// }]"#; - -// serde_json::from_str(data) -// } - fn parse_members_json() -> Result> { let data = include_str!("../res/acropolis_members.json"); serde_json::from_str(data) diff --git a/node/src/node_executor.rs b/node/src/node_executor.rs new file mode 100644 index 0000000000..665ca94898 --- /dev/null +++ b/node/src/node_executor.rs @@ -0,0 +1,10 @@ +use sc_executor::native_executor_instance; + +// Declare an instance of the native executor named `Executor`. Include the wasm binary as the +// equivalent wasm code. +native_executor_instance!( + pub Executor, + node_runtime::api::dispatch, + node_runtime::native_version, + frame_benchmarking::benchmarking::HostFunctions, +); diff --git a/node/src/node_rpc.rs b/node/src/node_rpc.rs new file mode 100644 index 0000000000..f33509e208 --- /dev/null +++ b/node/src/node_rpc.rs @@ -0,0 +1,188 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! A collection of node-specific RPC methods. +//! +//! Since `substrate` core functionality makes no assumptions +//! about the modules used inside the runtime, so do +//! RPC methods defined in `sc-rpc` crate. +//! It means that `client/rpc` can't have any methods that +//! need some strong assumptions about the particular runtime. +//! +//! The RPCs available in this crate however can make some assumptions +//! about how the runtime is constructed and what FRAME pallets +//! are part of it. Therefore all node-runtime-specific RPCs can +//! be placed here or imported from corresponding FRAME RPC definitions. + +#![warn(missing_docs)] + +use std::sync::Arc; + +use node_runtime::UncheckedExtrinsic; +use node_runtime::{opaque::Block, AccountId, Balance, BlockNumber, Hash, Index}; +use sc_consensus_babe::{Config, Epoch}; +use sc_consensus_babe_rpc::BabeRpcHandler; +use sc_consensus_epochs::SharedEpochChanges; +use sc_finality_grandpa::{SharedAuthoritySet, SharedVoterState}; +use sc_finality_grandpa_rpc::GrandpaRpcHandler; +use sc_keystore::KeyStorePtr; +use sc_rpc_api::DenyUnsafe; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_consensus::SelectChain; +use sp_consensus_babe::BabeApi; +use sp_transaction_pool::TransactionPool; + +/// Light client extra dependencies. +pub struct LightDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// Remote access to the blockchain (async). + pub remote_blockchain: Arc>, + /// Fetcher instance. + pub fetcher: Arc, +} + +/// Extra dependencies for BABE. +pub struct BabeDeps { + /// BABE protocol config. + pub babe_config: Config, + /// BABE pending epoch changes. + pub shared_epoch_changes: SharedEpochChanges, + /// The keystore that manages the keys of the node. + pub keystore: KeyStorePtr, +} + +/// Extra dependencies for GRANDPA +pub struct GrandpaDeps { + /// Voting round info. + pub shared_voter_state: SharedVoterState, + /// Authority set info. + pub shared_authority_set: SharedAuthoritySet, +} + +/// Full client dependencies. +pub struct FullDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// The SelectChain Strategy + pub select_chain: SC, + /// Whether to deny unsafe calls + pub deny_unsafe: DenyUnsafe, + /// BABE specific dependencies. + pub babe: BabeDeps, + /// GRANDPA specific dependencies. + pub grandpa: GrandpaDeps, +} + +/// Instantiate all Full RPC extensions. +pub fn create_full(deps: FullDeps) -> jsonrpc_core::IoHandler +where + C: ProvideRuntimeApi, + C: HeaderBackend + HeaderMetadata + 'static, + C: Send + Sync + 'static, + C::Api: substrate_frame_rpc_system::AccountNonceApi, + C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi< + Block, + Balance, + UncheckedExtrinsic, + >, + C::Api: BabeApi, + C::Api: BlockBuilder, + P: TransactionPool + 'static, + M: jsonrpc_core::Metadata + Default, + SC: SelectChain + 'static, +{ + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; + use substrate_frame_rpc_system::{FullSystem, SystemApi}; + + let mut io = jsonrpc_core::IoHandler::default(); + let FullDeps { + client, + pool, + select_chain, + deny_unsafe, + babe, + grandpa, + } = deps; + let BabeDeps { + keystore, + babe_config, + shared_epoch_changes, + } = babe; + let GrandpaDeps { + shared_voter_state, + shared_authority_set, + } = grandpa; + + io.extend_with(SystemApi::to_delegate(FullSystem::new( + client.clone(), + pool, + deny_unsafe, + ))); + // Making synchronous calls in light client freezes the browser currently, + // more context: https://github.com/paritytech/substrate/pull/3480 + // These RPCs should use an asynchronous caller instead. + io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new( + client.clone(), + ))); + io.extend_with(sc_consensus_babe_rpc::BabeApi::to_delegate( + BabeRpcHandler::new( + client, + shared_epoch_changes, + keystore, + babe_config, + select_chain, + deny_unsafe, + ), + )); + io.extend_with(sc_finality_grandpa_rpc::GrandpaApi::to_delegate( + GrandpaRpcHandler::new(shared_authority_set, shared_voter_state), + )); + + io +} + +/// Instantiate all Light RPC extensions. +pub fn create_light(deps: LightDeps) -> jsonrpc_core::IoHandler +where + C: sp_blockchain::HeaderBackend, + C: Send + Sync + 'static, + F: sc_client_api::light::Fetcher + 'static, + P: TransactionPool + 'static, + M: jsonrpc_core::Metadata + Default, +{ + use substrate_frame_rpc_system::{LightSystem, SystemApi}; + + let LightDeps { + client, + pool, + remote_blockchain, + fetcher, + } = deps; + let mut io = jsonrpc_core::IoHandler::default(); + io.extend_with(SystemApi::::to_delegate( + LightSystem::new(client, remote_blockchain, fetcher, pool), + )); + + io +} diff --git a/node/src/proposals_config.rs b/node/src/proposals_config.rs new file mode 100644 index 0000000000..aa394123ba --- /dev/null +++ b/node/src/proposals_config.rs @@ -0,0 +1,17 @@ +use node_runtime::ProposalsConfigParameters; + +/// Development chain config. 0 grace period for all proposals, ie. +/// proposals executed immediatly. Short voting period. +pub fn development() -> ProposalsConfigParameters { + ProposalsConfigParameters::with_grace_and_voting_periods(0, 200) +} + +/// Staging chain config. Shorter grace periods and voting periods than default. +pub fn staging() -> ProposalsConfigParameters { + ProposalsConfigParameters::with_grace_and_voting_periods(200, 600) +} + +/// The default configuration as defined in the runtime module +pub fn default() -> ProposalsConfigParameters { + ProposalsConfigParameters::default() +} diff --git a/node/src/service.rs b/node/src/service.rs index 703bb6f5f2..e9343e1ade 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -16,107 +16,141 @@ #![warn(unused_extern_crates)] -// Clippy linter warning. -#![allow(clippy::type_complexity)] // disable it because this is foreign code and can be changed any time - -// Clippy linter warning. -#![allow(clippy::redundant_closure_call)] // disable it because of the substrate lib design - -//! Service and ServiceFactory implementation. Specialized wrapper over substrate service. - -use client_db::Backend; -use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; -use inherents::InherentDataProviders; -use network::{construct_simple_protocol, NetworkService}; -use node_runtime::{self, opaque::Block, GenesisConfig, RuntimeApi}; -use offchain::OffchainWorkers; -use primitives::Blake2Hasher; -use runtime_primitives::traits::Block as BlockT; -use std::sync::Arc; -use substrate_client::{Client, LocalCallExecutor, LongestChain}; -pub use substrate_executor::{native_executor_instance, NativeExecutor}; -use substrate_service::{ - error::Error as ServiceError, AbstractService, Configuration, NetworkStatus, Service, - ServiceBuilder, -}; -use transaction_pool::{self, txpool::Pool as TransactionPool}; +// Substrate implementation issue. +#![allow(clippy::redundant_closure_call)] -construct_simple_protocol! { - /// Demo protocol attachment for substrate. - pub struct NodeProtocol where Block = Block { } -} +//! Service implementation. Specialized wrapper over substrate service. -// Declare an instance of the native executor named `Executor`. Include the wasm binary as the -// equivalent wasm code. -native_executor_instance!( - pub Executor, - node_runtime::api::dispatch, - node_runtime::native_version -); +use node_runtime::opaque::Block; +use node_runtime::RuntimeApi; +use sc_consensus::LongestChain; +use sc_finality_grandpa::{ + self as grandpa, FinalityProofProvider as GrandpaFinalityProofProvider, StorageAndProofProvider, +}; +use sc_service::{ + config::Configuration, error::Error as ServiceError, AbstractService, ServiceBuilder, +}; +use sp_inherents::InherentDataProviders; +use std::sync::Arc; + +use crate::node_executor; +use crate::node_rpc; /// Starts a `ServiceBuilder` for a full service. /// /// Use this macro if you don't actually need the full service, but just the builder in order to /// be able to perform chain operations. -#[macro_export] macro_rules! new_full_start { ($config:expr) => {{ - // type RpcExtension = jsonrpc_core::IoHandler; + use std::sync::Arc; + let mut import_setup = None; - let inherent_data_providers = inherents::InherentDataProviders::new(); + let mut rpc_setup = None; + let inherent_data_providers = sp_inherents::InherentDataProviders::new(); - let builder = substrate_service::ServiceBuilder::new_full::< - node_runtime::opaque::Block, - node_runtime::RuntimeApi, - crate::service::Executor, + let builder = sc_service::ServiceBuilder::new_full::< + Block, + RuntimeApi, + node_executor::Executor, >($config)? - .with_select_chain(|_config, backend| { - Ok(substrate_client::LongestChain::new(backend.clone())) - })? - .with_transaction_pool(|config, client| { - Ok(transaction_pool::txpool::Pool::new( - config, - transaction_pool::FullChainApi::new(client), + .with_select_chain(|_config, backend| Ok(sc_consensus::LongestChain::new(backend.clone())))? + .with_transaction_pool(|builder| { + let pool_api = sc_transaction_pool::FullChainApi::new(builder.client().clone()); + let config = builder.config(); + + Ok(sc_transaction_pool::BasicPool::new( + config.transaction_pool.clone(), + std::sync::Arc::new(pool_api), + builder.prometheus_registry(), )) })? - .with_import_queue(|_config, client, mut select_chain, _transaction_pool| { - let select_chain = select_chain - .take() - .ok_or_else(|| substrate_service::Error::SelectChainRequired)?; - let (grandpa_block_import, grandpa_link) = - grandpa::block_import::<_, _, _, node_runtime::RuntimeApi, _>( + .with_import_queue( + |_config, + client, + mut select_chain, + _transaction_pool, + spawn_task_handle, + prometheus_registry| { + let select_chain = select_chain + .take() + .ok_or_else(|| sc_service::Error::SelectChainRequired)?; + let (grandpa_block_import, grandpa_link) = grandpa::block_import( client.clone(), - &*client, + &(client.clone() as Arc<_>), select_chain, )?; - let justification_import = grandpa_block_import.clone(); - - let (block_import, babe_link) = babe::block_import( - babe::Config::get_or_compute(&*client)?, - grandpa_block_import, - client.clone(), - client.clone(), - )?; - - let import_queue = babe::import_queue( - babe_link.clone(), - block_import.clone(), - Some(Box::new(justification_import)), - None, - client.clone(), - client, - inherent_data_providers.clone(), - )?; - - import_setup = Some((block_import, grandpa_link, babe_link)); - Ok(import_queue) + let justification_import = grandpa_block_import.clone(); + + let (block_import, babe_link) = sc_consensus_babe::block_import( + sc_consensus_babe::Config::get_or_compute(&*client)?, + grandpa_block_import, + client.clone(), + )?; + + let import_queue = sc_consensus_babe::import_queue( + babe_link.clone(), + block_import.clone(), + Some(Box::new(justification_import)), + None, + client, + inherent_data_providers.clone(), + spawn_task_handle, + prometheus_registry, + )?; + + import_setup = Some((block_import, grandpa_link, babe_link)); + Ok(import_queue) + }, + )? + .with_rpc_extensions_builder(|builder| { + let grandpa_link = import_setup + .as_ref() + .map(|s| &s.1) + .expect("GRANDPA LinkHalf is present for full services or set up failed; qed."); + + let shared_authority_set = grandpa_link.shared_authority_set().clone(); + let shared_voter_state = grandpa::SharedVoterState::empty(); + + rpc_setup = Some((shared_voter_state.clone())); + + let babe_link = import_setup + .as_ref() + .map(|s| &s.2) + .expect("BabeLink is present for full services or set up failed; qed."); + + let babe_config = babe_link.config().clone(); + let shared_epoch_changes = babe_link.epoch_changes().clone(); + + let client = builder.client().clone(); + let pool = builder.pool().clone(); + let select_chain = builder + .select_chain() + .cloned() + .expect("SelectChain is present for full services or set up failed; qed."); + let keystore = builder.keystore().clone(); + + Ok(move |deny_unsafe| { + let deps = node_rpc::FullDeps { + client: client.clone(), + pool: pool.clone(), + select_chain: select_chain.clone(), + deny_unsafe, + babe: node_rpc::BabeDeps { + babe_config: babe_config.clone(), + shared_epoch_changes: shared_epoch_changes.clone(), + keystore: keystore.clone(), + }, + grandpa: node_rpc::GrandpaDeps { + shared_voter_state: shared_voter_state.clone(), + shared_authority_set: shared_authority_set.clone(), + }, + }; + + node_rpc::create_full(deps) + }) })?; - // We don't have any custom rpc commands... - // .with_rpc_extensions(|client, pool| -> RpcExtension { - // node_rpc::create(client, pool) - // })?; - (builder, import_setup, inherent_data_providers) + (builder, import_setup, inherent_data_providers, rpc_setup) }}; } @@ -126,58 +160,57 @@ macro_rules! new_full_start { /// concrete types instead. macro_rules! new_full { ($config:expr, $with_startup_data: expr) => {{ - use futures::sync::mpsc; - use network::DhtEvent; + use futures::prelude::*; + use sc_network::Event; + use sc_client_api::ExecutorProvider; + use sp_core::traits::BareCryptoStorePtr; let ( - is_authority, + role, force_authoring, name, - disable_grandpa + disable_grandpa, ) = ( - $config.roles.is_authority(), + $config.role.clone(), $config.force_authoring, - $config.name.clone(), - $config.disable_grandpa + $config.network.node_name.clone(), + $config.disable_grandpa, ); - // sentry nodes announce themselves as authorities to the network - // and should run the same protocols authorities do, but it should - // never actively participate in any consensus process. - let participates_in_consensus = is_authority && !$config.sentry_mode; - - let (builder, mut import_setup, inherent_data_providers) = new_full_start!($config); - - // Dht event channel from the network to the authority discovery module. Use bounded channel to ensure - // back-pressure. Authority discovery is triggering one event per authority within the current authority set. - // This estimates the authority set size to be somewhere below 10 000 thereby setting the channel buffer size to - // 10 000. - let (dht_event_tx, _dht_event_rx) = - mpsc::channel::(10_000); + let (builder, mut import_setup, inherent_data_providers, mut rpc_setup) = + new_full_start!($config); - let service = builder.with_network_protocol(|_| Ok(crate::service::NodeProtocol::new()))? - .with_finality_proof_provider(|client, backend| - Ok(Arc::new(grandpa::FinalityProofProvider::new(backend, client)) as _) - )? - .with_dht_event_tx(dht_event_tx)? - .build()?; + let service = builder + .with_finality_proof_provider(|client, backend| { + // GenesisAuthoritySetProvider is implemented for StorageAndProofProvider + let provider = client as Arc>; + Ok(Arc::new(grandpa::FinalityProofProvider::new(backend, provider)) as _) + })? + .build_full()?; let (block_import, grandpa_link, babe_link) = import_setup.take() - .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); + .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); + + let shared_voter_state = rpc_setup.take() + .expect("The SharedVoterState is present for Full Services or setup failed before. qed"); ($with_startup_data)(&block_import, &babe_link); - if participates_in_consensus { - let proposer = substrate_basic_authorship::ProposerFactory { - client: service.client(), - transaction_pool: service.transaction_pool(), - }; + if let sc_service::config::Role::Authority { .. } = &role { + let proposer = sc_basic_authorship::ProposerFactory::new( + service.client(), + service.transaction_pool(), + service.prometheus_registry().as_ref(), + ); let client = service.client(); let select_chain = service.select_chain() - .ok_or(substrate_service::Error::SelectChainRequired)?; + .ok_or(sc_service::Error::SelectChainRequired)?; + + let can_author_with = + sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()); - let babe_config = babe::BabeParams { + let babe_config = sc_consensus_babe::BabeParams { keystore: service.keystore(), client, select_chain, @@ -187,62 +220,95 @@ macro_rules! new_full { inherent_data_providers: inherent_data_providers.clone(), force_authoring, babe_link, + can_author_with, }; - let babe = babe::start_babe(babe_config)?; - service.spawn_essential_task(babe); - } + let babe = sc_consensus_babe::start_babe(babe_config)?; + service.spawn_essential_task_handle().spawn_blocking("babe-proposer", babe); + } + + // Spawn authority discovery module. + if matches!(role, sc_service::config::Role::Authority{..} | sc_service::config::Role::Sentry {..}) { + let (sentries, authority_discovery_role) = match role { + sc_service::config::Role::Authority { ref sentry_nodes } => ( + sentry_nodes.clone(), + sc_authority_discovery::Role::Authority ( + service.keystore(), + ), + ), + sc_service::config::Role::Sentry {..} => ( + vec![], + sc_authority_discovery::Role::Sentry, + ), + _ => unreachable!("Due to outer matches! constraint; qed.") + }; + + let network = service.network(); + let dht_event_stream = network.event_stream("authority-discovery").filter_map(|e| async move { match e { + Event::Dht(e) => Some(e), + _ => None, + }}).boxed(); + let authority_discovery = sc_authority_discovery::AuthorityDiscovery::new( + service.client(), + network, + sentries, + dht_event_stream, + authority_discovery_role, + service.prometheus_registry(), + ); + + service.spawn_task_handle().spawn("authority-discovery", authority_discovery); + } - // if the node isn't actively participating in consensus then it doesn't + // if the node isn't actively participating in consensus then it doesn't // need a keystore, regardless of which protocol we use below. - let keystore = if participates_in_consensus { - Some(service.keystore()) + let keystore = if role.is_authority() { + Some(service.keystore() as BareCryptoStorePtr) } else { None - }; - - let config = grandpa::Config { - // FIXME #1578 make this available through chainspec - gossip_duration: std::time::Duration::from_millis(333), - justification_period: 512, - name: Some(name), - observer_enabled: true, - keystore, - is_authority, - }; - - match (is_authority, disable_grandpa) { - (false, false) => { - // start the lightweight GRANDPA observer - service.spawn_task(Box::new(grandpa::run_grandpa_observer( - config, - grandpa_link, - service.network(), - service.on_exit(), - )?)); - }, - (true, false) => { - // start the full GRANDPA voter - let grandpa_config = grandpa::GrandpaParams { - config: config, - link: grandpa_link, - network: service.network(), - inherent_data_providers: inherent_data_providers.clone(), - on_exit: service.on_exit(), - telemetry_on_connect: Some(service.telemetry_on_connect_stream()), - voting_rule: grandpa::VotingRulesBuilder::default().build(), - }; - // the GRANDPA voter task is considered infallible, i.e. - // if it fails we take down the service with it. - service.spawn_essential_task(grandpa::run_grandpa_voter(grandpa_config)?); - }, - (_, true) => { - grandpa::setup_disabled_grandpa( - service.client(), - &inherent_data_providers, - service.network(), - )?; - }, + }; + + let config = grandpa::Config { + // FIXME #1578 make this available through chainspec + gossip_duration: std::time::Duration::from_millis(333), + justification_period: 512, + name: Some(name), + observer_enabled: false, + keystore, + is_authority: role.is_network_authority(), + }; + + let enable_grandpa = !disable_grandpa; + if enable_grandpa { + // start the full GRANDPA voter + // NOTE: non-authorities could run the GRANDPA observer protocol, but at + // this point the full voter should provide better guarantees of block + // and vote data availability than the observer. The observer has not + // been tested extensively yet and having most nodes in a network run it + // could lead to finality stalls. + let grandpa_config = grandpa::GrandpaParams { + config, + link: grandpa_link, + network: service.network(), + inherent_data_providers: inherent_data_providers.clone(), + telemetry_on_connect: Some(service.telemetry_on_connect_stream()), + voting_rule: grandpa::VotingRulesBuilder::default().build(), + prometheus_registry: service.prometheus_registry(), + shared_voter_state, + }; + + // the GRANDPA voter task is considered infallible, i.e. + // if it fails we take down the service with it. + service.spawn_essential_task_handle().spawn_blocking( + "grandpa-voter", + grandpa::run_grandpa_voter(grandpa_config)? + ); + } else { + grandpa::setup_disabled_grandpa( + service.client(), + &inherent_data_providers, + service.network(), + )?; } Ok((service, inherent_data_providers)) @@ -252,70 +318,49 @@ macro_rules! new_full { }} } -#[allow(dead_code)] -type ConcreteBlock = node_runtime::opaque::Block; -#[allow(dead_code)] -type ConcreteClient = Client< - Backend, - LocalCallExecutor, NativeExecutor>, - ConcreteBlock, - node_runtime::RuntimeApi, ->; -#[allow(dead_code)] -type ConcreteBackend = Backend; - -/// A specialized configuration object for setting up the node.. -pub type NodeConfiguration = - Configuration; - /// Builds a new service for a full client. -pub fn new_full(config: NodeConfiguration) --> Result< - Service< - ConcreteBlock, - ConcreteClient, - LongestChain, - NetworkStatus, - NetworkService::Hash>, - TransactionPool>, - OffchainWorkers< - ConcreteClient, - >::OffchainStorage, - ConcreteBlock, - > - >, - ServiceError, -> -{ +pub fn new_full(config: Configuration) -> Result { new_full!(config).map(|(service, _)| service) } /// Builds a new service for a light client. -pub fn new_light( - config: NodeConfiguration, -) -> Result { - // type RpcExtension = jsonrpc_core::IoHandler; +pub fn new_light(config: Configuration) -> Result { let inherent_data_providers = InherentDataProviders::new(); - let service = ServiceBuilder::new_light::(config)? + let service = ServiceBuilder::new_light::(config)? .with_select_chain(|_config, backend| Ok(LongestChain::new(backend.clone())))? - .with_transaction_pool(|config, client| { - Ok(TransactionPool::new( - config, - transaction_pool::FullChainApi::new(client), - )) + .with_transaction_pool(|builder| { + let fetcher = builder + .fetcher() + .ok_or_else(|| "Trying to start light transaction pool without active fetcher")?; + let pool_api = + sc_transaction_pool::LightChainApi::new(builder.client().clone(), fetcher); + let pool = sc_transaction_pool::BasicPool::with_revalidation_type( + builder.config().transaction_pool.clone(), + Arc::new(pool_api), + builder.prometheus_registry(), + sc_transaction_pool::RevalidationType::Light, + ); + Ok(pool) })? .with_import_queue_and_fprb( - |_config, client, backend, fetcher, _select_chain, _tx_pool| { + |_config, + client, + backend, + fetcher, + _select_chain, + _tx_pool, + spawn_task_handle, + registry| { let fetch_checker = fetcher .map(|fetcher| fetcher.checker().clone()) .ok_or_else(|| { "Trying to start light import queue without active fetch checker" })?; - let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>( + let grandpa_block_import = grandpa::light_block_import( client.clone(), backend, - &*client, + &(client.clone() as Arc<_>), Arc::new(fetch_checker), )?; @@ -323,35 +368,297 @@ pub fn new_light( let finality_proof_request_builder = finality_proof_import.create_finality_proof_request_builder(); - let (babe_block_import, babe_link) = babe::block_import( - babe::Config::get_or_compute(&*client)?, + let (babe_block_import, babe_link) = sc_consensus_babe::block_import( + sc_consensus_babe::Config::get_or_compute(&*client)?, grandpa_block_import, client.clone(), - client.clone(), )?; - let import_queue = babe::import_queue( + let import_queue = sc_consensus_babe::import_queue( babe_link, babe_block_import, None, Some(Box::new(finality_proof_import)), - client.clone(), client, inherent_data_providers.clone(), + spawn_task_handle, + registry, )?; Ok((import_queue, finality_proof_request_builder)) }, )? - .with_network_protocol(|_| Ok(NodeProtocol::new()))? .with_finality_proof_provider(|client, backend| { - Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _) + // GenesisAuthoritySetProvider is implemented for StorageAndProofProvider + let provider = client as Arc>; + Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, provider)) as _) })? - // We don't have any custom rpc extensions - // .with_rpc_extensions(|client, pool| -> RpcExtension { - // node_rpc::create(client, pool) - // })? - .build()?; + .with_rpc_extensions(|builder| { + let fetcher = builder + .fetcher() + .ok_or_else(|| "Trying to start node RPC without active fetcher")?; + let remote_blockchain = builder + .remote_backend() + .ok_or_else(|| "Trying to start node RPC without active remote blockchain")?; + + let light_deps = node_rpc::LightDeps { + remote_blockchain, + fetcher, + client: builder.client().clone(), + pool: builder.pool(), + }; + + Ok(node_rpc::create_light(light_deps)) + })? + .build_light()?; Ok(service) } + +// Tests are commented out until we find a solution to why +// building dependencies for the tests are taking so long on Travis CI + +// #[cfg(test)] +// mod tests { +// use crate::node_executor; +// use crate::node_rpc; +// use crate::service::{new_full, new_light}; +// use codec::{Decode, Encode}; +// use node_runtime::RuntimeApi; +// use node_runtime::{currency::CENTS, SLOT_DURATION}; +// use node_runtime::{opaque::Block, AccountId, DigestItem, Signature}; +// use node_runtime::{BalancesCall, Call, UncheckedExtrinsic}; +// use sc_consensus_babe::{BabeIntermediate, CompatibleDigestItem, INTERMEDIATE_KEY}; +// use sc_consensus_epochs::descendent_query; +// use sc_finality_grandpa::{self as grandpa}; +// use sc_service::AbstractService; +// use sp_consensus::{ +// BlockImport, BlockImportParams, BlockOrigin, Environment, ForkChoiceStrategy, Proposer, +// RecordProof, +// }; +// use sp_core::{crypto::Pair as CryptoPair, H256}; +// use sp_finality_tracker; +// use sp_keyring::AccountKeyring; +// use sp_runtime::traits::IdentifyAccount; +// use sp_runtime::{ +// generic::{BlockId, Digest, Era, SignedPayload}, +// traits::Verify, +// traits::{Block as BlockT, Header as HeaderT}, +// OpaqueExtrinsic, +// }; +// use sp_timestamp; +// use sp_transaction_pool::{ChainEvent, MaintainedTransactionPool}; +// use std::{any::Any, borrow::Cow, sync::Arc}; + +// type AccountPublic = ::Signer; + +// // Long running test. Run it locally only after the node changes. +// #[test] +// // It is "ignored", but the node-cli ignored tests are running on the CI. +// // This can be run locally with `cargo test --release -p node-cli test_sync -- --ignored`. +// #[ignore] +// fn test_sync() { +// let keystore_path = tempfile::tempdir().expect("Creates keystore path"); +// let keystore = +// sc_keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); +// let alice = keystore +// .write() +// .insert_ephemeral_from_seed::("//Alice") +// .expect("Creates authority pair"); + +// let chain_spec = crate::chain_spec::tests::integration_test_config_with_single_authority(); + +// // For the block factory +// let mut slot_num = 1u64; + +// // For the extrinsics factory +// let bob = Arc::new(AccountKeyring::Bob.pair()); +// let charlie = Arc::new(AccountKeyring::Charlie.pair()); +// let mut index = 0; + +// sc_service_test::sync( +// chain_spec, +// |config| { +// let mut setup_handles = None; +// new_full!( +// config, +// |block_import: &sc_consensus_babe::BabeBlockImport, +// babe_link: &sc_consensus_babe::BabeLink| { +// setup_handles = Some((block_import.clone(), babe_link.clone())); +// } +// ) +// .map(move |(node, x)| (node, (x, setup_handles.unwrap()))) +// }, +// |config| new_light(config), +// |service, &mut (ref inherent_data_providers, (ref mut block_import, ref babe_link))| { +// let mut inherent_data = inherent_data_providers +// .create_inherent_data() +// .expect("Creates inherent data."); +// inherent_data.replace_data(sp_finality_tracker::INHERENT_IDENTIFIER, &1u64); + +// let parent_id = BlockId::number(service.client().chain_info().best_number); +// let parent_header = service.client().header(&parent_id).unwrap().unwrap(); +// let parent_hash = parent_header.hash(); +// let parent_number = *parent_header.number(); + +// futures::executor::block_on(service.transaction_pool().maintain( +// ChainEvent::NewBlock { +// is_new_best: true, +// hash: parent_header.hash(), +// tree_route: None, +// header: parent_header.clone(), +// }, +// )); + +// let mut proposer_factory = sc_basic_authorship::ProposerFactory::new( +// service.client(), +// service.transaction_pool(), +// None, +// ); + +// let epoch_descriptor = babe_link +// .epoch_changes() +// .lock() +// .epoch_descriptor_for_child_of( +// descendent_query(&*service.client()), +// &parent_hash, +// parent_number, +// slot_num, +// ) +// .unwrap() +// .unwrap(); + +// let mut digest = Digest::::default(); + +// // even though there's only one authority some slots might be empty, +// // so we must keep trying the next slots until we can claim one. +// let babe_pre_digest = loop { +// inherent_data.replace_data( +// sp_timestamp::INHERENT_IDENTIFIER, +// &(slot_num * SLOT_DURATION), +// ); +// if let Some(babe_pre_digest) = sc_consensus_babe::test_helpers::claim_slot( +// slot_num, +// &parent_header, +// &*service.client(), +// &keystore, +// &babe_link, +// ) { +// break babe_pre_digest; +// } + +// slot_num += 1; +// }; + +// digest.push(::babe_pre_digest( +// babe_pre_digest, +// )); + +// let new_block = futures::executor::block_on(async move { +// let proposer = proposer_factory.init(&parent_header).await; +// proposer +// .unwrap() +// .propose( +// inherent_data, +// digest, +// std::time::Duration::from_secs(1), +// RecordProof::Yes, +// ) +// .await +// }) +// .expect("Error making test block") +// .block; + +// let (new_header, new_body) = new_block.deconstruct(); +// let pre_hash = new_header.hash(); +// // sign the pre-sealed hash of the block and then +// // add it to a digest item. +// let to_sign = pre_hash.encode(); +// let signature = alice.sign(&to_sign[..]); +// let item = ::babe_seal(signature.into()); +// slot_num += 1; + +// let mut params = BlockImportParams::new(BlockOrigin::File, new_header); +// params.post_digests.push(item); +// params.body = Some(new_body); +// params.intermediates.insert( +// Cow::from(INTERMEDIATE_KEY), +// Box::new(BabeIntermediate:: { epoch_descriptor }) as Box, +// ); +// params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + +// block_import +// .import_block(params, Default::default()) +// .expect("error importing test block"); +// }, +// |service, _| { +// let amount = 5 * CENTS; +// let to: AccountId = AccountPublic::from(bob.public()).into_account().into(); +// let from: AccountId = AccountPublic::from(charlie.public()).into_account().into(); +// let genesis_hash = service.client().block_hash(0).unwrap().unwrap(); +// let best_block_id = BlockId::number(service.client().chain_info().best_number); +// let (spec_version, transaction_version) = { +// let version = service.client().runtime_version_at(&best_block_id).unwrap(); +// (version.spec_version, version.transaction_version) +// }; +// let signer = charlie.clone(); + +// let function = Call::Balances(BalancesCall::transfer(to.into(), amount)); + +// let check_spec_version = frame_system::CheckSpecVersion::new(); +// let check_tx_version = frame_system::CheckTxVersion::new(); +// let check_genesis = frame_system::CheckGenesis::new(); +// let check_era = frame_system::CheckEra::from(Era::Immortal); +// let check_nonce = frame_system::CheckNonce::from(index); +// let check_weight = frame_system::CheckWeight::new(); +// let payment = pallet_transaction_payment::ChargeTransactionPayment::from(0); +// let validate_grandpa_equivocation = +// pallet_grandpa::ValidateEquivocationReport::new(); +// let extra = ( +// check_spec_version, +// check_tx_version, +// check_genesis, +// check_era, +// check_nonce, +// check_weight, +// payment, +// validate_grandpa_equivocation, +// ); +// let raw_payload = SignedPayload::from_raw( +// function, +// extra, +// ( +// spec_version, +// transaction_version, +// genesis_hash, +// genesis_hash, +// (), +// (), +// (), +// (), +// ), +// ); +// let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); +// let (function, extra, _) = raw_payload.deconstruct(); +// let xt = +// UncheckedExtrinsic::new_signed(function, from.into(), signature.into(), extra) +// .encode(); +// let v: Vec = Decode::decode(&mut xt.as_slice()).unwrap(); + +// index += 1; +// OpaqueExtrinsic(v) +// }, +// ); +// } + +// #[test] +// #[ignore] +// fn test_consensus() { +// sc_service_test::consensus( +// crate::chain_spec::tests::integration_test_config_with_two_authorities(), +// |config| new_full(config), +// |config| new_light(config), +// vec!["//Alice".into(), "//Bob".into()], +// ) +// } +// } diff --git a/package.json b/package.json index 236fe2699c..ee24adaf31 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,57 @@ -{ - "private": true, - "name": "joystream", - "license": "GPL-3.0-only", - "scripts": { - "test": "yarn && yarn workspaces run test" - }, - "workspaces": [ - "tests/network-tests" - ], - "devDependencies": { - "husky": "^4.2.5" - }, - "husky": { - "hooks": { - "pre-commit": "devops/git-hooks/pre-commit", - "pre-push": "devops/git-hooks/pre-push" - } - } -} +{ + "private": true, + "name": "joystream", + "version": "1.0.0", + "license": "GPL-3.0-only", + "scripts": { + "test": "yarn && yarn workspaces run test", + "test-migration": "yarn && yarn workspaces run test-migration", + "postinstall": "yarn workspace @joystream/types build", + "cargo-checks": "devops/git-hooks/pre-commit && devops/git-hooks/pre-push", + "cargo-build": "scripts/cargo-build.sh", + "lint": "yarn workspaces run lint" + }, + "workspaces": [ + "tests/network-tests", + "cli", + "types", + "storage-node", + "storage-node/packages/*", + "devops/eslint-config", + "devops/prettier-config", + "pioneer", + "pioneer/packages/apps*", + "pioneer/packages/page*", + "pioneer/packages/react*", + "pioneer/packages/joy-utils", + "pioneer/packages/joy-members", + "pioneer/packages/joy-pages", + "utils/api-examples" + ], + "resolutions": { + "@polkadot/api": "1.26.1", + "@polkadot/api-contract": "1.26.1", + "@polkadot/keyring": "3.0.1", + "@polkadot/types": "1.26.1", + "@polkadot/util": "3.0.1", + "@polkadot/util-crypto": "3.0.1", + "@polkadot/wasm-crypto": "1.2.1", + "babel-core": "^7.0.0-bridge.0", + "typescript": "^3.9.7" + }, + "devDependencies": { + "husky": "^4.2.5", + "prettier": "2.0.2", + "eslint": "^7.6.0" + }, + "husky": { + "hooks": { + "pre-commit": "devops/git-hooks/pre-commit", + "pre-push": "devops/git-hooks/pre-push" + } + }, + "engines": { + "node": ">=12.18.0", + "yarn": "^1.22.0" + } +} diff --git a/pioneer/.123trigger b/pioneer/.123trigger new file mode 100644 index 0000000000..553d62ba4d --- /dev/null +++ b/pioneer/.123trigger @@ -0,0 +1,2 @@ +11 +0.45.2 diff --git a/pioneer/.babelrc.js b/pioneer/.babelrc.js new file mode 100644 index 0000000000..5188ea0422 --- /dev/null +++ b/pioneer/.babelrc.js @@ -0,0 +1 @@ +module.exports = require('./babel.config.js'); diff --git a/pioneer/.codeclimate.yml b/pioneer/.codeclimate.yml new file mode 100644 index 0000000000..534a13e57f --- /dev/null +++ b/pioneer/.codeclimate.yml @@ -0,0 +1,3 @@ +exclude_patterns: +- "**/*.spec.js" +- "**/*.spec.ts" diff --git a/pioneer/.dockerignore b/pioneer/.dockerignore new file mode 100644 index 0000000000..1c8c93fa2d --- /dev/null +++ b/pioneer/.dockerignore @@ -0,0 +1,3 @@ +node_modules +build +.git diff --git a/pioneer/.editorconfig b/pioneer/.editorconfig new file mode 100644 index 0000000000..3c229b5dc1 --- /dev/null +++ b/pioneer/.editorconfig @@ -0,0 +1,10 @@ +root = true +[*] +indent_style=space +indent_size=2 +tab_width=2 +end_of_line=lf +charset=utf-8 +trim_trailing_whitespace=true +max_line_length=120 +insert_final_newline=true diff --git a/pioneer/.env-example b/pioneer/.env-example new file mode 100644 index 0000000000..0b0cd09dcf --- /dev/null +++ b/pioneer/.env-example @@ -0,0 +1,4 @@ +# You can define all your ENV in such a file and run docker as: +# docker run ... --env-file .env ... +WS_URL=ws://localhost:9944 +POLKADOT_UI_SAMPLE=42 diff --git a/pioneer/.eslintignore b/pioneer/.eslintignore new file mode 100644 index 0000000000..42a2471efd --- /dev/null +++ b/pioneer/.eslintignore @@ -0,0 +1,14 @@ +**/build/* +**/coverage/* +**/node_modules/* +packages/old-apps/* +packages/joy-election/* +packages/joy-forum/* +packages/joy-help/* +packages/joy-media/* +packages/joy-proposals/* +packages/joy-roles/* +packages/joy-settings/* +packages/joy-utils-old/* +.eslintrc.js +i18next-scanner.config.js diff --git a/pioneer/.eslintrc.js b/pioneer/.eslintrc.js new file mode 100644 index 0000000000..a909f51bfc --- /dev/null +++ b/pioneer/.eslintrc.js @@ -0,0 +1,33 @@ +// At some point don't depend on @polkadot rules and use @joystream/eslint-config +const base = require('@polkadot/dev/config/eslint'); + +// add override for any (a metric ton of them, initial conversion) +module.exports = { + ...base, + parserOptions: { + ...base.parserOptions, + project: [ + './tsconfig.json' + ] + }, + rules: { + ...base.rules, + '@typescript-eslint/no-explicit-any': 'off', + 'new-cap': 'off', + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/ban-ts-comment': 'error', + // why only required in VSCode!?!? is eslint plugin not working like eslint commandline? + // Or are we having to add this because of new versions of eslint-config-* ? + 'no-console': 'off', + // Override some extended config rules: + 'camelcase': 'off', + 'header/header': 'off', + 'sort-keys': 'off', + 'react/jsx-sort-props': 'off', + 'react/jsx-max-props-per-line': 'off', + 'sort-destructure-keys/sort-destructure-keys': 'off', + '@typescript-eslint/unbound-method': 'warn', // Doesn't work well with our version of Formik, see: https://github.com/formium/formik/issues/2589 + }, + // isolate pioneer from monorepo eslint rules + root: true +}; diff --git a/pioneer/.gitignore b/pioneer/.gitignore new file mode 100644 index 0000000000..9f92cfdb84 --- /dev/null +++ b/pioneer/.gitignore @@ -0,0 +1,28 @@ +build/ +coverage/ +node_modules/ +tmp/ +.idea/ +.vscode/ +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local +.npmrc +.yarn/* +!.yarn/releases +!.yarn/plugins +.pnp.* +cc-test-reporter +package-lock.json +npm-debug.log* +yarn-debug.log* +yarn-error.log* +!patches/** + +# Built Joystream types: +packages/joy-types/lib/ + +# Storybook +storybook-static/ diff --git a/pioneer/.gitlab-ci.yml b/pioneer/.gitlab-ci.yml new file mode 100644 index 0000000000..935bd94d54 --- /dev/null +++ b/pioneer/.gitlab-ci.yml @@ -0,0 +1,159 @@ +image: roffe/kubectl:latest +variables: + CI_REGISTRY: parity.azurecr.io + CI_REGISTRY_USER: parity + AUTO_DEVOPS_DOMAIN: poc-3.polkadot.io + +.kubernetes: &kubernetes + tags: + - kubernetes + +stages: + - dockerize + - test + - review + - staging + - production + - cleanup + +before_script: + - export DOCKER_IMAGE=$CI_REGISTRY/$CI_PROJECT_PATH_SLUG + - export DOCKER_TAG=$CI_COMMIT_REF_SLUG-$VERSION + - export DOCKER_IMAGE_FULL_NAME=$DOCKER_IMAGE:$DOCKER_TAG + +dockerize: + stage: dockerize + environment: + name: infrastructure_build + tags: + - kubernetes-parity-build + image: docker:git + services: + - docker:dind + variables: + DOCKER_DRIVER: overlay2 + DOCKER_HOST: tcp://localhost:2375 + script: + - echo $DOCKER_IMAGE + - echo $DOCKER_TAG + - echo $DOCKER_IMAGE_FULL_NAME + - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" + - docker build -t "$DOCKER_IMAGE_FULL_NAME" . + - docker push "$DOCKER_IMAGE_FULL_NAME" + only: + - master + +review: + stage: review + <<: *kubernetes + script: + - setup_kubernetes + - deploy + environment: + name: review/$CI_COMMIT_REF_NAME + url: https://$CI_ENVIRONMENT_SLUG.$AUTO_DEVOPS_DOMAIN + on_stop: stop_review + only: + refs: + - branches + kubernetes: active + except: + - master + +stop_review: + stage: cleanup + <<: *kubernetes + variables: + GIT_STRATEGY: none + script: + - setup_kubernetes + - delete + environment: + name: review/$CI_COMMIT_REF_NAME + action: stop + when: manual + allow_failure: true + only: + refs: + - branches + kubernetes: active + except: + - master + +staging: + stage: staging + <<: *kubernetes + script: + - setup_kubernetes + - deploy + environment: + name: staging + url: https://staging.$AUTO_DEVOPS_DOMAIN + only: + refs: + - master + kubernetes: active + +production: + stage: production + <<: *kubernetes + script: + - setup_kubernetes + - deploy + environment: + name: production + url: https://$AUTO_DEVOPS_DOMAIN + when: manual + only: + refs: + - master + kubernetes: active + +# --------------------------------------------------------------------------- +.auto_devops: &auto_devops | + # Auto DevOps variables and functions + [[ "$TRACE" ]] && set -x + export DOCKER_IMAGE=$CI_REGISTRY/$CI_PROJECT_PATH_SLUG + export DOCKER_TAG=$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHA + export DOCKER_IMAGE_FULL_NAME=$DOCKER_IMAGE:$DOCKER_TAG + + export AUTODEVOPS_HOST=$(echo $CI_ENVIRONMENT_URL | awk -F/ '{print $3}') + + function build() { + if [[ -n "$CI_REGISTRY_USER" ]]; then + echo "Logging to GitLab Container Registry with CI credentials..." + docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" + echo "" + fi + + echo "Building Dockerfile-based application..." + docker build -t "$DOCKER_IMAGE_FULL_NAME" . + + echo "Pushing to GitLab Container Registry..." + docker push "$DOCKER_IMAGE_FULL_NAME" + echo "" + } + + function setup_kubernetes() { + kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE" + kubectl create secret -n "$KUBE_NAMESPACE" \ + docker-registry gitlab-registry \ + --docker-server="$CI_REGISTRY" \ + --docker-username="$CI_REGISTRY_USER" \ + --docker-password="$CI_REGISTRY_PASSWORD" \ + --docker-email="$GITLAB_USER_EMAIL" \ + -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f - + } + + function deploy() { + cat ./deployment.template.yml | envsubst | kubectl apply -n "$KUBE_NAMESPACE" -f - + } + + function delete() { + kubectl -n "$KUBE_NAMESPACE" delete "deploy/$CI_ENVIRONMENT_SLUG-backend" + kubectl -n "$KUBE_NAMESPACE" delete "svc/$CI_ENVIRONMENT_SLUG-service" + kubectl -n "$KUBE_NAMESPACE" delete "ing/$CI_ENVIRONMENT_SLUG-ingress" + } + +before_script: + - *auto_devops diff --git a/pioneer/.npmignore b/pioneer/.npmignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/.nvmrc b/pioneer/.nvmrc new file mode 100644 index 0000000000..db24ab967f --- /dev/null +++ b/pioneer/.nvmrc @@ -0,0 +1 @@ +10.13.0 diff --git a/pioneer/.prettierignore b/pioneer/.prettierignore new file mode 100644 index 0000000000..1d085cacc9 --- /dev/null +++ b/pioneer/.prettierignore @@ -0,0 +1 @@ +** diff --git a/pioneer/.storybook/addons.ts b/pioneer/.storybook/addons.ts new file mode 100644 index 0000000000..49e8565cc6 --- /dev/null +++ b/pioneer/.storybook/addons.ts @@ -0,0 +1,3 @@ +import '@storybook/addon-knobs/register'; +import '@storybook/addon-actions/register'; +import '@storybook/addon-storysource/register'; diff --git a/pioneer/.storybook/config.tsx b/pioneer/.storybook/config.tsx new file mode 100644 index 0000000000..486a9a329d --- /dev/null +++ b/pioneer/.storybook/config.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { configure, addDecorator } from '@storybook/react'; +import '@storybook/addon-console'; +import StoryRouter from 'storybook-react-router'; + +import GlobalStyle from '@polkadot/react-components/styles'; +import 'semantic-ui-css/semantic.min.css' +import './style.css' + +addDecorator(StoryRouter()); + +addDecorator(story => ( +

+ + {story()} +
+)); + +configure(require.context('../packages', true, /\.stories\.tsx?$/), module) diff --git a/pioneer/.storybook/style.css b/pioneer/.storybook/style.css new file mode 100644 index 0000000000..ed3151fd12 --- /dev/null +++ b/pioneer/.storybook/style.css @@ -0,0 +1,4 @@ +.StorybookRoot { + background-color: #fafafa; + padding: 1rem 5rem; +} \ No newline at end of file diff --git a/pioneer/.storybook/webpack.config.js b/pioneer/.storybook/webpack.config.js new file mode 100644 index 0000000000..6c02952773 --- /dev/null +++ b/pioneer/.storybook/webpack.config.js @@ -0,0 +1,81 @@ +const path = require('path') +const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); +module.exports = ({ config }) => { + +// Post CSS loader for sources: +config.module.rules.push({ + test: /\.css$/, + include: path.resolve(__dirname, '../packages'), + exclude: /(node_modules)/, + use: [ + { + loader: require.resolve('postcss-loader'), + options: { + // Set postcss.config.js config path && ctx + config: { + path: '../postcss.config.js', + }, + ident: 'postcss', + plugins: () => [ + require('precss'), + require('autoprefixer'), + require('postcss-simple-vars'), + require('postcss-nested'), + require('postcss-import'), + require('postcss-clean')(), + require('postcss-flexbugs-fixes') + ] + } + } + ] +}); + +// TypeScript loader (via Babel to match polkadot/apps) +config.module.rules.push({ + test: /\.(js|ts|tsx)$/, + exclude: /(node_modules)/, + use: [ + { + loader: require.resolve('babel-loader'), + options: require('@polkadot/dev-react/config/babel') + }, + ], +}); +config.resolve.extensions.push('.js', '.ts', '.tsx'); + +// TSConfig, uses the same file as packages +config.resolve.plugins = config.resolve.plugins || []; +config.resolve.plugins.push( + new TsconfigPathsPlugin({ + configFile: path.resolve(__dirname, '../tsconfig.json'), + }) +); + +// Stories parser +config.module.rules.push({ + test: /\.stories\.tsx?$/, + loaders: [require.resolve('@storybook/source-loader')], + enforce: 'pre', +}); + +// CSS preprocessors +config.module.rules.push( + { + test: /\.s[ac]ss$/i, + use: [ + // Creates `style` nodes from JS strings + 'style-loader', + // Translates CSS into CommonJS + 'css-loader', + // Compiles Sass to CSS + 'sass-loader', + ], + }, + { + test: /\.less$/, + loaders: [ 'style-loader', 'css-loader', 'less-loader' ] + } +); + +return config; +}; diff --git a/pioneer/.stylelintrc b/pioneer/.stylelintrc new file mode 100644 index 0000000000..9393792fe4 --- /dev/null +++ b/pioneer/.stylelintrc @@ -0,0 +1,7 @@ +{ + "extends": [ + "stylelint-config-recommended", + "stylelint-config-styled-components" + ], + "defaultSeverity": "warning" +} diff --git a/pioneer/.travis.yml b/pioneer/.travis.yml new file mode 100644 index 0000000000..2a9e336eff --- /dev/null +++ b/pioneer/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +node_js: + - "12" +cache: + yarn: true + directories: + - node_modules +before_install: + - curl -o- -L https://yarnpkg.com/install.sh | bash + - export PATH=$HOME/.yarn/bin:$PATH +script: + - yarn + - yarn build diff --git a/pioneer/BOUNTIES.md b/pioneer/BOUNTIES.md new file mode 100644 index 0000000000..bb6d10db13 --- /dev/null +++ b/pioneer/BOUNTIES.md @@ -0,0 +1,19 @@ +# Bounties + +From time-to-time we will add bounties for features. + +These are generously provided by the [Web3 Foundation](https://web3.foundation/) and as such employees of Parity or those of the W3F are ineligible for them. (This includes people being by either Party for development or community work, related or un-related to polkadot-js). + +The idea is that these bounties should be left open to community participation, so only if there is no outside interest for a specific issue, should those directly or indirectly paid by the W3F for work, attempt to close an issue. (in which case it will be "un-bountied") + +Current bounties are tracked by the [!bounty](https://github.com/polkadot-js/apps/labels/%21bounty) label. + +## Process + +Once listed, the normal [Gitcoin](https://gitcoin.co/) process kicks in. This means application, work and payment is managed by this tool. The values for bounties are determined by the size estimation done by the team. + +## Some small requests + +Please don't start work on an issue until you have been approved via the gitcoin interface. We generally love enthusiasm and code in the repo, however short-cutting the process does create some issues for the management of the bounties. We certainly don't want to be playing favorites if 2 PRs for the same issue are created at the same time. And in cases where somebody else has been approved and an unapproved PR comes in... well, it gets really murky. + +When making changes, please do not force push in your PRs, especially not after a review has been started. We will clone your repo and work from that, doing a simple `pull` on a force-pushed branch ends up being, well, less than simple. We squash merge all PRs, so you do not clutter up the history by using stock-standard pushes to your branch. diff --git a/pioneer/CHANGELOG.md b/pioneer/CHANGELOG.md new file mode 100644 index 0000000000..6ffcc70be7 --- /dev/null +++ b/pioneer/CHANGELOG.md @@ -0,0 +1,478 @@ +# CHANGELOG + +## 0.51.1 Jul 27, 2020 + +- Support for Acala chain and types (Thanks to https://github.com/aniiantt) +- First batch of i18n for Arabic (Thanks to https://github.com/nightwolf3) +- Update for Polkadot council thresholds +- Council motion adjustment to cater for current and previous generations +- Adjust imminent proposals to not need own type adjustments +- Cleanup voting totals to only take free into account +- Support JSON v3 formats with kdf +- Always display time left in countdowns, not blocks +- Adjust progress component to be circular +- Rename "Deposit" button to "Send" button (less confusion) +- Ensure new generation tip cancel works for council & users +- Split council votes in backing & number of votes +- Adjust council motions to have the correct max display for nay votes +- Adjust payout toggles with increasing day increments +- Adjust button formats & layouts +- Adjust council slashing params to cater for current generation +- Fixed for keyboard locale detection +- Don't allow display in an iframe + +## 0.50.1 Jul 20, 2020 + +- Adjust CSPs for Electron (Thanks to https://github.com/EthWorks) +- Move delegation column to badge (Thanks to https://github.com/Tbaut) +- Display costs for preimage calls +- Add buttons for bid/unbid on Society +- Allow tip endorsements while in countdown +- Flatten events to group by same-kind in the same block +- Adjust identity validation to check for whitespacing +- Allow the use of VecFixed params with type detection/inputs +- Adjust controller changing to filter when stash === controller (no error, but warn) +- Allow the poll module via Governance proxy +- Expand proxy detection to deep-inspect batched calls +- Ensure various APIs are available (filtering) before attempting to render +- Ensure tooltips are correctly displayed on account hovers +- Cleanup council display for candidates +- Cleanup and simplify QR import logic (& always set genesisHash, even when not supplied) +- Always set genesisHash when accounts are derived +- Adjust breakpoints for `AccountName` via `AddressShort` +- Cleanup SUI dependencies with unused components, Progress, Responsive, Toggle +- Adjust module bundling splits, remove unused libraries & fonts +- Remove unmaintained page-123code (& references) +- Don't display finalized blocks when grandpa is not available + +## 0.49.1 Jul 13, 2020 + +- Update Subscan links with supported chains (https://github.com/carumusan) +- Enhance Electron desktop with CSP & best practices (Thanks to https://github.com/EthWorks) +- Activate Electron update feature by default (Thanks to https://github.com/EthWorks) +- Use external browser for embedded links in Electron (Thanks to https://github.com/EthWorks) +- Add testing around Electron features (Thanks to https://github.com/EthWorks) +- Support democracy account delegation (Thanks to https://github.com/Tbaut) +- Don't filter selected on multi account selector (Thanks to https://github.com/Tbaut) +- Add support for Polkadot denomination poll +- Allow input & display of OpaqueCall type from multisig +- Indicate own nominees on targets page (re-added with badges) +- Re-add indicator for own nominators in staking targets +- Add `?filter=` query param support on staking URLs +- Add generator for Kusama society designs +- Handle OpaqueCall in inputs and well as displays (multisig) +- Small layout adjustments for address display components +- Performance improvements on wrapped styles, component libraries +- Add support for display detected ASCII bytes as text +- Adjust type injection to override on-connect API defaults +- Adjust attestation display with no-balance filters + +## 0.48.1 Jul 6, 2020 + +- Fix for electron package build (Thanks to https://github.com/EthWorks) +- Allow for setting of sub identities via account action +- Adjust known account icons (Society & Treasury) +- Add Westend chain to Subscan link generator +- Display a warning with extensions and no injected accounts +- Retrieve all tips at once and sort by closing +- Fix identity set dialogs to never pass empty fields +- Optimize favorites retrieval & selection for staking (shared between) +- Support new registrar ProxyType in the signer +- Hide funds unbonding when non bonded +- Add withdraw action to staking menu (as available) +- Fix InputAddress component with state change warnings +- Bump to latest API and utilities + +## 0.47.1 Jul 1, 2020 + +- Update zh translation (Thanks to https://github.com/dushaobindoudou) +- Add DataHighway Harbour testnet endpoint (Thanks to https://github.com/ltfschoen) +- Small I18N key fix (Thanks to https://github.com/ltfschoen) +- Allow for Electron auto-update on Mac (Thanks to https://github.com/EthWorks) +- Swap to default conviction of 1x (Thanks to https://github.com/Tbaut) +- Make preimage hash selectable on FF (Thanks to https://github.com/Tbaut) +- show unbonding value in staking actions (Thanks to https://github.com/Tbaut) +- Default (via toggle) to only last 25% of eras for payouts +- Allow retracting of tips by proposer +- Allow tipping with new Substrate types (dual old/new support) +- Enable the full retrieval of all Map/Doublemap entries +- Support correct display of vesting with locks (& unlock via account) +- Adjust on-chain identity inputs with field validation +- Enable grouping of democracy locks by type +- Resolve identity links starting with https://twitter +- Display voted & unvoted council motions, referendums & tips +- Adjust toggles for file/bytes uploads +- Correct handling of recursive param structures in extrinsics +- Swap icons to use font-awesome directly, including official components +- Additional small UI cleanups and fixes + +## 0.46.1 Jun 22, 2020 + +- I18n for es (Thanks to https://github.com/wimel) +- Support for importing mini secrets via QR (Thanks to https://github.com/hanwencheng) +- Update SubstrateTEE types (Thanks to https://github.com/brenzi) +- Support for multisig calls with new weight parameters +- Split sign and send updates in the signer modal for better UI tracking +- Hide zero nonce of accounts/contracts pages +- Display API extrinsic construction errors in the extrinsics app +- Do not display signer proxies when there are none matching against accounts +- Sort recovery addresses to align with the Substrate implementation +- Check for funded controller on bonding +- Suggest max values for bonding (& bonding extra), adjusting checks +- Handle isForceEra to adjust era displays +- Display candidacy bond on council submission +- Adjust AddressMni & AddressSmall components to take advantage of bigger screens +- Display referendum & treasury tips voting status +- Add tips close buttons & countdown timer +- Disabled nominations via targets when in election +- Expand targets page to include waiting validators (full overview of all) +- Apply shared filters (name, toggles) on all validator lists +- Display balances in account view sidebar +- Adjust signer dialog ith split sign/send (better status displays) +- Adjust proxy checks for sudo calls to closer align with Polkadot +- Apply i18n caching, with no reload on translation page +- Add "Apply" i18n button to reflect editing changes in the UI +- Support Tuple inputs (params/extrinsics) for custom names +- `@polkadot/api` 1.20.1 +- `@polkadot/util` 2.15.1 + +## 0.45.2 Jun 16, 2020 + +- I18n for ja (Thanks to https://github.com/SotaWatanabe) +- I18n for pt (thanks to https://github.com/laurogripa) +- I18n for ru (Thanks to https://github.com/illlefr4u) +- Update Encointer types (thanks to https://github.com/brenzi) +- Improve Electron app security settings (Thanks to https://github.com/EthWorks) +- Rework signer dialog to cater for proxies (and multisig/proxy combinations) +- Construct payouts with oldest eras first (expire first) +- Show outstanding multisig approvals on accounts page +- Allow for addition of proxied accounts (access to proxy account only) +- Change claims to handle no statements required (new module now on Kusama) +- Publish docker image on release +- update collective calls to handle weights enhancements for latest Substrate +- Allow for tip endorsements with 0 value +- add Centrifuge live as a connection option +- Adjust Polkascan links with current active chains +- When collective proposal is in close state, hide vote buttons +- Cleanup technical committee display (header alignment) +- Adjust IPFS/IPNS network extraction for local gateways +- `@polkadot/api` 1.19.1 +- `@polkadot/util` 2.14.1 + +## 0.44.1 Jun 10, 2020 + +- Publish electron images on release (Thanks to https://github.com/EthWorks) +- Adjust with latest Arcardia types (Thanks to https://github.com/ETeissonniere) +- Extensions and fixes to the russian translations (Thanks to https://github.com/illlefr4u) +- Rewrite of the contracts app (Thanks to https://github.com/kwingram25) +- New types for SubstrateTEE (Thanks to https://github.com/brenzi) +- Adjust for new Polkadot CC1 & Kusama types +- Cater for new multisig module location +- Filter multisig signatories based on approvals, set final state based on threshold +- Adapt QR codes to cater for hashing on large payloads +- Adjust collective extrinsics to cater for weight & lengths +- Allow bonding with full free amount (this fixes bonding for vesting) +- Fixes for Kusama as well as Polkadot claims +- Allow Polkadot CC1 links to Polkascan & Subscan +- Update Polkascan links with new formats +- Don't display era progress when Forcing `isForceNone` +- Overall styling adjustments +- Cater for `{kusama,polkadot,westend}.dotapps.io` redirects +- `@polkadot/api` 1.18.1 +- `@polkadot/util` 2.13.1 + +## 0.43.1 May 26, 2020 + +- Support for Polkadot CC1 Claims (Thanks to https://github.com/amaurymartiny & https://github.com/Tbaut) +- Small typo fixes (Thanks to https://github.com/Swader) +- updates to russian translation (Thanks to https://github.com/illlefr4u) +- Adjustments to Electron build support (Thanks to https://github.com/EthWorks) +- Support for Polkadot CC1 types & RPC endpoints +- Detect & support new proposal close process in Substrate +- Adjust checks for on-click validator (immediate isActive) +- `@polkadot/api` 1.16.1 +- `@polkadot/util` 2.11.1 + +## 0.42.1 May 22, 2020 + +- Adjust Subscan proposal links (Thanks to https://github.com/illlefr4u) +- Add environment suport for docker images (Thanks to https://github.com/chevdor) +- Adjust overflows on small screens (Thanks to https://github.com/dushaobindoudou) +- Add links to Polkaassembly (Thanks to https://github.com/Tbaut) +- Address popup with detailed info (Thanks to https://github.com/kwingram25) +- Add Russian translation (Thanks to https://github.com/gregzaitsev) +- Russian translation adjustments (Thanks to https://github.com/illlefr4u) +- Add Nodle RPC endpoint (Thanks to https://github.com/ETeissonniere) +- Update Kulupu types (Thanks to https://github.com/sorpaas) +- Update Edgeware types (Thanks to https://github.com/drewstone) +- Update Encointer types (Thanks to https://github.com/brenzi) +- Update node-template types (Thanks to https://github.com/shawntabrizi) +- Update node-template types (Thanks to https://github.com/JoshOrndorff) +- Higher default contracts gas limit (Thanks to https://github.com/Stefie) +- Add block number display to event overview (Thanks to https://github.com/danforbes) +- Basic Electron support (Thanks to https://github.com/EthWorks) +- Documentation around IPFS pinning (Thanks to https://github.com/chevdor) +- Added IPFS/IPNS publishing (ipns via dotapps.io) +- Support for multisig wallets +- Ledger address on-wallet display option +- Add support for new per-staker payouts +- Allow for "best" selection in staking +- Simplified nominator & validator creation flows +- Display >64 nominators on staking pages (clipped payouts) +- Remove tooltips on staking and elsewhere (large number causes performance issues) +- Council isMember checks uses council in addition to elections +- Allow closing of council proposals +- Expand Treasury proposal inline in council (for approve/reject) +- Expand external proposals in council (preimage lookups) +- Allow for sudo with unchecked weight +- Adjust referendums to display turnout and sentiment +- Add columar modals with extra info +- Add table summaries with totals for free, bonded & stash payouts +- Add images to metadata update dropdowns +- Ecdsa keypair support +- Display delegations in voting breakdowns +- Adjust registrar modal with per-account filters +- Add i18n linting script +- Add i18n editor with translation file generator +- Custom i18n loader with caching +- Add JS sample for storage key generation +- Misc UI fixes & adjustments throughout +- Allow for tabes with aliasses (on renames) & redirects +- Align types and calls with latest substrate + +## 0.41.1 Apr 20, 2020 + +- Fix for searching child identities on parent name (Thanks to https://github.com/krogla) +- Support chains with no balances module (Thanks to https://github.com/Voxelot) +- Add out-of-the-box support for Encointer (Thanks to https://github.com/brenzi) +- Add ava.do endpoint for Kusama (Thanks to https://github.com/Swader) +- Show remaining time on staking payout actions, link payouts from actions +- Display per-validator nominators on waiting list +- Add support for Treasury tipping (display of available & creation) +- Adjust display of passing/failing calcs in democracy (incl. no display when other side is 0) +- Enable use of `system_chainType` to detect development chains +- Adjust Expander display for balances as used in accounts +- Adjust formatting outputs (via cleanup) for state queries +- Cleanup nowrap on Extender as part of tables +- Optimize retrieval of old-style validator/nominator payouts (not full historyDepth) +- Optimize AccountName with caching & when used in lists (no lookup information attached, but not shown) +- Optimize IdentityIcon with removal of extra queries +- Optimize Transfers, no unneeded useEffect +- Cleanups, remove unused components with no references (dropped in earlier refactoring) +- More components to functional, specific focus on TxModal extends +- Bumps to all @polkadot/* packages for latest support everywhere + +## 0.40.1 Apr 9, 2020 + +- Swap voting to aye/nay toggles (Thanks to https://github.com/Lowhearth) +- Cater for chains where no tip is present (Thanks to https://github.com/Sushisource) +- Export chain-specific settings via QR (thanks to https://github.com/hanwencheng) +- Improve support for WS_URL usage (Thanks to https://github.com/chevdor) +- Add out-of-the-box support for Centrifuge (Thanks to https://github.com/philipstanislaus) +- Cleanup docker image construction (Thanks to https://github.com/philipstanislaus) +- Add out-of-the-box support for node template (Thanks to https://github.com/JoshOrndorff) +- Text cleanups (Thanks to https://github.com/x5engine) +- Text cleanups (Thanks to https://github.com/ltfschoen) +- update Parachains to support latest Polkadot (Thanks to https://github.com/kwingram25) +- Rework multi address inputs (e.g. nominations & council) (Thanks to https://github.com/kwingram25) +- Introduce apps-config as a single source of config information +- Cater for metadata updates to extensions +- Rework explorer layouts, combining extrinsics & events into a single view +- Swap all layouts to be explicitly table-based (instead of table-like) +- Cater for latest Substrate referendum updates +- Allow for fast-tracking proposals +- Time countdowns where applicable, e.g. referendums +- Show referendum pass/fail status as well as change information +- Combine Accounts & Contacts into a single app +- Support for display of parent/child relationships in accounts +- Add ErrorBoundary around components +- Update Westend after reset +- Enable Subscan explorer +- Support for simple payouts on Substrate, with Payouts screen +- Extensive use of useCallback & React.memo for functional components +- Add Expander component for consistent UI +- Loading spinners used consistently +- Specific names for society & treasury addresses +- Cleanup all voting lock, consistent display +- ... loads of other under-the-hood improvements and cleanups + +## 0.39.1 Jan 31, 2020 + +- **Breaking** Drop support for V1 Substrate chains +- Translation into Chinese (Thanks to https://github.com/dushaobindoudou) +- Support for sign-only transactions (Thanks to https://github.com/mzolkiewski) +- Add support for WestEnd testnet +- Add support for social recovery in accounts +- Add counters for all proposal-based apps +- Disable spellcheck on all input fields (privacy) +- Query the paymentInfo API to get weight fee information +- Remove FF warning with https:// -> ws://localhost +- Staking now supports where the controller or stash accounts are not local +- Social app +- Add support for identity setting (via identity module) +- Add support for registrars to hand out identity judgements +- Use both internal and lib hooks as applicable (refactoring) +- Support QR codes (accounts) with optional names +- Cleanup all Modals, simplify +- Adjust balance display formats +- Update to latest libraries (incl. util 2.0 & api 1.0) + +## 0.38.1 + +- Fix summarybar in 123-code (Thanks to https://github.com/anakornk) +- Update Edgeware with correct keys (Thanks to https://github.com/drewstone) +- Add InputAddressMulti inputs, both to council and staking nominators +- Rework all layouts, removing cards for table-ike-layouts +- Technical comittee app +- Allow for external proposal and queued for dispatch in democracy +- Add pre-image support to democracy proposals (including imminent) +- Improved staking page rendering (background) +- Update to latest libraries + +## 0.37.1 + +- Support for Kusama CC3 +- Support for contracts with new ABI v2 (Thanks to https://github.com/kwingram25) +- Support for on-chain nicks +- Speed improvements for the staking pages +- Add account derivation from existing account +- Council voting with runner up & phragmen +- Allow favorites in validators pages +- Rework nominations to take favorites & current into account +- Enhance AddressCard with additional info (incl. vested) +- Move account/address actions to popup menu +- Convert a large number of components to use hooks +- Display validator graphs +- Refactor of backup modal (Thanks to @LukeSugiura) +- Enable language setting options (Thanks to @LukeSugiura) +- Allow for signRaw to be used in the signing toolbox (injected accounts) +- Display account names in status events +- Nomination targets dashboard +- Validator preferences are expressed as commission % as supported by chains +- Account locks are applied on a genesis range (e.g. CC2 & CC3 allow availability) +- ... lots of smaller enhancements & bug fixes + +## 0.36.1 + +- Api 0.95.1, Util 1.6.1, Extension 0.13.1 +- Support latest contracts ABI (via API), incl. rework of contracts UI +- Support for Kusama CC2 +- Support for Edgeware mainnet +- Experimental Ledger support +- Display forks on explorer (limited to Babe) +- Change settings to have Save as well as Save & Reload (depending on changes made) +- Updates to struct & enum rendering (as per extrinsic app) +- Backup, Password change & Delete don't show for built-in dev accounts +- Add commissions to the staking overview +- UI theme update +- A large number of components refactored for React functional components +- Allow dismiss of all notifications (via bounty) +- Migrate all buttons to have icons (via bounty) +- Proposal submission via modal (via bounty) +- i18n string extraction (via bounty) +- adjust signature validity (via bounty) +- Make the network selection clickable on network name (via bounty) +- ... and a number of cleanups all around + +## 0.35.1 + +- Api 0.91.1, Util 1.2.1, Extension 0.10.1 +- Support for accounts added via Qr (for instance, the Parity Signer) +- Support for accounts tied to specific chains (instead of just available to all) +- GenericAsset app transfers +- Support for Edgeware with default types +- Display received heartbeats for validators +- Allow optional params (really as optional) in RPC toolbox +- Add Polkascan for Kusama +- Fix account derivation with `///password` +- Lots of component & maintainability cleanups + +## 0.34.1 + +- Kusama support +- Full support for Substrate 2.x & Polkadot 0.5.0 networks +- Lots of UI updated to support both Substrate 1.x & 2.x chains +- Add of claims app for Kusama (and Polkadot) +- Basic Council, Parachains & Treasury apps +- Moved ui-* packages to react-* + +## 0.33.1 + +- Allow for externally injected accounts (i.e. via extension, polkadot-js & SingleSource) +- Links to extrnisics & addresses on Polkascan +- Rework Account & Address layouts with cards +- Transfer can happen from any point (via Transfer modal) +- Use new api.derive functions +- Introduce multi support (most via api.derive.*) +- Update all account and address modals +- Add seconding of proposals +- Staking updates, including un-bonding & withdrawals +- Update explorer with global query on hash/blocks +- Add filters on the staking page +- Vanitygen now supports sr25519 as well +- Fixes for importing of old JSON +- Latest @polkadot/util & @polkadot/api +- A large number of optimizations and smaller fixes + +## 0.32.1 + +- Support for Substrate 1.0 release & metadata v4 +- @polkadot/api 0.77.1 + +## 0.31.1 + +- Cleanups, fixes and features around the poc-4 staking module +- Number of UI enhancements + +## 0.30.1 + +- Staking page indicator for offline nodes (count & block) +- Rework page tabs and content layouts +- Cleanup of all UI summary headers +- Emberic Elem support (replaces Dried Danta) + +## 0.29.1 + +- @polkadot/util & @polkadot/api 0.75.1 + +## 0.28.1 + +- Support for substrate 1.0-rc + +## 0.27.1 + +- Bring in new staking & nominating functions +- Swap default keyring accounts (on creation) to sr25519 +- New faster crypto algorithms +- Misc. bug fixes all around + +## 0.26.1 + +- Swap keyring to HDKD derivation, mnemonic keys are now not backwards compatible with those created earlier. (Defaults are still for ed25519) +- Swap crypto to new WASM-backed version (and remove libsodium dependency) +- UI to allow for derived keys for ed25519 and sr25519 +- New mobile-friendly sidebar +- Fix issues with nominating (old non-bonds interface) + +## 0.25.1 + +- Swap to publishing -beta.x on merge (non-breaking testing) + + ## 0.24.1 + + Storage now handles Option type properly + + ## 0.23.1 + + JavaScript console introduced + +## 0.22.1 + +- Use new Compact transaction format - this requires the latest binaries from either Polkadot or Substrate + +## 0.21.1 + +- PoC-3 support with latest Substrate master & Polkadot master +- Add support for Charred Cherry (Substrate) and Alexander (Polkadot) testnets +- Too many changes to mention, master now only supports latest PoC-3 iteration +- Use https://poc-2.polkadot.io if access is required to PoC-2 era networks diff --git a/pioneer/CONTRIBUTING.md b/pioneer/CONTRIBUTING.md new file mode 100644 index 0000000000..947952e851 --- /dev/null +++ b/pioneer/CONTRIBUTING.md @@ -0,0 +1,45 @@ +# Contributing + +## What? + +Individuals making significant and valuable contributions are given commit-access to a project to contribute as they see fit. +A project is more like an open wiki than a standard guarded open source project. + +## Rules + +There are a few basic ground-rules for contributors (including the maintainer(s) of the project): + +1. **No `--force` pushes** or modifying the Git history in any way. If you need to rebase, ensure you do it in your own repo. +2. **Non-master branches**, prefixed with a short name moniker (e.g. `-`) must be used for ongoing work. +3. **All modifications** must be made in a **pull-request** to solicit feedback from other contributors. +4. A pull-request *must not be merged until CI* has finished successfully. + +#### Merging pull requests once CI is successful: +- A pull request with no large change to logic that is an urgent fix may be merged after a non-author contributor has reviewed it well. +- No PR should be merged until all reviews' comments are addressed. + +#### Reviewing pull requests: +When reviewing a pull request, the end-goal is to suggest useful changes to the author. Reviews should finish with approval unless there are issues that would result in: + +- Buggy behaviour. +- Undue maintenance burden. +- Breaking with house coding style. +- Pessimisation (i.e. reduction of speed as measured in the projects benchmarks). +- Feature reduction (i.e. it removes some aspect of functionality that a significant minority of users rely on). +- Uselessness (i.e. it does not strictly add a feature or fix a known issue). + +#### Reviews may not be used as an effective veto for a PR because: +- There exists a somewhat cleaner/better/faster way of accomplishing the same feature/fix. +- It does not fit well with some other contributors' longer-term vision for the project. + +## Releases + +Declaring formal releases remains the prerogative of the project maintainer(s). + +## Changes to this arrangement + +This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. + +## Heritage + +These contributing guidelines are modified from the "OPEN Open Source Project" guidelines for the Level project: [https://github.com/Level/community/blob/master/CONTRIBUTING.md](https://github.com/Level/community/blob/master/CONTRIBUTING.md) diff --git a/pioneer/Dockerfile b/pioneer/Dockerfile new file mode 100644 index 0000000000..e728afda0c --- /dev/null +++ b/pioneer/Dockerfile @@ -0,0 +1,26 @@ +FROM ubuntu:18.04 as builder + +# Install any needed packages +RUN apt-get update && apt-get install -y curl git gnupg + +# install nodejs +RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - +RUN apt-get install -y nodejs + +WORKDIR /app +RUN git clone https://github.com/polkadot-js/apps + +WORKDIR /app/apps +RUN npm install yarn -g +RUN yarn +RUN NODE_ENV=production yarn build + +FROM ubuntu:18.04 + +RUN apt-get update && apt-get -y install nginx + +COPY --from=builder /app/apps/packages/apps/build /var/www/html + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/pioneer/I18N.md b/pioneer/I18N.md new file mode 100644 index 0000000000..112e3b81ff --- /dev/null +++ b/pioneer/I18N.md @@ -0,0 +1,22 @@ +# I18N + +The apps UI allows all strings to be translated. Additionally it has a basic UI that allows for the creation of the required translation files, which will give an overview on the progress for a specific language. + +## Updating translations + +To update translations, the following process is required. + +- launch the apps UI, either locally or via https://polkadot.js.org/apps +- explicitly navigate to the i18n page, https://polkadot.js.org/apps/#/settings/i18n + +Here you will find a dropdown of all the available languages and all the modules that maps to the UI. On a single screen you will be able to see all the available strings for a specific module. + +- adjust any strings as required +- once completed with the changes, click the `Generate translation.json` button to download the translation file +- this file can now be added to the repo with a PR to https://github.com/polkadot-js/apps/tree/master/packages/apps/public/locales + +## Adding a new language (if not in dropdown above) + +The process is similar for the above, but does require a new folder with the language identifier to be added. Create [packages/apps/public/locales/](https://github.com/polkadot-js/apps/tree/master/packages/apps/public/locales) folder with an empty `translation.json` (containing only `{}`). After addition of the folder, run `yarn build:i18n` and then the new language will be available for update as per the process in the previous section. + +In addition to the language folder, the language also needs to be added to the dropdown for available languages, this can be found in [packages/apps-config/src/settings.languages.ts](https://github.com/polkadot-js/apps/blob/master/packages/apps-config/src/settings/languages.ts) diff --git a/pioneer/LICENSE b/pioneer/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/pioneer/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pioneer/README.md b/pioneer/README.md new file mode 100644 index 0000000000..b1640de317 --- /dev/null +++ b/pioneer/README.md @@ -0,0 +1,55 @@ +

+ +![Content Directory](https://user-images.githubusercontent.com/4144334/67765742-bbfab280-fa44-11e9-8b13-494b1bfb6014.jpeg) + +A Portal into the Joystream network. Provides a view and interaction layer from a browser. + +This can be accessed as a hosted application via [https://testnet.joystream.org](https://testnet.joystream.org). + +## overview + +The repo is split into a number of packages, each representing an application. These are - + +- [apps](packages/apps/) This is the main entry point. It handles the selection sidebar and routing to the specific application being displayed. +- [apps-electron](packages/apps-electron/) Desktop app running [apps](packages/apps/). +- [page-accounts](packages/page-accounts/) A basic account management app. +- [page-address-book](packages/page-address-book/) A basic address management app. +- [page-explorer](packages/page-explorer/) A simple block explorer. It only shows the most recent blocks, updating as they become available. +- [page-extrinsics](packages/page-extrinsics/) Submission of extrinsics to a node. +- [page-js](packages/page-js/) An online code editor with [@polkadot-js/api](https://github.com/polkadot-js/api/tree/master/packages/api) access to the currently connected node. +- [page-settings](packages/page-settings/) A basic settings management app, allowing choice of language, node to connect to, and theme +- [page-staking](packages/page-staking/) A basic staking management app, allowing staking and nominations. +- [page-storage](packages/page-storage/) A simple node storage query application. Multiple queries can be queued and updates as new values become available. +- [page-toolbox](packages/page-toolbox/) Submission of raw data to RPC endpoints and utility hashing functions. +- [page-transfer](packages/page-transfer/) A basic account management app, allowing transfer of Units/DOTs between accounts. + +In addition the following libraries are also included in the repo. These are to be moved to the [@polkadot/ui](https://github.com/polkadot-js/ui/) repository once it reaches a base level of stability and usability. (At this point with the framework being tested on the apps above, it makes development easier having it close) + +- [react-components](packages/react-components/) A reactive (using RxJS) application framework with a number of useful shared components. +- [react-signer](packages/react-signer/) Signer implementation for apps. +- [react-query](packages/react-query) Base components that use the RxJS Observable APIs + +## development + +Contributions are welcome! + +To start off, this repo (along with others in the [@polkadot](https://github.com/polkadot-js/) family) uses yarn workspaces to organise the code. As such, after cloning dependencies _should_ be installed via `yarn`, not via npm, the latter will result in broken dependencies. + +To get started - + +1. Clone the repo locally, via `git clone https://github.com/joystream/apps ` +2. Ensure that you have a recent LTS version of Node.js, for development purposes [Node >=10.13.0](https://nodejs.org/en/) is recommended. +3. Ensure that you have a recent version of Yarn, for development purposes [Yarn >=1.10.1](https://yarnpkg.com/docs/install) is required. +4. Install the dependencies by running `yarn` +5. Ready! Now you can launch the UI (assuming you have a local Polkadot Node running), via `yarn run start` +6. Access the UI via [http://localhost:3000](http://localhost:3000) + +### Storybook + +There is a [StoryBook](https://storybook.js.org) implementation, the UI for which can be started with `yarn storybook` and then accessed in a browser via http://localhost:3001 (and the server will open a new browser tab by default when it starts). + +Story code can be placed anywhere in the `packages` directory, and will be detected as long as the file name ends in `.stories.tsx. Stories should be defined in the [Component Story Format (CSF)](https://storybook.js.org/docs/formats/component-story-format) for consistency. + +There are several StoryBook addons available, the most useful of which is [Knobs](https://www.npmjs.com/package/@storybook/addon-knobs), which allows props to be altered in real time. + +Note that currently StoryBook only allows for stateless components; it has no connection to polkadot.js or any Substrate node. This means that existing components, which are often tightly coupled with the Polkadot API, cannot be used in storybook. diff --git a/pioneer/babel.config.js b/pioneer/babel.config.js new file mode 100644 index 0000000000..5b479645c9 --- /dev/null +++ b/pioneer/babel.config.js @@ -0,0 +1,5 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +module.exports = require('@polkadot/dev/config/babel'); diff --git a/pioneer/deployment.extras.yml b/pioneer/deployment.extras.yml new file mode 100644 index 0000000000..6372814aeb --- /dev/null +++ b/pioneer/deployment.extras.yml @@ -0,0 +1,33 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: production-ingress-substrate-ui + namespace: poc3-122 + annotations: + kubernetes.io/ingress.class: traefik + traefik.frontend.entryPoints: "https,http" +spec: + rules: + - host: substrate-ui.parity.io + http: + paths: + - backend: + serviceName: production-service + servicePort: 80 +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: production-ingress-substrate-ui-light + namespace: poc3-122 + annotations: + kubernetes.io/ingress.class: traefik + traefik.frontend.entryPoints: "https,http" +spec: + rules: + - host: substrate-ui-light.parity.io + http: + paths: + - backend: + serviceName: production-service + servicePort: 80 diff --git a/pioneer/deployment.template.yml b/pioneer/deployment.template.yml new file mode 100644 index 0000000000..e1be2eacef --- /dev/null +++ b/pioneer/deployment.template.yml @@ -0,0 +1,60 @@ +--- +apiVersion: v1 +data: +# AZURE_DOCKER_REGISTRY_CONFIG is base64 of this: +# {"auths":{"parity.azurecr.io":{"username":"parity","password":"","email":"admin@parity.io","auth":""}}} + .dockerconfigjson: $AZURE_DOCKER_REGISTRY_CONFIG +kind: Secret +metadata: + name: azure-docker-registry-key +type: kubernetes.io/dockerconfigjson +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: $CI_ENVIRONMENT_SLUG-backend +spec: + replicas: $REPLICAS + template: + metadata: + labels: + app: $CI_ENVIRONMENT_SLUG + component: backend + spec: + containers: + - name: $CI_ENVIRONMENT_SLUG-backend + image: $DOCKER_IMAGE_FULL_NAME + imagePullPolicy: Always + ports: + - containerPort: 80 + imagePullSecrets: + - name: azure-docker-registry-key +--- +apiVersion: v1 +kind: Service +metadata: + name: $CI_ENVIRONMENT_SLUG-service +spec: + selector: + app: $CI_ENVIRONMENT_SLUG + ports: + - name: http + port: 80 + targetPort: 80 + protocol: TCP +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: $CI_ENVIRONMENT_SLUG-ingress + annotations: + kubernetes.io/ingress.class: traefik + traefik.frontend.entryPoints: "https,http" +spec: + rules: + - host: $AUTODEVOPS_HOST + http: + paths: + - backend: + serviceName: $CI_ENVIRONMENT_SLUG-service + servicePort: 80 diff --git a/pioneer/docker/nginx.conf b/pioneer/docker/nginx.conf new file mode 100644 index 0000000000..dce1ffbd61 --- /dev/null +++ b/pioneer/docker/nginx.conf @@ -0,0 +1,27 @@ +user nginx; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + gzip on; + include /etc/nginx/conf.d/*.conf; +} diff --git a/pioneer/env.sh b/pioneer/env.sh new file mode 100755 index 0000000000..e01e134aad --- /dev/null +++ b/pioneer/env.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# This script is used when the docker container starts and does the magic to +# bring the ENV variables to the generated static UI. + +TARGET=./env-config.js + +# Recreate config file +echo -n > $TARGET + +declare -a vars=( + "WS_URL" + "SAMPLE" +) + +echo "window.process_env = {" >> $TARGET +for VAR in ${vars[@]}; do + echo " $VAR: \"${!VAR}\"," >> $TARGET +done +echo "}" >> $TARGET diff --git a/pioneer/gh-pages-refresh.sh b/pioneer/gh-pages-refresh.sh new file mode 100755 index 0000000000..b4aac5b4c8 --- /dev/null +++ b/pioneer/gh-pages-refresh.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +exit 0 + +# checkout latest +git fetch +git checkout gh-pages +git pull +git checkout --orphan gh-pages-temp + +# cleanup +rm -rf node_modules +rm -rf coverage +rm -rf packages +rm -rf test + +# add +git add -A +git commit -am "refresh history" + +# danger, force new +git branch -D gh-pages +git branch -m gh-pages +git push -f origin gh-pages diff --git a/pioneer/i18next-scanner.config.js b/pioneer/i18next-scanner.config.js new file mode 100644 index 0000000000..62c25d27da --- /dev/null +++ b/pioneer/i18next-scanner.config.js @@ -0,0 +1,73 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const fs = require('fs'); +const path = require('path'); +const typescript = require('typescript'); + +const findPackages = require('./scripts/findPackages'); + +function transform (file, enc, done) { + const { ext } = path.parse(file.path); + + if (ext === '.tsx') { + const content = fs.readFileSync(file.path, enc); + + const { outputText } = typescript.transpileModule(content, { + compilerOptions: { + target: 'es2018' + }, + fileName: path.basename(file.path) + }); + + const parserHandler = (key, options) => { + options.defaultValue = key; + + if (process.platform !== 'win32') { + options.ns = /packages\/(.*?)\/src/g.exec(file.path)[1].replace('page-', 'app-'); + } else { + options.ns = /packages\\(.*?)\\src/g.exec(file.path)[1].replace('page-', 'app-'); + } + + this.parser.set(key, options); + }; + + this.parser.parseFuncFromString(outputText, parserHandler); + } + + done(); +} + +module.exports = { + input: [ + 'packages/*/src/**/*.{ts,tsx}', + // Use ! to filter out files or directories + '!packages/*/src/**/*.spec.{ts,tsx}', + '!packages/*/src/i18n/**', + '!**/node_modules/**' + ], + options: { + debug: true, + defaultLng: 'en', + func: { + extensions: ['.tsx', '.ts'], + list: ['t', 'i18next.t', 'i18n.t'] + }, + keySeparator: false, // key separator + lngs: ['en'], + ns: findPackages().map(({ dir }) => dir.replace('page-', 'app-')), + nsSeparator: false, // namespace separator + resource: { + jsonIndent: 2, + lineEnding: '\n', + loadPath: 'packages/apps/public/locales/{{lng}}/{{ns}}.json', + savePath: 'packages/apps/public/locales/{{lng}}/{{ns}}.json' + }, + trans: { + component: 'Trans' + } + }, + output: './', + transform +}; diff --git a/pioneer/img/pioneer_new.svg b/pioneer/img/pioneer_new.svg new file mode 100644 index 0000000000..550f529e35 --- /dev/null +++ b/pioneer/img/pioneer_new.svg @@ -0,0 +1,106 @@ + + + + Group + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pioneer/jest.config.js b/pioneer/jest.config.js new file mode 100644 index 0000000000..342de2be88 --- /dev/null +++ b/pioneer/jest.config.js @@ -0,0 +1,26 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const config = require('@polkadot/dev/config/jest'); + +const findPackages = require('./scripts/findPackages'); + +const internalModules = findPackages() + .filter(({ name }) => !['@polkadot/apps'].includes(name)) + .reduce((modules, { dir, name }) => { + modules[`${name}(.*)$`] = `/packages/${dir}/src/$1`; + + return modules; + }, {}); + +module.exports = Object.assign({}, config, { + moduleNameMapper: { + ...internalModules, + '\\.(css|less)$': 'empty/object', + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object' + }, + transformIgnorePatterns: [ + '/node_modules' + ] +}); diff --git a/pioneer/lerna.json b/pioneer/lerna.json new file mode 100644 index 0000000000..be98869ad3 --- /dev/null +++ b/pioneer/lerna.json @@ -0,0 +1,14 @@ +{ + "lerna": "2.11.0", + "npmClient": "yarn", + "useWorkspaces": true, + "command": { + "publish": { + "allowBranch": "master" + } + }, + "packages": [ + "packages/*" + ], + "version": "0.51.1" +} diff --git a/pioneer/package.json b/pioneer/package.json new file mode 100644 index 0000000000..b050147ce3 --- /dev/null +++ b/pioneer/package.json @@ -0,0 +1,96 @@ +{ + "version": "0.51.1", + "license": "Apache-2", + "private": true, + "engines": { + "node": ">=12.18.0", + "yarn": "^1.22.0" + }, + "homepage": ".", + "name": "pioneer", + "scripts": { + "analyze": "yarn clean && BUILD_ANALYZE=1 yarn run build:code && yarn source-map-explorer packages/apps/build/main.*.js", + "build": "yarn run build:i18n && yarn run build:code", + "build:code": "NODE_ENV=production node ./scripts/dev-build-ts.js", + "build:i18n": "i18next-scanner --config i18next-scanner.config.js && node ./scripts/i18nSort.js", + "build:www": "rm -rf packages/apps/build && mkdir -p packages/apps/build && yarn run build:i18n && cd packages/apps && NODE_ENV=production webpack --config webpack.config.js", + "docs": "echo \"skipping docs\"", + "clean": "polkadot-dev-clean-build", + "clean:i18n": "rm -rf packages/apps/public/locales/en && mkdir -p packages/apps/public/locales/en", + "lint": "eslint --ext .js,.jsx,.ts,.tsx . && tsc --noEmit --pretty", + "lint:css": "stylelint './packages/**/src/**/*.tsx'", + "lint-only-errors": "eslint --quiet --ext .js,.jsx,.ts,.tsx . && tsc --noEmit --pretty", + "lint-autofix": "eslint --fix --ext .js,.jsx,.ts,.tsx . && tsc --noEmit --pretty", + "postinstall": "polkadot-dev-yarn-only", + "test": "echo \"skipping tests\"", + "vanitygen": "node packages/app-accounts/scripts/vanitygen.js", + "start": "yarn clean && cd packages/apps && webpack --config webpack.config.js", + "generate-schemas": "json2ts -i packages/joy-types/src/schemas/role.schema.json -o packages/joy-types/src/schemas/role.schema.ts", + "build-storybook": "build-storybook -c .storybook", + "storybook": "start-storybook -s ./packages/apps/public -p 3001" + }, + "devDependencies": { + "@babel/core": "^7.10.5", + "@babel/register": "^7.10.5", + "@babel/runtime": "^7.10.5", + "@pinata/sdk": "^1.1.10", + "@polkadot/dev": "^0.55.28", + "@polkadot/ts": "^0.3.29", + "@types/bn.js": "^4.11.6", + "@types/chart.js": "^2.9.23", + "@types/file-saver": "^2.0.1", + "@types/i18next": "^13.0.0", + "@types/jest": "^26.0.7", + "@types/react-beautiful-dnd": "^13.0.0", + "@types/react-copy-to-clipboard": "^4.3.0", + "@types/react-dom": "^16.9.8", + "@types/react-router-dom": "^5.1.5", + "@types/react-tooltip": "^4.2.4", + "@types/store": "^2.0.2", + "@types/styled-components": "^5.1.1", + "@types/styled-theming": "^2.2.4", + "concurrently": "^5.2.0", + "devtron": "^1.4.0", + "dnslink-cloudflare": "^2.0.4", + "electron": "^9.1.1", + "electron-builder": "^22.8.0", + "electron-builder-notarize": "^1.2.0", + "i18next-scanner": "^2.11.0", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "source-map-explorer": "^2.4.2", + "stylelint": "^13.6.1", + "stylelint-config-recommended": "^3.0.0", + "stylelint-config-styled-components": "^0.1.1", + "terser-webpack-plugin": "^3.0.7", + "webpack": "^4.44.0", + "webpack-cli": "^3.3.12", + "webpack-merge": "^4.2.2", + "webpack-plugin-serve": "^1.0.1", + "//": "Joystream-specific", + "react-i18next": "^11.7.0", + "@storybook/addon-knobs": "^5.2.5", + "@storybook/addon-storysource": "^5.2.5", + "@storybook/addon-actions": "^5.2.5", + "@storybook/addon-console": "^1.2.1", + "@storybook/react": "^5.2.5", + "json-schema-to-typescript": "^7.1.0", + "storybook-react-router": "^1.0.8", + "typescript": "^3.9.7", + "eslint-plugin-header": "^3.0.0", + "eslint-plugin-sort-destructure-keys": "^1.3.5" + }, + "dependencies": { + "@types/lodash": "^4.14.138", + "@types/marked": "^0.7.0", + "ajv": "^6.10.2", + "css-loader": "^3.2.0", + "less": "^3.10.3", + "less-loader": "^5.0.0", + "lodash": "^4.17.15", + "node-sass": "^4.13.0", + "sass-loader": "^8.0.0", + "style-loader": "^1.0.0", + "@joystream/types": "link:../types" + } +} diff --git a/pioneer/packages/apps-config/.skip-npm b/pioneer/packages/apps-config/.skip-npm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/apps-config/LICENSE b/pioneer/packages/apps-config/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/pioneer/packages/apps-config/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pioneer/packages/apps-config/README.md b/pioneer/packages/apps-config/README.md new file mode 100644 index 0000000000..885573b6c3 --- /dev/null +++ b/pioneer/packages/apps-config/README.md @@ -0,0 +1,43 @@ +# @polkadot/apps-config + +General config for various services, including settings, external links & types. This is a central source of all the configuration settings that can be tweaked. This also means that it can be customized (via PR) to support any additional chains. The internals are split into a number of settings - + +- [api](./src/api) - Here you can add any chain or node-type specific types configuration. When added, it means that when the UI connects to either a runtime with a spec name, or a chain with a specific name, the types will be automatically added to the API as used in the app. +- [links](./src/links) - These are all links to external providers such as explorers. This information is used anywhere where there is an external link to determine the providers for these links. +- [settings](./src/settings) - These are used in dropdowns, specifically under the settings page. +- [ui](./src/ui) - Logos & color settings for chain and node spec specific overrides. This means that when the runtime/chain is detected, the specific logo would be used. + +Customization for each of these are discussed next. + +## Api + +The API config can be done in one of two ways - + +- [chain](./src/api/chain) - Here we are mapping to a specific chain name. Generally the next type would be preferred, however if you are supporting multiple chains with individual configs, you would probably want to add the chain-specific information in here. +- [spec](./src/api/spec) - Here we are mapping from the runtime spec name of the chain to specific types. This means that when connected to a specific spec, these types will be injected. + +The actual type definitions you should be familiar with, it is exactly the same as you would upload via the settings page in JSON, or as detailed in the [API types pages](https://polkadot.js.org/api/start/types.extend.html#extending-types). + +## Links + +We are not going to spend too much time here, since it is generally applicable to explorers and like services. However, should you run one of these services, you can take a look at the existing configs and customize for your setup. + +## Settings + +There are 2 general areas of interest here - + +- [endpoints](./src/settings) - This is where we can add additional endpoints to appear in the dropdowns. We would like to keep this to mostly live networks, although based on demand can probably extend to a testing-only section as well. +- [ss58](./src/settings) - Should you wish to add your ss58Format to the settings dropdown, this is where the configuration take place + +## UI + +These are self-explanatory and config here actually does get used in other parts as well. Basically the information here is broken down into a couple of categories - + +- [general](./src/ui/general) + - color configuration based on chain +- [identityIcon](./src/ui/identityIcons) + - specific identityIcon to use based on node name +- [logos](./src/ui/logos) + - chains - Specific logos when connecting to a specific chain + - nodes - Logos that are used when connecting to a specific runtime spec type (catch-all) + - named - These logos are used as overrides when we pass an explicit logo name diff --git a/pioneer/packages/apps-config/package.json b/pioneer/packages/apps-config/package.json new file mode 100644 index 0000000000..817abc7dec --- /dev/null +++ b/pioneer/packages/apps-config/package.json @@ -0,0 +1,18 @@ +{ + "name": "@polkadot/apps-config", + "private": true, + "version": "0.51.1", + "main": "index.js", + "repository": "https://github.com/polkadot-js/apps.git", + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "dependencies": { + "@acala-network/type-definitions": "0.3.1", + "@babel/runtime": "^7.10.5", + "edgeware-node-types": "^2.3.4" + } +} diff --git a/pioneer/packages/apps-config/src/api/chain/arcadia.ts b/pioneer/packages/apps-config/src/api/chain/arcadia.ts new file mode 100644 index 0000000000..066edfd0ab --- /dev/null +++ b/pioneer/packages/apps-config/src/api/chain/arcadia.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export default { + Amendment: 'Call', + Application: { + candidate: 'AccountId', + candidate_deposit: 'Balance', + challenged_block: 'BlockNumber', + challenger: 'Option', + challenger_deposit: 'Option', + created_block: 'BlockNumber', + metadata: 'Vec', + voters_against: 'Vec<(AccountId, Balance)>', + voters_for: 'Vec<(AccountId, Balance)>', + votes_against: 'Option', + votes_for: 'Option' + }, + CertificateId: 'AccountId', + RootCertificate: { + child_revocations: 'Vec', + created: 'BlockNumber', + key: 'CertificateId', + owner: 'AccountId', + renewed: 'BlockNumber', + revoked: 'bool', + validity: 'BlockNumber' + } +}; diff --git a/pioneer/packages/apps-config/src/api/chain/berlin.ts b/pioneer/packages/apps-config/src/api/chain/berlin.ts new file mode 100644 index 0000000000..1c65366471 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/chain/berlin.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// structs need to be in order +/* eslint-disable sort-keys */ + +import * as edgewareDefinitions from 'edgeware-node-types/interfaces/definitions'; + +const edgTypes = Object + .values(edgewareDefinitions) + .reduce((res, { types }) => ({ ...res, ...types }), {}); + +export default { + ...edgTypes, + // aliases that don't do well as part of interfaces + 'voting::VoteType': 'VoteType', + 'voting::TallyType': 'TallyType', + 'voting::Tally': 'VotingTally', + // chain-specific overrides + Address: 'GenericAddress', + Keys: 'SessionKeys4', + StakingLedger: 'StakingLedgerTo223', + Votes: 'VotesTo230', + ReferendumInfo: 'ReferendumInfoTo239', + Weight: 'u32' +}; diff --git a/pioneer/packages/apps-config/src/api/chain/index.ts b/pioneer/packages/apps-config/src/api/chain/index.ts new file mode 100644 index 0000000000..95d5455f06 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/chain/index.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Arcadia from './arcadia'; +import Berlin from './berlin'; + +export default { + 'Arcadia Nodle Network': Arcadia, + Berlin +}; diff --git a/pioneer/packages/apps-config/src/api/index.ts b/pioneer/packages/apps-config/src/api/index.ts new file mode 100644 index 0000000000..e70c807a35 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/index.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import typesChain from './chain'; +import typesSpec from './spec'; + +export function getChainTypes (specName: string, chainName: string): Record> { + return { + ...(typesSpec[specName as 'edgeware'] || {}), + ...(typesChain[chainName as 'Berlin'] || {}) + }; +} + +export { + typesChain, + typesSpec +}; diff --git a/pioneer/packages/apps-config/src/api/spec/acala.ts b/pioneer/packages/apps-config/src/api/spec/acala.ts new file mode 100644 index 0000000000..087f4c0db6 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/acala.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import * as acalaDefinitions from '@acala-network/type-definitions'; +import { typesFromDefs } from '../util'; + +const acaTypes = typesFromDefs(acalaDefinitions); + +export default acaTypes; diff --git a/pioneer/packages/apps-config/src/api/spec/centrifuge-chain.ts b/pioneer/packages/apps-config/src/api/spec/centrifuge-chain.ts new file mode 100644 index 0000000000..74a69723fb --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/centrifuge-chain.ts @@ -0,0 +1,28 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// structs need to be in order +/* eslint-disable sort-keys */ + +export default { + // chain-specific + AnchorData: { + id: 'H256', + docRoot: 'H256', + anchoredBlock: 'u64' + }, + Fee: { + key: 'Hash', + price: 'Balance' + }, + PreCommitData: { + signingRoot: 'H256', + identity: 'H256', + expirationBlock: 'u64' + }, + Proof: { + leafHash: 'H256', + sortedHashes: 'H256' + } +}; diff --git a/pioneer/packages/apps-config/src/api/spec/edgeware.ts b/pioneer/packages/apps-config/src/api/spec/edgeware.ts new file mode 100644 index 0000000000..1c65366471 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/edgeware.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// structs need to be in order +/* eslint-disable sort-keys */ + +import * as edgewareDefinitions from 'edgeware-node-types/interfaces/definitions'; + +const edgTypes = Object + .values(edgewareDefinitions) + .reduce((res, { types }) => ({ ...res, ...types }), {}); + +export default { + ...edgTypes, + // aliases that don't do well as part of interfaces + 'voting::VoteType': 'VoteType', + 'voting::TallyType': 'TallyType', + 'voting::Tally': 'VotingTally', + // chain-specific overrides + Address: 'GenericAddress', + Keys: 'SessionKeys4', + StakingLedger: 'StakingLedgerTo223', + Votes: 'VotesTo230', + ReferendumInfo: 'ReferendumInfoTo239', + Weight: 'u32' +}; diff --git a/pioneer/packages/apps-config/src/api/spec/encointer-node-notee.ts b/pioneer/packages/apps-config/src/api/spec/encointer-node-notee.ts new file mode 100644 index 0000000000..f573ab4ce1 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/encointer-node-notee.ts @@ -0,0 +1,72 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// structs need to be in order +/* eslint-disable sort-keys */ +/* eslint-disable camelcase */ + +export default { + Address: 'AccountId', + LookupSource: 'AccountId', + CeremonyPhaseType: { + _enum: [ + 'Registering', + 'Assigning', + 'Attesting' + ] + }, + CeremonyIndexType: 'u32', + ParticipantIndexType: 'u64', + MeetupIndexType: 'u64', + AttestationIndexType: 'u64', + CurrencyIdentifier: 'Hash', + BalanceType: 'i128', + BalanceEntry: { + principal: 'i128', + last_update: 'BlockNumber' + }, + CurrencyCeremony: { + cid: 'CurrencyIdentifier', + cindex: 'CeremonyIndexType' + }, + Location: { + lat: 'i64', + lon: 'i64' + }, + Reputation: { + _enum: [ + 'Unverified', + 'UnverifiedReputable', + 'VerifiedUnlinked', + 'VerifiedLinked' + ] + }, + CurrencyPropertiesType: { + name_utf8: 'Text', + demurrage_per_block: 'i128' + }, + ClaimOfAttendance: { + claimant_public: 'AccountId', + ceremony_index: 'CeremonyIndexType', + currency_identifier: 'CurrencyIdentifier', + meetup_index: 'MeetupIndexType', + location: 'Location', + timestamp: 'Moment', + number_of_participants_confirmed: 'u32' + }, + Attestation: { + claim: 'ClaimOfAttendance', + signature: 'Signature', + public: 'AccountId' + }, + ProofOfAttendance: { + prover_public: 'AccountId', + ceremony_index: 'CeremonyIndexType', + currency_identifier: 'CurrencyIdentifier', + attendee_public: 'AccountId', + attendee_signature: 'Signature' + }, + // weight changed to u64 since 2.0.0-rc1 (commit 2051ecbf79e April 16th 2020 + Weight: 'u32' +}; diff --git a/pioneer/packages/apps-config/src/api/spec/encointer-node-teeproxy.ts b/pioneer/packages/apps-config/src/api/spec/encointer-node-teeproxy.ts new file mode 100644 index 0000000000..f3de7c7672 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/encointer-node-teeproxy.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// structs need to be in order +/* eslint-disable sort-keys */ +/* eslint-disable camelcase */ + +export default { + Address: 'AccountId', + LookupSource: 'AccountId', + CeremonyPhaseType: { + _enum: [ + 'Registering', + 'Assigning', + 'Attesting' + ] + }, + CeremonyIndexType: 'u32', + CurrencyIdentifier: 'Hash', + CurrencyCeremony: { + cid: 'CurrencyIdentifier', + cindex: 'CeremonyIndexType' + }, + Location: { + lat: 'i64', + lon: 'i64' + }, + CurrencyPropertiesType: { + name_utf8: 'Text', + demurrage_per_block: 'i128' + }, + ShardIdentifier: 'Hash', + Request: { + shard: 'ShardIdentifier', + cyphertext: 'Vec' + }, + Enclave: { + pubkey: 'AccountId', + mrenclave: 'Hash', + timestamp: 'u64', + url: 'Text' + }, + // weight changed to u64 since 2.0.0-rc1 (commit 2051ecbf79e April 16th 2020 + Weight: 'u32' +}; diff --git a/pioneer/packages/apps-config/src/api/spec/index.ts b/pioneer/packages/apps-config/src/api/spec/index.ts new file mode 100644 index 0000000000..22e2122aeb --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/index.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import acala from './acala'; +import centrifugeChain from './centrifuge-chain'; +import edgeware from './edgeware'; +import encointerNodeNotee from './encointer-node-notee'; +import encointerNodeTeeproxy from './encointer-node-teeproxy'; +import kulupu from './kulupu'; +import nodeTemplate from './node-template'; +import stablePoc from './stable-poc'; +import joystreamNode from './joystream-node'; + +export default { + acala, + 'centrifuge-chain': centrifugeChain, + edgeware, + 'encointer-node': encointerNodeNotee, + 'encointer-node-notee': encointerNodeNotee, + 'encointer-node-teeproxy': encointerNodeTeeproxy, + kulupu, + 'node-template': nodeTemplate, + 'stable-poc': stablePoc, + stable_poc: stablePoc, + 'joystream-node': joystreamNode +}; diff --git a/pioneer/packages/apps-config/src/api/spec/joystream-node.ts b/pioneer/packages/apps-config/src/api/spec/joystream-node.ts new file mode 100644 index 0000000000..7e436a96d4 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/joystream-node.ts @@ -0,0 +1,3 @@ +import { types } from '@joystream/types'; + +export default types; diff --git a/pioneer/packages/apps-config/src/api/spec/kulupu.ts b/pioneer/packages/apps-config/src/api/spec/kulupu.ts new file mode 100644 index 0000000000..076685d2c3 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/kulupu.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// structs need to be in order +/* eslint-disable sort-keys */ + +export default { + Difficulty: 'U256', + DifficultyAndTimestamp: { + difficulty: 'Difficulty', + timestamp: 'Moment' + }, + Era: { + genesisBlockHash: 'H256', + finalBlockHash: 'H256', + finalStateRoot: 'H256' + } +}; diff --git a/pioneer/packages/apps-config/src/api/spec/node-template.ts b/pioneer/packages/apps-config/src/api/spec/node-template.ts new file mode 100644 index 0000000000..8556c23f71 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/node-template.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// structs need to be in order +/* eslint-disable sort-keys */ + +export default { + Address: 'AccountId', + LookupSource: 'AccountId' +}; diff --git a/pioneer/packages/apps-config/src/api/spec/stable-poc.ts b/pioneer/packages/apps-config/src/api/spec/stable-poc.ts new file mode 100644 index 0000000000..d88f76d975 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/stable-poc.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// structs need to be in order +/* eslint-disable sort-keys */ + +export default { + AccountData: { + free: 'Balance', + reserved: 'Balance', + miscFrozen: 'Balance', + feeFrozen: 'Balance', + txCount: 'u32', + sessionIndex: 'u32' + }, + TemplateAccountData: { + txCount: 'u32', + sessionIndex: 'u32' + }, + TxCount: 'u32' +}; diff --git a/pioneer/packages/apps-config/src/api/util.ts b/pioneer/packages/apps-config/src/api/util.ts new file mode 100644 index 0000000000..451b492788 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/util.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export function typesFromDefs (definitions: Record }>): Record { + return Object + .values(definitions) + .reduce((res: Record, { types }): Record => ({ + ...res, + ...types + }), {}); +} diff --git a/pioneer/packages/apps-config/src/extensions/index.ts b/pioneer/packages/apps-config/src/extensions/index.ts new file mode 100644 index 0000000000..82417a2e2e --- /dev/null +++ b/pioneer/packages/apps-config/src/extensions/index.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// it would have been really good to import this from detect, however... not exported +type Browser = 'chrome' | 'firefox'; + +interface Extension { + desc: string; + link: string; + name: string; +} + +const availableExtensions: Record = [ + { + browsers: { + chrome: 'https://chrome.google.com/webstore/detail/polkadot%7Bjs%7D-extension/mopnmbcafieddcagagdcbnhejhlodfdd', + firefox: 'https://addons.mozilla.org/en-US/firefox/addon/polkadot-js-extension/' + }, + desc: 'Basic account injection and signer', + name: 'polkadot-js extension' + } +].reduce((available: Record, { browsers, desc, name }): Record => { + Object.entries(browsers).forEach(([browser, link]): void => { + available[browser as 'chrome'].push({ desc, link, name }); + }); + + return available; +}, { chrome: [], firefox: [] }); + +export { availableExtensions }; diff --git a/pioneer/packages/apps-config/src/links/commonwealth.ts b/pioneer/packages/apps-config/src/links/commonwealth.ts new file mode 100644 index 0000000000..92c82dfeca --- /dev/null +++ b/pioneer/packages/apps-config/src/links/commonwealth.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +const HASH_PATHS = ['proposal/councilmotion']; + +export default { + chains: { + Edgeware: 'edgeware', + Kusama: 'kusama', + 'Kusama CC3': 'kusama' + }, + create: (chain: string, path: string, data: BN | number | string, hash?: string): string => + `https://commonwealth.im/${chain}/${path}/${HASH_PATHS.includes(path) ? (hash || '') : data.toString()}`, + isActive: true, + paths: { + council: 'proposal/councilmotion', + proposal: 'proposal/democracyproposal', + referendum: 'proposal/referendum', + treasury: 'proposal/treasuryproposal' + }, + url: 'https://commonwealth.im/' +}; diff --git a/pioneer/packages/apps-config/src/links/index.ts b/pioneer/packages/apps-config/src/links/index.ts new file mode 100644 index 0000000000..5f0daa645a --- /dev/null +++ b/pioneer/packages/apps-config/src/links/index.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ExternalDef } from './types'; + +import Commonwealth from './commonwealth'; +import Polkascan from './polkascan'; +import Polkassembly from './polkassembly'; +import Subscan from './subscan'; + +const externals: Record = { + Commonwealth, + Polkascan, + Polkassembly, + Subscan +}; + +export default externals; diff --git a/pioneer/packages/apps-config/src/links/polkascan.ts b/pioneer/packages/apps-config/src/links/polkascan.ts new file mode 100644 index 0000000000..c345b8b931 --- /dev/null +++ b/pioneer/packages/apps-config/src/links/polkascan.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +export default { + chains: { + // 'Centrifuge Mainnet': 'centrifuge', + // Edgeware: 'edgeware', + // Kulupu: 'kulupu', + Kusama: 'kusama', + Polkadot: 'polkadot', + 'Polkadot CC1': 'polkadot-cc1' + }, + create: (chain: string, path: string, data: BN | number | string): string => + `https://polkascan.io/${chain}/${path}/${data.toString()}`, + isActive: true, + paths: { + address: 'account', + block: 'block', + council: 'council/motion', + extrinsic: 'transaction', + proposal: 'democracy/proposal', + referendum: 'democracy/referendum', + techcomm: 'techcomm/proposal', + treasury: 'treasury/proposal' + }, + url: 'https://polkascan.io/' +}; diff --git a/pioneer/packages/apps-config/src/links/polkassembly.ts b/pioneer/packages/apps-config/src/links/polkassembly.ts new file mode 100644 index 0000000000..a9683669d7 --- /dev/null +++ b/pioneer/packages/apps-config/src/links/polkassembly.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +export default { + chains: { + Kusama: 'kusama', + 'Kusama CC3': 'kusama' + }, + create: (chain: string, path: string, data: BN | number | string): string => + `https://${chain}.polkassembly.io/${path}/${data.toString()}`, + isActive: true, + paths: { + council: 'motion', + proposal: 'proposal', + referendum: 'referendum', + treasury: 'treasury' + }, + url: 'https://polkassembly.io/' +}; diff --git a/pioneer/packages/apps-config/src/links/subscan.ts b/pioneer/packages/apps-config/src/links/subscan.ts new file mode 100644 index 0000000000..f12d061292 --- /dev/null +++ b/pioneer/packages/apps-config/src/links/subscan.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +export default { + chains: { + Edgeware: 'edgeware', + Kulupu: 'kulupu', + Kusama: 'kusama', + 'Kusama CC3': 'kusama', + Polkadot: 'polkadot', + 'Polkadot CC1': 'polkadot-cc1', + Westend: 'westend' + }, + create: (chain: string, path: string, data: BN | number | string): string => + `https://${chain}.subscan.io/${path}/${data.toString()}`, + isActive: true, + paths: { + address: 'account', + block: 'block', + council: 'council', + extrinsic: 'extrinsic', + proposal: 'democracy_proposal', + referendum: 'referenda', + techcomm: 'tech', + treasury: 'treasury' + }, + url: 'https://subscan.io/' +}; diff --git a/pioneer/packages/apps-config/src/links/types.ts b/pioneer/packages/apps-config/src/links/types.ts new file mode 100644 index 0000000000..4098571852 --- /dev/null +++ b/pioneer/packages/apps-config/src/links/types.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +export type LinkTypes = 'address' | 'block' | 'council' | 'extrinsic' | 'proposal' | 'referendum' | 'techcomm' | 'treasury'; + +export interface ExternalDef { + chains: Record; + isActive: boolean; + paths: Partial>; + url: string; + create: (chain: string, path: string, data: BN | number | string, hash?: string) => string; +} diff --git a/pioneer/packages/apps-config/src/settings/endpoints.ts b/pioneer/packages/apps-config/src/settings/endpoints.ts new file mode 100644 index 0000000000..c7d662f519 --- /dev/null +++ b/pioneer/packages/apps-config/src/settings/endpoints.ts @@ -0,0 +1,174 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { TFunction } from 'i18next'; +import { Option } from './types'; + +interface LinkOption extends Option { + dnslink?: string; +} + +interface EnvWindow { + // eslint-disable-next-line camelcase + process_env?: { + WS_URL: string; + } +} + +function createDev (t: TFunction): LinkOption[] { + return [ + { + dnslink: 'local', + info: 'local', + text: t('rpc.local', 'Local Node (Own, 127.0.0.1:9944)', { ns: 'apps-config' }), + value: 'ws://127.0.0.1:9944/' + } + ]; +} + +function createLive (t: TFunction): LinkOption[] { + return [ + { + info: 'joystream', + text: t('rpc.joystream', 'Joystream (Current Testnet, hosted by Jsgenesis)', { ns: 'apps-config' }), + value: 'wss://rome-rpc-endpoint.joystream.org:9944' + }, + { + dnslink: 'polkadot', + info: 'polkadot', + text: t('rpc.polkadot.parity', 'Polkadot (Live, hosted by Parity)', { ns: 'apps-config' }), + value: 'wss://rpc.polkadot.io' + }, + { + dnslink: 'polkadot', + info: 'polkadot', + text: t('rpc.polkadot.w3f', 'Polkadot (Live, hosted by Web3 Foundation)', { ns: 'apps-config' }), + value: 'wss://cc1-1.polkadot.network' + }, + { + dnslink: 'kusama', + info: 'kusama', + text: t('rpc.kusama.parity', 'Kusama (Polkadot Canary, hosted by Parity)', { ns: 'apps-config' }), + value: 'wss://kusama-rpc.polkadot.io/' + }, + { + dnslink: 'kusama', + info: 'kusama', + text: t('rpc.kusama.w3f', 'Kusama (Polkadot Canary, hosted by Web3 Foundation)', { ns: 'apps-config' }), + value: 'wss://cc3-5.kusama.network/' + }, + { + dnslink: 'kusama', + info: 'kusama', + text: t('rpc.kusama.ava', 'Kusama (Polkadot Canary, user-run public nodes; see https://status.cloud.ava.do/)', { ns: 'apps-config' }), + value: 'wss://kusama.polkadot.cloud.ava.do/' + }, + { + dnslink: 'centrifuge', + info: 'centrifuge', + text: t('rpc.centrifuge', 'Centrifuge (Mainnet, hosted by Centrifuge)', { ns: 'apps-config' }), + value: 'wss://fullnode.centrifuge.io' + }, + { + dnslink: 'edgeware', + info: 'edgeware', + text: t('rpc.edgeware', 'Edgeware (Mainnet, hosted by Commonwealth Labs)', { ns: 'apps-config' }), + value: 'wss://mainnet1.edgewa.re' + }, + { + dnslink: 'kulupu', + info: 'substrate', + text: t('rpc.kulupu', 'Kulupu (Kulupu Mainnet, hosted by Kulupu)', { ns: 'apps-config' }), + value: 'wss://rpc.kulupu.network/ws' + } + ]; +} + +function createTest (t: TFunction): LinkOption[] { + return [ + { + dnslink: 'westend', + info: 'westend', + text: t('rpc.westend', 'Westend (Polkadot Testnet, hosted by Parity)', { ns: 'apps-config' }), + value: 'wss://westend-rpc.polkadot.io' + }, + { + info: 'acala', + text: t('rpc.mandala', 'Mandala (Acala Testnet, hosted by Acala)', { ns: 'apps-config' }), + value: 'wss://node-6684611762228215808.jm.onfinality.io/ws' + }, + { + info: 'edgeware', + text: t('rpc.berlin', 'Berlin (Edgeware Testnet, hosted by Commonwealth Labs)', { ns: 'apps-config' }), + value: 'wss://berlin1.edgewa.re' + }, + { + info: 'substrate', + text: t('rpc.flamingfir', 'Flaming Fir (Substrate Testnet, hosted by Parity)', { ns: 'apps-config' }), + value: 'wss://substrate-rpc.parity.io/' + }, + { + info: 'nodle', + text: t('rpc.arcadia', 'Arcadia (Nodle Testnet, hosted by Nodle)', { ns: 'apps-config' }), + value: 'wss://arcadia1.nodleprotocol.io/' + }, + { + info: 'datahighway', + isDisabled: true, + text: t('rpc.datahighway.harbour', 'Harbour (DataHighway Testnet, hosted by MXC)', { ns: 'apps-config' }), + value: 'wss://testnet-harbour.datahighway.com' + } + ]; +} + +function createCustom (t: TFunction): LinkOption[] { + const WS_URL = ( + (typeof process !== 'undefined' ? process.env?.WS_URL : undefined) || + (typeof window !== 'undefined' ? (window as EnvWindow).process_env?.WS_URL : undefined) + ); + + return WS_URL + ? [ + { + isHeader: true, + text: t('rpc.custom', 'Custom environment', { ns: 'apps-config' }), + value: '' + }, + { + info: 'WS_URL', + text: t('rpc.custom.entry', 'Custom {{WS_URL}}', { ns: 'apps-config', replace: { WS_URL } }), + value: WS_URL + } + ] + : []; +} + +// The available endpoints that will show in the dropdown. For the most part (with the exception of +// Polkadot) we try to keep this to live chains only, with RPCs hosted by the community/chain vendor +// info: The chain logo name as defined in ../logos, specifically in namedLogos +// text: The text to display on teh dropdown +// value: The actual hosted secure websocket endpoint +export default function create (t: TFunction): LinkOption[] { + return [ + ...createCustom(t), + { + isHeader: true, + text: t('rpc.header.live', 'Live networks', { ns: 'apps-config' }), + value: '' + }, + ...createLive(t), + { + isHeader: true, + text: t('rpc.header.test', 'Test networks', { ns: 'apps-config' }), + value: '' + }, + ...createTest(t), + { + isHeader: true, + text: t('rpc.header.dev', 'Development', { ns: 'apps-config' }), + value: '' + }, + ...createDev(t) + ].filter(({ isDisabled }) => !isDisabled); +} diff --git a/pioneer/packages/apps-config/src/settings/index.ts b/pioneer/packages/apps-config/src/settings/index.ts new file mode 100644 index 0000000000..0f4c4535f7 --- /dev/null +++ b/pioneer/packages/apps-config/src/settings/index.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import createEndpoints from './endpoints'; +import createLanguages from './languages'; +import createSs58 from './ss58'; + +export { + createEndpoints, + createLanguages, + createSs58 +}; diff --git a/pioneer/packages/apps-config/src/settings/languages.ts b/pioneer/packages/apps-config/src/settings/languages.ts new file mode 100644 index 0000000000..fdf8b09603 --- /dev/null +++ b/pioneer/packages/apps-config/src/settings/languages.ts @@ -0,0 +1,45 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { TFunction } from 'i18next'; +import { Option } from './types'; + +export default function create (t: TFunction): Option[] { + return [ + { + text: t('lng.detect', 'Default browser language (auto-detect)', { ns: 'apps-config' }), + value: 'default' + }, + // default/native + { + text: 'English', + value: 'en' + }, + // translations (sorted by language code) + { + text: 'عربى', + value: 'ar' + }, + { + text: 'Español', + value: 'es' + }, + { + text: '日本語', + value: 'ja' + }, + { + text: 'Português', + value: 'pt' + }, + { + text: 'русский', + value: 'ru' + }, + { + text: '汉语', + value: 'zh' + } + ]; +} diff --git a/pioneer/packages/apps-config/src/settings/ss58.ts b/pioneer/packages/apps-config/src/settings/ss58.ts new file mode 100644 index 0000000000..cbf61cdd01 --- /dev/null +++ b/pioneer/packages/apps-config/src/settings/ss58.ts @@ -0,0 +1,45 @@ +// Copyright 2017-2020 @polkadot/ui-settings authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { TFunction } from 'i18next'; +import { Option } from './types'; + +// Definitions here are with the following values - +// info: the name of a logo as defined in ../logos, specifically in namedLogos +// text: The text you wish to display in the dropdown +// value: The actual ss5Format value (as registered) +export default function create (t: TFunction): Option[] { + return [ + { + info: 'default', + text: t('ss58.default', 'Default for the connected node', { ns: 'apps-config' }), + value: -1 + }, + { + info: 'substrate', + text: t('ss58.substrate', 'Substrate (generic)', { ns: 'apps-config' }), + value: 42 + }, + { + info: 'polkadot', + text: t('ss58.polkadot', 'Polkadot (live)', { ns: 'apps-config' }), + value: 0 + }, + { + info: 'kusama', + text: t('ss58.kusama', 'Kusama (canary)', { ns: 'apps-config' }), + value: 2 + }, + { + info: 'edgeware', + text: t('ss58.edgeware', 'Edgeware (live)', { ns: 'apps-config' }), + value: 7 + }, + { + info: 'centrifuge', + text: t('ss58.centrifuge', 'Centrifuge (live)', { ns: 'apps-config' }), + value: 36 + } + ]; +} diff --git a/pioneer/packages/apps-config/src/settings/types.ts b/pioneer/packages/apps-config/src/settings/types.ts new file mode 100644 index 0000000000..3b8a8431c7 --- /dev/null +++ b/pioneer/packages/apps-config/src/settings/types.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export interface Option { + info?: string; + isDisabled?: boolean; + isHeader?: boolean; + text: React.ReactNode; + value: string | number; +} diff --git a/pioneer/packages/apps-config/src/ui/general/index.ts b/pioneer/packages/apps-config/src/ui/general/index.ts new file mode 100644 index 0000000000..969454725c --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/general/index.ts @@ -0,0 +1,50 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const defaultColor = undefined; // '#f19135' +const emptyColor = '#999'; + +const chainKulupu = '#003366'; +const chainPolkadot = '#e6007a'; +const chainKusama = '#000'; // '#d32e79'; +const chainWestend = '#da68a7'; + +const nodeCentrifuge = '#fcc367'; +const nodeEdgeware = '#0a95df'; +const nodeNodle = '#1ab394'; + +// overrides based on the actual matched chain name +const chainColors: Record = [ + ['Kulupu', chainKulupu], + ['Kusama', chainKusama], + ['Kusama CC1', chainKusama], + ['Kusama CC2', chainKusama], + ['Kusama CC3', chainKusama], + ['Polkadot', chainPolkadot], + ['Polkadot CC1', chainPolkadot], + ['Westend', chainWestend] +].reduce((colors, [chain, color]): Record => ({ + ...colors, + [chain.toLowerCase()]: color +}), {}); + +// overrides based on the actual software node type (all '-' converted to ' ') +const nodeColors: Record = [ + ['centrifuge chain', nodeCentrifuge], + ['edgeware node', nodeEdgeware], + ['nodle chain node', nodeNodle] + // ['node template', emptyColor], + // ['parity polkadot', emptyColor], + // ['substrate node', emptyColor] +].reduce((colors, [node, color]): Record => ({ + ...colors, + [node.toLowerCase().replace(/-/g, ' ')]: color +}), {}); + +export { + defaultColor, + chainColors, + emptyColor, + nodeColors +}; diff --git a/pioneer/packages/apps-config/src/ui/identityIcons/index.ts b/pioneer/packages/apps-config/src/ui/identityIcons/index.ts new file mode 100644 index 0000000000..6a30f3db62 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/identityIcons/index.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// overrides based on the actual software node type +const identityNodes: Record = [ + ['centrifuge chain', 'polkadot'], + ['edgeware-node', 'substrate'], + ['joystream-node', 'beachball'], + ['node-template', 'substrate'], + ['parity-polkadot', 'polkadot'], + ['polkadot-js', 'polkadot'], + ['substrate-node', 'substrate'] +].reduce((icons, [spec, icon]): Record => ({ + ...icons, + [spec.toLowerCase().replace(/-/g, ' ')]: icon +}), {}); + +export { + identityNodes +}; diff --git a/pioneer/packages/apps-config/src/ui/index.ts b/pioneer/packages/apps-config/src/ui/index.ts new file mode 100644 index 0000000000..c5b15d0661 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/index.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { chainColors, nodeColors } from './general'; +import { identityNodes } from './identityIcons'; + +function sanitize (value?: string): string { + return value?.toLowerCase().replace('-', ' ') || ''; +} + +export function getSystemIcon (systemName: string): 'beachball' | 'polkadot' | 'substrate' { + return (identityNodes[systemName.toLowerCase().replace(/-/g, ' ')] || 'substrate') as 'substrate'; +} + +export const getSystemChainColor = (systemChain: string, systemName: string): string | undefined => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return chainColors[sanitize(systemChain)] || nodeColors[sanitize(systemName)]; +}; diff --git a/pioneer/packages/apps-config/src/ui/logos/chains/kusama-128.gif b/pioneer/packages/apps-config/src/ui/logos/chains/kusama-128.gif new file mode 100644 index 0000000000..5d800a287f Binary files /dev/null and b/pioneer/packages/apps-config/src/ui/logos/chains/kusama-128.gif differ diff --git a/pioneer/packages/apps-config/src/ui/logos/empty.svg b/pioneer/packages/apps-config/src/ui/logos/empty.svg new file mode 100644 index 0000000000..554e3dfe9b --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/empty.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/extensions/polkadot-js.svg b/pioneer/packages/apps-config/src/ui/logos/extensions/polkadot-js.svg new file mode 100644 index 0000000000..7b60266ace --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/extensions/polkadot-js.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/index.ts b/pioneer/packages/apps-config/src/ui/logos/index.ts new file mode 100644 index 0000000000..b4d0036715 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/index.ts @@ -0,0 +1,84 @@ +// Copyright 2017-2020 @polkadot/apps-config authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + +// anything for a specific chain, most would probably fit into the node category (but allow for chain-specific) +import chainKusama from './chains/kusama-128.gif'; + +// defaults for the node type, assuming we don't have a specific chain, but rather match on the implementation +import nodeAcala from './nodes/acala-circle.svg'; +import nodeCentrifuge from './nodes/centrifuge.png'; +import nodeEdgeware from './nodes/edgeware-circle.svg'; +import nodeEncointerNotee from './nodes/encointer-notee.svg'; +import nodeEncointerTeeproxy from './nodes/encointer-teeproxy.svg'; +import nodeNodle from './nodes/nodle.svg'; +import nodePolkadot from './nodes/polkadot-circle.svg'; +import nodePolkadotJs from './nodes/polkadot-js.svg'; +import nodeSubstrate from './nodes/substrate-hexagon.svg'; +import nodeJoystream from './nodes/joystream-node.svg'; + +// extensions +import extensionPolkadotJs from './extensions/polkadot-js.svg'; + +// last-resort fallback, just something empty +import emptyLogo from './empty.svg'; + +// overrides based on the actual matched chain name +const chainLogos: Record = [ + ['Kusama', chainKusama], // new name after CC3 + ['Kusama CC1', chainKusama], + ['Kusama CC2', chainKusama], + ['Kusama CC3', chainKusama] +].reduce((logos, [chain, logo]): Record => ({ + ...logos, + [(chain as string).toLowerCase()]: logo +}), {}); + +// overrides based on the actual software node type (all '-' converted to ' ') +const nodeLogos: Record = [ + ['centrifuge chain', nodeCentrifuge], + ['Centrifuge Chain Node', nodeCentrifuge], + ['Edgeware Node', nodeEdgeware], + ['Encointer Node', nodeEncointerNotee], + ['Encointer Node noTEE', nodeEncointerNotee], + ['Encointer Node TEE proxy', nodeEncointerTeeproxy], + ['kulupu', nodeSubstrate], + ['node-template', nodeSubstrate], + ['Nodle Chain Node', nodeNodle], + ['parity-polkadot', nodePolkadot], + ['polkadot-js', nodePolkadotJs], + ['substrate-node', nodeSubstrate], + ['joystream-node', nodeJoystream] +].reduce((logos, [node, logo]): Record => ({ + ...logos, + [(node as string).toLowerCase().replace(/-/g, ' ')]: logo +}), {}); + +// overrides when we pass an explicit logo name +const namedLogos: Record = { + acala: nodeAcala, + alexander: nodePolkadot, + centrifuge: nodeCentrifuge, + edgeware: nodeEdgeware, + empty: emptyLogo, + kusama: chainKusama, + nodle: nodeNodle, + polkadot: nodePolkadot, + substrate: nodeSubstrate, + westend: nodePolkadot +}; + +// extension logos +const extensionLogos: Record = { + 'polkadot-js': extensionPolkadotJs +}; + +export { + chainLogos, + emptyLogo, + extensionLogos, + namedLogos, + nodeLogos +}; diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/acala-circle.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/acala-circle.svg new file mode 100644 index 0000000000..ef0d0aa565 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/acala-circle.svg @@ -0,0 +1,40 @@ + + + + Group 73 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/centrifuge.png b/pioneer/packages/apps-config/src/ui/logos/nodes/centrifuge.png new file mode 100644 index 0000000000..cee529cf4f Binary files /dev/null and b/pioneer/packages/apps-config/src/ui/logos/nodes/centrifuge.png differ diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/edgeware-circle.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/edgeware-circle.svg new file mode 100644 index 0000000000..cda424ab81 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/edgeware-circle.svg @@ -0,0 +1 @@ +logo-white-with-bgCreated with Sketch. \ No newline at end of file diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/edgeware.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/edgeware.svg new file mode 100644 index 0000000000..9735fcba60 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/edgeware.svg @@ -0,0 +1 @@ +logo-white-with-bgCreated with Sketch. \ No newline at end of file diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/encointer-notee.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/encointer-notee.svg new file mode 100644 index 0000000000..174e8d60ab --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/encointer-notee.svg @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/encointer-teeproxy.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/encointer-teeproxy.svg new file mode 100644 index 0000000000..8c0001d06b --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/encointer-teeproxy.svg @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/joystream-node.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/joystream-node.svg new file mode 100755 index 0000000000..67dd1b71f2 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/joystream-node.svg @@ -0,0 +1 @@ +Icon-mono-white-1bg-blue \ No newline at end of file diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/nodle.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/nodle.svg new file mode 100644 index 0000000000..be30c72c6f --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/nodle.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-circle.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-circle.svg new file mode 100644 index 0000000000..561ca97d18 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-circle.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-dots.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-dots.svg new file mode 100644 index 0000000000..ff6d17f008 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-dots.svg @@ -0,0 +1 @@ +Polkadot dot logo \ No newline at end of file diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-js.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-js.svg new file mode 100644 index 0000000000..7b60266ace --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-js.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-white.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-white.svg new file mode 100644 index 0000000000..94b215b991 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/polkadot-white.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-circle.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-circle.svg new file mode 100644 index 0000000000..eec7d998a4 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-circle.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-hexagon.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-hexagon.svg new file mode 100644 index 0000000000..45357eb5f4 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-hexagon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-white.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-white.svg new file mode 100644 index 0000000000..63c70e83ee --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/substrate-white.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/pioneer/packages/apps-electron/.gitignore b/pioneer/packages/apps-electron/.gitignore new file mode 100644 index 0000000000..775f4d1fa4 --- /dev/null +++ b/pioneer/packages/apps-electron/.gitignore @@ -0,0 +1,3 @@ +build +node_modules +release diff --git a/pioneer/packages/apps-electron/.skip-build b/pioneer/packages/apps-electron/.skip-build new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/apps-electron/.skip-npm b/pioneer/packages/apps-electron/.skip-npm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/apps-electron/README.md b/pioneer/packages/apps-electron/README.md new file mode 100644 index 0000000000..f71c0dc174 --- /dev/null +++ b/pioneer/packages/apps-electron/README.md @@ -0,0 +1,3 @@ +# @polkadot/apps-electron + +WARNING: This is not deemed stable yet for external use. diff --git a/pioneer/packages/apps-electron/appleEntitlements/entitlements.mac.plist b/pioneer/packages/apps-electron/appleEntitlements/entitlements.mac.plist new file mode 100644 index 0000000000..46f43d4a07 --- /dev/null +++ b/pioneer/packages/apps-electron/appleEntitlements/entitlements.mac.plist @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + + diff --git a/pioneer/packages/apps-electron/assets/icon.png b/pioneer/packages/apps-electron/assets/icon.png new file mode 100644 index 0000000000..f282e5a90e Binary files /dev/null and b/pioneer/packages/apps-electron/assets/icon.png differ diff --git a/pioneer/packages/apps-electron/package.json b/pioneer/packages/apps-electron/package.json new file mode 100644 index 0000000000..bbf8458b0a --- /dev/null +++ b/pioneer/packages/apps-electron/package.json @@ -0,0 +1,27 @@ +{ + "name": "@polkadot/apps-electron", + "main": "index.js", + "private": true, + "version": "0.51.1", + "dependencies": { + "@babel/core": "^7.10.5", + "@babel/polyfill": "^7.10.4", + "@polkadot/dev": "^0.55.28", + "@polkadot/react-components": "0.51.1", + "electron-log": "^4.2.2", + "electron-updater": "^4.3.4" + }, + "devDependencies": { + "@types/react": "^16.9.43", + "@types/react-dom": "^16.9.8", + "@types/tmp": "^0.2.0", + "babel-loader": "^8.1.0", + "copy-webpack-plugin": "^6.0.3", + "html-webpack-plugin": "^4.3.0", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "thread-loader": "^2.1.3", + "tmp": "^0.2.1", + "webpack": "^4.44.0" + } +} diff --git a/pioneer/packages/apps-electron/src/api/account-store-api.ts b/pioneer/packages/apps-electron/src/api/account-store-api.ts new file mode 100644 index 0000000000..0093a5a331 --- /dev/null +++ b/pioneer/packages/apps-electron/src/api/account-store-api.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringJson } from '@polkadot/ui-keyring/types'; + +export interface AccountStoreApi { + all: () => Promise<{ key: string, value: KeyringJson }[]> + get: (key: string) => Promise + remove: (key: string) => Promise + set: (key: string, value: KeyringJson) => Promise +} diff --git a/pioneer/packages/apps-electron/src/api/electron-main-api.ts b/pioneer/packages/apps-electron/src/api/electron-main-api.ts new file mode 100644 index 0000000000..1d315b794c --- /dev/null +++ b/pioneer/packages/apps-electron/src/api/electron-main-api.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AccountStoreApi } from './account-store-api'; + +export interface ElectronMainApi { + accountStore: AccountStoreApi +} diff --git a/pioneer/packages/apps-electron/src/api/global-exported-api.ts b/pioneer/packages/apps-electron/src/api/global-exported-api.ts new file mode 100644 index 0000000000..ade5235c82 --- /dev/null +++ b/pioneer/packages/apps-electron/src/api/global-exported-api.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ElectronMainApi } from './electron-main-api'; + +declare global { + interface Window { + ElectronMain: ElectronMainApi + } +} + +export const electronMainApi = window.ElectronMain; diff --git a/pioneer/packages/apps-electron/src/electron/autoUpdater.ts b/pioneer/packages/apps-electron/src/electron/autoUpdater.ts new file mode 100644 index 0000000000..210c6471db --- /dev/null +++ b/pioneer/packages/apps-electron/src/electron/autoUpdater.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AppUpdater } from 'electron-updater'; + +export async function setupAutoUpdater (): Promise { + const { autoUpdater } = await import('electron-updater'); + + await setLogger(autoUpdater); + autoUpdater.checkForUpdatesAndNotify().catch(console.error); +} + +async function setLogger (autoUpdater: AppUpdater): Promise { + const log = await import('electron-log'); + + log.transports.file.level = 'debug'; + autoUpdater.logger = log; +} diff --git a/pioneer/packages/apps-electron/src/electron/contentSecurityPolicy.ts b/pioneer/packages/apps-electron/src/electron/contentSecurityPolicy.ts new file mode 100644 index 0000000000..0eb86047d9 --- /dev/null +++ b/pioneer/packages/apps-electron/src/electron/contentSecurityPolicy.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { HeadersReceivedResponse, session } from 'electron'; + +export const setupContentSecurityPolicy = (environment: string): void => { + session.defaultSession.webRequest.onHeadersReceived((details, cb: (headersReceivedResponse: HeadersReceivedResponse) => void) => { + const headersReceivedResponse = { + responseHeaders: { + ...details.responseHeaders, + 'Content-Security-Policy': [`default-src 'self' ${environment === 'development' ? "'unsafe-eval'" : ''};` + + " style-src-elem 'self' https://fonts.googleapis.com/css 'unsafe-inline';" + + " font-src data: 'self' https://fonts.gstatic.com;" + + " style-src 'unsafe-inline';" + + " connect-src 'self' wss:;" + + " img-src 'self' data:"] + } + }; + + cb(headersReceivedResponse); + }); +}; diff --git a/pioneer/packages/apps-electron/src/electron/index.ts b/pioneer/packages/apps-electron/src/electron/index.ts new file mode 100644 index 0000000000..5e5524bff2 --- /dev/null +++ b/pioneer/packages/apps-electron/src/electron/index.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { app, shell } from 'electron'; +import { registerAccountStoreHandlers } from '../main/account-store'; +import { setupAutoUpdater } from './autoUpdater'; +import { setupContentSecurityPolicy } from './contentSecurityPolicy'; +import { createWindow } from './window'; + +const ENV = process.env.NODE_ENV || 'production'; + +const onReady = async () => { + registerAccountStoreHandlers(); + setupContentSecurityPolicy(ENV); + await createWindow(ENV); + await setupAutoUpdater(); +}; + +app.on('web-contents-created', (e, webContents) => { + webContents.on('new-window', (e, url) => { + e.preventDefault(); + shell.openExternal(url).catch(console.error); + }); +}); + +app.whenReady().then(onReady).catch(console.error); diff --git a/pioneer/packages/apps-electron/src/electron/window.ts b/pioneer/packages/apps-electron/src/electron/window.ts new file mode 100644 index 0000000000..804267adea --- /dev/null +++ b/pioneer/packages/apps-electron/src/electron/window.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { BrowserWindow, screen } from 'electron'; +import path from 'path'; + +export function createWindow (environment: string): Promise { + const { height, width } = screen.getPrimaryDisplay().workAreaSize; + + const win = new BrowserWindow({ + height, + webPreferences: { + contextIsolation: true, + enableRemoteModule: false, + nodeIntegration: false, + preload: path.join(__dirname, 'preload.js') + }, + width + }); + + if (environment === 'development') { + win.webContents.openDevTools(); + + return win.loadURL('http://0.0.0.0:3000/'); + } + + const mainFilePath = path.resolve(__dirname, 'index.html'); + + return win.loadFile(mainFilePath); +} diff --git a/pioneer/packages/apps-electron/src/index.tsx b/pioneer/packages/apps-electron/src/index.tsx new file mode 100644 index 0000000000..279485ce6f --- /dev/null +++ b/pioneer/packages/apps-electron/src/index.tsx @@ -0,0 +1,56 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// setup these right at front +import '@polkadot/apps/initSettings'; +import 'semantic-ui-css/semantic.min.css'; +import '@polkadot/react-components/i18n'; + +import React, { Suspense } from 'react'; +import ReactDOM from 'react-dom'; +import { HashRouter } from 'react-router-dom'; +import { ThemeProvider } from 'styled-components'; +import { Api } from '@polkadot/react-api'; +import Apps from '@polkadot/apps/Apps'; +import WindowDimensions from '@polkadot/apps/WindowDimensions'; +import Queue from '@polkadot/react-components/Status/Queue'; +import { BlockAuthors, Events } from '@polkadot/react-query'; +import settings from '@polkadot/ui-settings'; + +import { electronMainApi } from './api/global-exported-api'; +import { RemoteElectronStore } from './renderer/remote-electron-store'; + +const rootId = 'root'; +const rootElement = document.getElementById(rootId); +const theme = { theme: settings.uiTheme }; + +const store = new RemoteElectronStore(electronMainApi.accountStore); + +if (!rootElement) { + throw new Error(`Unable to find element with id '${rootId}'`); +} + +ReactDOM.render( + + + + + + + + + + + + + + + + + , + rootElement +); diff --git a/pioneer/packages/apps-electron/src/main/account-store.spec.ts b/pioneer/packages/apps-electron/src/main/account-store.spec.ts new file mode 100644 index 0000000000..d6f754ef59 --- /dev/null +++ b/pioneer/packages/apps-electron/src/main/account-store.spec.ts @@ -0,0 +1,60 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { FileStore } from '@polkadot/ui-keyring/stores'; +import { accountStoreIpcHandler } from './account-store'; +import { KeyringJson } from '@polkadot/ui-keyring/types'; +import { IpcMainHandler } from './ipc-main-handler'; +import * as tmp from 'tmp'; + +const exampleAccount = (address: string): KeyringJson => ({ + address, + meta: {} +}); + +describe('Account store', () => { + let accountStore: IpcMainHandler; + let tmpDir: tmp.DirResult; + + beforeEach(() => { + tmpDir = tmp.dirSync({ unsafeCleanup: true }); + accountStore = accountStoreIpcHandler(new FileStore(tmpDir.name)); + }); + + afterEach(() => { + tmpDir.removeCallback(); + }); + + it('all returns empty array at first', () => { + expect(accountStore['account-store-all']()).toEqual([]); + }); + + it('after adding accounts, they become visible', async () => { + await accountStore['account-store-set']('1', exampleAccount('a')); + await accountStore['account-store-set']('2', exampleAccount('b')); + + expect(accountStore['account-store-all']()).toEqual([{ + key: '1', value: exampleAccount('a') + }, { + key: '2', value: exampleAccount('b') + }]); + }); + + it('get returns account if exists', async () => { + await accountStore['account-store-set']('1', exampleAccount('a')); + expect(await accountStore['account-store-get']('1')).toEqual(exampleAccount('a')); + }); + + it('get returns null if account does not exist', async () => { + expect(await accountStore['account-store-get']('1')).toEqual(null); + }); + + it('account disappears from list after it is removed', async () => { + await accountStore['account-store-set']('1', exampleAccount('a')); + await accountStore['account-store-remove']('1'); + + expect(await accountStore['account-store-get']('1')).toEqual(null); + expect(accountStore['account-store-all']()).toEqual([]); + }); +}); diff --git a/pioneer/packages/apps-electron/src/main/account-store.ts b/pioneer/packages/apps-electron/src/main/account-store.ts new file mode 100644 index 0000000000..d842b52092 --- /dev/null +++ b/pioneer/packages/apps-electron/src/main/account-store.ts @@ -0,0 +1,47 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { app } from 'electron'; +import { FileStore } from '@polkadot/ui-keyring/stores'; +import { KeyringJson } from '@polkadot/ui-keyring/types'; +import path from 'path'; + +import { IpcMainHandler } from './ipc-main-handler'; +import { registerIpcHandler } from './register-ipc-handler'; + +const ACCOUNTS_SUBFOLDER = 'polkadot-accounts'; + +export const accountStoreIpcHandler = (fileStore: FileStore): IpcMainHandler => ({ + 'account-store-all': () => { + let result: { key: string, value: KeyringJson }[] = []; + + const collect = (key: string, value: KeyringJson) => { + result = [...result, { key, value }]; + }; + + fileStore.all(collect); + + return result; + }, + 'account-store-get': async (key: string) => new Promise((resolve) => { + try { + fileStore.get(key, resolve); + } catch (err) { + resolve(null); + } + }), + 'account-store-remove': async (key: string) => new Promise((resolve) => + fileStore.remove(key, resolve) + ), + 'account-store-set': async (key: string, value: KeyringJson) => new Promise((resolve) => + fileStore.set(key, value, resolve) + ) +}); + +export const registerAccountStoreHandlers = (): void => { + const defaultStorePath = path.join(app.getPath('userData'), ACCOUNTS_SUBFOLDER); + const fileStore = new FileStore(defaultStorePath); + + registerIpcHandler(accountStoreIpcHandler(fileStore)); +}; diff --git a/pioneer/packages/apps-electron/src/main/ipc-main-handler.ts b/pioneer/packages/apps-electron/src/main/ipc-main-handler.ts new file mode 100644 index 0000000000..9b752118b6 --- /dev/null +++ b/pioneer/packages/apps-electron/src/main/ipc-main-handler.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type IpcMainHandler = { + [channel: string]: (...args: any[]) => Promise | any +} diff --git a/pioneer/packages/apps-electron/src/main/register-ipc-handler.ts b/pioneer/packages/apps-electron/src/main/register-ipc-handler.ts new file mode 100644 index 0000000000..938657bf72 --- /dev/null +++ b/pioneer/packages/apps-electron/src/main/register-ipc-handler.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ipcMain } from 'electron'; +import { IpcMainHandler } from './ipc-main-handler'; + +export const registerIpcHandler = (ipcHandler: IpcMainHandler): void => { + for (const [channel, listener] of Object.entries(ipcHandler)) { + ipcMain.handle(channel, (_, ...args): void => { + listener(...args); + }); + } +}; diff --git a/pioneer/packages/apps-electron/src/preload.ts b/pioneer/packages/apps-electron/src/preload.ts new file mode 100644 index 0000000000..cfa75dd27c --- /dev/null +++ b/pioneer/packages/apps-electron/src/preload.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringJson } from '@polkadot/ui-keyring/types'; +import { contextBridge, ipcRenderer } from 'electron'; + +contextBridge.exposeInMainWorld('ElectronMain', { + accountStore: { + all: () => ipcRenderer.invoke('account-store-all'), + get: (key: string) => ipcRenderer.invoke('account-store-get', key), + remove: (key: string) => ipcRenderer.invoke('account-store-remove', key), + set: (key: string, value: KeyringJson) => ipcRenderer.invoke('account-store-set', key, value) + } +}); diff --git a/pioneer/packages/apps-electron/src/renderer/remote-electron-store.spec.ts b/pioneer/packages/apps-electron/src/renderer/remote-electron-store.spec.ts new file mode 100644 index 0000000000..c546c67cef --- /dev/null +++ b/pioneer/packages/apps-electron/src/renderer/remote-electron-store.spec.ts @@ -0,0 +1,90 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RemoteElectronStore } from './remote-electron-store'; + +describe('Remote Electron Store', () => { + const accountStore = { + all: jest.fn(), + get: jest.fn(), + remove: jest.fn(), + set: jest.fn() + }; + const remoteStore = new RemoteElectronStore(accountStore); + + beforeEach(() => { + accountStore.all.mockClear(); + accountStore.get.mockClear(); + accountStore.remove.mockClear(); + accountStore.set.mockClear(); + }); + + describe('all', () => { + it('calls callback for each returned account', async () => { + accountStore.all.mockResolvedValue([{ + key: 1, + value: 'a' + }, { + key: 2, + value: 'b' + }]); + const cb = jest.fn(); + + remoteStore.all(cb); + await Promise.resolve(); + + expect(cb).nthCalledWith(1, 1, 'a'); + expect(cb).nthCalledWith(2, 2, 'b'); + }); + }); + + describe('get', () => { + it('calls callback with returned account', async () => { + accountStore.get.mockResolvedValue('a'); + const cb = jest.fn(); + + remoteStore.get('1', cb); + await Promise.resolve(); + + expect(accountStore.get).toBeCalledWith('1'); + expect(cb).toBeCalledWith('a'); + }); + + it('calls callback with null if no accounts found', async () => { + accountStore.get.mockResolvedValue(null); + const cb = jest.fn(); + + remoteStore.get('1', cb); + await Promise.resolve(); + + expect(cb).toBeCalledWith(null); + }); + }); + + describe('remove', () => { + it('calls callback after success', async () => { + accountStore.remove.mockResolvedValue(null); + const cb = jest.fn(); + + remoteStore.remove('1', cb); + await Promise.resolve(); + + expect(accountStore.remove).toBeCalledWith('1'); + expect(cb).toBeCalledTimes(1); + }); + }); + + describe('set', () => { + it('calls callback after success', async () => { + accountStore.set.mockResolvedValue(null); + const cb = jest.fn(); + + remoteStore.set('1', 'a' as any, cb); + await Promise.resolve(); + + expect(accountStore.set).toBeCalledWith('1', 'a'); + expect(cb).toBeCalledTimes(1); + }); + }); +}); diff --git a/pioneer/packages/apps-electron/src/renderer/remote-electron-store.ts b/pioneer/packages/apps-electron/src/renderer/remote-electron-store.ts new file mode 100644 index 0000000000..d0e9a9d2fb --- /dev/null +++ b/pioneer/packages/apps-electron/src/renderer/remote-electron-store.ts @@ -0,0 +1,41 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringJson, KeyringStore } from '@polkadot/ui-keyring/types'; +import { AccountStoreApi } from '../api/account-store-api'; + +export class RemoteElectronStore implements KeyringStore { + readonly #accountStore: AccountStoreApi; + + constructor (accountStore: AccountStoreApi) { + this.#accountStore = accountStore; + } + + all (cb: (key: string, value: KeyringJson) => void): void { + this.#accountStore.all() + .then((result: { key: string, value: KeyringJson }[]) => result?.forEach(({ key, value }) => cb(key, value))) + .catch((e: Error) => { + throw new Error(`error getting all accounts: ${e.message}`); + }); + } + + get (key: string, cb: (value: KeyringJson) => void): void { + this.#accountStore.get(key) + .then(cb).catch((e: Error) => { + throw new Error(`error storing account: ${e.message}`); + }); + } + + remove (key: string, cb: (() => void) | undefined): void { + this.#accountStore.remove(key).then(cb).catch((e: Error) => { + throw new Error(`error removing account: ${e.message}`); + }); + } + + set (key: string, value: KeyringJson, cb: (() => void) | undefined): void { + this.#accountStore.set(key, value).then(cb).catch((e: Error) => { + throw new Error(`error saving account: ${e.message}`); + }); + } +} diff --git a/pioneer/packages/apps-electron/webpack.main.config.js b/pioneer/packages/apps-electron/webpack.main.config.js new file mode 100644 index 0000000000..b89d068495 --- /dev/null +++ b/pioneer/packages/apps-electron/webpack.main.config.js @@ -0,0 +1,55 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/* eslint-disable camelcase */ + +const TerserPlugin = require('terser-webpack-plugin'); +const path = require('path'); + +const ENV = process.env.NODE_ENV || 'production'; +const isProd = ENV === 'production'; + +function createWebpack () { + return [ + { + entry: { + electron: './src/electron', + preload: './src/preload.ts' + }, + mode: ENV, + module: { + rules: [ + { + exclude: /(node_modules)/, + test: /\.(js|ts|tsx)$/, + use: [ + require.resolve('thread-loader'), + { + loader: require.resolve('babel-loader'), + options: require('@polkadot/dev/config/babel') + } + ] + } + ] + }, + node: { + __dirname: false + }, + optimization: { + minimize: !!isProd, + minimizer: [new TerserPlugin()] + }, + output: { + filename: '[name].js', + path: path.join(__dirname, '/build') + }, + resolve: { + extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'] + }, + target: 'electron-main' + } + ]; +} + +module.exports = createWebpack(); diff --git a/pioneer/packages/apps-electron/webpack.renderer.config.js b/pioneer/packages/apps-electron/webpack.renderer.config.js new file mode 100644 index 0000000000..e7c2040159 --- /dev/null +++ b/pioneer/packages/apps-electron/webpack.renderer.config.js @@ -0,0 +1,32 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/* eslint-disable camelcase */ + +const path = require('path'); +const merge = require('webpack-merge'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const baseConfig = require('@polkadot/apps/webpack.base.config'); + +const ENV = process.env.NODE_ENV || 'production'; +const isProd = ENV === 'production'; +const context = __dirname; + +module.exports = merge( + baseConfig(ENV, context), + { + devtool: isProd ? 'none' : 'source-map', + plugins: [ + // It must be placed before HtmlWebpackPlugin + new CopyWebpackPlugin({ patterns: [{ from: '../apps/public' }] }), + new HtmlWebpackPlugin({ + PAGE_TITLE: 'Polkadot/Substrate Portal', + inject: true, + template: path.join(context, '../apps/public/index.html') + }) + ], + target: 'web' + } +); diff --git a/pioneer/packages/apps-routing/.skip-build b/pioneer/packages/apps-routing/.skip-build new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/apps-routing/.skip-npm b/pioneer/packages/apps-routing/.skip-npm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/apps-routing/LICENSE b/pioneer/packages/apps-routing/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/pioneer/packages/apps-routing/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pioneer/packages/apps-routing/README.md b/pioneer/packages/apps-routing/README.md new file mode 100644 index 0000000000..c200610646 --- /dev/null +++ b/pioneer/packages/apps-routing/README.md @@ -0,0 +1,3 @@ +# @polkadot/apps-routing + +The routing config for the application diff --git a/pioneer/packages/apps-routing/package.json b/pioneer/packages/apps-routing/package.json new file mode 100644 index 0000000000..74ce544316 --- /dev/null +++ b/pioneer/packages/apps-routing/package.json @@ -0,0 +1,16 @@ +{ + "name": "@polkadot/apps-routing", + "private": true, + "version": "0.51.1", + "main": "index.js", + "repository": "https://github.com/polkadot-js/apps.git", + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.10.5" + } +} diff --git a/pioneer/packages/apps-routing/src/accounts.ts b/pioneer/packages/apps-routing/src/accounts.ts new file mode 100644 index 0000000000..e31c435485 --- /dev/null +++ b/pioneer/packages/apps-routing/src/accounts.ts @@ -0,0 +1,20 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component, { useCounter } from '@polkadot/app-accounts'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [] + }, + icon: 'users', + name: 'accounts', + text: t('nav.accounts', 'Accounts', { ns: 'apps-routing' }), + useCounter + }; +} diff --git a/pioneer/packages/apps-routing/src/claims.ts b/pioneer/packages/apps-routing/src/claims.ts new file mode 100644 index 0000000000..510a87d809 --- /dev/null +++ b/pioneer/packages/apps-routing/src/claims.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component, { useCounter } from '@polkadot/app-claims'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsAccounts: true, + needsApi: [ + 'query.claims.claims' + ] + }, + icon: 'star', + name: 'claims', + text: t('nav.claims', 'Claim Tokens', { ns: 'apps-routing' }), + useCounter + }; +} diff --git a/pioneer/packages/apps-routing/src/contracts.ts b/pioneer/packages/apps-routing/src/contracts.ts new file mode 100644 index 0000000000..a3167e97d3 --- /dev/null +++ b/pioneer/packages/apps-routing/src/contracts.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-contracts'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsAccounts: true, + needsApi: [ + 'tx.contracts.call' + ] + }, + icon: 'compress', + name: 'contracts', + text: t('nav.contracts', 'Contracts', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/council.ts b/pioneer/packages/apps-routing/src/council.ts new file mode 100644 index 0000000000..2858736b9e --- /dev/null +++ b/pioneer/packages/apps-routing/src/council.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component, { useCounter } from '@polkadot/app-council'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [ + [ + 'query.electionsPhragmen.candidates', + 'query.elections.candidates' + ] + ] + }, + icon: 'building', + name: 'council', + text: t('nav.council', 'Council', { ns: 'apps-routing' }), + useCounter + }; +} diff --git a/pioneer/packages/apps-routing/src/dashboard.ts b/pioneer/packages/apps-routing/src/dashboard.ts new file mode 100644 index 0000000000..9550365cbb --- /dev/null +++ b/pioneer/packages/apps-routing/src/dashboard.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-dashboard'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + isHidden: true + }, + icon: 'th', + name: 'dashboard', + text: t('nav.dashboard', 'Dashboard', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/democracy.ts b/pioneer/packages/apps-routing/src/democracy.ts new file mode 100644 index 0000000000..b90c0e08b4 --- /dev/null +++ b/pioneer/packages/apps-routing/src/democracy.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component, { useCounter } from '@polkadot/app-democracy'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [ + 'tx.democracy.notePreimage' + ] + }, + icon: 'calendar-check', + name: 'democracy', + text: t('nav.democracy', 'Democracy', { ns: 'apps-routing' }), + useCounter + }; +} diff --git a/pioneer/packages/apps-routing/src/explorer.ts b/pioneer/packages/apps-routing/src/explorer.ts new file mode 100644 index 0000000000..19a7180303 --- /dev/null +++ b/pioneer/packages/apps-routing/src/explorer.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-explorer'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [] + }, + icon: 'braille', + name: 'explorer', + text: t('nav.explorer', 'Explorer', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/extrinsics.ts b/pioneer/packages/apps-routing/src/extrinsics.ts new file mode 100644 index 0000000000..fa38a845dc --- /dev/null +++ b/pioneer/packages/apps-routing/src/extrinsics.ts @@ -0,0 +1,20 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-extrinsics'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsAccounts: true, + needsApi: [] + }, + icon: 'sync', + name: 'extrinsics', + text: t('nav.extrinsics', 'Extrinsics', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/generic-asset.ts b/pioneer/packages/apps-routing/src/generic-asset.ts new file mode 100644 index 0000000000..3e9103de2f --- /dev/null +++ b/pioneer/packages/apps-routing/src/generic-asset.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-generic-asset'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + isHidden: false, + needsAccounts: true, + needsApi: [ + 'tx.genericAsset.transfer' + ] + }, + icon: 'cubes', + name: 'generic-asset', + text: t('nav.generic-asset', 'Generic asset', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/index.ts b/pioneer/packages/apps-routing/src/index.ts new file mode 100644 index 0000000000..47899c3931 --- /dev/null +++ b/pioneer/packages/apps-routing/src/index.ts @@ -0,0 +1,57 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Routes } from './types'; + +import appSettings from '@polkadot/ui-settings'; + +// When adding here, also ensure to add to Dummy.tsx + +import accounts from './accounts'; +import explorer from './explorer'; +import extrinsics from './extrinsics'; +import js from './js'; +import settings from './settings'; +import staking from './staking'; +import storage from './storage'; +import sudo from './sudo'; +import toolbox from './toolbox'; +import transfer from './transfer'; +// Joy packages +import members from './joy-members'; +import { terms, privacyPolicy } from './joy-pages'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Routes { + return appSettings.uiMode === 'light' + ? [ + members(t), + staking(t), + null, + transfer(t), + accounts(t), + settings(t), + // Those are hidden + terms(t), + privacyPolicy(t) + ] + : [ + members(t), + staking(t), + null, + transfer(t), + accounts(t), + settings(t), + null, + explorer(t), + storage(t), + extrinsics(t), + js(t), + toolbox(t), + sudo(t), + null, + // Those are hidden + terms(t), + privacyPolicy(t) + ]; +} diff --git a/pioneer/packages/apps-routing/src/joy-members.ts b/pioneer/packages/apps-routing/src/joy-members.ts new file mode 100644 index 0000000000..e8c3712d4d --- /dev/null +++ b/pioneer/packages/apps-routing/src/joy-members.ts @@ -0,0 +1,15 @@ +import { Route } from './types'; + +import Members from '@polkadot/joy-members/index'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: Members, + display: { + needsApi: ['query.members.nextMemberId'] + }, + icon: 'users', + name: 'members', + text: t('nav.membership', 'Membership', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/joy-pages.ts b/pioneer/packages/apps-routing/src/joy-pages.ts new file mode 100644 index 0000000000..a39470e322 --- /dev/null +++ b/pioneer/packages/apps-routing/src/joy-pages.ts @@ -0,0 +1,27 @@ +import { Route } from './types'; + +import { ToS, Privacy } from '@polkadot/joy-pages/index'; + +export function terms (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: ToS, + display: { + isHidden: true + }, + text: t('nav.terms', 'Terms of Service', { ns: 'apps-routing' }), + icon: 'file', + name: 'pages/tos' + }; +} + +export function privacyPolicy (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: Privacy, + display: { + isHidden: true + }, + text: t('nav.privacy', 'Privacy Policy', { ns: 'apps-routing' }), + icon: 'file', + name: 'pages/privacy' + }; +} diff --git a/pioneer/packages/apps-routing/src/js.ts b/pioneer/packages/apps-routing/src/js.ts new file mode 100644 index 0000000000..116f684788 --- /dev/null +++ b/pioneer/packages/apps-routing/src/js.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-js'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [] + }, + icon: 'code', + name: 'js', + text: t('nav.js', 'Javascript', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/parachains.ts b/pioneer/packages/apps-routing/src/parachains.ts new file mode 100644 index 0000000000..3193fc4fcc --- /dev/null +++ b/pioneer/packages/apps-routing/src/parachains.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-parachains'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [ + 'query.parachains.code' + ] + }, + icon: 'link', + name: 'parachains', + text: t('nav.parachains', 'Parachains', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/poll.ts b/pioneer/packages/apps-routing/src/poll.ts new file mode 100644 index 0000000000..8be49bcd9c --- /dev/null +++ b/pioneer/packages/apps-routing/src/poll.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-poll'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsAccounts: true, + needsApi: [ + 'tx.poll.vote' + ] + }, + icon: 'podcast', + name: 'poll', + text: t('nav.poll', 'Token poll', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/settings.ts b/pioneer/packages/apps-routing/src/settings.ts new file mode 100644 index 0000000000..9a56ba9225 --- /dev/null +++ b/pioneer/packages/apps-routing/src/settings.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component, { useCounter } from '@polkadot/app-settings'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: {}, + icon: 'cogs', + name: 'settings', + text: t('nav.settings', 'Settings', { ns: 'apps-routing' }), + useCounter + }; +} diff --git a/pioneer/packages/apps-routing/src/society.ts b/pioneer/packages/apps-routing/src/society.ts new file mode 100644 index 0000000000..d9dfce775d --- /dev/null +++ b/pioneer/packages/apps-routing/src/society.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component, { useCounter } from '@polkadot/app-society'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsAccounts: true, + needsApi: [ + 'query.society.pot' + ] + }, + icon: 'hand-spock', + name: 'society', + text: t('nav.society', 'Society', { ns: 'apps-routing' }), + useCounter + }; +} diff --git a/pioneer/packages/apps-routing/src/staking.ts b/pioneer/packages/apps-routing/src/staking.ts new file mode 100644 index 0000000000..0b21e50c6a --- /dev/null +++ b/pioneer/packages/apps-routing/src/staking.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-staking'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [ + ['tx.staking.bond'] + ] + }, + icon: 'certificate', + name: 'staking', + text: t('nav.staking', 'Staking', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/storage.ts b/pioneer/packages/apps-routing/src/storage.ts new file mode 100644 index 0000000000..89bc24666e --- /dev/null +++ b/pioneer/packages/apps-routing/src/storage.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-storage'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [] + }, + icon: 'database', + name: 'chainstate', + text: t('nav.storage', 'Chain state', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/sudo.ts b/pioneer/packages/apps-routing/src/sudo.ts new file mode 100644 index 0000000000..7918fb542e --- /dev/null +++ b/pioneer/packages/apps-routing/src/sudo.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-sudo'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsAccounts: true, + needsApi: [ + 'tx.sudo.setKey' + ], + needsSudo: true + }, + icon: 'unlock', + name: 'sudo', + text: t('nav.sudo', 'Sudo', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/techcomm.ts b/pioneer/packages/apps-routing/src/techcomm.ts new file mode 100644 index 0000000000..50798b6376 --- /dev/null +++ b/pioneer/packages/apps-routing/src/techcomm.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component, { useCounter } from '@polkadot/app-tech-comm'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsAccounts: true, + needsApi: [ + 'query.technicalCommittee.members' + ] + }, + icon: 'microchip', + name: 'techcomm', + text: t('nav.tech-comm', 'Tech. comm.', { ns: 'apps-routing' }), + useCounter + }; +} diff --git a/pioneer/packages/apps-routing/src/toolbox.ts b/pioneer/packages/apps-routing/src/toolbox.ts new file mode 100644 index 0000000000..436ecae94d --- /dev/null +++ b/pioneer/packages/apps-routing/src/toolbox.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component from '@polkadot/app-toolbox'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [] + }, + icon: 'tools', + name: 'toolbox', + text: t('nav.toolbox', 'Toolbox', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/transfer.ts b/pioneer/packages/apps-routing/src/transfer.ts new file mode 100644 index 0000000000..de46f28c29 --- /dev/null +++ b/pioneer/packages/apps-routing/src/transfer.ts @@ -0,0 +1,24 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Modal from '@polkadot/app-accounts/Accounts/modals/Transfer'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: Modal, + Modal, + display: { + isHidden: false, + needsAccounts: true, + needsApi: [ + 'tx.balances.transfer' + ] + }, + icon: 'paper-plane', + name: 'transfer', + text: t('nav.transfer', 'Transfer', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/treasury.ts b/pioneer/packages/apps-routing/src/treasury.ts new file mode 100644 index 0000000000..93cddac603 --- /dev/null +++ b/pioneer/packages/apps-routing/src/treasury.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from './types'; + +import Component, { useCounter } from '@polkadot/app-treasury'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component, + display: { + needsApi: [ + 'tx.treasury.proposeSpend' + ] + }, + icon: 'gem', + name: 'treasury', + text: t('nav.treasury', 'Treasury', { ns: 'apps-routing' }), + useCounter + }; +} diff --git a/pioneer/packages/apps-routing/src/types.ts b/pioneer/packages/apps-routing/src/types.ts new file mode 100644 index 0000000000..6dce432d8c --- /dev/null +++ b/pioneer/packages/apps-routing/src/types.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { IconName } from '@fortawesome/fontawesome-svg-core'; +import { AppProps, BareProps } from '@polkadot/react-components/types'; + +export interface RouteProps extends AppProps, BareProps { + location: any; +} + +export interface Route { + Component: React.ComponentType; + Modal?: React.ComponentType; + display: { + isHidden?: boolean; + isModal?: boolean; + needsAccounts?: boolean; + needsApi?: (string | string[])[]; + needsSudo?: boolean; + }; + icon: IconName; + isIgnored?: boolean; + name: string; + text: string; + useCounter?: () => number | string | null; +} + +export type Routes = (Route | null)[]; diff --git a/pioneer/packages/apps/.skip-npm b/pioneer/packages/apps/.skip-npm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/apps/LICENSE b/pioneer/packages/apps/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/pioneer/packages/apps/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/pioneer/packages/apps/README.md b/pioneer/packages/apps/README.md new file mode 100644 index 0000000000..6b2c62e013 --- /dev/null +++ b/pioneer/packages/apps/README.md @@ -0,0 +1 @@ +# @polkadot/apps diff --git a/pioneer/packages/apps/package.json b/pioneer/packages/apps/package.json new file mode 100644 index 0000000000..77da27ecb7 --- /dev/null +++ b/pioneer/packages/apps/package.json @@ -0,0 +1,40 @@ +{ + "name": "@polkadot/apps", + "private": true, + "version": "0.51.1", + "description": "An Apps portal into the Polkadot network", + "main": "index.js", + "homepage": ".", + "repository": "https://github.com/polkadot-js/apps.git", + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "dependencies": { + "@babel/core": "^7.10.5", + "@babel/polyfill": "^7.10.4", + "@babel/runtime": "^7.10.5", + "@polkadot/dev": "^0.55.28", + "@polkadot/react-components": "0.51.1", + "@polkadot/react-signer": "0.51.1", + "query-string": "^6.13.1" + }, + "devDependencies": { + "babel-loader": "^8.1.0", + "copy-webpack-plugin": "^6.0.3", + "css-loader": "^3.6.0", + "empty": "^0.10.1", + "file-loader": "^6.0.0", + "html-loader": "^1.1.0", + "html-webpack-plugin": "^4.3.0", + "markdown-loader": "^5.1.0", + "mini-css-extract-plugin": "^0.9.0", + "null-loader": "^4.0.0", + "style-loader": "^1.2.1", + "thread-loader": "^2.1.3", + "url-loader": "^4.1.0", + "webpack": "^4.44.0" + } +} diff --git a/pioneer/packages/apps/public/favicon.ico b/pioneer/packages/apps/public/favicon.ico new file mode 100644 index 0000000000..a3d783d522 Binary files /dev/null and b/pioneer/packages/apps/public/favicon.ico differ diff --git a/pioneer/packages/apps/public/index.html b/pioneer/packages/apps/public/index.html new file mode 100644 index 0000000000..d8318bd0c3 --- /dev/null +++ b/pioneer/packages/apps/public/index.html @@ -0,0 +1,34 @@ + + + + + + + + <%= htmlWebpackPlugin.options.PAGE_TITLE %> + <% if (htmlWebpackPlugin.options.IS_PROD) { %> + + + + <% } %> + + + + +
+
+ + + diff --git a/pioneer/packages/apps/public/ipfs/index.html b/pioneer/packages/apps/public/ipfs/index.html new file mode 100644 index 0000000000..c155814766 --- /dev/null +++ b/pioneer/packages/apps/public/ipfs/index.html @@ -0,0 +1,15 @@ + + + + Redirecting to ipfs gateway + + + + +

Redirecting to

+

https://ipfs.io/ipfs/QmT4DmaNmrSf7tu9cP8D6eH3e3xKLTxacm6WG3mrkUaP9f/

+ + \ No newline at end of file diff --git a/pioneer/packages/apps/public/ipfs/pin.json b/pioneer/packages/apps/public/ipfs/pin.json new file mode 100644 index 0000000000..7909c20614 --- /dev/null +++ b/pioneer/packages/apps/public/ipfs/pin.json @@ -0,0 +1 @@ +{"IpfsHash":"QmT4DmaNmrSf7tu9cP8D6eH3e3xKLTxacm6WG3mrkUaP9f","PinSize":6461792,"Timestamp":"2020-07-20T14:01:21.924Z"} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/ar/translation.json b/pioneer/packages/apps/public/locales/ar/translation.json new file mode 100644 index 0000000000..03c663153a --- /dev/null +++ b/pioneer/packages/apps/public/locales/ar/translation.json @@ -0,0 +1,398 @@ +{ + "0.1x voting balance, no lockup period": "رصيد التصويت، ليس هناك فترة توقف x0.1", + "1 day": "يوم واحد", + "1 hr": "ساعة واحدة", + "1 min": "دقيقة واحدة", + "1 s": "ثانية واحدة", + "1. Select your {{chain}} account": "{{١. حدد حساب {{سلسلتك", + "2. Enter the ETH address from the sale.": "٢. أدخل عنوان إيثيريوم من عملية البيع", + "2. Sign ETH transaction": "٣. توقيع معاملة إيثيريوم", + "": "<فارغ>", + "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": ".عقد منتشر إما تم نشره أو مرفق. يتم استخدام العنوان و واجهة التطبيق الثنائية لإنشاء الإعدادات", + "A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist": "سيتم خصم رسم بقيمة {{رسم الإنشاء}} من المرسل نظرًا لعدم وجود حساب الوجهة", + "A reason (to be stored-on-chain) as to why the recipient deserves a tip payout.": ".سبب (تخزين على السلسلة) يبرر أن المتلقي يستحق دفع مقابل مالي", + "ABI": "واجهة التطبيق الثنائية", + "Acceptance proposal to council": "قبول اقتراح مقدم إلى المجلس", + "Account balance:": ":رصيد الحساب", + "Add a tip to this extrinsic, paying the block author for greater priority": "أضف جائزة إلى هذا البرنامج الخارجي، لتدفع لكاتب الكتلة في مقابل أولوية أكبر", + "Add an existing code hash": "إضافة تجزئة رمز موجودة", + "Add an existing contract": "إضافة عقد موجود", + "Add item": "أضف شيئا", + "Adding an optional tip to the transaction could allow for higher priority, especially when the chain is busy.": ".قد تسمح إضافة بقشيش اختياري إلى المعاملة بأولوية أعلى، خاصة عندما تكون السلسلة مشغولة", + "Allocate a suggested tip amount. With enough endorsements, the suggested values are averaged and sent to the beneficiary.": ".تخصيص مبلغ تلميح مقترح. مع وجود عدد كافٍ من المصادقات، يتم حساب متوسط القيم المقترحة وإرسالها إلى المستفيد", + "Approved": "موافق عليه", + "As a council member, you can suggest an initial value for the tip, each other council member can suggest their own.": ".كعضو في المجلس، يمكنك أن تقترح قيمة أولية للجائزة، ويمكن أن يقترح كل عضو آخر من أعضاء المجلس قيمة أخرى", + "Authorize transaction": "السماح بالمعاملة", + "Aye": "نعم", + "Aye, I approve": "نعم, أنا أوافق", + "Call": "مكالمة", + "Call a contract": "اتصل بعقد", + "Call a method on this contract": "اتصل بطريقة على هذا العقد", + "Call results": "نتائج المكالمات", + "Call this message": "اتصل بهذه الرسالة", + "Cancel": "إلغاء", + "Claim": "مطالبة", + "Clear all": "مسح الكل", + "Close": "إغلاق", + "Close tip": "إغلاق بقشيش", + "Confirm ABI removal": "تأكيد إزالة واجهة التطبيق الثنائية", + "Confirm account removal": "تأكيد إزالة الحساب", + "Confirm address removal": "تأكيد إزالة العنوان", + "Confirm claim": "تأكيد المطالبة", + "Confirm code removal": "تأكيد إزالة الكود", + "Confirm contract removal": "تأكيد إزالة العقد", + "Continue": "إكمال", + "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "انسخ السلسلة أعلاه وقم بتوقيع معاملة إيثريوم مع الحساب الذي استخدمته أثناء عملية البيع المسبق في محفظة من اختيارك، باستخدام السلسلة كحمولة صافية، ثم الصق توقيع المعاملة المذكورة أدناه", + "Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:": ":انسخ السلسلة التالية وقم بالتوقيع عليها باستخدام حساب أثيريوم الذي استخدمته أثناء عملية البيع المسبق في محفظة من اختيارك، باستخدام السلسلة كحمولة صافية، ثم الصق توقيع المعاملة المذكورة أدناه", + "Create an account now.": "افتح حسابا الأن", + "Current account nonce: {{accountNonce}}": "{{نونس الحساب الجاري : {{نونس الحساب", + "Deploy": "نشر", + "Deploy a code hash": "نشر تجزئة رمز", + "Deploy this code hash as a smart contract": "نشر تجزئة هذا الكود كعقد ذكي", + "Deploy with this constructor": "نشر مع هذا المنشئ", + "Details": "تفاصيل", + "Dismiss all notifications": "استبعاد كل الإشعارات", + "Do not include a tip for the block author": "لا تقم بتضمين بقشيش لكاتب الكتلة", + "Don't use a proxy for this call": "لا تستخدم بروكسي لهذه المكالمة", + "Endorse": "تأييد", + "Endorsements ({{count}})": "({{موافقات ({{عد", + "Forget": "نسيان", + "Forget this code hash": "تم نسيان تجزئة الرمز هذه", + "Forget this contract": "تم نسيان هذا العقد", + "I agree": "أوافق", + "Include an optional tip for faster processing": "قم بتضمين بقشيش اختياري لمعالجة أسرع", + "Initializing connection": "جاري تهيئة الاتصال", + "Lifetime (# of blocks)": "(فترة الاستهلاك (# من الكتل", + "Loading": "جاري التحميل", + "Locked1x": "قفلx1", + "Locked2x": "قفلx2", + "Locked3x": "قفلx3", + "Locked4x": "قفلx4", + "Locked5x": "قفلx5", + "Locked6x": "قفلx6", + "Messages": "رسائل", + "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "(قد تنشأ مقترحات متعددة من جانب المجلس، سواء بالموافقة أو الرفض. طبق تصويتك على اقتراح المجلس الصحيح (متاح أيضا في صفحة طلبات المجلس", + "Multisig approval with hash (non-final approval)": "(اعتماد متعدد التوقيعات مع تجزئة (موافقة غير نهائية", + "Multisig approval with hash (not message with call)": "(اعتماد متعدد التوقيعات مع تجزئة (لا رسالة مع المكالمة", + "Multisig message with call (for final approval)": "(رسالة متعددة التوقيعات مع مكالمة (لموافقة نهائية", + "Multisig message with call (not approval with hash)": "(رسالة متعددة التوقيعات مع مكالمة (لاموافقة مع تجزئة", + "Mutates contract state": "كتم حالة العقد", + "Nay": "لا", + "Nay, I do not approve": "لا ,أنا لا أوافق", + "No": "لا", + "No approved proposals": "لا اقتراحات مصادق عليها", + "No code hashes available": "لا تتوفر أجزاء للكود", + "No contracts available": "لا عقود متوفرة", + "No documentation provided": "لم يتم توفير أي وثائق", + "No items": "لا أشياء", + "No open tips": "لا بقشيش مفتوحة", + "No pending proposals": "لا توجد اقتراحات معلقة", + "Nonce": "نونس", + "None": "لا شيء", + "Of the beneficiary amount, at least {{bondPercentage}} would need to be put up as collateral. The maximum of this and the minimum bond will be used to secure the proposal, refundable if it passes.": ".من مبلغ المستفيد، يجب تقديم {{نسبة السند}} على الأقل كضمان. سيتم استخدام الحد الأقصى من هذا السند والحد الأدنى للسند لتأمين المقترح، ويمكن استرداد المبلغ إذا تم تمريره", + "Override any applicable values for the specific signed output. These will be used to construct and display the signed transaction.": ".تجاوز أي قيم قابلة للتطبيق للإخراج الموقّع المحدد. سيتم استخدام هذه الإجراءات لإنشاء المعاملة الموقعة وعرضها\"", + "Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:": ":يرجى قراءة هذه الشروط والأحكام بعناية. من خلال إرسال هذا البيان، يُعتبر أنك قد قبلت هذه الشروط والأحكام. إذا لم توافق على هذه الشروط، فيرجى الامتناع عن الدخول أو المتابعة. يمكنك أيضًا العثور عليها في", + "Positive number": "عدد إيجابي", + "Pre-sale ethereum address": "عنوان إيثيريوم مسبق البيع", + "Preparing QR for signing": "إعداد ريال قطري للتوقيع", + "Present the QR code containing the signature to the UI. Once scanned it will be submitted for on-chain processing and execution.": ".تقديم رمز الاستجابة السريعة الذي يحتوي على التوقيع إلى ىواجهة المستخدم. بمجرد فحصه ، سيتم تقديمه للمعالجة والتنفيذ على السلسلة", + "Proposal can either be to approve or reject this spend. One approved, the change is applied by either removing the proposal or scheduling payout.": ".قد يكون الاقتراح إما الموافقة على هذا الإنفاق أو رفضه. إذا تمت الموافقة على التغيير، يتم تطبيقه إما بإزالة المقترح أو جدولة الدفع", + "Proposals": "اقتراحات", + "Propose tip": "اقتراح بقشيش", + "Reassign": "إعادة التعيين", + "Redeem": "إسترداد", + "Rejection proposal to council": "رفض اقتراح مقدم إلى المجلس", + "Remove": "إلغاء", + "Remove ABI": "إلغاء واجهة التطبيق الثنائية", + "Remove item": "امسح شيئا", + "Retrieving data": "استرجاع البيانات", + "Save": "حفظ", + "Scan Signature Qr": "QR مسح التوقيع", + "Scan the QR code with your QR scanner. Once approved, you will be required to present the signed QR back to the UI for submission.": ".امسح رمز الاستجابة السريعة باستخدام ماسح كيو ار . بمجرد الموافقة ، سيُطلب منك تقديم كيو ار الموقّع إلى واجهة المستخدم لتقديمه", + "Select Network": "اختر شبكة", + "Select the account to use for this action.": ".حدد الحساب الذي تريد استخدامه لهذا الإجراء", + "Select the account you wish to close from.": "حدد الحساب الذي ترغب في الإغلاق منه.", + "Select the account you wish to submit the proposal from.": ".حدد الحساب الذي ترغب في إرسال المقترح منه", + "Select the account you wish to submit the tip from.": ".حدد البقشيش الذي تريد إرسال الجائزة منه", + "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.": ".حدد الحساب الذي تريد التصويت منه. يمكنك الموافقة ب\"نعم\" أو الرفض \"لا\" للاقتراح", + "Select the council account you wish to use to make the proposal.": ".حدد حساب المجلس الذي ترغب في استخدامه لتقديم الاقتراح", + "Send to council": ".حدد حساب المجلس الذي ترغب في استخدامه لتقديم الاقتراح", + "Sending transaction": "إرسال المعاملة", + "Set sudo key": "تعيين مفتاح سودو", + "Set to 0 to make transaction immortal": "اضبط على 0 لإجراء المعاملة الخالدة", + "Sign (no submission)": "(التوقيع (لا إرسال", + "Sign and Submit": "التوقيع والإرسال", + "Sign for multisig": "تسجيل الدخول لمتعدد التوقيعات", + "Sign via Qr": "Qr وقع عبر كود", + "Sign via {{hardwareType}}": "{{سجّل عبر {{نوع البرنامج الصلب", + "Signed transaction": "المعاملة الموقّعة", + "Specify the user account to use for this contract call. And fees will be deducted from this account.": ".حدد حساب المستخدم الذي تريد استخدامه لمكالمة العقد هذه. وسيتم خصم الرسوم من هذا الحساب", + "Specify the user account to use for this deployment. And fees will be deducted from this account.": ".حدد حساب المستخدم الذي تريد استخدامه لهذا النشر. وسيتم خصم الرسوم من هذا الحساب", + "Specify the user account to use for this deployment. Any fees will be deducted from this account.": ".حدد حساب المستخدم الذي تريد استخدامه لهذا النشر. وسيتم خصم الرسوم من هذا الحساب", + "Submit": "إرسال", + "Submit (no signature)": "إرسال (لا يوجد توقيع)", + "Submit Sudo": "أرسل سودو", + "Submit proposal": "تقديم الاقتراح", + "Submit signed extrinsic": "تقديم توقيع لمهمة خارجية", + "Submit tip": "تقديم بقشيش", + "Submit tip endorsement": "تقديم مصادقة بقشيش", + "Submit tip request": "تقديم طلب بقشيش", + "Submit treasury proposal": "تقديم اقتراح خزينة", + "Sudo access": "وصول سودو", + "The ABI for the WASM code. In this step it is optional, but setting it here simplifies the setup of contract instances.": ". واجهة التطبيق الثنائية لكود واسم . في هذه الخطوة، يكون هذا الأمر اختياريًا، ولكن إعداده هنا يبسط عملية إعداد مثيلات العقد", + "The ABI for the WASM code. Since we will be making a call into the code, the ABI is required and stored for future operations such as sending messages.": ".واجهة التطبيق الثنائية لكود واسم. بما أننا سنقوم باتصال بالكود، فإن واجهة التطبيق الثنائية مطلوبة ويتم تخزينها للعمليات المستقبلية مثل إرسال الرسائل", + "The account to which the proposed balance will be transferred if approved": "الحساب الذي سيتم تحويل الرصيد المقترح إليه إذا تمت الموافقة عليه", + "The account to which the tip will be transferred if approved": "الحساب الذي سيتم تحويل البقشيش إليه في حال الموافقة عليه", + "The account you want to claim to.": ".الحساب الذي تريد المطالبة عليه", + "The actual fully constructed signed output. This can be used for submission via other channels.": ".المخرج الموقع الفعلي الذي تم إنشاؤه بالكامل. يمكن استخدام هذا لتقديمه عبر قنوات أخرى", + "The address for the deployed contract instance.": ".عنوان لحالة العقد المنتشر", + "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": ".الوقف مخصص لهذا العقد، أي المبلغ المحوَّل إلى العقد عند عدم التبرم", + "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": ".الوقف المخصص لهذا العقد، أي المبلغ المحوَّل إلى العقد كجزء من هذه المكالمة", + "The amount that is associated with this vote. This value is is locked for the duration of the vote.": ".المبلغ المرتبط بهذا التصويت. يتم تأمين هذه القيمة طوال مدة التصويت", + "The amount that will be allocated from the treasury pot": "المبلغ الذي سيتم تخصيصه من وعاء الخزينة", + "The beneficiary will receive the full amount if the proposal passes.": ".سيحصل المستفيد على المبلغ الكامل إذا تم تمرير الاقتراح", + "The beneficiary will received the tip as approved by council members.": "سوف يتلقى المستفيد البقشيش كما وافق عليها أعضاء المجلس", + "The call data that can be supplied to a final call to multi approvals": "بيانات المكالمة التي يمكن توفيرها للمكالمة النهائية للموافقات المتعددة", + "The code hash for the on-chain deployed code.": ".تجزئة الكود للرمز الذي تم نشره على السلسلة", + "The code is not recognized as being in valid WASM format": ".لم يتم التعرف على الكود على أنه فورما صالحة من واسم", + "The codeHash is not a valid hex hash": ".لا تعد تجزئة الكود تجزئة سداسية عشرية صالحة", + "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": ".نظام واسم المجمع للعقد الذي ترغب في نشره. سيتم إرفاق كل كود بلوب فريد بتجزئة رمز يمكن استخدامه لإنشاء مثيلات جديدة", + "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.": ".تم نشر عقد واسم من قبل. يتم تعريف هذا الأمر داخليًا بواسطة تجزئة الرمز، على أنه تم إنشاؤه أو إرفاقه", + "The council member that is proposing this, submission equates to an \"aye\" vote.": "عضو المجلس الذي يقترح هذا الاقتراح، يمثل التطبيق تصويت ب نعم", + "The council proposal to make the vote on": "اقتراح المجلس بإجراء التصويت", + "The deployment constructor information for this contract, as provided by the ABI.": ".معلومات منشئ النشر الخاصة بهذا العقد، كما تم توفيرها من قبل واجهة التطبيق الثنائية", + "The deposit is below the {{minimum}} minimum required for the proposal to be evaluated": "الإيداع أقل من الحد الأدنى المطلوب لـ {{الحد الأدنى}} لتقييم العرض", + "The deposit of {{deposit}} will be reserved until the proposal is completed": "سيتم حجز إيداع {{الإيداع }} حتى اكتمال العرض", + "The details of the transaction including the type, the description (as available from the chain metadata) as well as any parameters and fee estimations (as available) for the specific type of call.": ". تفاصيل المعاملة بما في ذلك النوع والوصف (كما هو متوفر من بيانات تعريف السلسلة) بالإضافة إلى أي إعدادات وتقديرات رسوم (حسب توفرها) لنوع الاتصال المحدد", + "The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected": "رصيد المستلم النهائي أقل أو يساوي {{الإيداع الموجود}} (المبلغ الموجود) ولن ينعكس", + "The hash for the proposal this vote applies to": "تجزئة الاقتراح الذي ينطبق عليه هذا التصويت", + "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": ".الحد الأقصى لكمية الغاز التي يمكن استخدامها بواسطة هذه المكالمة. إذا كان الرمز يتطلب المزيد، فستفشل المكالمة", + "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": ".الحد الأقصى لكمية الغاز التي يمكن استخدامها بواسطة هذا النشر، إذا تطلب الرمز المزيد، فسيفشل النشر", + "The maximum amount of gas to use for this contract call. If the call requires more, it will fail.": ".الحد الأقصى لكمية الغاز التي يجب استخدامها في مكالمة العقد هذه. إذا كانت المكالمة تتطلب المزيد، فسوف تفشل", + "The message to send to this contract. Parameters are adjusted based on the ABI provided.": ".الرسالة التي يجب إرسالها إلى هذا العقد. يتم تعديل الإعدادات استنادًا إلى واجهة التطبيق الثنائية المقدمة", + "The minimum amount that will be bonded": "الحد الأدنى للمبلغ الذي سيتم ربطه", + "The multisig signatory for this transaction.": ".الموقع المتعدد الأطراف لهذه المعاملة", + "The on-chain percentage for the treasury": "نسبة الخزينة بالنسبة للسلسلة", + "The proxy is one of the allowed proxies on the account, as set and filtered by the transaction type.": ".البروكسي هو واحد من البروكسيس المسموحة على هذا الحساب، كما تم تعيينه وتصفيته من خلال نوع المعاملة", + "The proxy to be used for this transaction.": ".البروكسي الذي سيتم استخدامه لهذه المعاملة", + "The reason why this tip should be paid.": "السبب وراء ضرورة دفع هذه البقشيش", + "The sending account that will be used to send this transaction. Any applicable fees will be paid by this account.": ".حساب المرسل الذي سيتم استخدامه لإرسال هذه المعاملة. سيتم دفع أي رسوم مطبقة بواسطة هذا الحساب", + "The signatory is one of the allowed accounts on the multisig, making a recorded approval for the transaction.": ".الموقع هو أحد الحسابات المسموح بها على التوقيعات المتعددة، حيث يتم الحصول على موافقة مسجلة على المعاملة", + "The signatory is one of the allowed accounts on the multisig. The transaction could either be the call or an approval for the hash of a call.": ".الموقع هو أحد الحسابات المسموح بها في التوقيعات المتعددة. قد تكون المعاملة إما مكالمة أو موافقة على تجزئة المكالمة", + "The suggested value for this tip": "القيمة المقترحة لهذه البقشيش", + "The the Ethereum address you used during the pre-sale (starting by \"0x\")": "(\\\"0x\\\"عنوان إيثريوم الذي استخدمته أثناء فترة ما قبل البيع (بدءًا من", + "The tip amount that should be allocated": "مبلغ البقشيش الذي يجب تخصيصه", + "The type of council proposal to submit.": ".نوع اقتراح المجلس الذي سيقدم", + "The unchecked weight as specified for the sudoUncheckedWeight call.": ".الوزن غير المؤكد كما هو محدد لمكالمة سودو الغيرالمتحققة من الوزن", + "The value is not in a valid address format": "القيمة ليست على هيئة عنوان صالح", + "The value is the amount that is being asked for and that will be allocated to the beneficiary if the proposal is approved.": "القيمة هي المبلغ المطلوب والذي سيتم تخصيصها للمستفيد في حالة الموافقة على المقترح", + "This Ethereum address does not have a claim.": "لا يشتمل عنوان إيثيروم هذا على المطالبة", + "This account will make the proposal and be responsible for the bond.": "سيقدم هذا الحساب الاقتراح وسيكون مسؤولاً عن السند", + "This could either be an approval for the hash or with full call details. The call as last approval triggers execution.": ".قد يكون ذلك إما موافقة على التجزئة أو مع تفاصيل المكالمة الكاملة. تؤدي المكالمة كآخر موافقة إلى مباشرة التنفيذ", + "This operation does not impact the associated on-chain code or any of its contracts.": ".لا تؤثر هذه العملية على رمز السلسلة المرتبط بها أو أي من عقوده", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.": ".لا تؤدي هذه العملية إلى إزالة محفوظات الحساب من السلسلة، ولا أي أموال مرتبطة من الحساب. إن عملية النسيان تحد من وصولك إلى الحساب على هذا المتصفح فقط", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.": ".لا تؤدي هذه العملية إلى إزالة محفوظات الحساب من السلسلة، ولا أي أموال مرتبطة من الحساب. إن عملية النسيان تحد من وصولك إلى العنوان على هذا المتصفح فقط", + "This operation does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.": ".لا تؤدي هذه العملية إلى إزالة محفوظات الحساب من السلسلة، ولا أي أموال مرتبطة من الحساب. إن عملية النسيان تحد من وصولك إلى العقد على هذا المتصفح فقط", + "This operation does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.": ".لا تؤدي هذه العملية إلى إزالة الرمز واسم وكذا واجهة التطبيق الثنائية المحمّلين من السلسلة، ولا إلى أي عقود تم نشرها. إن عملية النسيان تحد من وصولك إلى الرمز على هذا المتصفح فقط", + "Tip": "بقشيش", + "Tip (optional)": "(بقشيش (إختياري", + "Tippers ({{count}})": "({{معطيين البقشيش ({{احسب", + "To council": "إلى المجلس", + "Treasury overview": "نظرة عامة على الخزينة", + "Unable to find deployed contract code at the specified address": ".يتعذر العثور على رمز العقد الذي تم نشره على العنوان المحدد", + "Unable to find on-chain WASM code for the supplied codeHash": ".يتعذر العثور على كود واسم على السلسلة لرمز وحدة التحكم عن بعد المقدم", + "Unbonding {{value}}, ": "،{{إلغاء الربط {{قيمة", + "Uncaught error. Something went wrong with the query and rendering of this component. {{message}}": "{{خطأ غير مسلط. حدث خطأ ما في الاستعلام عن هذا المكون وتصويله. {{رسالة", + "Unlock the sending account to allow signing of this transaction.": ".قم بإلغاء قفل حساب الإرسال للسماح بتوقيع هذه المعاملة", + "Upload": "تحميل", + "Upload WASM": "WASM تحميل", + "Use a proxy for this call": "إستخدام بروكسي لهذا الاتصال", + "Use this account to request the tip from. This can be a normal or council account.": "استخدم هذا الحساب لطلب البقشيش منه. قد يكون هذا حسابًا طبيعيًا أو حسابًا في المجلس", + "View this externally": "اعرض هذا خارجيًا", + "Vote": "تصويت", + "Vote Aye": "صوت نعم", + "Vote Nay": "صوت لا", + "Vote on proposal": "التصويت على الاقتراح", + "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.": ". في انتظار تفويض من التمديد. يرجى فتح الامتداد المثبت والموافقة على الوصول أو رفضه", + "Warning: we did not find any attest statement for {{chain}}": "{{تحذير: لم نعثر على أي بيان يشهد لـهذه {{السلسلة", + "We found a pre-claim with this Polkadot address. However, attesting requires signing with this account. To continue with attesting, please add this account as an owned account first.": ".وجدنا مطالبة مسبقة بعنوان بولكدوت هذا. ومع ذلك، تتطلب المصادقة التوقيع باستخدام هذا الحساب. لمتابعة عملية التحقق، يرجى إضافة هذا الحساب كحساب مملوك أولاً", + "Withdraw these unbonded funds": "اسحب هذه الأموال غير المربوطة", + "Yes": "نعم", + "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.": ".أنت على وشك إزالة هذا الحساب من قائمة الحسابات المتوفرة لديك. بمجرد الانتهاء، إذا كنت بحاجة إلى الوصول إليه مرة أخرى، فسوف تضطر إلى إعادة إنشاء الحساب إما عبرالبذرة أو عبر ملف نسخ احتياطي", + "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.": ".أنت على وشك إزالة هذا العنوان من دفتر العناوين. بمجرد الانتهاء، إذا كنت بحاجة إلى الوصول إليه مرة أخرى، فسوف تضطر إلى إعادة إضافة العنوان", + "You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.": ".أنت على وشك إزالة هذا الرمز من قائمة أجزاء الرموز المتوفرة لديك. بمجرد الانتهاء، إذا كنت بحاجة إلى الوصول إليه مجددا، فسوف تضطر إلى إضافة تجزئة الرمز يدويًا مرة أخرى", + "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.": ".أنت على وشك إزالة واجهة التطبيق الثنائية لهذا الرمز. بمجرد الانتهاء، إذا كنت بحاجة إلى الوصول إليه مرة أخرى، فسيتوجب عليك إعادة تحميله يدويًا", + "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": ".أنت على وشك إزالة هذا العقد من قائمة العقود المتوفرة لديك. بمجرد الانتهاء، إذا كنت بحاجة إلى الوصول إليه مرة أخرى، فسوف تضطر إلى إضافة عنوان العقد يدويًا في علامة التبويب", + "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.": ". امن كنقطة نهاية 'wss'أنت متصل من مكان امن بويب'سوكت (دابليوإس يو ار إل) غير امن. بسبب سياسات الحماية للمتصفح متعدد المحتوى فإن هذا النوع من الإرتباط غير مسموح به. غيرخدمة ''استدعاء الإجراء عن بُعد'' إلى", + "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.": ".أنت لست مرتبط بعقدة. تأكد أن العقدة جارية وأن نقطة النهاية لويب'سوكت موصولة", + "You are using an ABI with an outdated format. Please generate a new one.": ".أنت تستخدم واجهة التطبيق الثنائية بفورما قديمة. يرجى إنشاء واحدة جديدة", + "You do not have access to the current sudo key": "ليس لديك حق الوصول إلى مفتاح سودو الحالي", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": ".ليس لديك أي حساب. بعض الميزات محجوبة عنك الان و ستكون متاحة عندما تنشأ حسابا", + "You need to sign an attestation for the following account:": ":يجب أن توقع شهادة للحساب التالي", + "You need to sign an attestation for the following accounts:": ":يجب أن توقع شهادة للحسابات التالية", + "You will no longer have sudo access": "لن يكون لديك حق الوصول إلى إذن الدخول سودو", + "Your Ethereum account": "حساب إيثيريوم الخاص بك", + "Your close will be applied for this council account.": "سيتم تطبيق إغلاقك على حساب المجلس هذا", + "Your endorsement will be applied for this account.": "سيتم تطبيق موافقتك على هذا الحساب.", + "address copied": "تم نسخ العنوان", + "address forgotten": "\"تم نسيان العنوان", + "approved": "موافق عليه", + "available": "متوفر", + "available to be unlocked": "متاح لإلغاء القفل", + "aye: bool": "نعم : بول", + "beneficiary": "المستفيد", + "bond": "السند", + "bonded": "مضمون بسندات", + "call from account": "اتصل من الحساب", + "claim to account": "مطالبة للحساب", + "click to copy": "انقر للنسخ", + "click to select or drag and drop a JSON ABI file": "انقر لتحديد أو سحب وإسقط واجهة التطبيق الثنائية لملف ج.سون", + "click to select or drag and drop the file here": "انقر لتحديد الملف أو سحبه وإفلاته هنا", + "clipboard": "الحافظة", + "code for this contract": "رمز لهذا العقد", + "code hash": "تجزئة الرمز", + "commission": "عمولة", + "compiled contract WASM": "WASM تم تجميع العقد", + "contract address": "عنوان العقد", + "contract to use": "عقد للاستخدام", + "conviction: Conviction": "قناعة : قناعة", + "copied": "نسخ", + "council proposal": "اقتراح المجلس", + "council proposal type": "نوع اقتراح المجلس", + "democracy": "ديمقراطية", + "deploy": "نشر", + "deployment account": "حساب النشر", + "deployment constructor": "منشئ النشر", + "deposit": "الإيداع", + "details": "تفاصيل", + "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": ".لا يبدو أن لديك مطالبة صالحة. يرجى التحقق من أنك وقعت المعاملة بشكل صحيح على حساب إيثيريوم المناسب", + "endowment": "الهبة", + "everything": "كل شيء", + "everything{{labelPost}}": "{{label Post}}كل شيء", + "exclude option": "إخراج الإختيار", + "execute": "تنفيذ", + "extrinsic hash": "تجزئة خارجية", + "fee": "الرسوم", + "file upload": "تحميل الملف", + "filter by name, address, or account index": "التصفية حسب الاسم أو العنوان أو مؤشر الحساب", + "finder": "مكتشف", + "has a valid claim for": "لديك مطالبة صالحة لـ", + "include option": "تضمين الإختيار", + "invalid ABI file selected": "تم تحديد ملف لواجهة التطبيق الثنائية غير صالح", + "lifetime": "مدى الحياة", + "lng.detect": "(لغة المتصفح الافتراضية (الكشف التلقائي", + "lock expired": "انتهت صلاحية القفل", + "locked": "مقفل", + "maximum gas allowed": "الحد الأقصى المسموح به للغاز", + "message to send": "رسالة للإرسال", + "minimum bond": "الحد الأدنى للسند", + "multisig call data": "بيانات مكالمات متعددة الأطراف", + "multisig signatory": "الموقع المتعدد الأطراف", + "nav.accounts": "حسابات", + "nav.claims": "المطالبة العملة الرمزية", + "nav.contracts": "عقود", + "nav.council": "مجلس", + "nav.dashboard": "لوحة القيادة", + "nav.democracy": "الديمقراطية", + "nav.explorer": "مستكشف", + "nav.extrinsics": "العوامل الخارجية", + "nav.generic-asset": "أصل عام", + "nav.github": "غيتهوب", + "nav.i18n": "I18n مترجم", + "nav.js": "جافا سكريبت", + "nav.parachains": "السلاسل الموازية", + "nav.settings": "الإعدادات", + "nav.society": "المجتمع", + "nav.staking": "الرهان", + "nav.storage": "حالة السلسلة", + "nav.sudo": "سودو", + "nav.tech-comm": "تقنية. كوم.", + "nav.toolbox": "مربع الأدوات", + "nav.transfer": "تحويل", + "nav.treasury": "الخزينة", + "nav.wiki": "ويكي", + "no tags": "لا توجد علامات", + "ongoing referendum": "الاستفتاء الجاري", + "only this network": "فقط هذه الشبكة", + "payment": "دفع", + "proposal bond": "اقتراح السند", + "proposal hash": "اقتراح التجزئة", + "proposals": "اقتراحات", + "proposed by": "مقترح من", + "proxy account": "حساب بروكسي", + "reason": "سبب", + "redeemable": "قابلة للاسترداد", + "reserved": "محجوز", + "rpc.arcadia": "(أركاديا (شبكة اختبار العقيدات، تستضيفها نودل", + "rpc.berlin": "(تستضيفها مختبرات الكومنولث, اختبار برامج إيدج ) برلين", + "rpc.custom": "بيئة مخصصة", + "rpc.edgeware": "(إيدج وير (شبكة رئيسية لإيدج وير, تستضيفها مختبرات الكومنولث", + "rpc.flamingfir": "(فليمين فير (اختبار مادة أساسية، يستضيفها التماثل", + "rpc.header.dev": "تنمية", + "rpc.header.live": "الشبكات المباشرة", + "rpc.header.test": "الشبكات التجريبية", + "rpc.kulupu": "(كولوبو (كولوبو شبكة رئيسية، استضافه كولوبو", + "rpc.kusama.ava": "(Polkadot Canary العقد العامة التي يديرها المستخدم؛ https://status.cloud.ava.do/انظر ) كوساما", + "rpc.kusama.parity": "(Polkadot Canary، مستضاف من المساواة) كوساما", + "rpc.kusama.w3f": "(Polkadot Canary، مستضاف من مؤسسة ويب3) كوساما", + "rpc.local": "نقطة التوصيل المحلية (خاصة, 127.0.0.1:9944)", + "rpc.westend": "(Polkadot Testnet، مستضاف من التعادل) ويست إند", + "send as RPC call": "إرسال كمكالمة إجراء عن بُعد", + "send as transaction": "إرسال كمعاملة", + "sending from my account": "إرسال من حسابي", + "session keys": "\"مفاتيح الجلسات", + "session next": "الجلسة التالية", + "signatory": "الموقع", + "spend period": "فترة الإنفاق", + "ss58.centrifuge": "(جهاز الطرد المركزي (مباشر", + "ss58.default": "التلقائي للعقدة المتصلة", + "ss58.edgeware": "(برامج إيدج (مباشر", + "ss58.kusama": "(كوساما (كانارى", + "ss58.polkadot": "(بولكادوت (مباشر", + "ss58.substrate": "(المادة (عامة", + "submit the following change": "أرسل التغيير التالي", + "submit with account": "إرسال مع الحساب", + "submit with council account": "إرسال مع حساب المجلس", + "sudo key": "مفتاح سودو", + "sudo with unchecked weight parameter": "سودو مع وزن غير محدد الإعداد", + "sudo without unchecked weight parameter": "سودو دون وزن غير محدد الإعداد", + "tip": "بقشيش", + "tip reason": "سبب البقشيش", + "tip value": "قيمة البقشيش", + "tips": "بكاشيش", + "total": "مجموع", + "transactions": "معاملات", + "transfer received": "تم استلام التحويل", + "transferrable": "قابل للتحويل", + "type": "نوع", + "unbonding": "لا يفوتك", + "unchecked weight for this call": "وزن غير محدد لهذه المكالمة", + "unlock account with password": "قم بإلغاء قفل الحساب باستخدام كلمة المرور", + "unstake threshold": "عتبة عدم الرهن", + "update on #{{index}}": "{{مراجعة على#{{المؤشر", + "use on any network": "استخدم على أي شبكة", + "using my account": "باستخدام حسابي", + "value": "قيمة", + "version {{version}}": "{{إحداث {{إحداث", + "vested": "راسخ", + "vesting": "الاستحقاق", + "via Council/Vote": "عن طريق المجلس/التصويت", + "via Democracy/Vote": "عن طريق الديمقراطية/التصويت", + "via Staking/Bond": "عبر الرهان/السندات", + "via Vesting": "عبر الاستحقاق", + "vote value": "قيمة التصويت", + "vote with account": "صوّت بالحساب", + "voting balance": "رصيد التصويت", + "wrong password supplied": "تم توفير كلمة مرور غير صحيحة", + "{{blocks}} blocks": "{{بلوكس {{بلوكس", + "{{count}} key/value pairs encoded for submission": "عد}} المفتاح /ترميز أزواج القيم لإرسالها}}", + "{{d}} days": "{{أيام {{ي", + "{{executionTime}}s execution time": "وقت التنفيذ}} من زمن التنفيذ}}", + "{{h}} hrs": "{{ساعات {{س", + "{{m}} mins": "{{دقائق {{د", + "{{name}} ({{size}} bytes)": "({{الإسم}} ({{الحجم}} من البايتات}}", + "{{percentage}}% of block time": "النسبة المئوية}}% من وقت البلوك}}", + "{{step}}. Sign with your ETH address": "{{قم بتسجيل الدخول باستخدام عنوان إيثيريوم الخاص بك {{خطوة", + "{{s}} s": "{{دقائق {{د", + "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "({{قفل}} (تاريخ إصدار {{أيام}}x رصيد التصويت، مقفل لـ x {{قيمة }}" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-123code.json b/pioneer/packages/apps/public/locales/en/app-123code.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-123code.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-accounts.json b/pioneer/packages/apps/public/locales/en/app-accounts.json new file mode 100644 index 0000000000..f77a9284cd --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-accounts.json @@ -0,0 +1,291 @@ +{ + "//hard///password": "//hard///password", + "//hard/soft": "//hard/soft", + "//hard/soft///password": "//hard/soft///password", + "": "", + "@YourTwitterName": "@YourTwitterName", + "@yourname:matrix.org": "@yourname:matrix.org", + "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.": "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.", + "Add": "Add", + "Add account": "Add account", + "Add account via Qr": "Add account via Qr", + "Add an account via seed": "Add an account via seed", + "Add an address": "Add an address", + "Add contact": "Add contact", + "Add identity judgment": "Add identity judgment", + "Add multisig": "Add multisig", + "Add proxied account": "Add proxied account", + "Add sub": "Add sub", + "Add via Qr": "Add via Qr", + "Add via backup file": "Add via backup file", + "Advanced creation options": "Advanced creation options", + "Amount to delegate for any democracy vote. This is adjusted using the available funds on the account.": "Amount to delegate for any democracy vote. This is adjusted using the available funds on the account.", + "An URL that is linked to this identity.": "An URL that is linked to this identity.", + "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.": "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.", + "Any democracy vote performed by the delegated account will result in an additional vote from the delegating account": "Any democracy vote performed by the delegated account will result in an additional vote from the delegating account", + "Approve this call hash": "Approve this call hash", + "Backup account": "Backup account", + "Cancel this call hash": "Cancel this call hash", + "Change": "Change", + "Change account password": "Change account password", + "Change democracy delegation": "Change democracy delegation", + "Change this account's password": "Change this account's password", + "Claim tokens...": "Claim tokens...", + "Clear expired democracy locks": "Clear expired democracy locks", + "Council": "Council", + "Create": "Create", + "Create a backup file for this account": "Create a backup file for this account", + "Create and backup account": "Create and backup account", + "Delegate": "Delegate", + "Delegate democracy vote": "Delegate democracy vote", + "Delegate democracy votes": "Delegate democracy votes", + "Deposit": "Deposit", + "Derive account from pair": "Derive account from pair", + "Derive account via derivation path": "Derive account via derivation path", + "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".": "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".", + "Development": "Development", + "Download": "Download", + "Either approve or reject this call.": "Either approve or reject this call.", + "Ensure that the extension has accounts, some accounts are visible globally and available for this chain and that you gave the application permission to access accounts from the extension to use them.": "Ensure that the extension has accounts, some accounts are visible globally and available for this chain and that you gave the application permission to access accounts from the extension to use them.", + "Erroneous": "Erroneous", + "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)": "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)", + "External": "External", + "For approvals outstanding approvers will be shown, for hashes that should be cancelled the first approver is required.": "For approvals outstanding approvers will be shown, for hashes that should be cancelled the first approver is required.", + "For final approvals, the actual full call data is required to execute the transaction": "For final approvals, the actual full call data is required to execute the transaction", + "Forget this account": "Forget this account", + "Forget this address": "Forget this address", + "Full Legal Name": "Full Legal Name", + "If the delegated account is currently voting in a referendum, the delegating vote and conviction will be added.": "If the delegated account is currently voting in a referendum, the delegating vote and conviction will be added.", + "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.": "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.", + "If you are moving accounts between applications, ensure that you use the correct type.": "If you are moving accounts between applications, ensure that you use the correct type.", + "Important notice": "Important notice", + "Initiate account recovery": "Initiate account recovery", + "Initiate recovery for another": "Initiate recovery for another", + "Injected": "Injected", + "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -": "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -", + "Judge": "Judge", + "Known good": "Known good", + "Learn more...": "Learn more...", + "Low quality": "Low quality", + "Make Transfer": "Make Transfer", + "Make recoverable": "Make recoverable", + "Mnemonic": "Mnemonic", + "Multisig": "Multisig", + "Multisig approval with hash (non-final approval)": "Multisig approval with hash (non-final approval)", + "Multisig approvals": "Multisig approvals", + "Multisig approvals pending": "Multisig approvals pending", + "Multisig message with call (for final approval)": "Multisig message with call (for final approval)", + "My On-Chain Name": "My On-Chain Name", + "My accounts": "My accounts", + "My contacts": "My contacts", + "Name given to this account. You can change it at any point in the future.": "Name given to this account. You can change it at any point in the future.", + "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".": "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".", + "Name given to this multisig. You can edit it at any later point in time.": "Name given to this multisig. You can edit it at any later point in time.", + "Name given to this proxied account. You can edit it at any later point in time.": "Name given to this proxied account. You can edit it at any later point in time.", + "No judgments": "No judgments", + "No matches found": "No matches found", + "No sub identities set.": "No sub identities set.", + "One of more extensions has been detected in your browser, however no accounts has been injected.": "One of more extensions has been detected in your browser, however no accounts has been injected.", + "Owned": "Owned", + "Paste here the address of the contact you want to add to your address book.": "Paste here the address of the contact you want to add to your address book.", + "Pending call hashes": "Pending call hashes", + "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.": "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.", + "Provide judgement": "Provide judgement", + "Provide the account QR from the module/external application for scanning. One detected as valid, you will be taken to the next step to add the account to your list.": "Provide the account QR from the module/external application for scanning. One detected as valid, you will be taken to the next step to add the account to your list.", + "Proxied": "Proxied", + "Proxied account has no owned proxies": "Proxied account has no owned proxies", + "Query Ledger": "Query Ledger", + "Raw seed": "Raw seed", + "Reasonable": "Reasonable", + "Register identity": "Register identity", + "Register sub-identities": "Register sub-identities", + "Remove": "Remove", + "Remove sub": "Remove sub", + "Restore": "Restore", + "Restore JSON": "Restore JSON", + "Retrieving sub-identities": "Retrieving sub-identities", + "Save": "Save", + "Save delegation": "Save delegation", + "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.": "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.", + "Saved": "Saved", + "Search for": "Search for", + "Select a contact or paste the address you want to send funds to.": "Select a contact or paste the address you want to send funds to.", + "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.": "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.", + "Select the account you wish to recover into this account.": "Select the account you wish to recover into this account.", + "Send": "Send", + "Send funds": "Send funds", + "Send funds from this account": "Send funds from this account", + "Send funds to this address": "Send funds to this address", + "Set Identity": "Set Identity", + "Set Subs": "Set Subs", + "Set on-chain identity": "Set on-chain identity", + "Set on-chain sub-identities": "Set on-chain sub-identities", + "Setup account as recoverable": "Setup account as recoverable", + "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...": "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...", + "Show address on hardware device": "Show address on hardware device", + "Since the multisig function like any other account, once created it is available for selection anywhere accounts are used and needs to be funded before use.": "Since the multisig function like any other account, once created it is available for selection anywhere accounts are used and needs to be funded before use.", + "Society": "Society", + "Start generation": "Start generation", + "Start recovery": "Start recovery", + "Stop generation": "Stop generation", + "Sudo key": "Sudo key", + "Supply a backed-up JSON file, encrypted with your account-specific password.": "Supply a backed-up JSON file, encrypted with your account-specific password.", + "Swap to a non-executing approval type, with subsequent calls providing the actual call data.": "Swap to a non-executing approval type, with subsequent calls providing the actual call data.", + "Technical committee": "Technical committee", + "Test account": "Test account", + "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.": "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.", + "The account you will send funds from.": "The account you will send funds from.", + "The address that has previously setup a proxy to one of the accounts that you control.": "The address that has previously setup a proxy to one of the accounts that you control.", + "The address that you have a valid proxy setup for.": "The address that you have a valid proxy setup for.", + "The addresses that are able to approve multisig transactions. You can select up to {{maxHelpers}} trusted addresses.": "The addresses that are able to approve multisig transactions. You can select up to {{maxHelpers}} trusted addresses.", + "The addresses that are able to help in recovery. You can select up to {{maxHelpers}} trusted helpers.": "The addresses that are able to help in recovery. You can select up to {{maxHelpers}} trusted helpers.", + "The amount to allocate and the conviction that will be applied to all votes made on a referendum.": "The amount to allocate and the conviction that will be applied to all votes made on a referendum.", + "The beneficiary will have access to the transferred fees when the transaction is included in a block.": "The beneficiary will have access to the transferred fees when the transaction is included in a block.", + "The call data for this transaction matching the hash. Once sent, the multisig will be executed against this.": "The call data for this transaction matching the hash. Once sent, the multisig will be executed against this.", + "The call hash from the list of available and unapproved calls.": "The call hash from the list of available and unapproved calls.", + "The call hashes that have not been executed as of yet.": "The call hashes that have not been executed as of yet.", + "The conviction that will be used for each delegated vote.": "The conviction that will be used for each delegated vote.", + "The delay between vouching and the availability of the recovered account.": "The delay between vouching and the availability of the recovered account.", + "The derivation path allows you to create different accounts from the same base mnemonic.": "The derivation path allows you to create different accounts from the same base mnemonic.", + "The email address associated with this identity.": "The email address associated with this identity.", + "The existing account password as specified when this account was created or when it was last changed.": "The existing account password as specified when this account was created or when it was last changed.", + "The helpers should be able to verify, via an off-chain mechanism, that the account owner indeed wishes to recover access and as such provide any approvals. In the cases of malicious recovery procedures, they will have the power to stop it.": "The helpers should be able to verify, via an off-chain mechanism, that the account owner indeed wishes to recover access and as such provide any approvals. In the cases of malicious recovery procedures, they will have the power to stop it.", + "The legal name for this identity.": "The legal name for this identity.", + "The local name for this account. Changing this does not affect your on-line identity, so this is only used to indicate the name of the account locally.": "The local name for this account. Changing this does not affect your on-line identity, so this is only used to indicate the name of the account locally.", + "The maximum amount you can delegate is the amount of funds available on the delegating account.": "The maximum amount you can delegate is the amount of funds available on the delegating account.", + "The minimum amount that an account should have to be deemed active": "The minimum amount that an account should have to be deemed active", + "The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.": "The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.", + "The name is for unique identification of the account in your owner lists.": "The name is for unique identification of the account in your owner lists.", + "The name that will be displayed in your accounts list.": "The name that will be displayed in your accounts list.", + "The new account password. Once set, all future account unlocks will be performed with this new password.": "The new account password. Once set, all future account unlocks will be performed with this new password.", + "The operation type to apply. For approvals both non-final and final approvals are supported.": "The operation type to apply. For approvals both non-final and final approvals are supported.", + "The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.": "The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.", + "The password previously used to encrypt this account.": "The password previously used to encrypt this account.", + "The password to unlock the selected account.": "The password to unlock the selected account.", + "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.": "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.", + "The recoverable account is protected against the loss of seed/access by a social process.": "The recoverable account is protected against the loss of seed/access by a social process.", + "The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.": "The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.", + "The selected account to perform the derivation on.": "The selected account to perform the derivation on.", + "The signatories has the ability to create transactions using the multisig and approve transactions sent by others.Once the threshold is reached with approvals, the multisig transaction is enacted on-chain.": "The signatories has the ability to create transactions using the multisig and approve transactions sent by others.Once the threshold is reached with approvals, the multisig transaction is enacted on-chain.", + "The signatory to send the approval/cancel from": "The signatory to send the approval/cancel from", + "The threshold for approval should be less or equal to the number of signatories for this multisig.": "The threshold for approval should be less or equal to the number of signatories for this multisig.", + "The threshold for approvals and the delay is the protection associated with the account. The delay should be such that any colluding recovery attempts does have a window to stop.": "The threshold for approvals and the delay is the protection associated with the account. The delay should be such that any colluding recovery attempts does have a window to stop.", + "The threshold for this multisig": "The threshold for this multisig", + "The threshold of vouches that is to be reached for the account to be recovered.": "The threshold of vouches that is to be reached for the account to be recovered.", + "The transferred balance will be subtracted (along with fees) from the sender account.": "The transferred balance will be subtracted (along with fees) from the sender account.", + "The twitter name for this identity.": "The twitter name for this identity.", + "These are trusted individuals that can verify and approve any recovery actions. With recovery, once the threshold is reached, the funds associated with the account can be moved to a new destination.": "These are trusted individuals that can verify and approve any recovery actions. With recovery, once the threshold is reached, the funds associated with the account can be moved to a new destination.", + "This account has a governance delegation": "This account has a governance delegation", + "This account is recoverable, with the following friends:": "This account is recoverable, with the following friends:", + "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).": "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).", + "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.": "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.", + "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.": "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.", + "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.", + "Type the name of your contact. This name will be used across all the apps. It can be edited later on.": "Type the name of your contact. This name will be used across all the apps. It can be edited later on.", + "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.": "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.", + "Undelegate": "Undelegate", + "Unlock": "Unlock", + "Unlock vested amount": "Unlock vested amount", + "Vanity generator": "Vanity generator", + "Verify the password entered above.": "Verify the password entered above.", + "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.": "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.", + "You can set a custom derivation path for this account using the following syntax \"//////\". The \"/\" and \"//\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.": "You can set a custom derivation path for this account using the following syntax \"//////\". The \"/\" and \"//\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.", + "You have {{claimCount}} accounts that need attestations. Use the Claim Tokens app on the left navigation bar to complete the process. Until you do, your balances for those accounts will not be reflected.": "You have {{claimCount}} accounts that need attestations. Use the Claim Tokens app on the left navigation bar to complete the process. Until you do, your balances for those accounts will not be reflected.", + "You will remove any delegation made by this acccount": "You will remove any delegation made by this acccount", + "a riot name linked to this identity": "a riot name linked to this identity", + "account forgotten": "account forgotten", + "account restored": "account restored", + "accounts": "accounts", + "added proxy": "added proxy", + "address": "address", + "address created": "address created", + "address edited": "address edited", + "address forgotten": "address forgotten", + "address {{index}}": "address {{index}}", + "amount": "amount", + "approval type": "approval type", + "available signatories": "available signatories", + "available social recovery helpers": "available social recovery helpers", + "backup file": "backup file", + "balance": "balance", + "balances": "balances", + "call data for final approval": "call data for final approval", + "case sensitive": "case sensitive", + "contacts": "contacts", + "conviction": "conviction", + "created account": "created account", + "created multisig": "created multisig", + "delay": "delay", + "delegate democracy vote": "delegate democracy vote", + "delegated account": "delegated account", + "delegating account": "delegating account", + "delegating amount": "delegating amount", + "delegation": "delegation", + "democracy vote delegation": "democracy vote delegation", + "deposit": "deposit", + "derivation path": "derivation path", + "derive root account": "derive root account", + "development seed": "development seed", + "display name": "display name", + "email": "email", + "exclude value": "exclude value", + "existential deposit": "existential deposit", + "filter by name or tags": "filter by name or tags", + "https://example.com": "https://example.com", + "identity": "identity", + "include field": "include field", + "include value": "include value", + "invalid/unknown registrar account": "invalid/unknown registrar account", + "judgement": "judgement", + "keypair crypto type": "keypair crypto type", + "legal name": "legal name", + "matches": "matches", + "mnemonic seed": "mnemonic seed", + "multisig": "multisig", + "multisig name": "multisig name", + "name": "name", + "new account": "new account", + "new address": "new address", + "no accounts yet, create or import an existing": "no accounts yet, create or import an existing", + "no addresses saved yet, add any existing address": "no addresses saved yet, add any existing address", + "no tags": "no tags", + "parent": "parent", + "password": "password", + "password (repeat)": "password (repeat)", + "pending hashes": "pending hashes", + "proxied account": "proxied account", + "proxied name": "proxied name", + "recover this account": "recover this account", + "recovery block delay": "recovery block delay", + "recovery threshold": "recovery threshold", + "registrar account": "registrar account", + "registrar index": "registrar index", + "riot": "riot", + "riot name": "riot name", + "secret": "secret", + "secret derivation path": "secret derivation path", + "seed (hex or string)": "seed (hex or string)", + "selected signatories": "selected signatories", + "send": "send", + "send from account": "send from account", + "send to address": "send to address", + "signatories": "signatories", + "signatory": "signatory", + "somebody@example.com": "somebody@example.com", + "stash address": "stash address", + "sub name": "sub name", + "tags": "tags", + "the account to make recoverable": "the account to make recoverable", + "the account to recover to": "the account to recover to", + "threshold": "threshold", + "transactions": "transactions", + "transferrable": "transferrable", + "trusted social recovery helpers": "trusted social recovery helpers", + "twitter": "twitter", + "type": "type", + "web": "web", + "website": "website", + "your current password": "your current password", + "your new password": "your new password" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-claims.json b/pioneer/packages/apps/public/locales/en/app-claims.json new file mode 100644 index 0000000000..2ab7fdefed --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-claims.json @@ -0,0 +1,30 @@ +{ + "1. Select your {{chain}} account": "1. Select your {{chain}} account", + "2. Enter the ETH address from the sale.": "2. Enter the ETH address from the sale.", + "2. Sign ETH transaction": "2. Sign ETH transaction", + "Account balance:": "Account balance:", + "Claim": "Claim", + "Confirm claim": "Confirm claim", + "Continue": "Continue", + "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below", + "Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:": "Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:", + "I agree": "I agree", + "Loading": "Loading", + "Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:": "Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:", + "Pre-sale ethereum address": "Pre-sale ethereum address", + "Redeem": "Redeem", + "The account you want to claim to.": "The account you want to claim to.", + "The the Ethereum address you used during the pre-sale (starting by \"0x\")": "The the Ethereum address you used during the pre-sale (starting by \"0x\")", + "This Ethereum address does not have a claim.": "This Ethereum address does not have a claim.", + "Warning: we did not find any attest statement for {{chain}}": "Warning: we did not find any attest statement for {{chain}}", + "We found a pre-claim with this Polkadot address. However, attesting requires signing with this account. To continue with attesting, please add this account as an owned account first.": "We found a pre-claim with this Polkadot address. However, attesting requires signing with this account. To continue with attesting, please add this account as an owned account first.", + "You need to sign an attestation for the following account:": "You need to sign an attestation for the following account:", + "You need to sign an attestation for the following accounts:": "You need to sign an attestation for the following accounts:", + "Your Ethereum account": "Your Ethereum account", + "claim to account": "claim to account", + "click to copy": "click to copy", + "copied": "copied", + "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.", + "has a valid claim for": "has a valid claim for", + "{{step}}. Sign with your ETH address": "{{step}}. Sign with your ETH address" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-contracts.json b/pioneer/packages/apps/public/locales/en/app-contracts.json new file mode 100644 index 0000000000..5ba148f6e6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-contracts.json @@ -0,0 +1,76 @@ +{ + "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.", + "ABI": "ABI", + "Add an existing code hash": "Add an existing code hash", + "Add an existing contract": "Add an existing contract", + "Call": "Call", + "Call a contract": "Call a contract", + "Call a method on this contract": "Call a method on this contract", + "Call results": "Call results", + "Call this message": "Call this message", + "Clear all": "Clear all", + "Confirm ABI removal": "Confirm ABI removal", + "Deploy": "Deploy", + "Deploy a code hash": "Deploy a code hash", + "Deploy this code hash as a smart contract": "Deploy this code hash as a smart contract", + "Deploy with this constructor": "Deploy with this constructor", + "Forget this code hash": "Forget this code hash", + "Forget this contract": "Forget this contract", + "Messages": "Messages", + "Mutates contract state": "Mutates contract state", + "No code hashes available": "No code hashes available", + "No contracts available": "No contracts available", + "No documentation provided": "No documentation provided", + "Remove": "Remove", + "Remove ABI": "Remove ABI", + "Save": "Save", + "Specify the user account to use for this contract call. And fees will be deducted from this account.": "Specify the user account to use for this contract call. And fees will be deducted from this account.", + "Specify the user account to use for this deployment. And fees will be deducted from this account.": "Specify the user account to use for this deployment. And fees will be deducted from this account.", + "Specify the user account to use for this deployment. Any fees will be deducted from this account.": "Specify the user account to use for this deployment. Any fees will be deducted from this account.", + "The ABI for the WASM code. In this step it is optional, but setting it here simplifies the setup of contract instances.": "The ABI for the WASM code. In this step it is optional, but setting it here simplifies the setup of contract instances.", + "The ABI for the WASM code. Since we will be making a call into the code, the ABI is required and stored for future operations such as sending messages.": "The ABI for the WASM code. Since we will be making a call into the code, the ABI is required and stored for future operations such as sending messages.", + "The address for the deployed contract instance.": "The address for the deployed contract instance.", + "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.", + "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.", + "The code hash for the on-chain deployed code.": "The code hash for the on-chain deployed code.", + "The code is not recognized as being in valid WASM format": "The code is not recognized as being in valid WASM format", + "The codeHash is not a valid hex hash": "The codeHash is not a valid hex hash", + "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.", + "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.": "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.", + "The deployment constructor information for this contract, as provided by the ABI.": "The deployment constructor information for this contract, as provided by the ABI.", + "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.", + "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.", + "The maximum amount of gas to use for this contract call. If the call requires more, it will fail.": "The maximum amount of gas to use for this contract call. If the call requires more, it will fail.", + "The message to send to this contract. Parameters are adjusted based on the ABI provided.": "The message to send to this contract. Parameters are adjusted based on the ABI provided.", + "The value is not in a valid address format": "The value is not in a valid address format", + "This operation does not impact the associated on-chain code or any of its contracts.": "This operation does not impact the associated on-chain code or any of its contracts.", + "This operation does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.": "This operation does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.", + "Unable to find deployed contract code at the specified address": "Unable to find deployed contract code at the specified address", + "Unable to find on-chain WASM code for the supplied codeHash": "Unable to find on-chain WASM code for the supplied codeHash", + "Upload": "Upload", + "Upload WASM": "Upload WASM", + "You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.": "You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.", + "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.": "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.", + "You are using an ABI with an outdated format. Please generate a new one.": "You are using an ABI with an outdated format. Please generate a new one.", + "address forgotten": "address forgotten", + "call from account": "call from account", + "click to select or drag and drop a JSON ABI file": "click to select or drag and drop a JSON ABI file", + "code for this contract": "code for this contract", + "code hash": "code hash", + "compiled contract WASM": "compiled contract WASM", + "contract address": "contract address", + "contract to use": "contract to use", + "deploy": "deploy", + "deployment account": "deployment account", + "deployment constructor": "deployment constructor", + "endowment": "endowment", + "execute": "execute", + "invalid ABI file selected": "invalid ABI file selected", + "maximum gas allowed": "maximum gas allowed", + "message to send": "message to send", + "send as RPC call": "send as RPC call", + "send as transaction": "send as transaction", + "value": "value", + "{{executionTime}}s execution time": "{{executionTime}}s execution time", + "{{percentage}}% of block time": "{{percentage}}% of block time" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-council.json b/pioneer/packages/apps/public/locales/en/app-council.json new file mode 100644 index 0000000000..7b5a4200c9 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-council.json @@ -0,0 +1,79 @@ +{ + "Aye {{counter}}": "Aye {{counter}}", + "Cancel slashes": "Cancel slashes", + "Close": "Close", + "Close proposal": "Close proposal", + "Council overview": "Council overview", + "Current prime member, default voting": "Current prime member, default voting", + "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.": "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.", + "Motions ({{count}})": "Motions ({{count}})", + "Nay {{counter}}": "Nay {{counter}}", + "No candidates found": "No candidates found", + "No council motions": "No council motions", + "No members found": "No members found", + "No runners up found": "No runners up found", + "Positive number between 1 and {{memberCount}}": "Positive number between 1 and {{memberCount}}", + "Propose": "Propose", + "Propose a council motion": "Propose a council motion", + "Propose external": "Propose external", + "Propose external (majority)": "Propose external (majority)", + "Propose motion": "Propose motion", + "Revert": "Revert", + "Revert pending slashes": "Revert pending slashes", + "Select and order council candidates you wish to vote for.": "Select and order council candidates you wish to vote for.", + "Select the account you wish close the proposal with.": "Select the account you wish close the proposal with.", + "Select the account you wish to make the proposal with.": "Select the account you wish to make the proposal with.", + "Select the account you wish to submit for candidacy.": "Select the account you wish to submit for candidacy.", + "Submit candidacy": "Submit candidacy", + "Submit your council candidacy": "Submit your council candidacy", + "The actual proposal to make, based on the selected call and parameters thereof.": "The actual proposal to make, based on the selected call and parameters thereof.", + "The bond that is reserved": "The bond that is reserved", + "The bond will be reserved for the duration of your candidacy and membership.": "The bond will be reserved for the duration of your candidacy and membership.", + "The council account for the proposal. The selection is filtered by the current members.": "The council account for the proposal. The selection is filtered by the current members.", + "The council account for this vote. The selection is filtered by the current members.": "The council account for this vote. The selection is filtered by the current members.", + "The council account that will apply the close for the current round.": "The council account that will apply the close for the current round.", + "The desired threshold. Here set to a default of 50%+1, as applicable for general proposals.": "The desired threshold. Here set to a default of 50%+1, as applicable for general proposals.", + "The hash of the proposal image, either already submitted or valid for the specific call.": "The hash of the proposal image, either already submitted or valid for the specific call.", + "The minimum number of council votes required to approve this motion": "The minimum number of council votes required to approve this motion", + "The preimage hash of the proposal": "The preimage hash of the proposal", + "The proposal that is being voted on. It will pass when the threshold is reached.": "The proposal that is being voted on. It will pass when the threshold is reached.", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted to council for a subsequent voting round.": "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted to council for a subsequent voting round.", + "The specific eras on which there are unapplied slashes. For each era a separate proposal is to be made.": "The specific eras on which there are unapplied slashes. For each era a separate proposal is to be made.", + "The unapplied slashed era to cancel.": "The unapplied slashed era to cancel.", + "The value associated with this vote. The amount will be locked (not available for transfer) and used in all subsequent elections.": "The value associated with this vote. The amount will be locked (not available for transfer) and used in all subsequent elections.", + "The vote to record for this proposal, either for or against.": "The vote to record for this proposal, either for or against.", + "The vote will be recorded for the selected account.": "The vote will be recorded for the selected account.", + "The votes for the members, runner-ups and candidates. These should be ordered based on your priority.": "The votes for the members, runner-ups and candidates. These should be ordered based on your priority.", + "This account will appear in the list of candidates. With enough votes in an election, it will become either a runner-up or a council member.": "This account will appear in the list of candidates. With enough votes in an election, it will become either a runner-up or a council member.", + "This account will be use to approve each candidate.": "This account will be use to approve each candidate.", + "Vote": "Vote", + "Vote for current candidates": "Vote for current candidates", + "Vote on proposal": "Vote on proposal", + "You are voting with this collective's prime account. The vote will be the default outcome in case of any abstentions.": "You are voting with this collective's prime account. The vote will be the default outcome in case of any abstentions.", + "aye": "aye", + "backing": "backing", + "candidacy bond": "candidacy bond", + "candidate account": "candidate account", + "candidates": "candidates", + "council candidates": "council candidates", + "era {{era}}, {{count}} slashes": "era {{era}}, {{count}} slashes", + "members": "members", + "motions": "motions", + "my ordered votes": "my ordered votes", + "nay": "nay", + "no unapplied slashes found": "no unapplied slashes found", + "preimage hash": "preimage hash", + "prime voter": "prime voter", + "proposal": "proposal", + "propose from account": "propose from account", + "runners up": "runners up", + "seats": "seats", + "sending account": "sending account", + "term progress": "term progress", + "the era to cancel for": "the era to cancel for", + "threshold": "threshold", + "votes": "votes", + "voting account": "voting account", + "voting end": "voting end", + "voting round": "voting round" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-dashboard.json b/pioneer/packages/apps/public/locales/en/app-dashboard.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-dashboard.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-democracy.json b/pioneer/packages/apps/public/locales/en/app-democracy.json new file mode 100644 index 0000000000..5bb7415e93 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-democracy.json @@ -0,0 +1,83 @@ +{ + "0.1x voting balance, no lockup period": "0.1x voting balance, no lockup period", + "Conviction locks do overlap and is additive, meaning that funds locked during a previous vote can be locked again.": "Conviction locks do overlap and is additive, meaning that funds locked during a previous vote can be locked again.", + "Democracy overview": "Democracy overview", + "Fast track": "Fast track", + "Fast track proposal": "Fast track proposal", + "If this proposal is passed, the changes will be applied via dispatch and the deposit returned.": "If this proposal is passed, the changes will be applied via dispatch and the deposit returned.", + "Image": "Image", + "No active proposals": "No active proposals", + "No active referendums": "No active referendums", + "No external proposal": "No external proposal", + "Nothing queued for execution": "Nothing queued for execution", + "Only applicable if the proposal has already passed and is ready for dispatch.": "Only applicable if the proposal has already passed and is ready for dispatch.", + "Preimage": "Preimage", + "Register the vote for or against the proposal.": "Register the vote for or against the proposal.", + "Second": "Second", + "Second proposal": "Second proposal", + "Seconding a proposal that indicates your backing for the proposal. Proposals with greater interest moves up the queue for potential next referendums.": "Seconding a proposal that indicates your backing for the proposal. Proposals with greater interest moves up the queue for potential next referendums.", + "Seconds ({{count}})": "Seconds ({{count}})", + "Select the account you wish to make the proposal with.": "Select the account you wish to make the proposal with.", + "Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected": "Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected", + "Submit preimage": "Submit preimage", + "Submit proposal": "Submit proposal", + "The account you want to register the preimage from": "The account you want to register the preimage from", + "The account you want to register the proposal from": "The account you want to register the proposal from", + "The amount reserved to store this image": "The amount reserved to store this image", + "The amount this total can be reduced by to change the referendum outcome. This assumes changes to the convictions of the existing votes, with no additional turnout.": "The amount this total can be reduced by to change the referendum outcome. This assumes changes to the convictions of the existing votes, with no additional turnout.", + "The amount this total should be increased by to change the referendum outcome. This assumes additional turnout with new votes at 1x conviction.": "The amount this total should be increased by to change the referendum outcome. This assumes additional turnout with new votes at 1x conviction.", + "The associated deposit for this proposal should be more then the minimum on-chain deposit required. It will be locked until the proposal passes.": "The associated deposit for this proposal should be more then the minimum on-chain deposit required. It will be locked until the proposal passes.", + "The balance associated with the vote will be locked as per the conviction specified and will not be available for transfer during this period.": "The balance associated with the vote will be locked as per the conviction specified and will not be available for transfer during this period.", + "The calculated storage costs based on the size and the per-bytes fee.": "The calculated storage costs based on the size and the per-bytes fee.", + "The conviction to use for this vote, with an appropriate lock period.": "The conviction to use for this vote, with an appropriate lock period.", + "The delay period to apply in blocks": "The delay period to apply in blocks", + "The external proposal to send to the technical committee": "The external proposal to send to the technical committee", + "The hash of the preimage for the proposal as previously submitted or intended.": "The hash of the preimage for the proposal as previously submitted or intended.", + "The hash of the selected proposal, use it for submitting the proposal": "The hash of the selected proposal, use it for submitting the proposal", + "The image (proposal) will be stored on-chain against the hash of the contents.": "The image (proposal) will be stored on-chain against the hash of the contents.", + "The locked value for this proposal": "The locked value for this proposal", + "The minimum deposit required": "The minimum deposit required", + "The preimage hash of the proposal": "The preimage hash of the proposal", + "The proposal is in the queue for future referendums. One proposal from this list will move forward to voting.": "The proposal is in the queue for future referendums. One proposal from this list will move forward to voting.", + "The proposal will be registered from this account and the balance lock will be applied here.": "The proposal will be registered from this account and the balance lock will be applied here.", + "The vote will be recorded for this account. If another account delegated to this one, the delegated votes will also be counted.": "The vote will be recorded for this account. If another account delegated to this one, the delegated votes will also be counted.", + "The voting period to apply in blocks": "The voting period to apply in blocks", + "This account will pay the fees for the preimage, based on the size thereof.": "This account will pay the fees for the preimage, based on the size thereof.", + "Vote": "Vote", + "Vote on proposal": "Vote on proposal", + "When submitting a proposal the hash needs to be known. Proposals can be submitted with hash-only, but upon dispatch the preimage needs to be available.": "When submitting a proposal the hash needs to be known. Proposals can be submitted with hash-only, but upon dispatch the preimage needs to be available.", + "activate": "activate", + "aye": "aye", + "calculated storage fee": "calculated storage fee", + "conviction": "conviction", + "delay": "delay", + "dispatch queue": "dispatch queue", + "enact": "enact", + "external": "external", + "imminent preimage (proposal already passed)": "imminent preimage (proposal already passed)", + "launch period": "launch period", + "locked": "locked", + "locked balance": "locked balance", + "minimum deposit": "minimum deposit", + "nay": "nay", + "preimage hash": "preimage hash", + "preimage {{hash}}": "preimage {{hash}}", + "proposal hash": "proposal hash", + "proposals": "proposals", + "propose": "propose", + "propose from account": "propose from account", + "proposer": "proposer", + "referenda": "referenda", + "remaining": "remaining", + "second with account": "second with account", + "send from account": "send from account", + "total": "total", + "transferrable": "transferrable", + "turnout": "turnout", + "voting period": "voting period", + "{{blocks}} blocks": "{{blocks}} blocks", + "{{percentage}} aye": "{{percentage}} aye", + "{{threshold}}, not passing": "{{threshold}}, not passing", + "{{threshold}}, passing": "{{threshold}}, passing", + "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-explorer.json b/pioneer/packages/apps/public/locales/en/app-explorer.json new file mode 100644 index 0000000000..27215c501a --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-explorer.json @@ -0,0 +1,49 @@ +{ + "Block details": "Block details", + "Chain info": "Chain info", + "Forks": "Forks", + "No blocks available": "No blocks available", + "No events available": "No events available", + "No logs available": "No logs available", + "No pending extrinsics are in the queue": "No pending extrinsics are in the queue", + "Node info": "Node info", + "best": "best", + "best #": "best #", + "best hash": "best hash", + "block hash or number to query": "block hash or number to query", + "blocks": "blocks", + "connected peers": "connected peers", + "epoch": "epoch", + "era": "era", + "events": "events", + "extrinsics": "extrinsics", + "finalized": "finalized", + "forks": "forks", + "hash": "hash", + "immortal": "immortal", + "index": "index", + "last block": "last block", + "logs": "logs", + "mortal, valid from #{{startAt}} to #{{endsAt}}": "mortal, valid from #{{startAt}} to #{{endsAt}}", + "no": "no", + "no peers connected": "no peers connected", + "not signed": "not signed", + "our best": "our best", + "parent": "parent", + "peer best": "peer best", + "pending extrinsics": "pending extrinsics", + "queued tx": "queued tx", + "recent blocks": "recent blocks", + "recent events": "recent events", + "refresh in": "refresh in", + "role": "role", + "session": "session", + "signer": "signer", + "state": "state", + "syncing": "syncing", + "system events": "system events", + "target": "target", + "total issuance": "total issuance", + "total peers": "total peers", + "yes": "yes" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-extrinsics.json b/pioneer/packages/apps/public/locales/en/app-extrinsics.json new file mode 100644 index 0000000000..da38c9ed9f --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-extrinsics.json @@ -0,0 +1,8 @@ +{ + "Extrinsic submission": "Extrinsic submission", + "Submit Transaction": "Submit Transaction", + "Submit Unsigned": "Submit Unsigned", + "free balance": "free balance", + "submit the following extrinsic": "submit the following extrinsic", + "using the selected account": "using the selected account" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-generic-asset.json b/pioneer/packages/apps/public/locales/en/app-generic-asset.json new file mode 100644 index 0000000000..855282cf2d --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-generic-asset.json @@ -0,0 +1,23 @@ +{ + "Asset ID": "Asset ID", + "Assets": "Assets", + "Enter the Asset ID of the token you want to manage.": "Enter the Asset ID of the token you want to manage.", + "Enter the Asset ID of the token you want to transfer.": "Enter the Asset ID of the token you want to transfer.", + "Forget this asset": "Forget this asset", + "Make Transfer": "Make Transfer", + "No assets found.": "No assets found.", + "Register": "Register", + "Register Asset": "Register Asset", + "Register an Asset": "Register an Asset", + "Select a contact or paste the address you want to send funds to.": "Select a contact or paste the address you want to send funds to.", + "The account you will send funds from.": "The account you will send funds from.", + "Transfer": "Transfer", + "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.", + "Type the name of this Asset. This name will be used across all the apps. It can be edited later on.": "Type the name of this Asset. This name will be used across all the apps. It can be edited later on.", + "amount": "amount", + "asset id": "asset id", + "name": "name", + "send from account": "send from account", + "send to address": "send to address", + "transferrable": "transferrable" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-i18n.json b/pioneer/packages/apps/public/locales/en/app-i18n.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-i18n.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-js.json b/pioneer/packages/apps/public/locales/en/app-js.json new file mode 100644 index 0000000000..bc8f98ddf5 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-js.json @@ -0,0 +1,6 @@ +{ + "Delete this custom example": "Delete this custom example", + "Name your example": "Name your example", + "Save snippet to local storage": "Save snippet to local storage", + "Select example": "Select example" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-parachains.json b/pioneer/packages/apps/public/locales/en/app-parachains.json new file mode 100644 index 0000000000..0a5836dac2 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-parachains.json @@ -0,0 +1,39 @@ +{ + "": "", + "Deregister this parachain": "Deregister this parachain", + "Download": "Download", + "Message data": "Message data", + "Message origin.": "Message origin.", + "Most recent head data": "Most recent head data", + "Not updated in the last block": "Not updated in the last block", + "Parachains overview": "Parachains overview", + "Positive number greater than or equal to {{nextFreeId}}": "Positive number greater than or equal to {{nextFreeId}}", + "Register a parachain": "Register a parachain", + "The code is not recognized as being in valid WASM format": "The code is not recognized as being in valid WASM format", + "The compiled runtime WASM for the parachain you wish to register.": "The compiled runtime WASM for the parachain you wish to register.", + "The compiled runtime WASM for this parachain.": "The compiled runtime WASM for this parachain.", + "The id number to assign to this parachain.": "The id number to assign to this parachain.", + "The id of the parachain to be deregistered.": "The id of the parachain to be deregistered.", + "The initial head state for the parachain.": "The initial head state for the parachain.", + "The initial head state is invalid.": "The initial head state is invalid.", + "The scheduling preference for this parachain.": "The scheduling preference for this parachain.", + "The scheduling setting for this parachain.": "The scheduling setting for this parachain.", + "There are no registered parachains": "There are no registered parachains", + "Unknown": "Unknown", + "Updated in the latest block": "Updated in the latest block", + "best block": "best block", + "code": "code", + "data": "data", + "heads": "heads", + "initial head state": "initial head state", + "next id": "next id", + "no messages": "no messages", + "origin": "origin", + "parachain id": "parachain id", + "parachains": "parachains", + "pending swap id": "pending swap id", + "relay dispatch queue": "relay dispatch queue", + "scheduling": "scheduling", + "swap to id": "swap to id", + "{{relayDispatchQueueSize}} dispatch messages pending": "{{relayDispatchQueueSize}} dispatch messages pending" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-poll.json b/pioneer/packages/apps/public/locales/en/app-poll.json new file mode 100644 index 0000000000..4b19028453 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-poll.json @@ -0,0 +1,41 @@ +{ + "1 billion DOTs; 10 decimals": "1 billion DOTs; 10 decimals", + "10 billion DOTs; 9 decimals": "10 billion DOTs; 9 decimals", + "10 million DOTs; status quo": "10 million DOTs; status quo", + "100 million DOTs; 11 decimals": "100 million DOTs; 11 decimals", + "All voters may alter their votes any number of times prior to the close of the poll.": "All voters may alter their votes any number of times prior to the close of the poll.", + "Any combination of the four options may be approved of by the voter. There is no need to select only one option!": "Any combination of the four options may be approved of by the voter. There is no need to select only one option!", + "Approving of all or none of the options is equivalent and will not affect the outcome of the poll.": "Approving of all or none of the options is equivalent and will not affect the outcome of the poll.", + "Aye, I can support this": "Aye, I can support this", + "Aye, I support this": "Aye, I support this", + "Completed": "Completed", + "Locked funds (e.g. for staking) are counted.": "Locked funds (e.g. for staking) are counted.", + "Nay, I cannot support this": "Nay, I cannot support this", + "Nay, I do not support this": "Nay, I do not support this", + "No change": "No change", + "No change from the original 2017 sale definitions; will mean a total of 10 million DOT from genesis.": "No change from the original 2017 sale definitions; will mean a total of 10 million DOT from genesis.", + "No change from the original 2017 sale definitions; will mean a total of 10m DOT from genesis.": "No change from the original 2017 sale definitions; will mean a total of 10m DOT from genesis.", + "No discretionary lock-voting is in place; all DOT used to vote counts the same.": "No discretionary lock-voting is in place; all DOT used to vote counts the same.", + "Retrieving totals...": "Retrieving totals...", + "Split of 1,000x from the original sale; will mean a total of 10b DOT from genesis. Apparent DOT price would be 1000x lower.": "Split of 1,000x from the original sale; will mean a total of 10b DOT from genesis. Apparent DOT price would be 1000x lower.", + "Split of 1000x": "Split of 1000x", + "Split of 1000x from the original sale; will mean a total of 10 billion DOT from genesis. Apparent DOT price would be 1000x lower and apparent account balances 1000x higher.": "Split of 1000x from the original sale; will mean a total of 10 billion DOT from genesis. Apparent DOT price would be 1000x lower and apparent account balances 1000x higher.", + "Split of 100x": "Split of 100x", + "Split of 100x from the original sale; will mean a total of 1 billion DOT from genesis. Apparent DOT price would be 100x lower and apparent account balances 100x higher.": "Split of 100x from the original sale; will mean a total of 1 billion DOT from genesis. Apparent DOT price would be 100x lower and apparent account balances 100x higher.", + "Split of 100x from the original sale; will mean a total of 1b DOT from genesis. Apparent DOT price would be 100x lower.": "Split of 100x from the original sale; will mean a total of 1b DOT from genesis. Apparent DOT price would be 100x lower.", + "Split of 10x": "Split of 10x", + "Split of 10x from the original sale; will mean a total of 100 million DOT from genesis. Apparent DOT price would be 10x lower and apparent account balances 10x higher.": "Split of 10x from the original sale; will mean a total of 100 million DOT from genesis. Apparent DOT price would be 10x lower and apparent account balances 10x higher.", + "Split of 10x from the original sale; will mean a total of 100m DOT from genesis. Apparent DOT price would be 10x lower.": "Split of 10x from the original sale; will mean a total of 100m DOT from genesis. Apparent DOT price would be 10x lower.", + "This poll is setup to judge the sentiment of the Polkadot token holders in adjusting the number of decimals that is used to identify one full DOT. It does not change the overall supply, but rather just allows for a different representation of the current supply.": "This poll is setup to judge the sentiment of the Polkadot token holders in adjusting the number of decimals that is used to identify one full DOT. It does not change the overall supply, but rather just allows for a different representation of the current supply.", + "This vote does not affect any economics of the Polkadot platform. Staking rewards, inflation, effective market capitalisation and the underlying balances of every account remain completely unchanged. It is \"merely\" about what units we use to denominate the balances into \"DOT\" for the purpose of display.": "This vote does not affect any economics of the Polkadot platform. Staking rewards, inflation, effective market capitalisation and the underlying balances of every account remain completely unchanged. It is \"merely\" about what units we use to denominate the balances into \"DOT\" for the purpose of display.", + "Vote": "Vote", + "Voting closed": "Voting closed", + "Voting costs nothing other than the transaction fee and can be done from all accounts with a non-zero spendable balance.": "Voting costs nothing other than the transaction fee and can be done from all accounts with a non-zero spendable balance.", + "Voting is made on a per-account basis; a single account must all vote the same way and cannot split its vote.": "Voting is made on a per-account basis; a single account must all vote the same way and cannot split its vote.", + "You can indicate your vote for any combination of the options laid out below.": "You can indicate your vote for any combination of the options laid out below.", + "denomination vote": "denomination vote", + "poll on token decimals": "poll on token decimals", + "vote using my account": "vote using my account", + "{{balance}} voted": "{{balance}} voted", + "{{percentage}}% turnout": "{{percentage}}% turnout" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-settings.json b/pioneer/packages/apps/public/locales/en/app-settings.json new file mode 100644 index 0000000000..5195129fb4 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-settings.json @@ -0,0 +1,51 @@ +{ + "Additional types as a JSON file (or edit below)": "Additional types as a JSON file (or edit below)", + "Address Prefix": "Address Prefix", + "Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)": "Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)", + "Apply to UI": "Apply to UI", + "Chain specifications": "Chain specifications", + "Color": "Color", + "Decimals": "Decimals", + "Decimals decides the smallest unit of the token, which is 1/10^decimals": "Decimals decides the smallest unit of the token, which is 1/10^decimals", + "Developer": "Developer", + "Extensions": "Extensions", + "General": "General", + "Generate {{lng}}/translation.json": "Generate {{lng}}/translation.json", + "Genesis Hash": "Genesis Hash", + "Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched": "Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched", + "Manage your connection to Ledger S": "Manage your connection to Ledger S", + "Metadata {{count}}": "Metadata {{count}}", + "Name of the network. It is only for display purposes.": "Name of the network. It is only for display purposes.", + "Network Name": "Network Name", + "No upgradable extensions found": "No upgradable extensions found", + "Override the default identity icon display with a specific theme": "Override the default identity icon display with a specific theme", + "Override the default ss58 prefix for address generation": "Override the default ss58 prefix for address generation", + "Prefix indicates the ss58 address format in this network, it is a number between 0 ~ 255 that describes the precise format of the bytes of the address": "Prefix indicates the ss58 address format in this network, it is a number between 0 ~ 255 that describes the precise format of the bytes of the address", + "Random": "Random", + "Reset": "Reset", + "Save": "Save", + "Save & Reload": "Save & Reload", + "Save the type definitions for your custom structures as key-value pairs in a valid JSON file. The key should be the name of your custom structure and the value an object containing your type definitions.": "Save the type definitions for your custom structures as key-value pairs in a valid JSON file. The key should be the name of your custom structure and the value an object containing your type definitions.", + "Select the remote endpoint, either from the dropdown on manual entered via the custom toggle": "Select the remote endpoint, either from the dropdown on manual entered via the custom toggle", + "The RPC node can be selected from the pre-defined list or manually entered, depending on the chain you wish to connect to.": "The RPC node can be selected from the pre-defined list or manually entered, depending on the chain you wish to connect to.", + "The color used to distinguish this network with others, use color code with 3 or 6 digits, like \"#FFF\" or \"#111111\"": "The color used to distinguish this network with others, use color code with 3 or 6 digits, like \"#FFF\" or \"#111111\"", + "This is not a valid JSON object.": "This is not a valid JSON object.", + "Translate": "Translate", + "Unit": "Unit", + "Unit decides the name of 1 unit token, e.g. \"DOT\" for Polkadot": "Unit decides the name of 1 unit token, e.g. \"DOT\" for Polkadot", + "Update metadata": "Update metadata", + "Your custom types have been added": "Your custom types have been added", + "address prefix": "address prefix", + "custom endpoint": "custom endpoint", + "default icon theme": "default icon theme", + "default interface language": "default interface language", + "do not include empty strings in the generated file": "do not include empty strings in the generated file", + "include all empty strings in the generated file": "include all empty strings in the generated file", + "interface operation mode": "interface operation mode", + "manage hardware connections": "manage hardware connections", + "remote node/endpoint to connect to": "remote node/endpoint to connect to", + "the language to display translations for": "the language to display translations for", + "the module to display strings for": "the module to display strings for", + "upgradable extensions": "upgradable extensions", + "{{done}}/{{total}}, {{progress}}% done": "{{done}}/{{total}}, {{progress}}% done" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-society.json b/pioneer/packages/apps/public/locales/en/app-society.json new file mode 100644 index 0000000000..0eabc9637d --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-society.json @@ -0,0 +1,45 @@ +{ + "Approvals": "Approvals", + "Approve or reject this defender.": "Approve or reject this defender.", + "Approve this candidacy.": "Approve this candidacy.", + "Aye, I approve": "Aye, I approve", + "Bid": "Bid", + "Bid to join": "Bid to join", + "Close": "Close", + "Current society head, exempt": "Current society head, exempt", + "Nay, I do not approve": "Nay, I do not approve", + "No active members": "No active members", + "No bids": "No bids", + "No candidates": "No candidates", + "Rejections": "Rejections", + "Skeptics": "Skeptics", + "Society overview": "Society overview", + "Submit bid": "Submit bid", + "The actual account you wish to sub,it the bid with": "The actual account you wish to sub,it the bid with", + "The address to vote from (must be a member)": "The address to vote from (must be a member)", + "The amount to associate with your bid, should be less than the pot.": "The amount to associate with your bid, should be less than the pot.", + "The amount to tie to your bid. The lowest bidder moves forward.": "The amount to tie to your bid. The lowest bidder moves forward.", + "Unbid": "Unbid", + "Vote": "Vote", + "Vote for candidate": "Vote for candidate", + "Vote for defender": "Vote for defender", + "Your canidate/bid account. Once accepted this account will become a member.": "Your canidate/bid account. Once accepted this account will become a member.", + "bid account": "bid account", + "bid amount": "bid amount", + "bids": "bids", + "candidates": "candidates", + "challenge": "challenge", + "defender": "defender", + "design samples": "design samples", + "kind": "kind", + "members": "members", + "pot": "pot", + "rotation": "rotation", + "society head": "society head", + "strikes": "strikes", + "value": "value", + "vote for candidate": "vote for candidate", + "vote for defender": "vote for defender", + "vote from account": "vote from account", + "votes": "votes" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-staking.json b/pioneer/packages/apps/public/locales/en/app-staking.json new file mode 100644 index 0000000000..f8878112d7 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-staking.json @@ -0,0 +1,200 @@ +{ + "A controller account should not be set to manage multiple stashes. The selected controller is already controlling {{stashId}}": "A controller account should not be set to manage multiple stashes. The selected controller is already controlling {{stashId}}", + "A controller account should not be set to manages multiple stashes. The selected controller is already controlling {{stashId}}": "A controller account should not be set to manages multiple stashes. The selected controller is already controlling {{stashId}}", + "A controller account should not map to another stash. This selected controller is a stash, controlled by {{bondedId}}": "A controller account should not map to another stash. This selected controller is a stash, controlled by {{bondedId}}", + "Account actions": "Account actions", + "Active nominations ({{count}})": "Active nominations ({{count}})", + "All rewards will go towards the selected output destination when a payout is made.": "All rewards will go towards the selected output destination when a payout is made.", + "All the listed validators and all their nominators will receive their rewards.": "All the listed validators and all their nominators will receive their rewards.", + "Amount to add to the currently bonded funds. This is adjusted using the available funds on the account.": "Amount to add to the currently bonded funds. This is adjusted using the available funds on the account.", + "Any account can request payout for stakers, this is not limited to accounts that will be rewarded.": "Any account can request payout for stakers, this is not limited to accounts that will be rewarded.", + "Aura": "Aura", + "Babe": "Babe", + "Bond": "Bond", + "Bond & Nominate": "Bond & Nominate", + "Bond & Validate": "Bond & Validate", + "Bond more": "Bond more", + "Bond more funds": "Bond more funds", + "Bonding Preferences": "Bonding Preferences", + "Change controller account": "Change controller account", + "Change reward destination": "Change reward destination", + "Change session keys": "Change session keys", + "Change validator preferences": "Change validator preferences", + "Changing the key only takes effect at the start of the next session. The input here is generates from the author_rotateKeys command": "Changing the key only takes effect at the start of the next session. The input here is generates from the author_rotateKeys command", + "Display overview information for the selected validator, including blocks produced.": "Display overview information for the selected validator, including blocks produced.", + "Distinct stash and controller accounts are recommended to ensure fund security. You will be allowed to make the transaction, but take care to not tie up all funds, only use a portion of the available funds during this period.": "Distinct stash and controller accounts are recommended to ensure fund security. You will be allowed to make the transaction, but take care to not tie up all funds, only use a portion of the available funds during this period.", + "Ensure that not all funds are locked, funds need to be available for fees.": "Ensure that not all funds are locked, funds need to be available for fees.", + "Filter available candidates based on name, address or short account index.": "Filter available candidates based on name, address or short account index.", + "For fund security, your session key should not match your stash key.": "For fund security, your session key should not match your stash key.", + "Grandpa": "Grandpa", + "I'm Online": "I'm Online", + "Inactive nominations ({{count}})": "Inactive nominations ({{count}})", + "Inject Keys": "Inject Keys", + "Inject session keys (advanced)": "Inject session keys (advanced)", + "Keys from rotateKeys": "Keys from rotateKeys", + "Max, {{eras}} eras": "Max, {{eras}} eras", + "New stake": "New stake", + "No active validators found": "No active validators found", + "No active validators to check": "No active validators to check", + "No funds staked yet. Bond funds to validate or nominate a validator": "No funds staked yet. Bond funds to validate or nominate a validator", + "No pending era payouts from validators": "No pending era payouts from validators", + "No pending payouts for your stashes": "No pending payouts for your stashes", + "No waiting validators found": "No waiting validators found", + "Nominate": "Nominate", + "Nominate Validators": "Nominate Validators", + "Nominate selected": "Nominate selected", + "Nominate validators": "Nominate validators", + "Nominations ({{count}})": "Nominations ({{count}})", + "Nominator": "Nominator", + "Nominators can be selected automatically based on the current on-chain conditions or supplied manually as selected from the list of all currently available validators. In both cases, your favorites appear for the selection.": "Nominators can be selected automatically based on the current on-chain conditions or supplied manually as selected from the list of all currently available validators. In both cases, your favorites appear for the selection.", + "Once bonded, it wil need to be unlocked/withdrawn and will be locked for at least the bonding duration.": "Once bonded, it wil need to be unlocked/withdrawn and will be locked for at least the bonding duration.", + "Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, the nominations will show as inactive.": "Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, the nominations will show as inactive.", + "One of your available nomination accounts, keyed by the stash. The transaction will be sent from the controller.": "One of your available nomination accounts, keyed by the stash. The transaction will be sent from the controller.", + "Parachains": "Parachains", + "Payout": "Payout", + "Payout all": "Payout all", + "Payout all stakers": "Payout all stakers", + "Payouts": "Payouts", + "Retrieving info for all applicable eras, this will take some time": "Retrieving info for all applicable eras, this will take some time", + "Retrieving info for last {{numEras}} eras, this will take some time": "Retrieving info for last {{numEras}} eras, this will take some time", + "Retrieving info for the selected eras, this will take some time": "Retrieving info for the selected eras, this will take some time", + "Rewards (once paid) can be deposited to either the stash or controller, with different effects.": "Rewards (once paid) can be deposited to either the stash or controller, with different effects.", + "Select best": "Select best", + "Select targets manually (no auto-selection based on current profitability)": "Select targets manually (no auto-selection based on current profitability)", + "Session Key": "Session Key", + "Set Session Key": "Set Session Key", + "Set controller": "Set controller", + "Set nominees": "Set nominees", + "Set reward destination": "Set reward destination", + "Set validator preferences": "Set validator preferences", + "Setup Nominator {{step}}/{{NUM_STEPS}}": "Setup Nominator {{step}}/{{NUM_STEPS}}", + "Setup Validator {{step}}/{{NUM_STEPS}}": "Setup Validator {{step}}/{{NUM_STEPS}}", + "Since this transaction deals with funding, the stash account will be used.": "Since this transaction deals with funding, the stash account will be used.", + "Staking overview": "Staking overview", + "Stash": "Stash", + "Stop": "Stop", + "Submit key": "Submit key", + "Targets": "Targets", + "The Threshold must be a positive number": "The Threshold must be a positive number", + "The Threshold must lower than 11": "The Threshold must lower than 11", + "The UI puts a limit of 40 payouts at a time, where each payout is a single validator for a single era.": "The UI puts a limit of 40 payouts at a time, where each payout is a single validator for a single era.", + "The amount of funds to unbond, this is adjusted using the bonded funds on the stash account.": "The amount of funds to unbond, this is adjusted using the bonded funds on the stash account.", + "The amount placed at-stake should allow some free funds for future transactions.": "The amount placed at-stake should allow some free funds for future transactions.", + "The amount placed at-stake should be no more that 95% of your available amount to protect against slashing events.": "The amount placed at-stake should be no more that 95% of your available amount to protect against slashing events.", + "The amount placed at-stake should not be your full available available amount to allow for transaction fees.": "The amount placed at-stake should not be your full available available amount to allow for transaction fees.", + "The amount that will be used on a per-validator basis to calculate profits for that validator.": "The amount that will be used on a per-validator basis to calculate profits for that validator.", + "The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for the validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.": "The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for the validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.", + "The bonded amount is less than the minimum bond amount of {{existentialDeposit}}": "The bonded amount is less than the minimum bond amount of {{existentialDeposit}}", + "The bonding duration for any staked funds. After this period needs to be withdrawn.": "The bonding duration for any staked funds. After this period needs to be withdrawn.", + "The bonding duration for any staked funds. Needs to be unlocked and withdrawn to become available.": "The bonding duration for any staked funds. Needs to be unlocked and withdrawn to become available.", + "The commission is deducted from all rewards before the remainder is split with nominators.": "The commission is deducted from all rewards before the remainder is split with nominators.", + "The controller does no have sufficient funds available to cover transaction fees. Ensure that a funded controller is used.": "The controller does no have sufficient funds available to cover transaction fees. Ensure that a funded controller is used.", + "The controller is the account that is be used to control any nominating or validating actions. I will sign this transaction.": "The controller is the account that is be used to control any nominating or validating actions. I will sign this transaction.", + "The controller is the account that will be used to control any nominating or validating actions. Should not match another stash or controller.": "The controller is the account that will be used to control any nominating or validating actions. Should not match another stash or controller.", + "The destination account for any payments as either a nominator or validator": "The destination account for any payments as either a nominator or validator", + "The funds will only be available for withdrawal after the unbonding period, however will not be part of the staked amount after the next validator election. You can follow the unlock countdown in the UI.": "The funds will only be available for withdrawal after the unbonding period, however will not be part of the staked amount after the next validator election. You can follow the unlock countdown in the UI.", + "The hex output from author_rotateKeys, as executed on the validator node. The keys will show as pending until applied at the start of a new session.": "The hex output from author_rotateKeys, as executed on the validator node. The keys will show as pending until applied at the start of a new session.", + "The key type and crypto type to use for this key. Be aware that different keys have different crypto requirements. You should be familiar with the type requirements for the different keys.": "The key type and crypto type to use for this key. Be aware that different keys have different crypto requirements. You should be familiar with the type requirements for the different keys.", + "The percentage reward (0-100) that should be applied for the validator": "The percentage reward (0-100) that should be applied for the validator", + "The seed and derivation path will be submitted to the validator node. this is an advanced operation, only to be performed when you are sure of the security and connection risks.": "The seed and derivation path will be submitted to the validator node. this is an advanced operation, only to be performed when you are sure of the security and connection risks.", + "The selected controller tied to this stash. Once set, this account will be able to control the actions performed by the stash account.": "The selected controller tied to this stash. Once set, this account will be able to control the actions performed by the stash account.", + "The selected validators to nominate, either via the \"currently best algorithm\" or via a manual selection.": "The selected validators to nominate, either via the \"currently best algorithm\" or via a manual selection.", + "The specified value is greater than the recommended amount. You may not be adequately protected against slashing events or have funds for future fees.": "The specified value is greater than the recommended amount. You may not be adequately protected against slashing events or have funds for future fees.", + "The specified value is greater than your free balance. The node will bond the maximum amount available.": "The specified value is greater than your free balance. The node will bond the maximum amount available.", + "The specified value is too large and does not allow funds to pay future transaction fees.": "The specified value is too large and does not allow funds to pay future transaction fees.", + "The stash account that is used. This will allow the controller to perform all non-funds related operations on behalf of the account.": "The stash account that is used. This will allow the controller to perform all non-funds related operations on behalf of the account.", + "The stash and controller pair as linked. This operation will be performed via the controller.": "The stash and controller pair as linked. This operation will be performed via the controller.", + "The stash and controller pair, here the controller will be used to send the transaction.": "The stash and controller pair, here the controller will be used to send the transaction.", + "The stash and controller pair. This transaction, managing preferences, will be sent from the controller.": "The stash and controller pair. This transaction, managing preferences, will be sent from the controller.", + "The stash and controller pair. This transaction, setting the session keys, will be sent from the controller.": "The stash and controller pair. This transaction, setting the session keys, will be sent from the controller.", + "The stash that is to be affected. The transaction will be sent from the associated controller account.": "The stash that is to be affected. The transaction will be sent from the associated controller account.", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the free amount available)": "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the free amount available)", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the total amount available)": "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the total amount available)", + "There is currently an ongoing election for new validator candidates. As such staking operations are not permitted.": "There is currently an ongoing election for new validator candidates. As such staking operations are not permitted.", + "Think of the stash as your cold wallet and the controller as your hot wallet. Funding operations are controlled by the stash, any other non-funding actions by the controller itself.": "Think of the stash as your cold wallet and the controller as your hot wallet. Funding operations are controlled by the stash, any other non-funding actions by the controller itself.", + "This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.": "This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.", + "This pubic key is what will be visible in your queued keys list. It is generated based on the seed and the crypto used.": "This pubic key is what will be visible in your queued keys list. It is generated based on the seed and the crypto used.", + "To ensure optimal fund security using the same stash/controller is strongly discouraged, but not forbidden.": "To ensure optimal fund security using the same stash/controller is strongly discouraged, but not forbidden.", + "Unbond": "Unbond", + "Unbond funds": "Unbond funds", + "Use an automatic selection of the currently most profitable validators": "Use an automatic selection of the currently most profitable validators", + "Validate": "Validate", + "Validator": "Validator", + "Validator stats": "Validator stats", + "Waiting": "Waiting", + "Waiting nominations ({{count}})": "Waiting nominations ({{count}})", + "Withdraw unbonded funds": "Withdraw unbonded funds", + "Your stash account. The transaction will be sent from the associated controller.": "Your stash account. The transaction will be sent from the associated controller.", + "additional bonded funds": "additional bonded funds", + "amount to use for estimation": "amount to use for estimation", + "auto-selected targets for nomination": "auto-selected targets for nomination", + "available": "available", + "average": "average", + "balance": "balance", + "bonded": "bonded", + "candidate accounts": "candidate accounts", + "comm.": "comm.", + "commission": "commission", + "controller": "controller", + "controller account": "controller account", + "crypto type to use": "crypto type to use", + "ed25519, Edwards": "ed25519, Edwards", + "elected stake": "elected stake", + "era points": "era points", + "eras": "eras", + "filter by name, address or index": "filter by name, address or index", + "generated public key": "generated public key", + "intentions": "intentions", + "key type to set": "key type to set", + "last #": "last #", + "last block": "last block", + "last reward": "last reward", + "limit to elected": "limit to elected", + "next": "next", + "nominated accounts": "nominated accounts", + "nominators": "nominators", + "on-chain bonding duration": "on-chain bonding duration", + "only query most recent {{partialEras}} of {{historyDepth}} eras": "only query most recent {{partialEras}} of {{historyDepth}} eras", + "only with an identity": "only with an identity", + "other stake": "other stake", + "own stake": "own stake", + "payment destination": "payment destination", + "payout stakers for (multiple)": "payout stakers for (multiple)", + "payout stakers for (single)": "payout stakers for (single)", + "payout/stash": "payout/stash", + "payout/validator": "payout/validator", + "points": "points", + "prev": "prev", + "priority {{index}}": "priority {{index}}", + "profit/era": "profit/era", + "profit/era est": "profit/era est", + "request payout from": "request payout from", + "reward commission percentage": "reward commission percentage", + "rewards": "rewards", + "rewards & slashes": "rewards & slashes", + "selected validators": "selected validators", + "sr15519, Schnorrkel": "sr15519, Schnorrkel", + "staked": "staked", + "stash account": "stash account", + "stashes": "stashes", + "suri (seed & derivation)": "suri (seed & derivation)", + "the associated controller": "the associated controller", + "the stash account to nominate with": "the stash account to nominate with", + "total issuance": "total issuance", + "total stake": "total stake", + "total staked": "total staked", + "unbond amount": "unbond amount", + "validator to query": "validator to query", + "validators": "validators", + "validators/nominators": "validators/nominators", + "value bonded": "value bonded", + "waiting": "waiting", + "with/without identity": "with/without identity", + "{{count}} own stashes": "{{count}} own stashes", + "{{currency}} average": "{{currency}} average", + "{{currency}} clipped": "{{currency}} clipped", + "{{currency}} rewards": "{{currency}} rewards", + "{{currency}} slashed": "{{currency}} slashed", + "{{currency}} total": "{{currency}} total", + "{{days}} days": "{{days}} days" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-storage.json b/pioneer/packages/apps/public/locales/en/app-storage.json new file mode 100644 index 0000000000..7c48d4b59f --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-storage.json @@ -0,0 +1,8 @@ +{ + "Constants": "Constants", + "Raw storage": "Raw storage", + "Storage": "Storage", + "hex-encoded storage key": "hex-encoded storage key", + "selected constant query": "selected constant query", + "selected state query": "selected state query" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-sudo.json b/pioneer/packages/apps/public/locales/en/app-sudo.json new file mode 100644 index 0000000000..b047b74a94 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-sudo.json @@ -0,0 +1,14 @@ +{ + "Reassign": "Reassign", + "Set sudo key": "Set sudo key", + "Submit Sudo": "Submit Sudo", + "Sudo access": "Sudo access", + "The unchecked weight as specified for the sudoUncheckedWeight call.": "The unchecked weight as specified for the sudoUncheckedWeight call.", + "You do not have access to the current sudo key": "You do not have access to the current sudo key", + "You will no longer have sudo access": "You will no longer have sudo access", + "submit the following change": "submit the following change", + "sudo key": "sudo key", + "sudo with unchecked weight parameter": "sudo with unchecked weight parameter", + "sudo without unchecked weight parameter": "sudo without unchecked weight parameter", + "unchecked weight for this call": "unchecked weight for this call" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-tech-comm.json b/pioneer/packages/apps/public/locales/en/app-tech-comm.json new file mode 100644 index 0000000000..e4cfe4028c --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-tech-comm.json @@ -0,0 +1,31 @@ +{ + "Close": "Close", + "Close proposal": "Close proposal", + "Committee prime member, default voting": "Committee prime member, default voting", + "No committee proposals": "No committee proposals", + "No members found": "No members found", + "Positive number between 1 and {{count}}": "Positive number between 1 and {{count}}", + "Proposals ({{count}})": "Proposals ({{count}})", + "Propose a committee motion": "Propose a committee motion", + "Select the account you wish close the proposal with.": "Select the account you wish close the proposal with.", + "Select the account you wish to make the proposal with.": "Select the account you wish to make the proposal with.", + "Submit proposal": "Submit proposal", + "Tech. committee": "Tech. committee", + "The committee account that will apply the close for the current round.": "The committee account that will apply the close for the current round.", + "The minimum number of committee votes required to approve this motion": "The minimum number of committee votes required to approve this motion", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted for a subsequent voting round.": "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted for a subsequent voting round.", + "Vote": "Vote", + "Vote on proposal": "Vote on proposal", + "You are voting with this collective's prime account. The vote will be the default outcome in case of any abstentions.": "You are voting with this collective's prime account. The vote will be the default outcome in case of any abstentions.", + "aye": "aye", + "members": "members", + "nay": "nay", + "prime member": "prime member", + "proposal": "proposal", + "proposals": "proposals", + "propose from account": "propose from account", + "sending account": "sending account", + "threshold": "threshold", + "total": "total", + "voting end": "voting end" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-toolbox.json b/pioneer/packages/apps/public/locales/en/app-toolbox.json new file mode 100644 index 0000000000..cef4a96cab --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-toolbox.json @@ -0,0 +1,44 @@ +{ + "Crypto not detected": "Crypto not detected", + "Cryptography used to create this signature. It is auto-detected on valid signatures.": "Cryptography used to create this signature. It is auto-detected on valid signatures.", + "Detection on the input string to determine if it is hex or non-hex.": "Detection on the input string to determine if it is hex or non-hex.", + "Hash data": "Hash data", + "No": "No", + "RPC calls": "RPC calls", + "Sign message": "Sign message", + "Submit RPC call": "Submit RPC call", + "The account that signed the input": "The account that signed the input", + "The account's password specified at the creation of this account.": "The account's password specified at the creation of this account.", + "The actual JSONRPC module and function to make a call to.": "The actual JSONRPC module and function to make a call to.", + "The blake2b 256-bit hash of the actual input data.": "The blake2b 256-bit hash of the actual input data.", + "The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.": "The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.", + "The input data to hash. This can be either specified as a hex value (0x-prefix) or as a string.": "The input data to hash. This can be either specified as a hex value (0x-prefix) or as a string.", + "The input data to sign. This can be either specified as a hex value (0x-prefix) or as a string.": "The input data to sign. This can be either specified as a hex value (0x-prefix) or as a string.", + "The resulting signature of the input data, as done with the crypto algorithm from the account. (This could be non-deterministic for some types such as sr25519).": "The resulting signature of the input data, as done with the crypto algorithm from the account. (This could be non-deterministic for some types such as sr25519).", + "The selected account to be unlocked.": "The selected account to be unlocked.", + "The signature as by the account being checked, supplied as a hex-formatted string.": "The signature as by the account being checked, supplied as a hex-formatted string.", + "This account that will perform the message signing.": "This account that will perform the message signing.", + "This external account cannot be used to sign data. Only Limited support is currently available for signing from any non-internal accounts.": "This external account cannot be used to sign data. Only Limited support is currently available for signing from any non-internal accounts.", + "This injected account cannot be used to sign data since the extension does not support raw signing.": "This injected account cannot be used to sign data since the extension does not support raw signing.", + "Unlock": "Unlock", + "Unlock account": "Unlock account", + "Unlock the account for signing. Once active the signature will be generated based on the content provided.": "Unlock the account for signing. Once active the signature will be generated based on the content provided.", + "Verify signature": "Verify signature", + "Yes": "Yes", + "You need to unlock this account to be able to sign data.": "You need to unlock this account to be able to sign data.", + "account": "account", + "call the selected endpoint": "call the selected endpoint", + "from the following data": "from the following data", + "hex input data": "hex input data", + "password": "password", + "select the account you wish to sign data with": "select the account you wish to sign data with", + "sign data from account": "sign data from account", + "sign the following data": "sign the following data", + "signature crypto type": "signature crypto type", + "signature of supplied data": "signature of supplied data", + "the resulting hash is": "the resulting hash is", + "the supplied signature": "the supplied signature", + "using the following data": "using the following data", + "verify using address": "verify using address", + "with an index of": "with an index of" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/app-treasury.json b/pioneer/packages/apps/public/locales/en/app-treasury.json new file mode 100644 index 0000000000..a350554d1f --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/app-treasury.json @@ -0,0 +1,79 @@ +{ + "A reason (to be stored-on-chain) as to why the recipient deserves a tip payout.": "A reason (to be stored-on-chain) as to why the recipient deserves a tip payout.", + "Acceptance proposal to council": "Acceptance proposal to council", + "Allocate a suggested tip amount. With enough endorsements, the suggested values are averaged and sent to the beneficiary.": "Allocate a suggested tip amount. With enough endorsements, the suggested values are averaged and sent to the beneficiary.", + "Approved": "Approved", + "As a council member, you can suggest an initial value for the tip, each other council member can suggest their own.": "As a council member, you can suggest an initial value for the tip, each other council member can suggest their own.", + "Cancel": "Cancel", + "Close": "Close", + "Close tip": "Close tip", + "Endorse": "Endorse", + "Endorsements ({{count}})": "Endorsements ({{count}})", + "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)", + "No approved proposals": "No approved proposals", + "No open tips": "No open tips", + "No pending proposals": "No pending proposals", + "Of the beneficiary amount, at least {{bondPercentage}} would need to be put up as collateral. The maximum of this and the minimum bond will be used to secure the proposal, refundable if it passes.": "Of the beneficiary amount, at least {{bondPercentage}} would need to be put up as collateral. The maximum of this and the minimum bond will be used to secure the proposal, refundable if it passes.", + "Proposal can either be to approve or reject this spend. One approved, the change is applied by either removing the proposal or scheduling payout.": "Proposal can either be to approve or reject this spend. One approved, the change is applied by either removing the proposal or scheduling payout.", + "Proposals": "Proposals", + "Propose tip": "Propose tip", + "Rejection proposal to council": "Rejection proposal to council", + "Select the account you wish to close from.": "Select the account you wish to close from.", + "Select the account you wish to submit the proposal from.": "Select the account you wish to submit the proposal from.", + "Select the account you wish to submit the tip from.": "Select the account you wish to submit the tip from.", + "Select the council account you wish to use to make the proposal.": "Select the council account you wish to use to make the proposal.", + "Send to council": "Send to council", + "Submit proposal": "Submit proposal", + "Submit tip": "Submit tip", + "Submit tip endorsement": "Submit tip endorsement", + "Submit tip request": "Submit tip request", + "Submit treasury proposal": "Submit treasury proposal", + "The account to which the proposed balance will be transferred if approved": "The account to which the proposed balance will be transferred if approved", + "The account to which the tip will be transferred if approved": "The account to which the tip will be transferred if approved", + "The amount that will be allocated from the treasury pot": "The amount that will be allocated from the treasury pot", + "The beneficiary will receive the full amount if the proposal passes.": "The beneficiary will receive the full amount if the proposal passes.", + "The beneficiary will received the tip as approved by council members.": "The beneficiary will received the tip as approved by council members.", + "The council member that is proposing this, submission equates to an \"aye\" vote.": "The council member that is proposing this, submission equates to an \"aye\" vote.", + "The council proposal to make the vote on": "The council proposal to make the vote on", + "The hash for the proposal this vote applies to": "The hash for the proposal this vote applies to", + "The minimum amount that will be bonded": "The minimum amount that will be bonded", + "The on-chain percentage for the treasury": "The on-chain percentage for the treasury", + "The reason why this tip should be paid.": "The reason why this tip should be paid.", + "The suggested value for this tip": "The suggested value for this tip", + "The tip amount that should be allocated": "The tip amount that should be allocated", + "The type of council proposal to submit.": "The type of council proposal to submit.", + "The value is the amount that is being asked for and that will be allocated to the beneficiary if the proposal is approved.": "The value is the amount that is being asked for and that will be allocated to the beneficiary if the proposal is approved.", + "This account will make the proposal and be responsible for the bond.": "This account will make the proposal and be responsible for the bond.", + "Tip": "Tip", + "Tippers ({{count}})": "Tippers ({{count}})", + "To council": "To council", + "Treasury overview": "Treasury overview", + "Use this account to request the tip from. This can be a normal or council account.": "Use this account to request the tip from. This can be a normal or council account.", + "Vote": "Vote", + "Vote on proposal": "Vote on proposal", + "Your close will be applied for this council account.": "Your close will be applied for this council account.", + "Your endorsement will be applied for this account.": "Your endorsement will be applied for this account.", + "approved": "approved", + "available": "available", + "beneficiary": "beneficiary", + "bond": "bond", + "council proposal": "council proposal", + "council proposal type": "council proposal type", + "deposit": "deposit", + "fee": "fee", + "finder": "finder", + "minimum bond": "minimum bond", + "payment": "payment", + "proposal bond": "proposal bond", + "proposal hash": "proposal hash", + "proposals": "proposals", + "reason": "reason", + "spend period": "spend period", + "submit with account": "submit with account", + "submit with council account": "submit with council account", + "tip reason": "tip reason", + "tip value": "tip value", + "tips": "tips", + "total": "total", + "value": "value" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/apps-config.json b/pioneer/packages/apps/public/locales/en/apps-config.json new file mode 100644 index 0000000000..533dda51c1 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/apps-config.json @@ -0,0 +1,24 @@ +{ + "lng.detect": "Default browser language (auto-detect)", + "rpc.arcadia": "Arcadia (Nodle Testnet, hosted by Nodle)", + "rpc.berlin": "Berlin (Edgeware Testnet, hosted by Commonwealth Labs)", + "rpc.custom": "Custom environment", + "rpc.edgeware": "Edgeware (Edgeware Mainnet, hosted by Commonwealth Labs)", + "rpc.flamingfir": "Flaming Fir (Substrate Testnet, hosted by Parity)", + "rpc.header.dev": "Development", + "rpc.header.live": "Live networks", + "rpc.header.test": "Test networks", + "rpc.kulupu": "Kulupu (Kulupu Mainnet, hosted by Kulupu)", + "rpc.kusama.ava": "Kusama (Polkadot Canary, user-run public nodes; see https://status.cloud.ava.do/)", + "rpc.kusama.parity": "Kusama (Polkadot Canary, hosted by Parity)", + "rpc.kusama.w3f": "Kusama (Polkadot Canary, hosted by Web3 Foundation)", + "rpc.local": "Local Node (Own, 127.0.0.1:9944)", + "rpc.mandala": "Mandala (Acala Testnet, hosted by Acala)", + "rpc.westend": "Westend (Polkadot Testnet, hosted by Parity)", + "ss58.centrifuge": "Centrifuge (live)", + "ss58.default": "Default for the connected node", + "ss58.edgeware": "Edgeware (live)", + "ss58.kusama": "Kusama (canary)", + "ss58.polkadot": "Polkadot (live)", + "ss58.substrate": "Substrate (generic)" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/apps-electron.json b/pioneer/packages/apps/public/locales/en/apps-electron.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/apps-electron.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/apps-routing.json b/pioneer/packages/apps/public/locales/en/apps-routing.json new file mode 100644 index 0000000000..b765fafbc2 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/apps-routing.json @@ -0,0 +1,25 @@ +{ + "nav.accounts": "Accounts", + "nav.claims": "Claim Tokens", + "nav.contracts": "Contracts", + "nav.council": "Council", + "nav.dashboard": "Dashboard", + "nav.democracy": "Democracy", + "nav.explorer": "Explorer", + "nav.extrinsics": "Extrinsics", + "nav.generic-asset": "Generic asset", + "nav.github": "GitHub", + "nav.i18n": "I18n Translator", + "nav.js": "Javascript", + "nav.parachains": "Parachains", + "nav.settings": "Settings", + "nav.society": "Society", + "nav.staking": "Staking", + "nav.storage": "Chain state", + "nav.sudo": "Sudo", + "nav.tech-comm": "Tech. comm.", + "nav.toolbox": "Toolbox", + "nav.transfer": "Transfer", + "nav.treasury": "Treasury", + "nav.wiki": "Wiki" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/apps.json b/pioneer/packages/apps/public/locales/en/apps.json new file mode 100644 index 0000000000..6055b34bd2 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/apps.json @@ -0,0 +1,14 @@ +{ + "Create an account now.": "Create an account now.", + "Initializing connection": "Initializing connection", + "Select Network": "Select Network", + "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.": "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.", + "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.": "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.", + "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.": "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.", + "nav.github": "nav.github", + "nav.wiki": "nav.wiki", + "transfer received": "transfer received", + "update on #{{index}}": "update on #{{index}}", + "version {{version}}": "version {{version}}" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/index.json b/pioneer/packages/apps/public/locales/en/index.json new file mode 100644 index 0000000000..962b58bcc3 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/index.json @@ -0,0 +1,32 @@ +[ + "app-accounts.json", + "app-claims.json", + "app-contracts.json", + "app-council.json", + "app-democracy.json", + "app-explorer.json", + "app-extrinsics.json", + "app-generic-asset.json", + "app-js.json", + "app-parachains.json", + "app-poll.json", + "app-settings.json", + "app-society.json", + "app-staking.json", + "app-storage.json", + "app-sudo.json", + "app-tech-comm.json", + "app-toolbox.json", + "app-treasury.json", + "apps-config.json", + "apps-routing.json", + "apps.json", + "joy-election.json", + "joy-media.json", + "joy-members.json", + "joy-roles.json", + "react-components.json", + "react-params.json", + "react-query.json", + "react-signer.json" +] \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-election.json b/pioneer/packages/apps/public/locales/en/joy-election.json new file mode 100644 index 0000000000..e019945c6b --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-election.json @@ -0,0 +1,6 @@ +{ + "Applicants": "Applicants", + "Council members": "Council members", + "Dashboard": "Dashboard", + "Votes": "Votes" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-forum.json b/pioneer/packages/apps/public/locales/en/joy-forum.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-forum.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-media.json b/pioneer/packages/apps/public/locales/en/joy-media.json new file mode 100644 index 0000000000..80d5fe6006 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-media.json @@ -0,0 +1,5 @@ +{ + "Explore": "Explore", + "My channels": "My channels", + "My videos": "My videos" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-members.json b/pioneer/packages/apps/public/locales/en/joy-members.json new file mode 100644 index 0000000000..edd3baf259 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-members.json @@ -0,0 +1,5 @@ +{ + "All members": "All members", + "Dashboard": "Dashboard", + "Register": "Register" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-pages.json b/pioneer/packages/apps/public/locales/en/joy-pages.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-pages.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-proposals.json b/pioneer/packages/apps/public/locales/en/joy-proposals.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-proposals.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-roles.json b/pioneer/packages/apps/public/locales/en/joy-roles.json new file mode 100644 index 0000000000..ac57dcd80a --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-roles.json @@ -0,0 +1,6 @@ +{ + "My roles": "My roles", + "My roles and applications": "My roles and applications", + "Opportunities": "Opportunities", + "Working groups": "Working groups" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-settings.json b/pioneer/packages/apps/public/locales/en/joy-settings.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-utils-old.json b/pioneer/packages/apps/public/locales/en/joy-utils-old.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-utils-old.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/joy-utils.json b/pioneer/packages/apps/public/locales/en/joy-utils.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/joy-utils.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/react-api.json b/pioneer/packages/apps/public/locales/en/react-api.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/react-api.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/react-components.json b/pioneer/packages/apps/public/locales/en/react-components.json new file mode 100644 index 0000000000..7adf6a05f1 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/react-components.json @@ -0,0 +1,78 @@ +{ + "0.1x voting balance, no lockup period": "0.1x voting balance, no lockup period", + "Aye, I approve": "Aye, I approve", + "Cancel": "Cancel", + "Confirm account removal": "Confirm account removal", + "Confirm address removal": "Confirm address removal", + "Confirm code removal": "Confirm code removal", + "Confirm contract removal": "Confirm contract removal", + "Details": "Details", + "Dismiss all notifications": "Dismiss all notifications", + "Forget": "Forget", + "Nay, I do not approve": "Nay, I do not approve", + "No items": "No items", + "Positive number": "Positive number", + "Retrieving data": "Retrieving data", + "Select the account to use for this action.": "Select the account to use for this action.", + "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.": "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.", + "Submit": "Submit", + "Submit signed extrinsic": "Submit signed extrinsic", + "The amount that is associated with this vote. This value is is locked for the duration of the vote.": "The amount that is associated with this vote. This value is is locked for the duration of the vote.", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.": "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.": "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.", + "This operation does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.": "This operation does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.", + "Unbonding {{value}}, ": "Unbonding {{value}}, ", + "Uncaught error. Something went wrong with the query and rendering of this component. {{message}}": "Uncaught error. Something went wrong with the query and rendering of this component. {{message}}", + "View this externally": "View this externally", + "Vote Aye": "Vote Aye", + "Vote Nay": "Vote Nay", + "Withdraw these unbonded funds": "Withdraw these unbonded funds", + "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.": "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.", + "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.": "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.", + "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.", + "address copied": "address copied", + "available to be unlocked": "available to be unlocked", + "beneficiary": "beneficiary", + "bond": "bond", + "bonded": "bonded", + "click to select or drag and drop the file here": "click to select or drag and drop the file here", + "clipboard": "clipboard", + "commission": "commission", + "democracy": "democracy", + "everything": "everything", + "extrinsic hash": "extrinsic hash", + "filter by name, address, or account index": "filter by name, address, or account index", + "lifetime": "lifetime", + "lock expired": "lock expired", + "locked": "locked", + "no tags": "no tags", + "ongoing referendum": "ongoing referendum", + "only this network": "only this network", + "proposed by": "proposed by", + "redeemable": "redeemable", + "reserved": "reserved", + "session keys": "session keys", + "session next": "session next", + "tip": "tip", + "total": "total", + "transactions": "transactions", + "transferrable": "transferrable", + "type": "type", + "unbonding": "unbonding", + "unstake threshold": "unstake threshold", + "use on any network": "use on any network", + "using my account": "using my account", + "value": "value", + "vested": "vested", + "vesting": "vesting", + "via Council/Vote": "via Council/Vote", + "via Democracy/Vote": "via Democracy/Vote", + "via Staking/Bond": "via Staking/Bond", + "via Vesting": "via Vesting", + "vote value": "vote value", + "vote with account": "vote with account", + "voting balance": "voting balance", + "{{blocks}} blocks": "{{blocks}} blocks", + "{{name}} ({{size}} bytes)": "{{name}} ({{size}} bytes)", + "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/react-hooks.json b/pioneer/packages/apps/public/locales/en/react-hooks.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/react-hooks.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/react-params.json b/pioneer/packages/apps/public/locales/en/react-params.json new file mode 100644 index 0000000000..c5d88c40cb --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/react-params.json @@ -0,0 +1,24 @@ +{ + "": "", + "Add item": "Add item", + "Aye": "Aye", + "Locked1x": "Locked1x", + "Locked2x": "Locked2x", + "Locked3x": "Locked3x", + "Locked4x": "Locked4x", + "Locked5x": "Locked5x", + "Locked6x": "Locked6x", + "Nay": "Nay", + "No": "No", + "None": "None", + "Remove item": "Remove item", + "Yes": "Yes", + "aye: bool": "aye: bool", + "conviction: Conviction": "conviction: Conviction", + "details": "details", + "exclude option": "exclude option", + "file upload": "file upload", + "include option": "include option", + "type": "type", + "{{count}} key/value pairs encoded for submission": "{{count}} key/value pairs encoded for submission" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/react-query.json b/pioneer/packages/apps/public/locales/en/react-query.json new file mode 100644 index 0000000000..7bb27dab47 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/react-query.json @@ -0,0 +1,12 @@ +{ + "1 day": "1 day", + "1 hr": "1 hr", + "1 min": "1 min", + "1 s": "1 s", + "everything": "everything", + "everything{{labelPost}}": "everything{{labelPost}}", + "{{d}} days": "{{d}} days", + "{{h}} hrs": "{{h}} hrs", + "{{m}} mins": "{{m}} mins", + "{{s}} s": "{{s}} s" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/react-signer.json b/pioneer/packages/apps/public/locales/en/react-signer.json new file mode 100644 index 0000000000..04b2c96902 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/react-signer.json @@ -0,0 +1,54 @@ +{ + "A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist": "A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist", + "Add a tip to this extrinsic, paying the block author for greater priority": "Add a tip to this extrinsic, paying the block author for greater priority", + "Adding an optional tip to the transaction could allow for higher priority, especially when the chain is busy.": "Adding an optional tip to the transaction could allow for higher priority, especially when the chain is busy.", + "Authorize transaction": "Authorize transaction", + "Close": "Close", + "Current account nonce: {{accountNonce}}": "Current account nonce: {{accountNonce}}", + "Do not include a tip for the block author": "Do not include a tip for the block author", + "Don't use a proxy for this call": "Don't use a proxy for this call", + "Include an optional tip for faster processing": "Include an optional tip for faster processing", + "Lifetime (# of blocks)": "Lifetime (# of blocks)", + "Multisig approval with hash (non-final approval)": "Multisig approval with hash (non-final approval)", + "Multisig approval with hash (not message with call)": "Multisig approval with hash (not message with call)", + "Multisig message with call (for final approval)": "Multisig message with call (for final approval)", + "Multisig message with call (not approval with hash)": "Multisig message with call (not approval with hash)", + "Nonce": "Nonce", + "Override any applicable values for the specific signed output. These will be used to construct and display the signed transaction.": "Override any applicable values for the specific signed output. These will be used to construct and display the signed transaction.", + "Preparing QR for signing": "Preparing QR for signing", + "Present the QR code containing the signature to the UI. Once scanned it will be submitted for on-chain processing and execution.": "Present the QR code containing the signature to the UI. Once scanned it will be submitted for on-chain processing and execution.", + "Scan Signature Qr": "Scan Signature Qr", + "Scan the QR code with your QR scanner. Once approved, you will be required to present the signed QR back to the UI for submission.": "Scan the QR code with your QR scanner. Once approved, you will be required to present the signed QR back to the UI for submission.", + "Sending transaction": "Sending transaction", + "Set to 0 to make transaction immortal": "Set to 0 to make transaction immortal", + "Sign (no submission)": "Sign (no submission)", + "Sign and Submit": "Sign and Submit", + "Sign for multisig": "Sign for multisig", + "Sign via Qr": "Sign via Qr", + "Sign via {{hardwareType}}": "Sign via {{hardwareType}}", + "Signed transaction": "Signed transaction", + "Submit (no signature)": "Submit (no signature)", + "The actual fully constructed signed output. This can be used for submission via other channels.": "The actual fully constructed signed output. This can be used for submission via other channels.", + "The call data that can be supplied to a final call to multi approvals": "The call data that can be supplied to a final call to multi approvals", + "The deposit is below the {{minimum}} minimum required for the proposal to be evaluated": "The deposit is below the {{minimum}} minimum required for the proposal to be evaluated", + "The deposit of {{deposit}} will be reserved until the proposal is completed": "The deposit of {{deposit}} will be reserved until the proposal is completed", + "The details of the transaction including the type, the description (as available from the chain metadata) as well as any parameters and fee estimations (as available) for the specific type of call.": "The details of the transaction including the type, the description (as available from the chain metadata) as well as any parameters and fee estimations (as available) for the specific type of call.", + "The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected": "The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected", + "The multisig signatory for this transaction.": "The multisig signatory for this transaction.", + "The proxy is one of the allowed proxies on the account, as set and filtered by the transaction type.": "The proxy is one of the allowed proxies on the account, as set and filtered by the transaction type.", + "The proxy to be used for this transaction.": "The proxy to be used for this transaction.", + "The sending account that will be used to send this transaction. Any applicable fees will be paid by this account.": "The sending account that will be used to send this transaction. Any applicable fees will be paid by this account.", + "The signatory is one of the allowed accounts on the multisig, making a recorded approval for the transaction.": "The signatory is one of the allowed accounts on the multisig, making a recorded approval for the transaction.", + "The signatory is one of the allowed accounts on the multisig. The transaction could either be the call or an approval for the hash of a call.": "The signatory is one of the allowed accounts on the multisig. The transaction could either be the call or an approval for the hash of a call.", + "This could either be an approval for the hash or with full call details. The call as last approval triggers execution.": "This could either be an approval for the hash or with full call details. The call as last approval triggers execution.", + "Tip (optional)": "Tip (optional)", + "Unlock the sending account to allow signing of this transaction.": "Unlock the sending account to allow signing of this transaction.", + "Use a proxy for this call": "Use a proxy for this call", + "multisig call data": "multisig call data", + "multisig signatory": "multisig signatory", + "proxy account": "proxy account", + "sending from my account": "sending from my account", + "signatory": "signatory", + "unlock account with password": "unlock account with password", + "wrong password supplied": "wrong password supplied" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/en/translation.json b/pioneer/packages/apps/public/locales/en/translation.json new file mode 100644 index 0000000000..ebb9bf8838 --- /dev/null +++ b/pioneer/packages/apps/public/locales/en/translation.json @@ -0,0 +1,1143 @@ +{ + "//hard///password": "", + "//hard/soft": "", + "//hard/soft///password": "", + "0.1x voting balance, no lockup period": "", + "0x...": "", + "1 billion DOTs; 10 decimals": "", + "1 day": "", + "1 hr": "", + "1 min": "", + "1 s": "", + "1. Select your {{chain}} account": "", + "10 billion DOTs; 9 decimals": "", + "10 million DOTs; status quo": "", + "100 million DOTs; 11 decimals": "", + "2. Sign ETH transaction": "", + "": "", + "": "", + "@YourTwitterName": "", + "@yourname:matrix.org": "", + "A controller account should not be set to manage multiple stashes. The selected controller is already controlling {{stashId}}": "", + "A controller account should not be set to manages multiple stashes. The selected controller is already controlling {{stashId}}": "", + "A controller account should not map to another stash. This selected controller is a stash, controlled by {{bondedId}}": "", + "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": "", + "A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist": "", + "A reason (to be stored-on-chain) as to why the recipient deserves a tip payout.": "", + "ABI": "", + "Acceptance proposal to council": "", + "Account actions": "", + "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.": "", + "Active nominations ({{count}})": "", + "Add a tip to this extrinsic, paying the block author for greater priority": "", + "Add account": "", + "Add account via Qr": "", + "Add an account via seed": "", + "Add an address": "", + "Add an existing code hash": "", + "Add an existing contract": "", + "Add contact": "", + "Add identity judgment": "", + "Add item": "", + "Add multisig": "", + "Add via Qr": "", + "Add via backup file": "", + "Adding an optional tip to the transaction could allow for higher priority, especially when the chain is busy.": "", + "Additional types as a JSON file (or edit below)": "", + "Address Prefix": "", + "Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)": "", + "Advanced creation options": "", + "All members": "", + "All rewards will go towards the selected output destination when a payout is made.": "", + "All the listed validators and all their nominators will receive their rewards.": "", + "All voters may alter their votes any number of times prior to the close of the poll.": "", + "Allocate a suggested tip amount. With enough endorsements, the suggested values are averaged and sent to the beneficiary.": "", + "Amount to add to the currently bonded funds. This is adjusted using the available funds on the account.": "", + "An URL that is linked to this identity.": "", + "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.": "", + "Any account can request payout for stakers, this is not limited to accounts that will be rewarded.": "", + "Any combination of the four options may be approved of by the voter. There is no need to select only one option!": "", + "Applicants": "", + "Approvals": "", + "Approve or reject this defender.": "", + "Approve this call hash": "", + "Approve this candidacy.": "", + "Approved": "", + "Approving of all or none of the options is equivalent and will not affect the outcome of the poll.": "", + "As a council member, you can suggest an initial value for the tip, each other council member can suggest their own.": "", + "Asset ID": "", + "Assets": "", + "Aura": "", + "Authorize transaction": "", + "Aye": "", + "Aye, I approve": "", + "Babe": "", + "Backup account": "", + "Block details": "", + "Bond": "", + "Bond & Nominate": "", + "Bond & Validate": "", + "Bond more": "", + "Bond more funds": "", + "Bonding Preferences": "", + "Call": "", + "Call a contract": "", + "Call a method on this contract": "", + "Call results": "", + "Call this message": "", + "Cancel": "", + "Cancel slashes": "", + "Cancel this call hash": "", + "Chain info": "", + "Chain specifications": "", + "Change": "", + "Change account password": "", + "Change controller account": "", + "Change democracy delegation": "", + "Change reward destination": "", + "Change session keys": "", + "Change this account's password": "", + "Change validator preferences": "", + "Changing the key only takes effect at the start of the next session. The input here is generates from the author_rotateKeys command": "", + "Claim": "", + "Clear all": "", + "Clear expired democracy locks": "", + "Close": "", + "Close proposal": "", + "Color": "", + "Committee prime member, default voting": "", + "Confirm ABI removal": "", + "Confirm account removal": "", + "Confirm address removal": "", + "Confirm claim": "", + "Confirm code removal": "", + "Confirm contract removal": "", + "Constants": "", + "Continue": "", + "Conviction locks do overlap and is additive, meaning that funds locked during a previous vote can be locked again.": "", + "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "", + "Council": "", + "Council members": "", + "Council overview": "", + "Create": "", + "Create a backup file for this account": "", + "Create an account now.": "", + "Create and backup account": "", + "Crypto not detected": "", + "Cryptography used to create this signature. It is auto-detected on valid signatures.": "", + "Current account nonce: {{accountNonce}}": "", + "Current prime member, default voting": "", + "Current society head, exempt": "", + "Dashboard": "", + "Decimals": "", + "Decimals decides the smallest unit of the token, which is 1/10^decimals": "", + "Delegate democracy votes": "", + "Delete this custom example": "", + "Democracy overview": "", + "Deploy": "", + "Deploy a code hash": "", + "Deploy this code hash as a smart contract": "", + "Deploy with this constructor": "", + "Deposit": "", + "Deregister this parachain": "", + "Derive account from pair": "", + "Derive account via derivation path": "", + "Details": "", + "Detection on the input string to determine if it is hex or non-hex.": "", + "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".": "", + "Developer": "", + "Development": "", + "Dismiss all notifications": "", + "Display overview information for the selected validator, including blocks produced.": "", + "Distinct stash and controller accounts are recommended to ensure fund security. You will be allowed to make the transaction, but take care to not tie up all funds, only use a portion of the available funds during this period.": "", + "Do not include a tip for the block author": "", + "Download": "", + "Either approve or reject this call.": "", + "Endorse": "", + "Endorsements ({{count}})": "", + "Ensure that not all funds are locked, funds need to be available for fees.": "", + "Enter the Asset ID of the token you want to manage.": "", + "Enter the Asset ID of the token you want to transfer.": "", + "Erroneous": "", + "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)": "", + "Explore": "", + "Extensions": "", + "External": "", + "Extrinsic submission": "", + "Fast track": "", + "Fast track proposal": "", + "Filter available candidates based on name, address or short account index.": "", + "For approvals outstanding approvers will be shown, for hashes that should be cancelled the first approver is required.": "", + "For final approvals, the actual full call data is required to execute the transaction": "", + "For fund security, your session key should not match your stash key.": "", + "Forget": "", + "Forget this account": "", + "Forget this address": "", + "Forget this asset": "", + "Forget this code hash": "", + "Forget this contract": "", + "Forks": "", + "Full Legal Name": "", + "General": "", + "Generate {{lng}}/translation.json": "", + "Genesis Hash": "", + "Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched": "", + "Grandpa": "", + "Hash data": "", + "I'm Online": "", + "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.": "", + "If this proposal is passed, the changes will be applied via dispatch and the deposit returned.": "", + "If you are moving accounts between applications, ensure that you use the correct type.": "", + "Important notice": "", + "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.": "", + "Inactive nominations ({{count}})": "", + "Include an optional tip for faster processing": "", + "Initializing connection": "", + "Initiate account recovery": "", + "Initiate recovery for another": "", + "Inject Keys": "", + "Inject session keys (advanced)": "", + "Injected": "", + "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -": "", + "Judge": "", + "Keys from rotateKeys": "", + "Known good": "", + "Learn more...": "", + "Lifetime (# of blocks)": "", + "Locked funds (e.g. for staking) are counted.": "", + "Locked1x": "", + "Locked2x": "", + "Locked3x": "", + "Locked4x": "", + "Locked5x": "", + "Locked6x": "", + "Low quality": "", + "Make Transfer": "", + "Make recoverable": "", + "Manage your connection to Ledger S": "", + "Message data": "", + "Message origin.": "", + "Messages": "", + "Metadata {{count}}": "", + "Mnemonic": "", + "Most recent head data": "", + "Motions ({{count}})": "", + "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "", + "Multisig": "", + "Multisig approval with hash (non-final approval)": "", + "Multisig approval with hash (not message with call)": "", + "Multisig approvals": "", + "Multisig message with call (for final approval)": "", + "Multisig message with call (not approval with hash)": "", + "Mutates contract state": "", + "My On-Chain Name": "", + "My accounts": "", + "My channels": "", + "My contacts": "", + "My roles": "", + "My roles and applications": "", + "My videos": "", + "Name given to this account. You can change it at any point in the future.": "", + "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".": "", + "Name given to this multisig. You can edit it at any later point in time.": "", + "Name of the network. It is only for display purposes.": "", + "Name your example": "", + "Nay": "", + "Nay, I do not approve": "", + "Network Name": "", + "New stake": "", + "No": "", + "No active members": "", + "No active proposals": "", + "No active referendums": "", + "No active validators found": "", + "No active validators to check": "", + "No approved proposals": "", + "No assets found.": "", + "No bids": "", + "No blocks available": "", + "No candidates": "", + "No candidates found": "", + "No change": "", + "No change from the original 2017 sale definitions; will mean a total of 10 million DOT from genesis.": "", + "No change from the original 2017 sale definitions; will mean a total of 10m DOT from genesis.": "", + "No code hashes available": "", + "No committee proposals": "", + "No contracts available": "", + "No council motions": "", + "No discretionary lock-voting is in place; all DOT used to vote counts the same.": "", + "No documentation provided": "", + "No events available": "", + "No external proposal": "", + "No funds staked yet. Bond funds to validate or nominate a validator": "", + "No items": "", + "No judgments": "", + "No logs available": "", + "No matches found": "", + "No members found": "", + "No open tips": "", + "No pending era payouts from validators": "", + "No pending extrinsics are in the queue": "", + "No pending payouts for your stashes": "", + "No pending proposals": "", + "No runners up found": "", + "No sub identities set.": "", + "No upgradable extensions found": "", + "No waiting validators found": "", + "Node info": "", + "Nominate": "", + "Nominate Validators": "", + "Nominate selected": "", + "Nominate validators": "", + "Nominations ({{count}})": "", + "Nominator": "", + "Nominators can be selected automatically based on the current on-chain conditions or supplied manually as selected from the list of all currently available validators. In both cases, your favorites appear for the selection.": "", + "Nonce": "", + "None": "", + "Nothing queued for execution": "", + "Of the beneficiary amount, at least {{bondPercentage}} would need to be put up as collateral. The maximum of this and the minimum bond will be used to secure the proposal, refundable if it passes.": "", + "Once bonded, it wil need to be unlocked/withdrawn and will be locked for at least the bonding duration.": "", + "Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, the nominations will show as inactive.": "", + "One of your available nomination accounts, keyed by the stash. The transaction will be sent from the controller.": "", + "Only applicable if the proposal has already passed and is ready for dispatch.": "", + "Opportunities": "", + "Override any applicable values for the specific signed output. These will be used to construct and display the signed transaction.": "", + "Override the default identity icon display with a specific theme": "", + "Override the default ss58 prefix for address generation": "", + "Owned": "", + "Parachains": "", + "Parachains overview": "", + "Paste here the address of the contact you want to add to your address book.": "", + "Payout": "", + "Payout all": "", + "Payout all stakers": "", + "Pending call hashes": "", + "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.": "", + "Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:": "", + "Positive number": "", + "Positive number between 1 and {{count}}": "", + "Positive number between 1 and {{memberCount}}": "", + "Positive number greater than or equal to {{nextFreeId}}": "", + "Prefix indicates the ss58 address format in this network, it is a number between 0 ~ 255 that describes the precise format of the bytes of the address": "", + "Preimage": "", + "Preparing QR for signing": "", + "Present the QR code containing the signature to the UI. Once scanned it will be submitted for on-chain processing and execution.": "", + "Proposal can either be to approve or reject this spend. One approved, the change is applied by either removing the proposal or scheduling payout.": "", + "Proposals": "", + "Proposals ({{count}})": "", + "Propose": "", + "Propose a committee motion": "", + "Propose a council motion": "", + "Propose external": "", + "Propose external (majority)": "", + "Propose motion": "", + "Propose tip": "", + "Provide judgement": "", + "Provide the account QR from the module/external application for scanning. One detected as valid, you will be taken to the next step to add the account to your list.": "", + "Query Ledger": "", + "RPC calls": "", + "Random": "", + "Raw seed": "", + "Raw storage": "", + "Reasonable": "", + "Reassign": "", + "Redeem": "", + "Register": "", + "Register Asset": "", + "Register a parachain": "", + "Register an Asset": "", + "Register identity": "", + "Register the vote for or against the proposal.": "", + "Rejection proposal to council": "", + "Rejections": "", + "Remove": "", + "Remove ABI": "", + "Remove item": "", + "Reset": "", + "Restore": "", + "Restore JSON": "", + "Retrieving data": "", + "Retrieving info for all applicable eras, this will take some time": "", + "Revert": "", + "Revert pending slashes": "", + "Rewards (once paid) can be deposited to either the stash or controller, with different effects.": "", + "Save": "", + "Save & Reload": "", + "Save snippet to local storage": "", + "Save the type definitions for your custom structures as key-value pairs in a valid JSON file. The key should be the name of your custom structure and the value an object containing your type definitions.": "", + "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.": "", + "Saved": "", + "Scan Signature Qr": "", + "Scan the QR code with your QR scanner. Once approved, you will be required to present the signed QR back to the UI for submission.": "", + "Search for": "", + "Second": "", + "Second proposal": "", + "Seconding a proposal that indicates your backing for the proposal. Proposals with greater interest moves up the queue for potential next referendums.": "", + "Seconds ({{count}})": "", + "Select Network": "", + "Select a contact or paste the address you want to send funds to.": "", + "Select and order council candidates you wish to vote for.": "", + "Select best": "", + "Select example": "", + "Select targets manually (no auto-selection based on current profitability)": "", + "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.": "", + "Select the account to use for this action.": "", + "Select the account you wish close the proposal with.": "", + "Select the account you wish to make the proposal with.": "", + "Select the account you wish to recover into this account.": "", + "Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected": "", + "Select the account you wish to submit for candidacy.": "", + "Select the account you wish to submit the proposal from.": "", + "Select the account you wish to submit the tip from.": "", + "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.": "", + "Select the council account you wish to use to make the proposal.": "", + "Select the remote endpoint, either from the dropdown on manual entered via the custom toggle": "", + "Send funds": "", + "Send funds from this account": "", + "Send funds to this address": "", + "Send to council": "", + "Sending transaction": "", + "Session Key": "", + "Set Identity": "", + "Set Session Key": "", + "Set controller": "", + "Set nominees": "", + "Set on-chain identity": "", + "Set on-chain sub-identities": "", + "Set reward destination": "", + "Set sudo key": "", + "Set to 0 to make transaction immortal": "", + "Set validator preferences": "", + "Setup Nominator {{step}}/{{NUM_STEPS}}": "", + "Setup Validator {{step}}/{{NUM_STEPS}}": "", + "Setup account as recoverable": "", + "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...": "", + "Show address on hardware device": "", + "Sign (no submission)": "", + "Sign and Submit": "", + "Sign for multisig": "", + "Sign message": "", + "Sign via Qr": "", + "Sign via {{hardwareType}}": "", + "Signed transaction": "", + "Since the multisig function like any other account, once created it is available for selection anywhere accounts are used and needs to be funded before use.": "", + "Since this transaction deals with funding, the stash account will be used.": "", + "Skeptics": "", + "Society": "", + "Society overview": "", + "Specify the user account to use for this contract call. And fees will be deducted from this account.": "", + "Specify the user account to use for this deployment. And fees will be deducted from this account.": "", + "Specify the user account to use for this deployment. Any fees will be deducted from this account.": "", + "Split of 1,000x from the original sale; will mean a total of 10b DOT from genesis. Apparent DOT price would be 1000x lower.": "", + "Split of 1000x": "", + "Split of 1000x from the original sale; will mean a total of 10 billion DOT from genesis. Apparent DOT price would be 1000x lower and apparent account balances 1000x higher.": "", + "Split of 100x": "", + "Split of 100x from the original sale; will mean a total of 1 billion DOT from genesis. Apparent DOT price would be 100x lower and apparent account balances 100x higher.": "", + "Split of 100x from the original sale; will mean a total of 1b DOT from genesis. Apparent DOT price would be 100x lower.": "", + "Split of 10x": "", + "Split of 10x from the original sale; will mean a total of 100 million DOT from genesis. Apparent DOT price would be 10x lower and apparent account balances 10x higher.": "", + "Split of 10x from the original sale; will mean a total of 100m DOT from genesis. Apparent DOT price would be 10x lower.": "", + "Staking overview": "", + "Start generation": "", + "Start recovery": "", + "Stash": "", + "Stop": "", + "Stop generation": "", + "Storage": "", + "Submit": "", + "Submit (no signature)": "", + "Submit RPC call": "", + "Submit Sudo": "", + "Submit Transaction": "", + "Submit Unsigned": "", + "Submit candidacy": "", + "Submit key": "", + "Submit preimage": "", + "Submit proposal": "", + "Submit signed extrinsic": "", + "Submit tip": "", + "Submit tip endorsement": "", + "Submit tip request": "", + "Submit treasury proposal": "", + "Submit your council candidacy": "", + "Sudo access": "", + "Sudo key": "", + "Supply a backed-up JSON file, encrypted with your account-specific password.": "", + "Swap to a non-executing approval type, with subsequent calls providing the actual call data.": "", + "Targets": "", + "Tech. committee": "", + "Technical committee": "", + "Test account": "", + "The ABI for the WASM code. In this step it is optional, but setting it here simplifies the setup of contract instances.": "", + "The ABI for the WASM code. Since we will be making a call into the code, the ABI is required and stored for future operations such as sending messages.": "", + "The RPC node can be selected from the pre-defined list or manually entered, depending on the chain you wish to connect to.": "", + "The Threshold must be a positive number": "", + "The Threshold must lower than 11": "", + "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.": "", + "The account that signed the input": "", + "The account to which the proposed balance will be transferred if approved": "", + "The account to which the tip will be transferred if approved": "", + "The account you want to claim to.": "", + "The account you want to register the preimage from": "", + "The account you want to register the proposal from": "", + "The account you will send funds from.": "", + "The account's password specified at the creation of this account.": "", + "The actual JSONRPC module and function to make a call to.": "", + "The actual fully constructed signed output. This can be used for submission via other channels.": "", + "The actual proposal to make, based on the selected call and parameters thereof.": "", + "The address for the deployed contract instance.": "", + "The address to vote from (must be a member)": "", + "The addresses that are able to approve multisig transactions. You can select up to {{maxHelpers}} trusted addresses.": "", + "The addresses that are able to help in recovery. You can select up to {{maxHelpers}} trusted helpers.": "", + "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": "", + "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": "", + "The amount of funds to unbond, this is adjusted using the bonded funds on the stash account.": "", + "The amount placed at-stake should be no more that 95% of your available amount to protect against slashing events.": "", + "The amount that is associated with this vote. This value is is locked for the duration of the vote.": "", + "The amount that will be allocated from the treasury pot": "", + "The amount that will be used on a per-validator basis to calculate profits for that validator.": "", + "The amount this total can be reduced by to change the referendum outcome. This assumes changes to the convictions of the existing votes, with no additional turnout.": "", + "The amount this total should be increased by to change the referendum outcome. This assumes additional turnout with new votes at 1x conviction.": "", + "The amount to allocate and the conviction that will be applied to all votes made on a referendum.": "", + "The associated deposit for this proposal should be more then the minimum on-chain deposit required. It will be locked until the proposal passes.": "", + "The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for the validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.": "", + "The balance associated with the vote will be locked as per the conviction specified and will not be available for transfer during this period.": "", + "The beneficiary will have access to the transferred fees when the transaction is included in a block.": "", + "The beneficiary will receive the full amount if the proposal passes.": "", + "The beneficiary will received the tip as approved by council members.": "", + "The blake2b 256-bit hash of the actual input data.": "", + "The bond will be reserved for the duration of your candidacy and membership.": "", + "The bonded amount is less than the minimum bond amount of {{existentialDeposit}}": "", + "The bonding duration for any staked funds. After this period needs to be withdrawn.": "", + "The bonding duration for any staked funds. Needs to be unlocked and withdrawn to become available.": "", + "The call data for this transaction matching the hash. Once sent, the multisig will be executed against this.": "", + "The call data that can be supplied to a final call to multi approvals": "", + "The call hash from the list of available and unapproved calls.": "", + "The call hashes that have not been executed as of yet.": "", + "The code hash for the on-chain deployed code.": "", + "The code is not recognized as being in valid WASM format": "", + "The codeHash is not a valid hex hash": "", + "The color used to distinguish this network with others, use color code with 3 or 6 digits, like \"#FFF\" or \"#111111\"": "", + "The commission is deducted from all rewards before the remainder is split with nominators.": "", + "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": "", + "The compiled runtime WASM for the parachain you wish to register.": "", + "The compiled runtime WASM for this parachain.": "", + "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.": "", + "The controller does no have sufficient funds available to cover transaction fees. Ensure that a funded controller is used.": "", + "The controller is the account that is be used to control any nominating or validating actions. I will sign this transaction.": "", + "The controller is the account that will be used to control any nominating or validating actions. Should not match another stash or controller.": "", + "The conviction to use for this vote, with an appropriate lock period.": "", + "The council account for the proposal. The selection is filtered by the current members.": "", + "The council account for this vote. The selection is filtered by the current members.": "", + "The council account that will apply the close for the current round.": "", + "The council member that is proposing this, submission equates to an \"aye\" vote.": "", + "The council proposal to make the vote on": "", + "The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.": "", + "The delay between vouching and the availability of the recovered account.": "", + "The delay period to apply in blocks": "", + "The deployment constructor information for this contract, as provided by the ABI.": "", + "The deposit is below the {{minimum}} minimum required for the proposal to be evaluated": "", + "The deposit of {{deposit}} will be reserved until the proposal is completed": "", + "The derivation path allows you to create different accounts from the same base mnemonic.": "", + "The desired threshold. Here set to a default of 50%+1, as applicable for general proposals.": "", + "The destination account for any payments as either a nominator or validator": "", + "The details of the transaction including the type, the description (as available from the chain metadata) as well as any parameters and fee estimations (as available) for the specific type of call.": "", + "The email address associated with this identity.": "", + "The existing account password as specified when this account was created or when it was last changed.": "", + "The external proposal to send to the technical committee": "", + "The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected": "", + "The funds will only be available for withdrawal after the unbonding period, however will not be part of the staked amount after the next validator election. You can follow the unlock countdown in the UI.": "", + "The hash for the proposal this vote applies to": "", + "The hash of the preimage for the proposal as previously submitted or intended.": "", + "The hash of the proposal image, either already submitted or valid for the specific call.": "", + "The hash of the selected proposal, use it for submitting the proposal": "", + "The helpers should be able to verify, via an off-chain mechanism, that the account owner indeed wishes to recover access and as such provide any approvals. In the cases of malicious recovery procedures, they will have the power to stop it.": "", + "The hex output from author_rotateKeys, as executed on the validator node. The keys will show as pending until applied at the start of a new session.": "", + "The id number to assign to this parachain.": "", + "The id of the parachain to be deregistered.": "", + "The image (proposal) will be stored on-chain against the hash of the contents.": "", + "The initial head state for the parachain.": "", + "The initial head state is invalid.": "", + "The input data to hash. This can be either specified as a hex value (0x-prefix) or as a string.": "", + "The input data to sign. This can be either specified as a hex value (0x-prefix) or as a string.": "", + "The key type and crypto type to use for this key. Be aware that different keys have different crypto requirements. You should be familiar with the type requirements for the different keys.": "", + "The legal name for this identity.": "", + "The local name for this account. Changing this does not affect your on-line identity, so this is only used to indicate the name of the account locally.": "", + "The locked value for this proposal": "", + "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": "", + "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": "", + "The maximum amount you can delegate is the amount of funds available on the delegating account.": "", + "The message to send to this contract. Parameters are adjusted based on the ABI provided.": "", + "The minimum amount that an account should have to be deemed active": "", + "The minimum amount that will be bonded": "", + "The minimum deposit required": "", + "The minimum number of committee votes required to approve this motion": "", + "The minimum number of council votes required to approve this motion": "", + "The multisig signatory for this transaction.": "", + "The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.": "", + "The name is for unique identification of the account in your owner lists.": "", + "The name that will be displayed in your accounts list.": "", + "The new account password. Once set, all future account unlocks will be performed with this new password.": "", + "The on-chain percentage for the treasury": "", + "The operation type to apply. For approvals both non-final and final approvals are supported.": "", + "The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.": "", + "The password previously used to encrypt this account.": "", + "The password to unlock the selected account.": "", + "The percentage reward (0-100) that should be applied for the validator": "", + "The preimage hash of the proposal": "", + "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.": "", + "The proposal is in the queue for future referendums. One proposal from this list will move forward to voting.": "", + "The proposal that is being voted on. It will pass when the threshold is reached.": "", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted to council for a subsequent voting round.": "", + "The proposal will be registered from this account and the balance lock will be applied here.": "", + "The proxy is one of the allowed proxies on the account, as set and filtered by the transaction type.": "", + "The proxy to be used for this transaction.": "", + "The reason why this tip should be paid.": "", + "The recoverable account is protected against the loss of seed/access by a social process.": "", + "The resulting signature of the input data, as done with the crypto algorithm from the account. (This could be non-deterministic for some types such as sr25519).": "", + "The scheduling preference for this parachain.": "", + "The scheduling setting for this parachain.": "", + "The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.": "", + "The seed and derivation path will be submitted to the validator node. this is an advanced operation, only to be performed when you are sure of the security and connection risks.": "", + "The selected account to be unlocked.": "", + "The selected account to perform the derivation on.": "", + "The selected controller tied to this stash. Once set, this account will be able to control the actions performed by the stash account.": "", + "The selected validators to nominate, either via the \"currently best algorithm\" or via a manual selection.": "", + "The sending account that will be used to send this transaction. Any applicable fees will be paid by this account.": "", + "The signatories has the ability to create transactions using the multisig and approve transactions sent by others.Once the threshold is reached with approvals, the multisig transaction is enacted on-chain.": "", + "The signatory is one of the allowed accounts on the multisig, making a recorded approval for the transaction.": "", + "The signatory is one of the allowed accounts on the multisig. The transaction could either be the call or an approval for the hash of a call.": "", + "The signatory to send the approval/cancel from": "", + "The signature as by the account being checked, supplied as a hex-formatted string.": "", + "The specific eras on which there are unapplied slashes. For each era a separate proposal is to be made.": "", + "The specified value is greater than your free balance. The node will bond the maximum amount available.": "", + "The specified value is too large and does not allow funds to pay future transaction fees.": "", + "The stash account that is used. This will allow the controller to perform all non-funds related operations on behalf of the account.": "", + "The stash and controller pair as linked. This operation will be performed via the controller.": "", + "The stash and controller pair, here the controller will be used to send the transaction.": "", + "The stash and controller pair. This transaction, managing preferences, will be sent from the controller.": "", + "The stash and controller pair. This transaction, setting the session keys, will be sent from the controller.": "", + "The stash that is to be affected. The transaction will be sent from the associated controller account.": "", + "The suggested value for this tip": "", + "The threshold for approval should be less or equal to the number of signatories for this multisig.": "", + "The threshold for approvals and the delay is the protection associated with the account. The delay should be such that any colluding recovery attempts does have a window to stop.": "", + "The threshold for this multisig": "", + "The threshold of vouches that is to be reached for the account to be recovered.": "", + "The tip amount that should be allocated": "", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the total amount available)": "", + "The transferred balance will be subtracted (along with fees) from the sender account.": "", + "The twitter name for this identity.": "", + "The type of council proposal to submit.": "", + "The unapplied slashed era to cancel.": "", + "The unchecked weight as specified for the sudoUncheckedWeight call.": "", + "The value associated with this vote. The amount will be locked (not available for transfer) and used in all subsequent elections.": "", + "The value is not in a valid address format": "", + "The value is the amount that is being asked for and that will be allocated to the beneficiary if the proposal is approved.": "", + "The vote to record for this proposal, either for or against.": "", + "The vote will be recorded for the selected account.": "", + "The vote will be recorded for this account. If another account delegated to this one, the delegated votes will also be counted.": "", + "The votes for the members, runner-ups and candidates. These should be ordered based on your priority.": "", + "The voting period to apply in blocks": "", + "There are no registered parachains": "", + "There is currently an ongoing election for new validator candidates. As such staking operations are not permitted.": "", + "These are trusted individuals that can verify and approve any recovery actions. With recovery, once the threshold is reached, the funds associated with the account can be moved to a new destination.": "", + "Think of the stash as your cold wallet and the controller as your hot wallet. Funding operations are controlled by the stash, any other non-funding actions by the controller itself.": "", + "This account is recoverable, with the following friends:": "", + "This account that will perform the message signing.": "", + "This account will appear in the list of candidates. With enough votes in an election, it will become either a runner-up or a council member.": "", + "This account will be use to approve each candidate.": "", + "This account will make the proposal and be responsible for the bond.": "", + "This account will pay the fees for the preimage, based on the size thereof.": "", + "This could either be an approval for the hash or with full call details. The call as last approval triggers execution.": "", + "This external account cannot be used to sign data. Only Limited support is currently available for signing from any non-internal accounts.": "", + "This injected account cannot be used to sign data since the extension does not support raw signing.": "", + "This is not a valid JSON object.": "", + "This operation does not impact the associated on-chain code or any of its contracts.": "", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.": "", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.": "", + "This operation does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.": "", + "This operation does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.": "", + "This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.": "", + "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).": "", + "This poll is setup to judge the sentiment of the Polkadot token holders in adjusting the number of decimals that is used to identify one full DOT. It does not change the overall supply, but rather just allows for a different representation of the current supply.": "", + "This pubic key is what will be visible in your queued keys list. It is generated based on the seed and the crypto used.": "", + "This vote does not affect any economics of the Polkadot platform. Staking rewards, inflation, effective market capitalisation and the underlying balances of every account remain completely unchanged. It is \"merely\" about what units we use to denominate the balances into \"DOT\" for the purpose of display.": "", + "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.": "", + "Tip": "", + "Tip (optional)": "", + "To council": "", + "To ensure optimal fund security using the same stash/controller is strongly discouraged, but not forbidden.": "", + "Transfer": "", + "Translate": "", + "Treasury overview": "", + "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.": "", + "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "", + "Type the name of this Asset. This name will be used across all the apps. It can be edited later on.": "", + "Type the name of your contact. This name will be used across all the apps. It can be edited later on.": "", + "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.": "", + "Unable to find deployed contract code at the specified address": "", + "Unable to find on-chain WASM code for the supplied codeHash": "", + "Unbond": "", + "Unbond funds": "", + "Unbonding {{value}}, ": "", + "Uncaught error. Something went wrong with the query and rendering of this component. {{message}}": "", + "Undelegate": "", + "Unit": "", + "Unit decides the name of 1 unit token, e.g. \"DOT\" for Polkadot": "", + "Unknown": "", + "Unknown Chain": "", + "Unknown Owner": "", + "Unlock": "", + "Unlock account": "", + "Unlock the account for signing. Once active the signature will be generated based on the content provided.": "", + "Unlock the sending account to allow signing of this transaction.": "", + "Unlock vested amount": "", + "Update metadata": "", + "Upload": "", + "Upload WASM": "", + "Use an automatic selection of the currently most profitable validators": "", + "Use this account to request the tip from. This can be a normal or council account.": "", + "Validate": "", + "Validator": "", + "Validator stats": "", + "Vanity generator": "", + "Verify signature": "", + "Verify the password entered above.": "", + "View this externally": "", + "Vote": "", + "Vote Aye": "", + "Vote Nay": "", + "Vote for candidate": "", + "Vote for current candidates": "", + "Vote for defender": "", + "Vote on proposal": "", + "Votes": "", + "Voting costs nothing other than the transaction fee and can be done from all accounts with a non-zero spendable balance.": "", + "Voting is made on a per-account basis; a single account must all vote the same way and cannot split its vote.": "", + "Waiting": "", + "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.": "", + "Waiting nominations ({{count}})": "", + "Warning: we did not find any attest statement for {{chain}}": "", + "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.": "", + "When submitting a proposal the hash needs to be known. Proposals can be submitted with hash-only, but upon dispatch the preimage needs to be available.": "", + "Withdraw these unbonded funds": "", + "Working groups": "", + "Yes": "", + "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.": "", + "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.": "", + "You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.": "", + "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.": "", + "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": "", + "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.": "", + "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.": "", + "You are using an ABI with an outdated format. Please generate a new one.": "", + "You are voting with this collective's prime account. The vote will be the default outcome in case of any abstentions.": "", + "You can indicate your vote for any combination of the options laid out below.": "", + "You can set a custom derivation path for this account using the following syntax \"//////\". The \"/\" and \"//\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.": "", + "You do not have access to the current sudo key": "", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": "", + "You need to sign an attestation for the following account:": "", + "You need to sign an attestation for the following accounts:": "", + "You need to unlock this account to be able to sign data.": "", + "You will no longer have sudo access": "", + "Your Ethereum account": "", + "Your custom types have been added": "", + "Your endorsement will be applied for this account.": "", + "Your stash account. The transaction will be sent from the associated controller.": "", + "a riot name linked to this identity": "", + "account": "", + "account forgotten": "", + "account restored": "", + "accounts": "", + "activate": "", + "additional bonded funds": "", + "address": "", + "address copied": "", + "address created": "", + "address edited": "", + "address forgotten": "", + "address prefix": "", + "address {{index}}": "", + "amount": "", + "amount to use for estimation": "", + "approval type": "", + "approved": "", + "asset id": "", + "auto-selected targets for nomination": "", + "available": "", + "available signatories": "", + "available social recovery helpers": "", + "available to be unlocked": "", + "average": "", + "aye": "", + "aye: bool": "", + "backing": "", + "backup file": "", + "balances": "", + "beneficiary": "", + "best": "", + "best #": "", + "best block": "", + "best hash": "", + "bids": "", + "block hash or number to query": "", + "blocks": "", + "bond": "", + "bonded": "", + "call data for final approval": "", + "call from account": "", + "call the selected endpoint": "", + "candidate account": "", + "candidate accounts": "", + "candidates": "", + "case sensitive": "", + "challenge": "", + "claim to account": "", + "click to copy": "", + "click to select or drag and drop a JSON ABI file": "", + "click to select or drag and drop the file here": "", + "clipboard": "", + "code": "", + "code for this contract": "", + "code hash": "", + "commission": "", + "compiled contract WASM": "", + "connected peers": "", + "contacts": "", + "contract address": "", + "contract to use": "", + "controller": "", + "controller account": "", + "conviction": "", + "conviction: Conviction": "", + "copied": "", + "council candidates": "", + "council proposal": "", + "council proposal type": "", + "created account": "", + "created multisig": "", + "crypto type to use": "", + "custom endpoint": "", + "data": "", + "default icon theme": "", + "default interface language": "", + "defender": "", + "delay": "", + "delegation": "", + "democracy": "", + "denomination vote": "", + "deploy": "", + "deployment account": "", + "deployment constructor": "", + "deposit": "", + "derivation path": "", + "derive root account": "", + "design samples": "", + "details": "", + "development seed": "", + "dispatch queue": "", + "display name": "", + "do not include empty strings in the generated file": "", + "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": "", + "ed25519, Edwards": "", + "elected stake": "", + "email": "", + "enact": "", + "endowment": "", + "epoch": "", + "era": "", + "era points": "", + "era {{era}}, {{count}} slashes": "", + "eras": "", + "events": "", + "everything": "", + "exclude option": "", + "exclude value": "", + "execute": "", + "existential deposit": "", + "external": "", + "extrinsic hash": "", + "extrinsics": "", + "fee": "", + "filter by name or tags": "", + "filter by name, address or index": "", + "filter by name, address, or account index": "", + "finalized": "", + "finder": "", + "forks": "", + "free balance": "", + "from the following data": "", + "generated public key": "", + "has a valid claim for": "", + "hash": "", + "heads": "", + "hex input data": "", + "hex-encoded storage key": "", + "https://example.com": "", + "identity": "", + "imminent preimage (proposal already passed)": "", + "immortal": "", + "include all empty strings in the generated file": "", + "include option": "", + "include value": "", + "index": "", + "initial head state": "", + "intentions": "", + "interface operation mode": "", + "invalid ABI file selected": "", + "invalid/unknown registrar account": "", + "judgement": "", + "key type to set": "", + "keypair crypto type": "", + "kind": "", + "last #": "", + "last block": "", + "last reward": "", + "launch period": "", + "legal name": "", + "lifetime": "", + "lock expired": "", + "locked": "", + "locked balance": "", + "logs": "", + "manage hardware connections": "", + "matches": "", + "maximum gas allowed": "", + "members": "", + "message to send": "", + "minimum bond": "", + "minimum deposit": "", + "mnemonic seed": "", + "mortal, valid from #{{startAt}} to #{{endsAt}}": "", + "motions": "", + "multisig": "", + "multisig name": "", + "multisig signatory": "", + "my ordered votes": "", + "name": "", + "nay": "", + "new account": "", + "new address": "", + "next": "", + "next id": "", + "no": "", + "no accounts yet, create or import an existing": "", + "no addresses saved yet, add any existing address": "", + "no messages": "", + "no peers connected": "", + "no tags": "", + "no unapplied slashes found": "", + "nominated accounts": "", + "nominators": "", + "not signed": "", + "on-chain bonding duration": "", + "ongoing referendum": "", + "only this network": "", + "origin": "", + "other stake": "", + "our best": "", + "own stake": "", + "parachain id": "", + "parachains": "", + "parent": "", + "password": "", + "password (repeat)": "", + "payment": "", + "payment destination": "", + "payout stakers for (multiple)": "", + "payout stakers for (single)": "", + "payout/stash": "", + "payout/validator": "", + "peer best": "", + "pending extrinsics": "", + "pending hashes": "", + "pending swap id": "", + "pgp hash": "", + "points": "", + "poll on token decimals": "", + "pot": "", + "preimage hash": "", + "preimage {{hash}}": "", + "prev": "", + "prime member": "", + "prime voter": "", + "priority {{index}}": "", + "profit/era est": "", + "proposal": "", + "proposal bond": "", + "proposal hash": "", + "proposals": "", + "propose": "", + "propose from account": "", + "proposed by": "", + "proposer": "", + "proxy account": "", + "queued tx": "", + "reason": "", + "recent blocks": "", + "recent events": "", + "recover this account": "", + "recovery block delay": "", + "recovery threshold": "", + "redeemable": "", + "referenda": "", + "refresh in": "", + "registrar account": "", + "registrar index": "", + "relay dispatch queue": "", + "remaining": "", + "remote node/endpoint to connect to": "", + "request payout from": "", + "reserved": "", + "reward commission percentage": "", + "rewards": "", + "rewards & slashes": "", + "riot": "", + "riot name": "", + "role": "", + "rotation": "", + "runners up": "", + "scheduling": "", + "seats": "", + "second with account": "", + "secret": "", + "secret derivation path": "", + "seed (hex or string)": "", + "select the account you wish to sign data with": "", + "selected constant query": "", + "selected signatories": "", + "selected state query": "", + "selected validators": "", + "send": "", + "send as RPC call": "", + "send as transaction": "", + "send from account": "", + "send to address": "", + "sending account": "", + "sending from my account": "", + "session": "", + "session keys": "", + "session next": "", + "sha2 image hash": "", + "sign data from account": "", + "sign the following data": "", + "signatories": "", + "signatory": "", + "signature crypto type": "", + "signature of supplied data": "", + "signer": "", + "society head": "", + "somebody@example.com": "", + "spend period": "", + "sr15519, Schnorrkel": "", + "staked": "", + "stash account": "", + "stashes": "", + "state": "", + "strikes": "", + "sub name": "", + "submit the following change": "", + "submit the following extrinsic": "", + "submit with account": "", + "submit with council account": "", + "sudo key": "", + "sudo with unchecked weight parameter": "", + "sudo without unchecked weight parameter": "", + "suri (seed & derivation)": "", + "swap to id": "", + "syncing": "", + "system events": "", + "tags": "", + "target": "", + "term progress": "", + "the account to make recoverable": "", + "the account to recover to": "", + "the associated controller": "", + "the era to cancel for": "", + "the language to display translations for": "", + "the module to display strings for": "", + "the resulting hash is": "", + "the stash account to nominate with": "", + "the supplied signature": "", + "threshold": "", + "tip": "", + "tip reason": "", + "tip value": "", + "tips": "", + "total": "", + "total issuance": "", + "total peers": "", + "total stake": "", + "total staked": "", + "transactions": "", + "transfer received": "", + "transferrable": "", + "trusted social recovery helpers": "", + "turnout": "", + "twitter": "", + "type": "", + "unbond amount": "", + "unbonding": "", + "unchecked weight for this call": "", + "unlock account with password": "", + "unstake threshold": "", + "update on #{{index}}": "", + "upgradable extensions": "", + "use on any network": "", + "using my account": "", + "using the following data": "", + "using the selected account": "", + "validator to query": "", + "validators": "", + "validators/nominators": "", + "value": "", + "value bonded": "", + "verify using address": "", + "version {{version}}": "", + "vested": "", + "via Council/Vote": "", + "via Democracy/Vote": "", + "via Staking/Bond": "", + "via Vesting": "", + "vote for candidate": "", + "vote for defender": "", + "vote from account": "", + "vote using my account": "", + "vote value": "", + "vote with account": "", + "votes": "", + "voting account": "", + "voting balance": "", + "voting end": "", + "voting period": "", + "voting round": "", + "waiting": "", + "web": "", + "website": "", + "with an index of": "", + "wrong password supplied": "", + "yes": "", + "your current password": "", + "your new password": "", + "{{balance}} voted": "", + "{{blocks}} blocks": "", + "{{count}} key/value pairs encoded for submission": "", + "{{count}} own stashes": "", + "{{currency}} average": "", + "{{currency}} clipped": "", + "{{currency}} rewards": "", + "{{currency}} slashed": "", + "{{currency}} total": "", + "{{done}}/{{total}}, {{progress}}% done": "", + "{{d}} days": "", + "{{h}} hrs": "", + "{{m}} mins": "", + "{{name}} ({{size}} bytes)": "", + "{{percentage}} aye": "", + "{{percentage}}% turnout": "", + "{{relayDispatchQueueSize}} dispatch messages pending": "", + "{{s}} s": "", + "{{threshold}}, not passing": "", + "{{threshold}}, passing": "", + "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/es/translation.json b/pioneer/packages/apps/public/locales/es/translation.json new file mode 100644 index 0000000000..9e19dd33f3 --- /dev/null +++ b/pioneer/packages/apps/public/locales/es/translation.json @@ -0,0 +1,1116 @@ +{ + "//hard///password": "//fuerte///contraseña", + "//hard/soft": "//fuerte/suave", + "//hard/soft///password": "//fuerte/suave///contraseña", + "0.1x voting balance, no lockup period": "0.1x aumento en el saldo de la votación, sin período de bloqueo", + "1 day": "1 día", + "1 hr": "1 hora", + "1 min": "1 minuto", + "1 s": "1 segundo", + "1. Select your {{chain}} account": "Selecciona tu cuenta para {{chain}}", + "2. Enter the ETH address from the sale.": "Introduzca la address de ETH de la venta.", + "2. Sign ETH transaction": "Firma de la transacción de ETH", + "": "", + "": "", + "A controller account should not be set to manages multiple stashes. The selected controller is already controlling {{stashId}}": "Una cuenta controller no debe ser configurada para manejar múltiples stashes. El controller seleccionado ya está controlando {{stashId}}", + "A controller account should not map to another stash. This selected controller is a stash, controlled by {{bondedId}}": "La cuenta controller no debe ser asignada a otro stash. Este controller seleccionado es un stash, controlado por {{bondedId}}", + "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": "Un contrato de despliegue que ha sido desplegado o agregado. La dirección y el ABI se utilizan para construir los parámetros.", + "A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist": "Se deducirá una comisión de {{creationFee}} del remitente, ya que la cuenta de destino no existe.", + "A reason (to be stored-on-chain) as to why the recipient deserves a tip payout.": "Un motivo (para ser almacenado en la cadena) de por qué el destinatario merece un pago de propina.", + "ABI": "ABI", + "Acceptance proposal to council": "Propuesta de aceptación al consejo", + "Account actions": "Acciones de la cuenta", + "Account balance:": "Balance de la cuenta", + "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.": "Las cuentas incorporadas de cualquiera de estas extensiones aparecerán en esta aplicación y estarán disponibles para su uso. La lista anterior se actualiza a medida que se dispone de más extensiones con capacidad de firma externa.", + "Active nominations ({{count}})": "({{count}}) Nominaciones activas", + "Add a tip to this extrinsic, paying the block author for greater priority": "Añade una propina a este extrinsic, pagando al autor del bloque por una mayor prioridad", + "Add account": "Añadir cuenta", + "Add account via Qr": "Añadir cuenta desde el Qr", + "Add an account via seed": "Añadir cuenta desde la semilla", + "Add an address": "Añadir una dirección", + "Add an existing code hash": "Añadir un hash de código existente", + "Add an existing contract": "Añadir un contrato existente", + "Add contact": "Añadir contacto", + "Add identity judgment": "Añadir el reconocimiento de la identidad", + "Add item": "Añade elemento", + "Add multisig": "Añadir multisig", + "Add via Qr": "Añadir desde el Qr", + "Add via backup file": "Añadir archivo de backup", + "Adding an optional tip to the transaction could allow for higher priority, especially when the chain is busy.": "Añadir una propina opcional a la transacción podría permitir una mayor prioridad, especialmente cuando la cadena está saturada.", + "Additional types as a JSON file (or edit below)": "Tipos adicionales como un fichero JSON (o editar abajo)", + "Address Prefix": "Título de la dirección", + "Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)": "Ajustar el modo desde lo básico (con un número limitado de aplicaciones para principiantes) a lo más completo (con todas las aplicaciones básicas y avanzadas disponibles)", + "Advanced creation options": "Opciones avanzadas para la creación", + "All rewards will go towards the selected output destination when a payout is made.": "Todas las recompensas irán hacia el destino de salida seleccionado cuando se efectúe el pago.", + "All the listed validators and all their nominators will receive their rewards.": "Todos los validadores de la lista y todos sus nominadores recibirán su recompensa.", + "Allocate a suggested tip amount. With enough endorsements, the suggested values are averaged and sent to the beneficiary.": "Asigna una cantidad sugerida de propina. Con suficiente respaldo, los valores sugeridos serán un promedio y se enviarán al beneficiario.", + "Amount to add to the currently bonded funds. This is adjusted using the available funds on the account.": "Cantidad a añadir a los fondos actualmente en reserva. Se ajusta con los fondos disponibles en la cuenta.", + "An URL that is linked to this identity.": "URL que está vinculada a esta identidad.", + "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.": "Se creará un archivo de seguridad encriptado una vez que haya pulsado el botón \"Descargar\". Éste puede ser usado para reimportar su cuenta en cualquier otro equipo.", + "Any account can request payout for stakers, this is not limited to accounts that will be rewarded.": "Cualquier cuenta puede solicitar el pago de los stakers, esto no se limita a las cuentas que serán retribuidas.", + "Approvals": "Aprobaciones", + "Approve or reject this defender.": "Aprobar o rechazar a este representante.", + "Approve this call hash": "Aprobar esta llamada de hash", + "Approve this candidacy.": "Aprobar esta candidatura.", + "Approved": "Aprobado", + "As a council member, you can suggest an initial value for the tip, each other council member can suggest their own.": "Como miembro del consejo, puede sugerir un valor inicial para la propina, cada miembro del consejo puede sugerir el suyo propio.", + "Asset ID": "ID de los activos", + "Assets": "Activos", + "Aura": "Any account can request payout for stakers, this is not limited to accounts that will be rewarded.", + "Authorize transaction": "Autorizar transacción", + "Aye": "Si", + "Aye, I approve": "Si, lo apruebo", + "Babe": "Babe", + "Backup account": "Copia de seguridad de la cuenta", + "Block details": "Detalles del bloque", + "Bond": "Vínculo", + "Bond & Nominate": "Vincular y nominar", + "Bond & Validate": "Vincular y validar", + "Bond more": "Vincular más", + "Bond more funds": "Vincular más fondos", + "Bonding Preferences": "Preferencias de la vinculación", + "Call": "Llamada", + "Call a contract": "Llamada a contrato", + "Call a method on this contract": "Llamada a un método en este contrato", + "Call results": "Resultados de la llamada", + "Call this message": "Llamada a este mensaje", + "Cancel": "Cancelar", + "Cancel slashes": "Cancelar penalización", + "Cancel this call hash": "Cancelar esta llamada de hash", + "Chain info": "Información de la cadena", + "Chain specifications": "Especificaciones de la cadena", + "Change": "Cambio", + "Change account password": "Cambiar la contraseña de la cuenta", + "Change controller account": "Cambiar la cuenta controller", + "Change reward destination": "Cambiar el destino de la recompensa", + "Change session keys": "Cambiar las claves de la sesión", + "Change this account's password": "Cambiar la contraseña de esta cuenta", + "Change validator preferences": "Cambiar preferencias del validador", + "Changing the key only takes effect at the start of the next session. The input here is generates from the author_rotateKeys command": "El cambio de la clave sólo tiene efecto al comienzo de la siguiente sesión. La entrada aquí se genera desde el comando author_rotateKeys", + "Claim": "Reclamación", + "Claim tokens...": "Reclamar tokens...", + "Clear all": "Limpiar todo", + "Clear expired democracy locks": "Limpiar los bloqueos de la democracia bloqueados", + "Close": "Cerrar", + "Close proposal": "Cerrar propuesta", + "Color": "Color", + "Committee prime member, default voting": "Miembro principal del Comité, votación por defecto", + "Confirm ABI removal": "Confirmar la eliminación de ABI", + "Confirm account removal": "Confirmar la eliminación de la cuenta", + "Confirm address removal": "Confirmar la eliminación de la dirección", + "Confirm claim": "Confirme la reclamación", + "Confirm code removal": "Confirmar la eliminación del código", + "Confirm contract removal": "Confirmar la eliminación del contrato", + "Constants": "Constantes", + "Continue": "Continuar", + "Conviction locks do overlap and is additive, meaning that funds locked during a previous vote can be locked again.": "Los bloqueos de sentencias se superponen y son acumulativos, lo que significa que los fondos bloqueados durante una votación anterior pueden ser bloqueados de nuevo.", + "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "Copie la secuencia anterior y firme una transacción de Ethereum con la cuenta que utilizó durante la preventa en la cartera de su elección, utilizando la secuencia como fuente, y luego pegue la firma de la transacción a continuación", + "Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:": "Copie la siguiente secuencia y fírmela con la cuenta Ethereum que utilizó durante la preventa en la cartera de su elección, utilizando la secuencia como fuente, y luego pegue la firma de la transacción a continuación:", + "Council": "Council", + "Council overview": "Información del Consejo", + "Create": "Crear", + "Create a backup file for this account": "Crear un archivo de backup para esta cuenta", + "Create an account now.": "Cree una cuenta ahora.", + "Create and backup account": "Crear y realizar copia de seguridad de esta cuenta", + "Crypto not detected": "Crypto no detectado", + "Cryptography used to create this signature. It is auto-detected on valid signatures.": "Criptografía utilizada para crear esta firma. Se detecta automáticamente en las firmas válidas.", + "Current account nonce: {{accountNonce}}": "Nonce de la cuenta actual: {{accountNonce}}", + "Current prime member, default voting": "Miembro principal actual, votación por defecto", + "Current society head, exempt": "El actual responsable de la sociedad, exento", + "Decimals": "Decimales", + "Decimals decides the smallest unit of the token, which is 1/10^decimals": "Los decimales deciden la unidad más pequeña del token, que es 1/10^decimales", + "Delete this custom example": "Eliminar este ejemplo personalizado", + "Democracy overview": "Resumen de la democracia", + "Deploy": "Despliegue", + "Deploy a code hash": "Despliegue el hash del código", + "Deploy this code hash as a smart contract": "Despliegue este hash del código como un contrato inteligente", + "Deploy with this constructor": "Despliegue con este constructor", + "Deposit": "Depósito", + "Deregister this parachain": "Anular esta parachain", + "Derive account from pair": "Cuenta derivada desde el par", + "Derive account via derivation path": "Cuenta derivada desde el path de origen", + "Details": "Detalles", + "Detection on the input string to determine if it is hex or non-hex.": "Detección en la cadena de entrada para determinar si es hexadecimal o no hexadecimal.", + "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".": "Determina qué criptografía se utilizará para crear esta cuenta. Tenga en cuenta que para validar en Polkadot, la cuenta session debe utilizar \"ed25519\".", + "Developer": "Desarrolladora", + "Development": "Desarrollo", + "Dismiss all notifications": "Descartar todas las notificaciones", + "Display overview information for the selected validator, including blocks produced.": "Mostrar información general del validador seleccionado, incluyendo los bloques producidos.", + "Distinct stash and controller accounts are recommended to ensure fund security. You will be allowed to make the transaction, but take care to not tie up all funds, only use a portion of the available funds during this period.": "Se recomiendan cuentas de stash y controller distintas para asegurar la seguridad de los fondos. Se le permitirá realizar la transacción, pero tenga cuidado de no bloquear todos los fondos, utilice sólo una parte de los fondos disponibles durante este período.", + "Do not include a tip for the block author": "No incluya una propina para el autor del bloque", + "Download": "Descarga", + "Either approve or reject this call.": "Puede aprobar o rechazar esta llamada.", + "Endorse": "Apoyar", + "Endorsements ({{count}})": "({{count}}) apoyos", + "Ensure that not all funds are locked, funds need to be available for fees.": "Asegúrese de que no todos los fondos estén bloqueados, los fondos deben estar disponibles para las tasas.", + "Enter the Asset ID of the token you want to manage.": "Introduzca el ID del activo del token que quiere manejar.", + "Enter the Asset ID of the token you want to transfer.": "Introduzca el ID del activo del token que desee transferir.", + "Erroneous": "Erróneo", + "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)": "{{count}} evaluada en {{elapsed}}s ({{avg}} claves/s)", + "Extensions": "Extensiones", + "External": "Externo", + "Extrinsic submission": "Entrega de extrinsic", + "Fast track": "Vía rápida", + "Fast track proposal": "Propuesta por vía rápida", + "Filter available candidates based on name, address or short account index.": "Filtrar los candidatos disponibles en función del nombre, la dirección o el resumen de la cuenta.", + "For fund security, your session key should not match your stash key.": "Para la seguridad de los fondos, la llave de la sesión no debe coincidir con la de stash.", + "Forget": "Olvidar", + "Forget this account": "Olvide esta cuenta", + "Forget this address": "Olvide esta dirección", + "Forget this asset": "Olvida este activo", + "Forget this code hash": "Olvide este hash de código", + "Forget this contract": "Olvide este contrato", + "Forks": "Bifurcaciones", + "General": "General", + "Generate {{lng}}/translation.json": "Generar {{lng}}/translation.json", + "Genesis Hash": "Hash del génesis", + "Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched": "El Hash del Génesis se refiere al estado inicial de la cadena, no puede ser cambiado una vez que la cadena es lanzada", + "Grandpa": "Grandpa", + "Hash data": "Hash del dato", + "I agree": "Estoy de acuerdo.", + "I'm Online": "Estoy en línea", + "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.": "Si la cuenta del destinatario es nueva, el saldo debe ser mayor que el de su depósito actual. Del mismo modo, si el saldo de la cuenta de origen cae por debajo de ese valor, la cuenta será retirada del state.", + "If this proposal is passed, the changes will be applied via dispatch and the deposit returned.": "Si se aprueba esta propuesta, los cambios se aplicarán mediante notificación y se devolverá el depósito.", + "If you are moving accounts between applications, ensure that you use the correct type.": "Si está moviendo cuentas entre aplicaciones, asegúrese de usar el tipo correcto.", + "Important notice": "Noticia importante", + "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.": "En el cálculo del resultado de la elección, este orden de prioridad de los votos se utilizará para determinar la puntuación final de los candidatos.", + "Inactive nominations ({{count}})": "({{count}}) Nominaciones inactivas", + "Include an optional tip for faster processing": "Incluye un consejo opcional para un procesamiento más rápido", + "Initializing connection": "Iniciando la conexión", + "Initiate account recovery": "Inicie la recuperación de la cuenta", + "Initiate recovery for another": "Inicie la recuperación para otra", + "Inject Keys": "Introducir las llaves", + "Inject session keys (advanced)": "Introducir las llaves de sesión (avanzado)", + "Injected": "inyectada", + "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -": "Se recomienda que cree/almacene sus cuentas de forma segura y externa mediante la aplicación. En {{yourBrowser}} las siguientes extensiones están disponibles para su uso -", + "Judge": "Jurado", + "Keys from rotateKeys": "Llave de rotateKeys", + "Known good": "Conocido como bueno", + "Learn more...": "Aprenda más...", + "Lifetime (# of blocks)": "Tiempo de vida (# de bloques)", + "Loading": "Cargando", + "Locked1x": "Bloqueado x1", + "Locked2x": "Bloqueado x2", + "Locked3x": "Bloqueado x3", + "Locked4x": "Bloqueado x4", + "Locked5x": "Bloqueado x5", + "Locked6x": "Bloqueado x6", + "Low quality": "Baja calidad", + "Make Transfer": "Realizar transferencia", + "Make recoverable": "Hacer recuperable", + "Manage your connection to Ledger S": "Manejar tu conexión con Ledger S", + "Message data": "Datos del mensaje", + "Message origin.": "Origen del mensaje.", + "Messages": "Mensajes", + "Metadata {{count}}": "Metadatos {{count}}", + "Mnemonic": "Mnemonic", + "Most recent head data": "Datos más recientes de la cabecera", + "Motions ({{count}})": "Mociones ({{count}})", + "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "Podrían existir múltiples propuestas del consejo, tanto de aprobación como de rechazo. Aplique su voto a la propuesta correcta del consejo (también disponible en la página de mociones del consejo)", + "Multisig": "Multisig", + "Multisig approval with hash (non-final approval)": "Aprobación de Multisig con hash (aprobación no definitiva)", + "Multisig approval with hash (not message with call)": "Aprobación de Multisig con hash ( sin mensaje en la llamada)", + "Multisig approvals": "Aprobaciones multisig", + "Multisig message with call (for final approval)": "Mensaje Multisig con llamada (para aprobación final)", + "Multisig message with call (not approval with hash)": "Mensaje multisig con llamada (no aprobar con el hash)", + "Mutates contract state": "Estado del contrato mútuo", + "My accounts": "Mis cuentas", + "My contacts": "Mis contactos", + "Name given to this account. You can change it at any point in the future.": "Nombre otorgado a esta cuenta. Puede cambiarlo en cualquier momento en el futuro.", + "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".": "Nombre otorgado a esta cuenta. Puede editarlo. Para usar la cuenta para validar o nominar, es una práctica recomendable añadir la función de la cuenta en el nombre, ejemplo: \"nombre_que_se_desea - stash\".", + "Name given to this multisig. You can edit it at any later point in time.": "Nombre asignado a esta multisig. Puede editarlo en cualquier momento en el futuro.", + "Name of the network. It is only for display purposes.": "Nombre de la red. Es sólo para fines de visualización.", + "Name your example": "Nombre de su ejemplo", + "Nay": "No", + "Nay, I do not approve": "No, no lo apruebo", + "Network Name": "Nombre de la red", + "New stake": "Nueva inversión", + "No": "No", + "No active members": "No hay miembros activos", + "No active proposals": "No hay propuestas activas", + "No active referendums": "No hay referémdums activos", + "No active validators found": "No se han encontrado validadores activos", + "No active validators to check": "No hay validadores activos para comprobar", + "No approved proposals": "Propuestas no aprobadas", + "No assets found.": "No se han encontrado activos", + "No bids": "No hay pujas", + "No blocks available": "No hay bloques disponibles", + "No candidates": "No hay candidatos", + "No candidates found": "No se han encontrado candidatos", + "No code hashes available": "No hay hash de código disponible", + "No committee proposals": "No hay propuestas del comité", + "No contracts available": "No hay contrato disponible", + "No council motions": "No hay mociones del consejo", + "No documentation provided": "No se ha facilitado documentación", + "No events available": "No hay eventos disponibles", + "No external proposal": "No hay proposiciones externas", + "No funds staked yet. Bond funds to validate or nominate a validator": "No hay fondos en juego por el momento. Vincule fondos para validar o nominar un validador", + "No items": "No hay elementos", + "No judgments": "No hay juicio", + "No logs available": "No hay logs disponibles", + "No matches found": "No se han encontrado coincidencias", + "No members found": "No se han encontrado miembros", + "No open tips": "No hay propinas abiertas", + "No pending era payouts from validators": "No hay pagos pendientes en la era de los validadores", + "No pending extrinsics are in the queue": "No hay extrinsics pendientes en la lista", + "No pending payouts for your stashes": "No hay pagos pendientes de sus stashes", + "No pending proposals": "No hay propuestas pendientes", + "No runners up found": "No se han encontrado mensajeros", + "No upgradable extensions found": "No se han encontrado extensiones actualizables", + "No waiting validators found": "No se han encontrado validadores en espera", + "Node info": "Información del nodo", + "Nominate": "Nominar", + "Nominate Validators": "Nominar validadores", + "Nominate selected": "Nominar seleccionados", + "Nominate validators": "Nominar validadores", + "Nominations ({{count}})": "({{count}}) Nominaciones", + "Nominator": "Nominador", + "Nominators can be selected automatically based on the current on-chain conditions or supplied manually as selected from the list of all currently available validators. In both cases, your favorites appear for the selection.": "Los nominadores pueden seleccionarse automáticamente en base a las condiciones actuales de la cadena o suministrarse manualmente según se seleccione de la lista de todos los validadores disponibles actualmente. En ambos casos, sus favoritos aparecen para la selección.", + "Nonce": "Nonce", + "None": "Ninguno", + "Not updated in the last block": "No se actualizó en el último bloque", + "Nothing queued for execution": "No hay nada en espera de ejecución", + "Of the beneficiary amount, at least {{bondPercentage}} would need to be put up as collateral. The maximum of this and the minimum bond will be used to secure the proposal, refundable if it passes.": "Del importe del beneficiario, al menos {{{bondPercentage}} tendría que ser puesto como garantía. El máximo de esto y el mínimo de la fianza se utilizarán para asegurar la propuesta, reembolsable si se aprueba.", + "Once bonded, it wil need to be unlocked/withdrawn and will be locked for at least the bonding duration.": "Una vez vinculado, tendrá que ser desbloqueado/retirado y estará bloqueado por lo menos por la duración de la vinculación.", + "Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, the nominations will show as inactive.": "Una vez transmitida la nueva selección sólo tendrá efecto en 2 eras ya que los criterios de selección para la siguiente era se hicieron al final de la era anterior. Hasta entonces, las nominaciones se mostrarán como inactivas.", + "One of your available nomination accounts, keyed by the stash. The transaction will be sent from the controller.": "Una de sus cuentas de nominación disponibles, marcada por stash. La transacción será enviada desde el controller.", + "Only applicable if the proposal has already passed and is ready for dispatch.": "Sólo se aplica si la propuesta ya ha pasado y está lista para ser notificada.", + "Override any applicable values for the specific signed output. These will be used to construct and display the signed transaction.": "Anular los valores aplicables para la salida firmada específica. Estos se utilizarán para construir y mostrar la transacción firmada.", + "Override the default identity icon display with a specific theme": "Sustituir la visualización del icono de identidad por defecto por un tema específico", + "Override the default ss58 prefix for address generation": "Sustituir el prefijo ss58 por defecto para la generación de direcciones", + "Owned": "Propiedad de", + "Parachains": "Parachains", + "Parachains overview": "Resumen de parachains", + "Paste here the address of the contact you want to add to your address book.": "Pegue aquí la dirección del contacto que quiere añadir a su lista de direcciones.", + "Payout": "Pago", + "Payout all": "Pagar todo", + "Payout all stakers": "Pagar a todos los stakers", + "Pending call hashes": "Llamadas de hash pendientes", + "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.": "Por favor, asegúrese de guardar este archivo en un lugar seguro puesto que es necesario, junto con su contraseña, para restaurar su cuenta.", + "Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:": "Por favor, lea estos términos y condiciones cuidadosamente. Al enviar esta solicitud, se considera que ha aceptado estos términos y condiciones. Si no está de acuerdo con estos términos, por favor absténgase de acceder o proceder. También puede encontrarlos en:", + "Positive number": "Número positivo", + "Positive number between 1 and {{count}}": "Número positivo entre 1 y {{count}}", + "Positive number between 1 and {{memberCount}}": "Número positivo entre 1 y {{memberCount}}", + "Positive number greater than or equal to {{nextFreeId}}": "Número positivo mayor o igual a {{nextFreeId}}", + "Pre-sale ethereum address": "Dirección de Ethereum de la preventa", + "Prefix indicates the ss58 address format in this network, it is a number between 0 ~ 255 that describes the precise format of the bytes of the address": "El prefijo indica el formato de la dirección ss58 en esta red, es un número entre 0 ~ 255 que describe el formato preciso de los bytes de la dirección", + "Preimage": "Preimagen", + "Present the QR code containing the signature to the UI. Once scanned it will be submitted for on-chain processing and execution.": "Presente el código QR que contiene la firma a la UI. Una vez escaneado, será presentado para su procesamiento y ejecución en cadena.", + "Proposal can either be to approve or reject this spend. One approved, the change is applied by either removing the proposal or scheduling payout.": "La propuesta puede ser la aprobación o el rechazo de este gasto. Una vez aprobada, el cambio se aplica ya sea quitando la propuesta o programando el pago.", + "Proposals": "Propuestas", + "Proposals ({{count}})": "({{count}}) Propuestas", + "Propose": "Propuesta", + "Propose a committee motion": "Proponer una moción de comité", + "Propose a council motion": "Proponer una moción del consejo", + "Propose external": "Proposición externa", + "Propose external (majority)": "Proposición externa (mayoría)", + "Propose motion": "Proponer una moción", + "Propose tip": "Proponer gratificación", + "Provide judgement": "Juicio proporcionado", + "Provide the account QR from the module/external application for scanning. One detected as valid, you will be taken to the next step to add the account to your list.": "Proporcionar el QR de la cuenta del módulo/aplicación externa para el análisis. Una vez detectada como válida, se le llevará al siguiente paso para añadir la cuenta a su lista.", + "Query Ledger": "Consultar el libro de cuentas", + "RPC calls": "llamadas RPC", + "Random": "Aleatorio", + "Raw seed": "Semillas en bruto", + "Raw storage": "Almacenamiento en bruto", + "Reasonable": "Razonable", + "Reassign": "Reasignar", + "Redeem": "Canjear", + "Register": "Registrarse", + "Register Asset": "Registro de activos", + "Register a parachain": "Registrar una parachain", + "Register an Asset": "Registrar un activo", + "Register identity": "Registro de identidad", + "Register the vote for or against the proposal.": "Registre el voto a favor o en contra de la propuesta.", + "Rejection proposal to council": "Propuesta de rechazo al consejo", + "Rejections": "Rechazos", + "Remove": "Eliminar", + "Remove ABI": "Eliminar ABI", + "Remove item": "Quitar el elemento", + "Reset": "Restablecer", + "Restore": "Restaurar", + "Restore JSON": "Restaurar JSON", + "Retrieving data": "Recuperación de datos", + "Retrieving info for all applicable eras, this will take some time": "Recuperar la información de todas las eras aplicables, esto requerirá cierto tiempo", + "Revert": "Revertir", + "Revert pending slashes": "Revertir recortes pendientes", + "Rewards (once paid) can be deposited to either the stash or controller, with different effects.": "Las recompensas (una vez pagadas) pueden ser depositadas en stash o en controller, con diferentes resultados.", + "Save": "Guardar", + "Save & Reload": "Guardar y refrescar", + "Save snippet to local storage": "Guarda el valor en el almacenamiento local", + "Save the type definitions for your custom structures as key-value pairs in a valid JSON file. The key should be the name of your custom structure and the value an object containing your type definitions.": "Guarde las definiciones de tipos para sus estructuras personalizadas como pares clave-valor en un archivo JSON válido. La clave debe ser el nombre de su estructura personalizada y el valor un objeto que contenga sus definiciones de tipo.", + "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.": "Guarde este archivo de seguridad en un lugar protegido. Además, la contraseña asociada a esta cuenta es necesaria junto con el archivo de copia de seguridad para poder restaurar su cuenta.", + "Saved": "Guardado", + "Scan Signature Qr": "Escanear la firma Qr", + "Scan the QR code with your QR scanner. Once approved, you will be required to present the signed QR back to the UI for submission.": "Escanea el código QR con tu escáner QR. Una vez aprobado, se le pedirá que presente el QR firmado de nuevo a la UI para su presentación.", + "Search for": "Busque por", + "Second": "Apoyar", + "Second proposal": "Apoyar la proposición", + "Seconding a proposal that indicates your backing for the proposal. Proposals with greater interest moves up the queue for potential next referendums.": "Secundar una propuesta que indique su respaldo a la proposición. Las propuestas con mayor interés suben en la cola de posibles próximos referéndums.", + "Seconds ({{count}})": "({{count}}) segundos", + "Select Network": "Seleccione la red", + "Select a contact or paste the address you want to send funds to.": "Seleccione un contacto o pegue la dirección a la que quiere enviar los fondos.", + "Select and order council candidates you wish to vote for.": "Seleccione y ordene los candidatos al consejo por los que desea votar.", + "Select best": "Seleccionar el mejor", + "Select example": "Seleccione un ejemplo", + "Select targets manually (no auto-selection based on current profitability)": "Seleccionar los objetivos manualmente (no hay selección automática basada en la rentabilidad actual)", + "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.": "Seleccione el archivo de la clave JSON que se descargó cuando creó la cuenta. Este archivo JSON contiene su clave privada encriptada con su contraseña.", + "Select the account to use for this action.": "Seleccione la cuenta a utilizar para esta acción.", + "Select the account you wish close the proposal with.": "Seleccione la cuenta con la que desea cerrar la propuesta.", + "Select the account you wish to make the proposal with.": "Seleccione la cuenta con la que desea hacer la propuesta.", + "Select the account you wish to recover into this account.": "Seleccione la cuenta que desea recuperar en esta cuenta.", + "Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected": "Seleccione la cuenta con la que desea participar. Esto bloqueará sus fondos hasta que la propuesta sea aprobada o rechazada", + "Select the account you wish to submit for candidacy.": "Seleccione la cuenta que desea presentar para la candidatura.", + "Select the account you wish to submit the proposal from.": "Seleccione la cuenta desde la que desea presentar la propuesta.", + "Select the account you wish to submit the tip from.": "Seleccione la cuenta desde la que desea enviar la propina.", + "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.": "Seleccione la cuenta con la que desea votar. Puede aprobar \"sí\" o negar \"no\" la propuesta.", + "Select the council account you wish to use to make the proposal.": "Seleccione la cuenta del consejo que desea utilizar para hacer la propuesta.", + "Select the remote endpoint, either from the dropdown on manual entered via the custom toggle": "Seleccione el punto remoto final, ya sea desde el desplegable de manual introducido a través de la tecla personalizada", + "Send funds": "Enviar fondos", + "Send funds from this account": "Enviar fondos desde esta cuenta", + "Send funds to this address": "Enviar fondos a esta cuenta", + "Send to council": "Enviar al consejo", + "Sending transaction": "Enviar transacción", + "Session Key": "Llave session", + "Set Identity": "Establecer identidad", + "Set Session Key": "Establecer la clave de Session", + "Set controller": "Establecer controller", + "Set nominees": "Establecer nominados", + "Set on-chain identity": "Establecer la identidad en cadena", + "Set reward destination": "Establecer destino de las recompensas", + "Set sudo key": "Establecer llave sudo", + "Set to 0 to make transaction immortal": "Establecer a 0 para hacer la transacción inmortal", + "Set validator preferences": "Establecer preferencias del validador", + "Setup Nominator {{step}}/{{NUM_STEPS}}": "Configuración nominador {{step}}/{{NUM_STEPS}}", + "Setup Validator {{step}}/{{NUM_STEPS}}": "Configurar validador {{step}}/{{NUM_STEPS}}", + "Setup account as recoverable": "Configurar la cuenta como recuperable", + "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...": "Si la búsqueda distingue entre mayúsculas y minúsculas, por ejemplo, si selecciona \"no\", la búsqueda de \"Algunos\" puede dar como resultado direcciones que contengan \"algUnos\" o \"algunOs\"...", + "Show address on hardware device": "Mostrar la dirección en el dispositivo hardware", + "Sign (no submission)": "Firmar (no enviar)", + "Sign and Submit": "Firmar y enviar", + "Sign for multisig": "Firmar para multisig", + "Sign message": "Firmar mensaje", + "Sign via Qr": "Firmar desde Qr", + "Sign via {{hardwareType}}": "Firmar desde {{hardwareType}}", + "Signed transaction": "Transacción firmada", + "Since the multisig function like any other account, once created it is available for selection anywhere accounts are used and needs to be funded before use.": "Dado que la multisig funciona como cualquier otra cuenta, una vez creada estará disponible para su selección en cualquier lugar donde se utilicen cuentas y deberá ser financiada antes de su uso.", + "Since this transaction deals with funding, the stash account will be used.": "Dado que esta transacción tiene que ver con la financiación, se utilizará la cuenta de stash.", + "Skeptics": "Escépticos", + "Society": "Sociedad", + "Society overview": "Resumen de la sociedad", + "Specify the user account to use for this contract call. And fees will be deducted from this account.": "Especifique la cuenta de usuario que se utilizará para esta llamada del contrato. Y los gastos serán restados de esta cuenta.", + "Specify the user account to use for this deployment. And fees will be deducted from this account.": "Especifique la cuenta de usuario que se utilizará para este despliegue. Y los gastos se deducirán de esta cuenta.", + "Staking overview": "Resumen del staking", + "Start generation": "Empezar la generación", + "Start recovery": "Empezar la recuperación", + "Stash": "Stash", + "Stop": "Parar", + "Stop generation": "Parar la generación", + "Storage": "Almacenamiento", + "Submit": "Enviar", + "Submit (no signature)": "Enviar (sin firma)", + "Submit RPC call": "Enviar llamada RPC", + "Submit Sudo": "Enviar sudo", + "Submit Transaction": "Enviar transacción", + "Submit Unsigned": "Enviar sin firma", + "Submit candidacy": "Presentar la candidatura", + "Submit key": "Enviar llave", + "Submit preimage": "Enviar preimagen", + "Submit proposal": "Subir propuesta", + "Submit signed extrinsic": "Presentar extrinsic firmado", + "Submit tip": "Subir propina", + "Submit tip endorsement": "Presentar la aprobación de la propina", + "Submit tip request": "Presentar la solicitud de propina", + "Submit treasury proposal": "Presentar la propuesta de tesorería", + "Submit your council candidacy": "Presente su candidatura al consejo", + "Sudo access": "Acceso sudo", + "Sudo key": "Clave Sudo", + "Supply a backed-up JSON file, encrypted with your account-specific password.": "Proporcione una copia de seguridad del archivo JSON, encriptado con la contraseña correspondiente a la cuenta.", + "Targets": "Objetivos", + "Tech. committee": "Comité técnico", + "Technical committee": "Comité técnico", + "Test account": "Cuenta de prueba", + "The RPC node can be selected from the pre-defined list or manually entered, depending on the chain you wish to connect to.": "El nodo RPC puede ser seleccionado de la lista preestablecida o introducido manualmente, dependiendo de la cadena a la que se desee conectar.", + "The Threshold must be a positive number": "El umbral debe ser un número positivo", + "The Threshold must lower than 11": "El umbral debe ser menor que 11", + "The UI puts a limit of 40 payouts at a time, where each payout is a single validator for a single era.": "La UI pone un límite de 40 remuneraciones a la vez, donde cada pago es un único validador para una sola era.", + "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.": "La contraseña de la cuenta, tal como se indicó al crear la cuenta. Se utiliza para cifrar el archivo de copia de seguridad y posteriormente se desencripta cuando se restaura la cuenta.", + "The account that signed the input": "La cuenta que firmó la entrada", + "The account to which the proposed balance will be transferred if approved": "La cuenta a la que se transferirá el saldo propuesto si se aprueba", + "The account to which the tip will be transferred if approved": "La cuenta a la que se transferirá la propina si se aprueba", + "The account you want to claim to.": "La cuenta que desea reclamar.", + "The account you want to register the preimage from": "La cuenta de la que quieres registrar la imagen previa de", + "The account you want to register the proposal from": "La cuenta en la que desea registrar la propuesta de", + "The account you will send funds from.": "La cuenta desde la que enviará los fondos.", + "The account's password specified at the creation of this account.": "La contraseña de la cuenta especificada en la creación de esta cuenta.", + "The actual JSONRPC module and function to make a call to.": "El actual módulo JSONRPC y la función de hacer una llamada.", + "The actual proposal to make, based on the selected call and parameters thereof.": "La propuesta actual a realizar, basada en la convocatoria seleccionada y sus parámetros.", + "The address for the deployed contract instance.": "La dirección de la instancia del contrato desplegado", + "The address to vote from (must be a member)": "La dirección desde la que se desea votar (debe ser un miembro)", + "The addresses that are able to approve multisig transactions. You can select up to {{maxHelpers}} trusted addresses.": "Las direcciones que pueden aprobar las transacciones multisig. Puede seleccionar hasta {{maxHelpers}} direcciones de confianza.", + "The addresses that are able to help in recovery. You can select up to {{maxHelpers}} trusted helpers.": "Las direcciones que pueden ayudar en la recuperación. Puede seleccionar hasta {{maxHelpers}} asistentes de confianza.", + "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": "La dotación asignada para este contrato, es decir, la cantidad transferida al contrato por instanciación.", + "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": "El valor asignado a este contrato, es decir, la cantidad transferida al contrato como parte de esta llamada.", + "The amount of funds to unbond, this is adjusted using the bonded funds on the stash account.": "La cantidad de fondos a desbloquear, se ajusta utilizando los fondos depositados en la cuenta stash", + "The amount placed at-stake should be no more that 95% of your available amount to protect against slashing events.": "La cantidad colocada en la participación no debe ser más del 95% de la cantidad disponible para protegerse contra los eventos de recorte.", + "The amount that is associated with this vote. This value is is locked for the duration of the vote.": "La cantidad que se asocia con este voto. Este valor está bloqueado durante la duración del voto.", + "The amount that will be allocated from the treasury pot": "La cantidad que se asignará del fondo del tesoro", + "The amount that will be used on a per-validator basis to calculate profits for that validator.": "La cantidad que se utilizará en base a cada validador para calcular los beneficios de ese validador.", + "The amount this total can be reduced by to change the referendum outcome. This assumes changes to the convictions of the existing votes, with no additional turnout.": "La cantidad de este total puede reducirse para cambiar el resultado del referéndum. Esto supone cambios en las opiniones de los votos existentes, sin ninguna participación adicional.", + "The amount this total should be increased by to change the referendum outcome. This assumes additional turnout with new votes at 1x conviction.": "La cantidad de este total debería incrementarse para cambiar el resultado del referéndum. Esto supone una participación adicional con nuevos votos a 1x de sentencia.", + "The associated deposit for this proposal should be more then the minimum on-chain deposit required. It will be locked until the proposal passes.": "El depósito asociado a esta propuesta debería ser mayor que el depósito mínimo en cadena requerido. Estará bloqueado hasta que la propuesta pase.", + "The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for the validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.": "La auto-selección se hace sobre la rentabilidad actual de los validadores teniendo en cuenta sus favoritos. Se ajusta en base a la comisión y el rango actual de respaldo del validador. El cálculo puede cambiar y cambiará con el tiempo, por lo que es más bien una selección basada en el estado actual de la red, no un indicador de la rentabilidad futura.", + "The balance associated with the vote will be locked as per the conviction specified and will not be available for transfer during this period.": "El saldo asociado a la votación será bloqueado según la sentencia especificada y no estará disponible para su transferencia durante este período.", + "The beneficiary will have access to the transferred fees when the transaction is included in a block.": "El beneficiario tendrá acceso a los pagos realizados cuando la transacción se incluya en un bloque.", + "The beneficiary will receive the full amount if the proposal passes.": "El beneficiario recibirá la cantidad total si la propuesta es aprobada.", + "The beneficiary will received the tip as approved by council members.": "El beneficiario recibirá la propina según lo aprobado por los miembros del consejo.", + "The blake2b 256-bit hash of the actual input data.": "El hash blake2b de 256-bit de los datos de entrada reales.", + "The bonding duration for any staked funds. After this period needs to be withdrawn.": "La duración de la vinculación de los fondos en juego. Después de este período debe ser retirado.", + "The bonding duration for any staked funds. Needs to be unlocked and withdrawn to become available.": "La duración de la vinculación de los fondos. Necesita ser desbloqueado y retirado para estar disponible.", + "The call hashes that have not been executed as of yet.": "Los hashes de llamada que aún no han sido ejecutados.", + "The code hash for the on-chain deployed code.": "El hash del código para el despliegue en cadena del código.", + "The code is not recognized as being in valid WASM format": "El código no se reconoce en el formato válido WASM", + "The codeHash is not a valid hex hash": "El código hash no es un hash hex válido", + "The color used to distinguish this network with others, use color code with 3 or 6 digits, like \"#FFF\" or \"#111111\"": "El color usado para distinguir esta red de las demás, usa un código de color de 3 o 6 dígitos, como \"#FFF\" o \"#111111\"", + "The commission is deducted from all rewards before the remainder is split with nominators.": "La comisión se deduce de todas las recompensas antes de que el resto se reparta con los nominadores.", + "The committee account that will apply the close for the current round.": "La cuenta del comité que aplicará el cierre para la ronda actual.", + "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": "El WASM compilado para el contrato que desea desplegar. Cada blob de código único se adjuntará con un hash de código que puede ser usado para crear nuevas instancias.", + "The compiled runtime WASM for the parachain you wish to register.": "El tiempo de ejecución compilado WASM para la parachain que desea registrar.", + "The compiled runtime WASM for this parachain.": "Runtime compilado en WASM para esta parachain.", + "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.": "El contrato WASM previamente desplegado. Internamente se identifica por el hash del código, ya sea creado o adjunto.", + "The controller is the account that is be used to control any nominating or validating actions. I will sign this transaction.": "El controller es la cuenta que se utiliza para controlar cualquier acción de nominación o validación. Firmaré esta transacción.", + "The controller is the account that will be used to control any nominating or validating actions. Should not match another stash or controller.": "El controller es la cuenta que se utilizará para controlar cualquier acción de nominación o validación. No debe coincidir con stash o controller de otro tipo.", + "The conviction to use for this vote, with an appropriate lock period.": "La sentencia a utilizar para esta votación, con un período de bloqueo apropiado.", + "The council account for the proposal. The selection is filtered by the current members.": "La cuenta del consejo para la propuesta. La selección es filtrada por los miembros actuales.", + "The council account for this vote. The selection is filtered by the current members.": "La cuenta del consejo para esta votación. La selección es filtrada por los miembros actuales.", + "The council account that will apply the close for the current round.": "La cuenta del consejo que aplicará el fin de la ronda actual.", + "The council member that is proposing this, submission equates to an \"aye\" vote.": "El miembro del consejo que propone esto, la sumisión equivale a un voto de \"sí\".", + "The council proposal to make the vote on": "La propuesta del consejo para hacer la votación de", + "The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.": "Los datos que fueron firmados. Se utiliza en combinación con la firma para la verificación. Puede ser hexadecimal o una cadena.", + "The delay between vouching and the availability of the recovered account.": "La espera entre el registro y la disponibilidad de dicha cuenta recuperada.", + "The delay period to apply in blocks": "El período de demora que se aplicará en los bloques", + "The deployment constructor information for this contract, as provided by the ABI.": "La información del constructor de despliegue para este contrato, según lo proporcionado por el ABI.", + "The deposit is below the {{minimum}} minimum required for the proposal to be evaluated": "El depósito está por debajo del {{minimum}} mínimo requerido para que la propuesta sea evaluada", + "The deposit of {{deposit}} will be reserved until the proposal is completed": "El depósito de {{deposit}} se reservará hasta que se complete la propuesta", + "The derivation path allows you to create different accounts from the same base mnemonic.": "La ruta de la derivación permite crear diferentes cuentas desde la misma base mnemotécnica.", + "The desired threshold. Here set to a default of 50%+1, as applicable for general proposals.": "El umbral deseado. Aquí se establece un valor por defecto de 50%+1, como se aplica a las propuestas generales.", + "The destination account for any payments as either a nominator or validator": "La cuenta de destino para cualquier pago como nominador o validador", + "The details of the transaction including the type, the description (as available from the chain metadata) as well as any parameters and fee estimations (as available) for the specific type of call.": "Los detalles de la transacción, incluidos el tipo, la descripción (según se indica en los metadatos de la cadena), así como los parámetros y las estimaciones de las tasas (según se indica) para el tipo específico de llamada.", + "The email address associated with this identity.": "La dirección de correo electrónico asociado a esta identidad.", + "The existing account password as specified when this account was created or when it was last changed.": "Contraseña de la cuenta existente, tal como se especificó cuando se creó la cuenta o cuando se modificó por última vez.", + "The external proposal to send to the technical committee": "La propuesta externa de enviar al comité técnico", + "The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected": "El saldo final del receptor es menor o igual a {{existentialDeposit}} (the existential amount) y no se reflejará", + "The funds will only be available for withdrawal after the unbonding period, however will not be part of the staked amount after the next validator election. You can follow the unlock countdown in the UI.": "Los fondos sólo estarán disponibles para ser retirados después del período de desbloqueo, sin embargo no serán parte de la cantidad depositada después de la próxima elección del validador. Puedes seguir la cuenta atrás de desbloqueo en la UI.", + "The hash for the proposal this vote applies to": "El hash de la propuesta a la que se aplica esta votación", + "The hash of the preimage for the proposal as previously submitted or intended.": "El hash de la preimagen de la propuesta como se ha presentado o se pretende presentar.", + "The hash of the proposal image, either already submitted or valid for the specific call.": "El hash de la imagen de la propuesta, ya presentada o válida para la convocatoria específica.", + "The hash of the selected proposal, use it for submitting the proposal": "El hash de la propuesta seleccionada, utilizado para la presentación de la propuesta", + "The helpers should be able to verify, via an off-chain mechanism, that the account owner indeed wishes to recover access and as such provide any approvals. In the cases of malicious recovery procedures, they will have the power to stop it.": "Los asistentes deben poder verificar, mediante un mecanismo fuera de la cadena, que el titular de la cuenta desea efectivamente recuperar el acceso y, como tal, proporcionar cualquier aprobación. En los casos de procedimientos de recuperación malintencionados, tendrán la capacidad de impedirlo.", + "The hex output from author_rotateKeys, as executed on the validator node. The keys will show as pending until applied at the start of a new session.": "La salida hexadecimal de author_rotateKeys, como se ejecuta en el nodo validador. Las claves se mostrarán como pendientes hasta que se apliquen al comienzo de una nueva sesión.", + "The id number to assign to this parachain.": "El número de identificación para asignar a esta parachain", + "The id of the parachain to be deregistered.": "El ID de la parachain para ser anulada del registro", + "The image (proposal) will be stored on-chain against the hash of the contents.": "La imagen (propuesta) se almacenará en cadena frente al hash de los contenidos.", + "The initial head state for the parachain.": "Estado inicial de las cabeceras para la parachain.", + "The initial head state is invalid.": "El estado inicial de las cabeceras es inválido.", + "The input data to hash. This can be either specified as a hex value (0x-prefix) or as a string.": "Los datos de entrada al hash. Esto puede ser especificado como un valor hexadecimal (0x-prefijo) o como una cadena.", + "The input data to sign. This can be either specified as a hex value (0x-prefix) or as a string.": "Los datos de entrada para firmar. Esto puede ser especificado como un valor hexadecimal (0x-prefijo) o como una cadena.", + "The key type and crypto type to use for this key. Be aware that different keys have different crypto requirements. You should be familiar with the type requirements for the different keys.": "El tipo de llave y el tipo de criptografía a utilizar para esta llave. Tenga en cuenta que las diferentes claves tienen diferentes requisitos de criptografía. Debe estar familiarizado con los requisitos de tipo para las diferentes claves.", + "The legal name for this identity.": "Nombre legal de esta identidad", + "The local name for this account. Changing this does not affect your on-line identity, so this is only used to indicate the name of the account locally.": "Nombre local de esta cuenta. Cambiar esto no afecta a su identidad en línea, por lo que sólo se utiliza para indicar el nombre de la cuenta de forma local.", + "The locked value for this proposal": "El valor bloqueado para esta propuesta", + "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": "La cantidad máxima de gas que puede ser utilizada por esta llamada. Si el código requiere más, la llamada fallará.", + "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": "La cantidad máxima de gas que puede ser utilizada para este despliegue, si el código requiere más, el despliegue fallará.", + "The message to send to this contract. Parameters are adjusted based on the ABI provided.": "El mensaje para enviar a este contrato. Los parámetros se ajustan en base al ABI proporcionado.", + "The minimum amount that an account should have to be deemed active": "Cantidad mínima que debe tener una cuenta para que se considere activa", + "The minimum amount that will be bonded": "La cantidad mínima que se fijará en la fianza", + "The minimum deposit required": "El depósito mínimo necesario", + "The minimum number of committee votes required to approve this motion": "El número mínimo de votos del comité requerido para aprobar esta moción", + "The minimum number of council votes required to approve this motion": "El número mínimo de votos del consejo necesarios para aprobar esta moción", + "The multisig signatory for this transaction.": "El firmante multisig de esta transacción.", + "The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.": "El nombre de esta cuenta y cómo aparecerá bajo sus direcciones. Con una identidad en la cadena, puede estar disponible para otros.", + "The name is for unique identification of the account in your owner lists.": "El nombre es para la identificación específica de la cuenta en su lista de propietario.", + "The name that will be displayed in your accounts list.": "El nombre que aparecerá en su lista de cuentas.", + "The new account password. Once set, all future account unlocks will be performed with this new password.": "La nueva contraseña de la cuenta. Una vez establecida, todos los posteriores desbloqueos de la cuenta se realizarán con esta nueva contraseña.", + "The on-chain percentage for the treasury": "El porcentaje en cadena para el tesoro", + "The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.": "La contraseña y la verificación para esta cuenta. Esto es necesario para autentificar cualquier transacción realizada y para cifrar el par de claves.", + "The password previously used to encrypt this account.": "La contraseña utilizada anteriormente para cifrar esta cuenta.", + "The password to unlock the selected account.": "La contraseña para desbloquear la cuenta seleccionada.", + "The percentage reward (0-100) that should be applied for the validator": "La recompensa en porcentaje (0-100) que debe aplicarse para el validador", + "The preimage hash of the proposal": "El hash de la preimagen de la propuesta", + "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.": "La clave privada de su cuenta se deriva de esta semilla. Esta semilla debe mantenerse de forma secreta ya que cualquiera que la posea tiene acceso a los fondos de la cuenta. Si usted es validador, use la semilla de la cuenta de sesión con el parámetro \"--key\" de su nodo.", + "The proposal is in the queue for future referendums. One proposal from this list will move forward to voting.": "La propuesta está en la lista de espera para futuros referéndums. Una propuesta de esta lista pasará a la votación.", + "The proposal that is being voted on. It will pass when the threshold is reached.": "La propuesta que está siendo votada. Pasará cuando se alcance el umbral.", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted for a subsequent voting round.": "La propuesta que se verá afectada. Una vez cerrada para la actual ronda de votación, tendría que ser presentada de nuevo para una ronda de votación posterior.", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted to council for a subsequent voting round.": "La propuesta que se verá afectada. Una vez cerrada para la actual ronda de votación, tendría que ser presentada de nuevo al consejo para una ronda de votación posterior.", + "The proposal will be registered from this account and the balance lock will be applied here.": "La propuesta se registrará desde esta cuenta y el bloqueo del saldo se aplicará aquí.", + "The reason why this tip should be paid.": "La razón por la que esta propina debe ser pagada.", + "The recoverable account is protected against the loss of seed/access by a social process.": "La cuenta que se recupera está protegida contra la pérdida de la semilla/acceso por un proceso social.", + "The resulting signature of the input data, as done with the crypto algorithm from the account. (This could be non-deterministic for some types such as sr25519).": "La firma resultante de los datos de entrada, como se hizo con el algoritmo criptográfico de la cuenta. (Esto podría ser no determinante para algunos tipos como sr25519).", + "The scheduling preference for this parachain.": "Preferencia de planificación para esta parachain", + "The scheduling setting for this parachain.": "Planificación de la configuración de esta parachain", + "The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.": "El valor secreto de la semilla de esta cuenta. Asegúrese de mantenerlo en un lugar seguro, con acceso a la semilla puede reconstruir la cuenta.", + "The seed and derivation path will be submitted to the validator node. this is an advanced operation, only to be performed when you are sure of the security and connection risks.": "La semilla y la derivación se presentarán al nodo validador. Esta es una operación avanzada, sólo se realizará cuando esté seguro de los riesgos de seguridad y conexión.", + "The selected account to be unlocked.": "La cuenta seleccionada para ser desbloqueada.", + "The selected account to perform the derivation on.": "Cuenta seleccionada para realizar la derivación.", + "The selected controller tied to this stash. Once set, this account will be able to control the actions performed by the stash account.": "El controller seleccionado atado a esta stash. Una vez configurado, esta cuenta podrá controlar las acciones realizadas por la cuenta de stash.", + "The selected validators to nominate, either via the \"currently best algorithm\" or via a manual selection.": "Los validadores seleccionados para nominar, ya sea a través del \"mejor algoritmo actual\" o a través de una selección manual.", + "The sending account that will be used to send this transaction. Any applicable fees will be paid by this account.": "La cuenta de envío que se utilizará para enviar esta transacción. Cualquier tarifa aplicable será pagada por esta cuenta.", + "The signatories has the ability to create transactions using the multisig and approve transactions sent by others.Once the threshold is reached with approvals, the multisig transaction is enacted on-chain.": "Los firmantes tienen la capacidad de crear transacciones utilizando la multisig y aprobar las transacciones enviadas por otros. Una vez que se alcanza el umbral con las aprobaciones, la transacción de multisig se aprueba en cadena.", + "The signatory is one of the allowed accounts on the multisig, making a recorded approval for the transaction.": "El autor de la firma es una de las cuentas permitidas en la multisig, haciendo una aprobación registrada de la transacción.", + "The signatory is one of the allowed accounts on the multisig. The transaction could either be the call or an approval for the hash of a call.": "El autor es una de las cuentas permitidas en la Multisig. La transacción puede ser la llamada o una aprobación para el hash de una llamada.", + "The signatory to send the approval/cancel from": "La firma para enviar la aprobación/cancelación", + "The signature as by the account being checked, supplied as a hex-formatted string.": "La firma como la cuenta que se está comprobando, suministrada como una cadena de formato hexadecimal.", + "The specific eras on which there are unapplied slashes. For each era a separate proposal is to be made.": "Las eras específicas en las que hay recortes no aplicados. Para cada era se debe hacer una propuesta separada.", + "The specified value is greater than your free balance. The node will bond the maximum amount available.": "El valor especificado es mayor que su saldo disponible. El nodo vinculará la cantidad máxima disponible.", + "The stash account that is used. This will allow the controller to perform all non-funds related operations on behalf of the account.": "La cuenta de stash que se utiliza. Esto permitirá al controller realizar todas las operaciones no relacionadas con los fondos en nombre de la cuenta.", + "The stash and controller pair as linked. This operation will be performed via the controller.": "El par de stash y controller están unidas. Esta operación se realizará a través del controller.", + "The stash and controller pair, here the controller will be used to send the transaction.": "El par de stash y controller, aquí el controller se utilizará para enviar la transacción.", + "The stash and controller pair. This transaction, managing preferences, will be sent from the controller.": "El par de stash y controller. Esta transacción, que gestiona las preferencias, será enviada desde el controller.", + "The stash and controller pair. This transaction, setting the session keys, will be sent from the controller.": "El par stash y controller. Esta transacción, que gestiona las preferencias, será enviada desde controller.", + "The stash that is to be affected. The transaction will be sent from the associated controller account.": "La cuenta stash que se verá aceptada. La transacción se enviará desde la cuenta de controller asociada.", + "The suggested value for this tip": "El valor sugerido para esta propina", + "The the Ethereum address you used during the pre-sale (starting by \"0x\")": "La dirección de Ethereum que se usó durante la preventa (empieza por \"0x\")", + "The threshold for approval should be less or equal to the number of signatories for this multisig.": "El umbral de aprobación debe ser menor o igual al número de firmas de esta multisig.", + "The threshold for approvals and the delay is the protection associated with the account. The delay should be such that any colluding recovery attempts does have a window to stop.": "El umbral para las aprobaciones y el retraso es la protección asociada a la cuenta. El retraso debe ser tal que cualquier intento de recuperación fraudulento tenga una oportunidad de detenerse.", + "The threshold for this multisig": "El umbral para esta multisig", + "The threshold of vouches that is to be reached for the account to be recovered.": "El umbral de garantía que se debe alcanzar para que la cuenta se recupere.", + "The tip amount that should be allocated": "La cantidad de propina que debe asignarse", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the free amount available)": "El importe total del saldo de stash que estará en juego en las próximas rondas (debe ser inferior a la cantidad libre disponible)", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the total amount available)": "La cantidad total del saldo de stash que estará en juego en las próximas rondas (debe ser menor que la cantidad total disponible)", + "The transferred balance will be subtracted (along with fees) from the sender account.": "El saldo transferido se restará (junto con las tasas) de la cuenta del remitente.", + "The twitter name for this identity.": "El nombre de Twitter para esta identidad.", + "The type of council proposal to submit.": "El tipo de propuesta del consejo a presentar.", + "The unapplied slashed era to cancel.": "La era de los recortes no aplicados a cancelar.", + "The unchecked weight as specified for the sudoUncheckedWeight call.": "El valor no comprobado, como se especifica en la llamada sudoUncheckedWeight.", + "The value associated with this vote. The amount will be locked (not available for transfer) and used in all subsequent elections.": "El valor asociado a este voto. La cantidad será bloqueada (no disponible para su transferencia) y utilizada en todas las elecciones subsiguientes.", + "The value is not in a valid address format": "El valor no está en un formato de dirección válido", + "The value is the amount that is being asked for and that will be allocated to the beneficiary if the proposal is approved.": "El valor es la cantidad que se pide y que se asignará al beneficiario si se aprueba la propuesta.", + "The vote to record for this proposal, either for or against.": "El voto a registrar para esta propuesta, ya sea a favor o en contra.", + "The vote will be recorded for the selected account.": "El voto se registrará para la cuenta seleccionada.", + "The vote will be recorded for this account. If another account delegated to this one, the delegated votes will also be counted.": "El voto se registrará en esta cuenta. Si otra cuenta delega en ésta, también se contarán los votos delegados.", + "The votes for the members, runner-ups and candidates. These should be ordered based on your priority.": "Los votos para los miembros, candidatos y suplentes. Estos deben ser ordenados en base a su prioridad.", + "The voting period to apply in blocks": "El período de votación se aplicará en bloques", + "There are no registered parachains": "No hay parachains registradas", + "There is currently an ongoing election for new validator candidates. As such staking operations are not permitted.": "Actualmente hay una elección en curso para nuevos candidatos a validador. Como tal, las operaciones de staking no están permitidas.", + "These are trusted individuals that can verify and approve any recovery actions. With recovery, once the threshold is reached, the funds associated with the account can be moved to a new destination.": "Se trata de personas de confianza que pueden verificar y aprobar cualquier acción de recuperación. Con la recuperación, una vez que se alcanza el umbral, los fondos asociados a la cuenta pueden ser transferidos a un nuevo destino.", + "Think of the stash as your cold wallet and the controller as your hot wallet. Funding operations are controlled by the stash, any other non-funding actions by the controller itself.": "Piensa en stash como tu cartera fría y en controller como tu cartera caliente. Las operaciones de financiación son controladas por stash, cualquier otra acción no financiera será desde controller.", + "This Ethereum address does not have a claim.": "Esta dirección de Ethereum no tiene ninguna reclamación.", + "This account is recoverable, with the following friends:": "Esta cuenta es recuperable, con los siguientes conocidos:", + "This account that will perform the message signing.": "Esta cuenta realizará la firma del mensaje.", + "This account will appear in the list of candidates. With enough votes in an election, it will become either a runner-up or a council member.": "Esta cuenta aparecerá en la lista de candidatos. Con suficientes votos en una elección, se convertirá en finalista o miembro del consejo.", + "This account will be use to approve each candidate.": "Esta cuenta se utilizará para aprobar cada candidato.", + "This account will make the proposal and be responsible for the bond.": "Esta cuenta hará la propuesta y será responsable de la fianza.", + "This account will pay the fees for the preimage, based on the size thereof.": "Esta cuenta pagará los gastos de la preimagen, según el tamaño de la misma.", + "This could either be an approval for the hash or with full call details. The call as last approval triggers execution.": "Esto podría ser una aprobación para el hash o con todos los detalles de la llamada. La llamada como última aprobación desencadena la ejecución.", + "This external account cannot be used to sign data. Only Limited support is currently available for signing from any non-internal accounts.": "Esta cuenta externa no puede utilizarse para firmar datos. Actualmente sólo se dispone de un apoyo limitado para firmar desde cualquier cuenta no interna.", + "This injected account cannot be used to sign data since the extension does not support raw signing.": "Esta cuenta inyectada no puede utilizarse para firmar datos, ya que la extensión no admite la firma en bruto.", + "This is not a valid JSON object.": "Este no es un objeto JSON válido.", + "This operation does not impact the associated on-chain code or any of its contracts.": "Esta operación no afecta al código asociado de la cadena ni a ninguno de sus contratos.", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.": "Esta operación no elimina el historial de la cuenta de la cadena, ni los fondos asociados de la cuenta. La operación de olvidar sólo limita el acceso a la cuenta en este navegador.", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.": "Esta operación no elimina el historial de la cuenta de la cadena, ni los fondos asociados de la cuenta. La operación \"Olvidar\" sólo limita el acceso a la dirección de este navegador.", + "This operation does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.": "Esta operación no elimina el historial del contrato de la cadena, ni los fondos asociados de su cuenta. La operación de olvido sólo limita su acceso al contrato en este navegador.", + "This operation does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.": "Esta operación no elimina el código cargado WASM y ABI de la cadena, ni los contratos desplegados. La operación \"Olvidar\" sólo limita su acceso al código de este navegador.", + "This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.": "Esta operación presentará la semilla a través de una llamada RPC. No realice esta operación en un nodo RPC público, pero asegúrese de que el nodo es local, conectado a su validador y seguro.", + "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).": "Esta contraseña se utiliza para cifrar su clave privada. ¡Debe ser fuerte y única! La necesitará para firmar las transacciones con esta cuenta. Puede recuperar esta cuenta usando dicha contraseña junto con el archivo de copia de seguridad (generado en el siguiente paso).", + "This pubic key is what will be visible in your queued keys list. It is generated based on the seed and the crypto used.": "Esta llave pública es lo que será visible en tu lista de claves en espera. Se genera en base a la semilla y a la crypto utilizada.", + "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.": "Esto se aplicará a cualquier uso futuro de esta cuenta tal y como está almacenada en este navegador. Asegúrese de guardar de forma segura esta nueva contraseña, que es fuerte y exclusiva de la cuenta.", + "Tip": "Propina", + "Tip (optional)": "Sugerencia (opcional)", + "To council": "Al consejo", + "To ensure optimal fund security using the same stash/controller is strongly discouraged, but not forbidden.": "Se desaconseja encarecidamente, pero no se prohíbe, asegurar una óptima seguridad de los fondos utilizando la misma stash/controller.", + "Transfer": "Transferir", + "Translate": "Traducir", + "Treasury overview": "Visión general de la Tesorería", + "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.": "Escriba aquí lo que quiere que contenga su dirección. Esta herramienta generará las claves y mostrará las direcciones asociadas que mejor se ajusten a su búsqueda. Puede usar \"?\" como comodín para un carácter.", + "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "Escriba la cantidad que desea transferir. Tenga en cuenta que puede seleccionar la unidad de la derecha, por ejemplo, enviar 1 millón es equivalente a enviar 0,001.", + "Type the name of this Asset. This name will be used across all the apps. It can be edited later on.": "Escriba el nombre de este activo. Este nombre se utilizará en todas las aplicaciones. Puede ser editado más adelante.", + "Type the name of your contact. This name will be used across all the apps. It can be edited later on.": "Escriba el nombre de su contacto. Este nombre se usará en todas las aplicaciones. Puede ser editado más tarde.", + "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.": "Escriba la contraseña elegida en la creación de la cuenta. Se utilizó para encriptar la clave privada de su cuenta en el archivo de respaldo.", + "Unable to find deployed contract code at the specified address": "No se puede encontrar el código de contrato desplegado en la dirección especificada", + "Unable to find on-chain WASM code for the supplied codeHash": "No se ha podido encontrar el código WASM de la cadena para el hash del código suministrado.", + "Unbond": "Desvincular", + "Unbond funds": "Desvincular fondos", + "Unbonding {{value}}, ": "Desvinculando {{value}},", + "Uncaught error. Something went wrong with the query and rendering of this component. {{message}}": "{{message}} Un error no descubierto. Algo salió mal con la consulta y la representación de este componente.", + "Unit": "Unidad", + "Unit decides the name of 1 unit token, e.g. \"DOT\" for Polkadot": "La unidad decide el nombre de un token de unidad, por ejemplo \"DOT\" para Polkadot", + "Unknown": "desconocido", + "Unlock": "Desbloquear", + "Unlock account": "Desbloquear cuenta", + "Unlock the account for signing. Once active the signature will be generated based on the content provided.": "Desbloquea la cuenta para firmar. Una vez activada, la firma se generará en base al contenido proporcionado.", + "Unlock the sending account to allow signing of this transaction.": "Desbloquee la cuenta de envío para permitir la firma de esta transacción.", + "Update metadata": "Actualizar metadatos", + "Updated in the latest block": "Actualizado en el último bloque", + "Upload": "Cargar", + "Upload WASM": "Cargar WASM", + "Use an automatic selection of the currently most profitable validators": "Utilizar una selección automática de los validadores más rentables actualmente", + "Use this account to request the tip from. This can be a normal or council account.": "Utilice esta cuenta para solicitar la propina de. Esta puede ser una cuenta normal o del consejo.", + "Validate": "Validar", + "Validator": "Validador", + "Validator stats": "Estadísticas del validador", + "Vanity generator": "Generador Vanity", + "Verify signature": "Verificar firma", + "Verify the password entered above.": "Verifica la contraseña introducida anteriormente.", + "View this externally": "Vea esto externamente", + "Vote": "Voto", + "Vote Aye": "Voto a favor", + "Vote Nay": "Voto negativo", + "Vote for candidate": "Voto para el candidato", + "Vote for current candidates": "Voto por los candidatos actuales", + "Vote for defender": "Voto por el defensor", + "Vote on proposal": "Voto en la propuesta", + "Waiting": "Esperando", + "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.": "Esperando la autorización de la extensión. Por favor, abra la extensión instalada y apruebe o rechace el acceso.", + "Waiting nominations ({{count}})": "Esperando nominaciones ({{count}})", + "Warning: we did not find any attest statement for {{chain}}": "Advertencia: no hemos encontrado ninguna declaración para {{{chain}}", + "We found a pre-claim with this Polkadot address. However, attesting requires signing with this account. To continue with attesting, please add this account as an owned account first.": "Encontramos una reclamación previa con esta dirección de Polkadot. Sin embargo, para dar fe es necesario firmar con esta cuenta. Para continuar con la certificación, por favor agregue esta cuenta como una cuenta propia primero.", + "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.": "Le facilitaremos un archivo de copia de seguridad generado después de crear su cuenta. Mientras tenga acceso a su cuenta, siempre podrá descargar este archivo más tarde haciendo clic en el botón \"Backup\" de la sección de Cuentas.", + "When submitting a proposal the hash needs to be known. Proposals can be submitted with hash-only, but upon dispatch the preimage needs to be available.": "Cuando se presenta una propuesta, el hash debe ser conocido. Las propuestas pueden ser presentadas con hash únicamente, pero en el momento de su envío la imagen previa tiene que estar disponible.", + "Withdraw these unbonded funds": "Retirar estos fondos no vinculados", + "Yes": "Si", + "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.": "Está a punto de eliminar esta cuenta de su lista de cuentas disponibles. Una vez completada, en caso de que necesite acceder a ella de nuevo, tendrá que volver a crear la cuenta, ya sea a través de una semilla o de un archivo de copia de seguridad.", + "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.": "Está a punto de eliminar esta dirección de su agenda. Una vez completada, si necesita acceder a ella de nuevo, tendrá que volver a añadir la dirección.", + "You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.": "Está a punto de eliminar este código de su lista de códigos disponibles. Una vez completado, en caso de que necesite acceder a él de nuevo, tendrá que añadir manualmente el código de nuevo.", + "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.": "Estás a punto de eliminar el ABI de este código. Una vez completado, si necesita acceder a él de nuevo, tendrá que volver a cargarlo manualmente.", + "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": "Está a punto de eliminar este contrato de su lista de contratos disponibles. Una vez completado, en caso de que necesite acceder a él de nuevo, tendrá que añadir manualmente la dirección del contrato en la pestaña Instantánea.", + "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.": "Se está conectando desde un lugar seguro a un WebSocket inseguro ({{wsUrl}}). Debido a las políticas de seguridad de contenido mixto del navegador, este tipo de conexión no está permitida. Cambie el servicio RPC a un punto 'wss' seguro .", + "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.": "No estás conectado a un nodo. Asegúrese de que su nodo está funcionando y que el Websocket es accesible.", + "You are using an ABI with an outdated format. Please generate a new one.": "Estás usando un ABI con un formato anticuado. Por favor, genere uno nuevo.", + "You are voting with this collective's prime account. The vote will be the default outcome in case of any abstentions.": "Está votando con la cuenta principal de este colectivo. El voto será el resultado por defecto en caso de cualquier abstención.", + "You can set a custom derivation path for this account using the following syntax \"//////\". The \"/\" and \"//\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.": "Puede establecer una ruta de derivación personalizada para esta cuenta usando la siguiente sintaxis \"//////\". La \"/\" y \"//\" pueden ser repetidas y mezcladas`. La \"///password\" es opcional y sólo debe aparecer una vez.", + "You do not have access to the current sudo key": "No tiene acceso a la llave sudo actual", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": "No tiene ninguna cuenta. Algunas características están actualmente ocultas y sólo estarán disponibles una vez que tenga cuentas.", + "You have {{claimCount}} accounts that need attestations. Use the Claim Tokens app on the left navigation bar to complete the process. Until you do, your balances for those accounts will not be reflected.": "Tiene {{claimCount}} cuentas que necesitan ser verificadas. Use la aplicación \"Claim Tokens\" en la barra de navegación izquierda para completar el proceso. Hasta que lo haga, su saldo para esas cuentas no se reflejará.", + "You need to sign an attestation for the following account:": "Necesita firmar un certificado para la siguiente cuenta:", + "You need to sign an attestation for the following accounts:": "Necesita firmar un certificado para las siguientes cuentas:", + "You need to unlock this account to be able to sign data.": "Necesitas desbloquear esta cuenta para poder firmar los datos.", + "You will no longer have sudo access": "Ya no tendrá acceso al sudo", + "Your Ethereum account": "Su cuenta de Ethereum", + "Your custom types have been added": "Sus tipos personalizados han sido añadidos", + "Your endorsement will be applied for this account.": "Su aval se aplicará a esta cuenta.", + "Your stash account. The transaction will be sent from the associated controller.": "Su cuenta stash. La transacción será enviada desde la cuenta controller asociada.", + "a riot name linked to this identity": "el nombre de riot asociado a esta identidad", + "account": "cuenta", + "account forgotten": "cuenta olvidada", + "account restored": "cuenta restaurada", + "accounts": "cuentas", + "activate": "activar", + "additional bonded funds": "recursos adicionales vinculados", + "address": "dirección", + "address copied": "dirección copiada", + "address created": "dirección creada", + "address edited": "dirección editada", + "address forgotten": "dirección olvidada", + "address prefix": "prefijo de la dirección", + "amount": "cantidad", + "amount to use for estimation": "cantidad a usar para la estimación", + "approval type": "tipo de aprobación", + "approved": "aprobado", + "asset id": "id del activo", + "auto-selected targets for nomination": "candidatos auto seleccionados para la nominación", + "available": "disponible", + "available signatories": "firmantes disponibles", + "available social recovery helpers": "asistentes de recuperación social disponible", + "average": "promedio", + "aye": "si", + "aye: bool": "Si: bool", + "backing": "respaldo", + "backup file": "archivo de recuperación", + "balance": "balance", + "balances": "balance", + "beneficiary": "beneficiario", + "best": "mejor", + "best #": "mejor #", + "best block": "Mejor bloque", + "best hash": "mejor hash", + "bids": "pujas", + "block hash or number to query": "hash del bloque o número en la lista", + "blocks": "bloques", + "bond": "vínculo", + "bonded": "vinculado", + "call from account": "llamada desde la cuenta", + "call the selected endpoint": "llamar al punto final seleccionado", + "candidate account": "cuenta del candidato", + "candidate accounts": "cuentas candidatas", + "candidates": "candidatos", + "case sensitive": "distingue mayúsculas y minúsculas", + "challenge": "reto", + "claim to account": "reclamar a cuenta", + "click to copy": "haga click para copiar", + "click to select or drag and drop a JSON ABI file": "haga clic para seleccionar o arrastrar y soltar un archivo ABI JSON", + "click to select or drag and drop the file here": "haga clic para seleccionar o arrastrar y soltar el archivo aquí", + "clipboard": "portapapeles", + "code": "código", + "code for this contract": "código para este contrato", + "code hash": "hash del código", + "commission": "comisión", + "compiled contract WASM": "contrato compilado en WASM", + "connected peers": "pares conectados", + "contacts": "contactos", + "contract address": "dirección del contrato", + "contract to use": "contrato a usar", + "controller": "controller", + "controller account": "cuenta controller", + "conviction": "sentencia", + "conviction: Conviction": "convicción: Convicción", + "copied": "copiado", + "council candidates": "candidatos al consejo", + "council proposal": "propuesta del consejo", + "council proposal type": "tipo de propuesta del consejo", + "created account": "cuenta creada", + "created multisig": "multisig creada", + "crypto type to use": "typo de crypto a usar", + "custom endpoint": "endpoint personalizado", + "data": "dato", + "default icon theme": "tema del icono por defecto", + "default interface language": "lenguaje del interfaz por defecto", + "defender": "defensor", + "delay": "retraso", + "democracy": "democracia", + "deploy": "despliegue", + "deployment account": "cuenta para el despliegue", + "deposit": "depósito", + "derivation path": "ruta de procedencia", + "derive root account": "derivar la cuenta raíz", + "details": "detalles", + "development seed": "semilla de desarrollo", + "dispatch queue": "cola de envío", + "display name": "nombre de visualización", + "do not include empty strings in the generated file": "no incluir cadenas vacías en el archivo generado", + "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": "no parece tener una reclamación válida. Por favor, compruebe que ha firmado la transacción correctamente en la cuenta correcta de ETH.", + "ed25519, Edwards": "ed25519, Edwards", + "elected stake": "stake elegido", + "email": "correo electrónico", + "enact": "promulgar", + "endowment": "donación", + "epoch": "época", + "era": "era", + "era points": "puntos era", + "era {{era}}, {{count}} slashes": "era {{era}}, recortes {{count}}", + "eras": "eras", + "events": "eventos", + "everything": "todo", + "exclude option": "excluir opción", + "exclude value": "excluir valor", + "execute": "ejecutar", + "existential deposit": "depósito existencial", + "external": "externo", + "extrinsic hash": "hash del extrinsic", + "extrinsics": "extrinsics", + "fee": "comisión", + "filter by name or tags": "filtrar por nombre o etiquetas", + "filter by name, address or index": "filtrar por nombre, dirección o índice", + "filter by name, address, or account index": "filtrar por nombre, dirección o índice de cuenta", + "finalized": "finalizado", + "finder": "buscador", + "forks": "bifurcaciones", + "free balance": "balance libre", + "from the following data": "desde los siguientes datos", + "generated public key": "clave pública generada", + "has a valid claim for": "tiene una reclamación válida para", + "hash": "hash", + "heads": "cabeceras", + "hex input data": "dato hexadecimal introducido", + "hex-encoded storage key": "llave de almacenamiento con codificación hexadecimal", + "identity": "identidad", + "imminent preimage (proposal already passed)": "preimagen inminente (propuesta ya aprobada)", + "immortal": "inmortal", + "include all empty strings in the generated file": "incluir todas las cadenas vacías en el archivo generado", + "include option": "incluir opción", + "include value": "incluye valor", + "index": "índice", + "initial head state": "Estado inicial de la cabecera", + "intentions": "intenciones", + "interface operation mode": "modo de operación de la interfaz", + "invalid ABI file selected": "archivo ABI seleccionado inválido", + "invalid/unknown registrar account": "cuenta de registro inválida/desconocida", + "judgement": "juicio", + "key type to set": "tipo de clave a establecer", + "keypair crypto type": "par de claves criptográficas", + "kind": "clase", + "last #": "último #", + "last block": "último bloque", + "last reward": "última recompensa", + "launch period": "período de lanzamiento", + "legal name": "nombre legal", + "lifetime": "tiempo de vida", + "lng.detect": "Idioma predeterminado del navegador (detección automática)", + "lock expired": "bloqueo expirado", + "locked": "bloqueado", + "locked balance": "balance bloqueado", + "logs": "logs", + "manage hardware connections": "manejar conexiones hardware", + "matches": "coincidencias", + "maximum gas allowed": "máximo de gas permitido", + "members": "miembros", + "message to send": "mensaje para enviar", + "minimum bond": "vinculación mínima", + "minimum deposit": "depósito mínimo", + "mnemonic seed": "semilla mnemotécnica", + "mortal, valid from #{{startAt}} to #{{endsAt}}": "mortal, válido desde #{{startAt}} hasta #{{endsAt}}", + "motions": "mociones", + "multisig": "multisig", + "multisig name": "nombre multisig", + "my ordered votes": "mis votos ordenados", + "name": "nombre", + "nav.accounts": "Cuentas", + "nav.claims": "Reclamar Tokens", + "nav.contracts": "Contratos", + "nav.council": "Consejo", + "nav.dashboard": "Panel de control", + "nav.democracy": "Democracia", + "nav.explorer": "Explorador", + "nav.extrinsics": "Extrinsics", + "nav.generic-asset": "Activo genérico", + "nav.github": "Github", + "nav.i18n": "Traductor I18n", + "nav.js": "Javascript", + "nav.parachains": "Parachains", + "nav.settings": "Ajustes", + "nav.society": "Sociedad", + "nav.staking": "Participación", + "nav.storage": "Estado de la cadena", + "nav.sudo": "Sudo", + "nav.tech-comm": "Tech. comm.", + "nav.toolbox": "Caja de herramientas", + "nav.transfer": "Transferencia", + "nav.treasury": "Tesorería", + "nav.wiki": "Wiki", + "nay": "no", + "new account": "nueva cuenta", + "new address": "nueva dirección", + "next": "siguiente", + "next id": "siguiente ID", + "no": "no", + "no accounts yet, create or import an existing": "no hay cuentas aún, cree o importe un existente", + "no addresses saved yet, add any existing address": "no hay direcciones almacenadas aún, añada cualquier dirección existente", + "no messages": "no hay mensajes", + "no peers connected": "no hay pares conectados", + "no tags": "sin etiquetas", + "no unapplied slashes found": "no se encontraron recortes no aplicados", + "nominated accounts": "cuentas nominadas", + "nominators": "nominadores", + "not signed": "sin firmar", + "on-chain bonding duration": "duración de la vinculación en cadena", + "ongoing referendum": "referéndum en curso", + "only this network": "sólo esta red", + "origin": "principio", + "other stake": "otro stake", + "our best": "el mejor", + "own stake": "stake propio", + "parachain id": "id de la parachain", + "parachains": "parachains", + "parent": "pariente", + "password": "contraseña", + "password (repeat)": "contraseña (repita)", + "payment": "pago", + "payment destination": "destino del pago", + "payout stakers for (multiple)": "pago de los participantes (multiple)", + "payout stakers for (single)": "pago de los participantes (único)", + "payout/stash": "pago/stash", + "payout/validator": "pago/validador", + "peer best": "mejor par", + "pending extrinsics": "extrinsics pendientes", + "pending hashes": "hashes pendientes", + "pending swap id": "pendiente del ID de intercambio", + "points": "puntos", + "pot": "espacio", + "preimage hash": "hash de la preimgaen", + "prev": "previo", + "prime member": "primer miembro", + "prime voter": "votante principal", + "priority {{index}}": "prioridad {{index}}", + "profit/era est": "beneficio/era est", + "proposal": "propuesta", + "proposal bond": "vinculación de la propuesta", + "proposal hash": "hash de la propuesta", + "proposals": "propuestas", + "propose": "proponer", + "propose from account": "proponer desde la cuenta", + "proposed by": "propuesto por", + "proposer": "autor", + "queued tx": "tx en lista", + "reason": "razón", + "recent blocks": "bloques recientes", + "recent events": "eventos recientes", + "recover this account": "recuperar esta cuenta", + "recovery block delay": "retraso en el bloque de recuperación", + "recovery threshold": "umbral de recuperación", + "redeemable": "canjeable", + "referenda": "referéndum", + "refresh in": "refrescar en", + "registrar account": "cuenta de registro", + "registrar index": "índice de registro", + "relay dispatch queue": "lista de envío de relay", + "remaining": "pendiente", + "remote node/endpoint to connect to": "endpoint/nodo remoto para conectarse", + "request payout from": "solicitar el pago de", + "reserved": "reservado", + "reward commission percentage": "porcentaje de comisión de recompensa", + "rewards": "recompensas", + "rewards & slashes": "recompensas y penalizaciones", + "riot": "riot", + "riot name": "nombre de riot", + "role": "rol", + "rotation": "rotación", + "rpc.arcadia": "Arcadia (Testnet Nodle, ofrecido por Nodle)", + "rpc.berlin": "Berlin (Testnet Edgeware, ofrecido por Commonwealth Labs)", + "rpc.custom": "Entorno personalizado", + "rpc.edgeware": "Edgeware (red principal Edgeware, ofrecido por Commonwealth Labs)", + "rpc.flamingfir": "Flaming Fir (Testnet Substrate, ofrecido por Parity)", + "rpc.header.dev": "Desarrollo", + "rpc.header.live": "Redes en funcionamiento", + "rpc.header.test": "Redes de prueba", + "rpc.kulupu": "Kulupu (Red principal Kulupu, ofrecido por Kulupu)", + "rpc.kusama.ava": "Kusama (Red canaria de Polkadot, nodos públicos gestionados por usuarios; consulte https://status.cloud.ava.do/)", + "rpc.kusama.parity": "Kusama (Red canaria de Polkadot, ofrecido por Parity)", + "rpc.kusama.w3f": "Kusama (Red canaria de Polkadot, ofrecido por la fundación Web3)", + "rpc.local": "Nodo local (propio, 127.0.0.1:9944)", + "rpc.mandala": "Mandala (Acala Testnet, hosted by Acala)", + "rpc.westend": "Westend (Testnet de Polkadot, ofrecido por Parity)", + "runners up": "primeros puestos", + "scheduling": "planificación", + "seats": "puestos", + "second with account": "apoyar con la cuenta", + "secret": "secreto", + "secret derivation path": "ruta secreta de procedencia", + "seed (hex or string)": "semilla (hexadecimal o secuencia)", + "select the account you wish to sign data with": "seleccionar la cuenta que desea para firmar el dato", + "selected constant query": "consulta constante seleccionada", + "selected signatories": "firmantes seleccionados", + "selected state query": "consulta de estado seleccionado", + "selected validators": "validadores elegidos", + "send": "enviar", + "send as RPC call": "enviar como una llamada a RPC", + "send as transaction": "enviar como una transacción", + "send from account": "enviar desde la cuenta", + "send to address": "enviar a la dirección", + "sending account": "enviando cuenta", + "sending from my account": "enviando desde mi cuenta", + "session": "sesión", + "session keys": "claves de sesión", + "session next": "siguiente sesión", + "sign data from account": "firmar dato desde la cuenta", + "sign the following data": "firmar el siguiente dato", + "signatories": "firmantes", + "signatory": "firmante", + "signature crypto type": "tipo de firma criptográfica", + "signature of supplied data": "firma de los datos suministrados", + "signer": "firmante", + "society head": "cabecera de la sociedad", + "spend period": "período de gasto", + "sr15519, Schnorrkel": "sr15519, Schnorrkel", + "ss58.centrifuge": "Centrifuge (en vivo)", + "ss58.default": "Por defecto para el nodo conectado", + "ss58.edgeware": "Edgeware (en vivo)", + "ss58.kusama": "Kusama (canary)", + "ss58.polkadot": "Polkadot (en vivo)", + "ss58.substrate": "Substrate (genérica)", + "staked": "staked", + "stash account": "cuenta stash", + "stashes": "stashes", + "state": "estado", + "strikes": "huelga", + "submit the following change": "enviar el siguiente cambio", + "submit the following extrinsic": "enviar el siguiente extrinsic", + "submit with account": "enviar con la cuenta", + "submit with council account": "presentar con la cuenta del consejo", + "sudo key": "llave sudo", + "sudo with unchecked weight parameter": "sudo con un parámetro de valor no comprobado", + "sudo without unchecked weight parameter": "sudo sin parámetro de valor controlado", + "suri (seed & derivation)": "suri (semilla y derivación)", + "swap to id": "intercambio de id", + "syncing": "sincronizando", + "system events": "eventos del sistema", + "tags": "etiquetas", + "target": "meta", + "term progress": "progreso del término", + "the account to make recoverable": "la cuenta para que será recuperable", + "the account to recover to": "la cuenta para recuperar", + "the associated controller": "el controller asociado", + "the era to cancel for": "la era para cancelar por", + "the language to display translations for": "el idioma para mostrar las traducciones", + "the module to display strings for": "módulo para mostrar las cadenas de", + "the resulting hash is": "el hash resultante es", + "the stash account to nominate with": "la cuenta stash para nominar con", + "the supplied signature": "la firma suministrada", + "threshold": "umbral", + "tip": "propina", + "tip reason": "razón de la propina", + "tip value": "valor de la propina", + "tips": "consejos", + "total": "total", + "total issuance": "emisión total", + "total peers": "pares totales", + "total stake": "stake total", + "total staked": "staked total", + "transactions": "transacciones", + "transfer received": "transferencia recibida", + "transferrable": "transferible", + "trusted social recovery helpers": "asistentes de confianza para la recuperación social", + "turnout": "participación", + "twitter": "twitter", + "type": "tipo", + "unbond amount": "desvincular cantidad", + "unbonding": "desvincular", + "unchecked weight for this call": "valor sin control para esta llamada", + "unlock account with password": "desbloquear la cuenta con la contraseña", + "unstake threshold": "umbral de separación", + "update on #{{index}}": "actualización en #{{index}}", + "upgradable extensions": "extensiones actualizables", + "use on any network": "usar en cualquier red", + "using my account": "usar mi cuenta", + "using the following data": "usando el siguiente dato", + "using the selected account": "usando la cuenta seleccionada", + "validator to query": "validador para consultar", + "validators": "validadores", + "validators/nominators": "validadores/nominadores", + "value": "valor", + "value bonded": "valor vinculado", + "verify using address": "verificar usando la dirección", + "version {{version}}": "versión {{version}}", + "vested": "establecido", + "via Council/Vote": "a través del Consejo/Voto", + "via Democracy/Vote": "a través de Democracia/Voto", + "via Staking/Bond": "a través de Staking/Vinculación", + "via Vesting": "a través de la adquisición", + "vote for candidate": "voto para el candidato", + "vote for defender": "voto por el defensor", + "vote from account": "voto desde la cuenta", + "vote value": "valor total", + "vote with account": "votar con la cuenta", + "votes": "votos", + "voting account": "cuenta de votación", + "voting balance": "balance de la votación", + "voting end": "fin de la votación", + "voting period": "período de votación", + "voting round": "ronda de votación", + "waiting": "esperando", + "web": "web", + "website": "sitio web", + "with an index of": "con el índice de", + "wrong password supplied": "contraseña incorrecta suministrada", + "yes": "si", + "your current password": "su contraseña actual", + "your new password": "su nueva contraseña", + "{{blocks}} blocks": "{{blocks}} bloques", + "{{count}} key/value pairs encoded for submission": "{{count}} pares clave/valor codificados para su entrega", + "{{count}} own stashes": "{{count}}", + "{{currency}} average": "media {{currency}}", + "{{currency}} clipped": "{{currency}} recortado", + "{{currency}} rewards": "{{currency}} ganancias", + "{{currency}} slashed": "{{currency}} recortados", + "{{currency}} total": "{{currency}} total", + "{{done}}/{{total}}, {{progress}}% done": "{{done}}/{{total}}, {{progress}}% terminado", + "{{d}} days": "{{d}} días", + "{{h}} hrs": "{{h}} horas", + "{{m}} mins": "{{m}} minutos", + "{{name}} ({{size}} bytes)": "{{name}} ({{size}} bytes)", + "{{percentage}} aye": "{{percentage}} si", + "{{relayDispatchQueueSize}} dispatch messages pending": "{{relayDispatchQueueSize}} enviar mensajes pendientes", + "{{step}}. Sign with your ETH address": "{{step}}. Firme con su dirección de ETH", + "{{s}} s": "{{s}} segundos", + "{{threshold}}, not passing": "{{threshold}}, no pasará", + "{{threshold}}, passing": "{{threshold}}, de aprobación", + "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}}x saldo de votos, bloqueado por {{lock}}x sentencia ({{period}} días." +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/index.json b/pioneer/packages/apps/public/locales/index.json new file mode 100644 index 0000000000..1e2c2f8657 --- /dev/null +++ b/pioneer/packages/apps/public/locales/index.json @@ -0,0 +1,10 @@ +[ + "ar", + "en", + "es", + "ja", + "ko", + "pt", + "ru", + "zh" +] \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/ja/translation.json b/pioneer/packages/apps/public/locales/ja/translation.json new file mode 100644 index 0000000000..a91fcbbb7e --- /dev/null +++ b/pioneer/packages/apps/public/locales/ja/translation.json @@ -0,0 +1,565 @@ +{ + "//hard///password": "//ハード///パスワード", + "//hard/soft": "//ハード/ソフト", + "//hard/soft///password": "//ハード/ソフト///パスワード", + "1 day": "1日", + "1 hr": "1時間", + "1 min": "1分", + "1 s": "1秒", + "1. Select your {{chain}} account": "{{chain}} を選択する", + "2. Enter the ETH address from the sale.": "セールに参加したETHアドレスを入力する", + "2. Sign ETH transaction": "ETHトランザクションにサインする", + "": "<空欄>", + "": "", + "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": "デプロイされた、またはアタッチされたコントラクト。アドレスとABIは、パラメータを構築するために使用されます。", + "ABI": "ABI", + "Account balance:": "アカウント残高", + "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.": "外部ツールから挿入されたアカウントがここで表示され利用できます。上のリストは外部ツールが更新されるに従ってアップデートされます。", + "Add account": "アカウントを追加する", + "Add account via Qr": "QRを追加する", + "Add an account via seed": "シード経由でアカウントを追加する", + "Add an address": "アドレスを追加する", + "Add an existing code hash": "既存のコードハッシュを追加する", + "Add an existing contract": "既存のコントラクトを追加する", + "Add contact": "コントラクトを追加する", + "Add identity judgment": "アイデンティティーを追加する", + "Add item": "アイデムを追加", + "Add multisig": "マルチシグを追加する", + "Add via Qr": "QRを追加する", + "Add via backup file": "バックアップファイルを追加する", + "Additional types as a JSON file (or edit below)": "追加の型が定義されたJSONファイル (もしくは下で編集してください)", + "Address Prefix": "アドレスプレフィックス", + "Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)": "通常モード(初心者向け機能のみ)か全機能モード(初心者向け+上級者向け機能)かを設定します", + "Advanced creation options": "追加オプション", + "An URL that is linked to this identity.": "アイデンティティーと紐づくURL", + "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.": "ダウンロードのボタンを押すと暗号化されたバックアップファイルが作成されます。このファイルは異なるツールで鍵をインストールするときに使われます。", + "Approvals": "承認", + "Approve or reject this defender.": "このディフェンダーを承認するか拒否するか", + "Approve this call hash": "このコールハッシュを承認する", + "Approve this candidacy.": "この候補を承認する", + "Asset ID": "アセットID", + "Assets": "アセット", + "Authorize transaction": "認証されたトランザクション", + "Aye": "はい", + "Aye, I approve": "はい、承認します", + "Backup account": "バックアップアカウント", + "Block details": "ブロックの詳細", + "Call": "コール", + "Call a contract": "コントラクトをコールする", + "Call a method on this contract": "このコントラクトのメソッドをコールする", + "Call results": "結果をコールする", + "Call this message": "このメッセージをコールする", + "Cancel slashes": "スラッシュを削除する", + "Cancel this call hash": "このコールハッシュを拒否する", + "Chain info": "チェーン情報", + "Chain specifications": "チェーンの詳細", + "Change": "変更", + "Change account password": "アカウントのパスワードを変更", + "Change this account's password": "このアカウントのパスワードを変更", + "Claim": "取得", + "Claim tokens...": "トークンを取得する", + "Clear all": "全てをコールする", + "Clear expired democracy locks": "期限切れのデモクラシーのロックを精算する", + "Close": "閉じる", + "Close proposal": "ポータルを閉じる", + "Color": "カラー", + "Confirm ABI removal": "ABIが取り除かれたのを確認する", + "Confirm claim": "取得を確認する", + "Constants": "定数", + "Continue": "続ける", + "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "上記の文字列をコピーし、その文字列をペイロードとして使用して、プレセール時に使用したアカウントでEthereumの取引を任意のウォレットに署名し、下記の取引署名オブジェクトを貼り付けます。", + "Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:": "以下の文字列をコピーして、その文字列をペイロードとして、プレセール時に使用したEthereumアカウントで任意のウォレットに署名し、以下のトランザクション署名オブジェクトを貼り付けます。", + "Council": "カウンシル", + "Council overview": "カウンシル概要", + "Create": "作成", + "Create a backup file for this account": "このアカウントのバックアップファイルを作成する", + "Create an account now.": "アカウントを作成する", + "Create and backup account": "アカウントのバックアップを作成する", + "Current prime member, default voting": "現在のプライムメンバー、デフォルトの投票", + "Decimals": "デシマル", + "Decimals decides the smallest unit of the token, which is 1/10^decimals": "小数はトークンの最小単位を決定します。", + "Delete this custom example": "このカスタム例を削除します。", + "Deploy": "デプロイ", + "Deploy a code hash": "コードハッシュをデプロイする", + "Deploy this code hash as a smart contract": "スマートコントラクトとしてこのコードハッシュをデプロイする", + "Deploy with this constructor": "このコンストラクターを一緒にデプロイする", + "Deposit": "デポジット", + "Deregister this parachain": "このパラチェーンの登録を解除する", + "Derive account from pair": "ペアーからアカウントを派生させる", + "Derive account via derivation path": "アカウントを分岐させる", + "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".": "このアカウントを作るために使われた暗号を確認する。セッションアカウントは\"ed25519\"を使わなければなりません。", + "Developer": "ディベロッパー", + "Development": "開発", + "Download": "ダウンロード", + "Either approve or reject this call.": "このコールを承認or拒否する", + "Enter the Asset ID of the token you want to manage.": "管理したいトークンのアセットIDを入力します。", + "Enter the Asset ID of the token you want to transfer.": "転送するトークンのアセットIDを入力します。", + "Erroneous": "不正確", + "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)": "{{elapsed}}s ({{avg}} keys/s) の中の {{count}} 鍵を評価する", + "Extensions": "エクステンション", + "External": "外部の", + "Extrinsic submission": "Extrinsic提出", + "Forget this account": "このアカウントを忘れました", + "Forget this address": "このアドレスを忘れました", + "Forget this asset": "アセットを忘れました", + "Forget this code hash": "このコードハッシュを忘れました", + "Forget this contract": "このコントラクトを忘れました", + "Forks": "フォーク", + "General": "一般", + "Generate {{lng}}/translation.json": "{{lng}}/translation.json を生成する", + "Genesis Hash": "ジェネシスハッシュ", + "Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched": "ジェネシスハッシュはチェーンの初期状態を指し、チェーンが起動すると変更できません。", + "Hash data": "ハッシュデータ", + "I agree": "同意する", + "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.": "受信者のアカウントが新しい場合、残高が外部の預金高より高い必要があります。同様に、もしアカウント残高がこの値より低い場合、アカウントは取り除かれます。", + "If you are moving accounts between applications, ensure that you use the correct type.": "もしアプリケーション間のアカウントで行き来している場合、正しい型を使用しているか注意してください。", + "Important notice": "重要な事項", + "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.": "選挙結果を算出する際には、この優先順位をつけた投票順で、候補者の最終的な得点を決定することになります。", + "Initializing connection": "接続を初期化する", + "Initiate account recovery": "アカウントのリカバリーを初期化する", + "Initiate recovery for another": "他の人のためにリカバリーを初期化する", + "Injected": "挿入された", + "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -": "このアプリから", + "Judge": "ジャッジ", + "Known good": "良いと知られている", + "Learn more...": "もっと学ぶ....", + "Loading": "ローディング", + "Locked1x": "Locked1x", + "Locked2x": "Locked2x", + "Locked3x": "Locked3x", + "Locked4x": "Locked4x", + "Locked5x": "Locked5x", + "Locked6x": "Locked6x", + "Low quality": "低いクオリティー", + "Make Transfer": "送信を作成", + "Make recoverable": "リカバリー可能にする", + "Manage your connection to Ledger S": "Ledger Sへの接続を管理します", + "Message data": "メッセージデータ", + "Message origin.": "メッセージの起源", + "Messages": "メッセージ", + "Metadata {{count}}": "メタデータ {{count}}", + "Mnemonic": "ニーモニック", + "Most recent head data": "最新のヘッドデータ", + "Motions ({{count}})": "モーション({{count}})", + "Multisig": "マルチシグ", + "Multisig approvals": "マルチシグの承認", + "Mutates contract state": "コントラクトの状態を変える", + "My accounts": "アカウント", + "My contacts": "コントラクト", + "Name given to this account. You can change it at any point in the future.": "このアカウントに与えられた名前。将来的にはいつでも変更することができます。", + "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".": "このアカウントに与えられた名前。編集することができます。このアカウントを使用して検証や指名を行うには、アカウント名にそのアカウントの機能を追加するのが良い方法です。(例:\"name_you_want - stash\".)", + "Name given to this multisig. You can edit it at any later point in time.": "このマルチシグに与えられた名前。後からでも編集することができます。", + "Name of the network. It is only for display purposes.": "ネットワークの名前。これは表示をする目的のみで使用されます。", + "Name your example": "あなたの例に名前をつけます。", + "Nay": "いいえ", + "Network Name": "ネットワーク名", + "No": "いいえ", + "No assets found.": "アセットが見当たりません", + "No blocks available": "使用できるブロックがありません", + "No candidates found": "候補者がいません", + "No code hashes available": "使用可能なコードハッシュがありません", + "No contracts available": "使用可能なコントラクトがありません", + "No council motions": "カウンシルモーションがありません", + "No documentation provided": "ドキュメンテーションが提示されていません", + "No events available": "使用できる有効なイベントがありません", + "No judgments": "ノー・ジャッジメント", + "No logs available": "使用できるログがありません", + "No matches found": "該当するものがありませんでした", + "No members found": "メンバーがいませんでした", + "No pending extrinsics are in the queue": "このキューにはペンディング中のextrinsics がありません", + "No runners up found": "次点は見つかりませんでした", + "Node info": "ノード情報", + "None": "なし", + "Not updated in the last block": "最後のブロックにはアップデートされていません", + "Override the default identity icon display with a specific theme": "アイコンのテーマを設定します", + "Override the default ss58 prefix for address generation": "ss58を基準とするアドレスを生成するときに使われる接頭辞の値を上書きします", + "Owned": "所有", + "Parachains overview": "パラチェーンの概要", + "Paste here the address of the contact you want to add to your address book.": "アドレス帳に追加したい連絡先のアドレスをここに貼り付けてください。", + "Pending call hashes": "ペンディングコールハッシュ", + "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.": "このファイルは、アカウントを復元するためにパスワードと一緒に必要なので、必ず安全な場所に保存してください。", + "Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:": "本規約をよくお読みください。本ステートメントを送信することで、本規約に同意したものとみなされます。これらの条件に同意しない場合は、アクセスまたは先に進むことをお控えください。また、以下のサイトにも掲載されています", + "Positive number between 1 and {{memberCount}}": "1から {{memberCount}} 間の整数です", + "Positive number greater than or equal to {{nextFreeId}}": "{{nextFreeId}}よりも大きいか同じ整数値", + "Pre-sale ethereum address": "プレセールのEthereumアドレス", + "Propose": "提案", + "Propose a council motion": "カウンシルモーションを提出する", + "Provide judgement": "ジャッジメントを提供する", + "Provide the account QR from the module/external application for scanning. One detected as valid, you will be taken to the next step to add the account to your list.": "スキャンするためにモジュール/外部アプリケーションからアカウントのQRを提供します。有効として検出された場合、次のステップに移行しあなたのリストにアカウントが追加されます。", + "Query Ledger": "クエリーレッジャー", + "RPC calls": "RPCコール", + "Raw seed": "生のシード", + "Raw storage": "生のストレージ", + "Reasonable": "適切な", + "Reassign": "再度アサインする", + "Redeem": "再取得", + "Register": "登録", + "Register Asset": "アセットを登録する", + "Register a parachain": "パラチェーンを登録する", + "Register an Asset": "アセットを登録する", + "Register identity": "アイデンティティを登録する", + "Remove": "取り除く", + "Remove ABI": "ABIを取り除く", + "Remove item": "アイテムを取り除く", + "Reset": "リセット", + "Restore": "復元する", + "Restore JSON": "JSONを復元する", + "Save": "保存", + "Save & Reload": "保存して更新", + "Save snippet to local storage": "ローカルのストレージに一部を保存します。", + "Save the type definitions for your custom structures as key-value pairs in a valid JSON file. The key should be the name of your custom structure and the value an object containing your type definitions.": "カスタム構造体のための型定義をキー・値の対となるように有効なJSONファイルに保存します。キーにはカスタム構造体の名前を、値には型定義が入ったオブジェクトを設定してください。", + "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.": "このバックアップファイルを安全な場所に保存してください。さらに、アカウントを復元するには、このバックアップファイルと一緒に、このアカウントに関連付けられたパスワードが必要です。", + "Saved": "セーブ", + "Search for": "検索する", + "Select Network": "ネットワークを選択する", + "Select a contact or paste the address you want to send funds to.": "連絡先を選択するか、送金先のアドレスを貼り付けてください。", + "Select example": "例を選択します。", + "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.": "アカウントを作成した際にダウンロードしたJSONキーファイルを選択します。このJSONファイルには、パスワードで暗号化された秘密鍵が含まれています。", + "Select the account you wish to recover into this account.": "このアカウントに復元したいアカウントを選択します。", + "Select the remote endpoint, either from the dropdown on manual entered via the custom toggle": "ドロップダウンの中からリモートノードを選択するかカスタムエンドポイントをオンにして記入します", + "Send funds": "資金を送る", + "Send funds from this account": "このアカウントから資金を送る", + "Send funds to this address": "このアカウントに資金を送る", + "Set Identity": "アイデンティティをセットする", + "Set on-chain identity": "オンチェーンアイデンティティをセットする", + "Set sudo key": "sudoキーをセットする", + "Setup account as recoverable": "アカウントを復元可能にセットする", + "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...": "大文字小文字を区別して検索する必要があります。例えば、\"no \"を選択した場合、\"Some \"を検索すると、\"some \"または \"sOme \"を含むアドレスが返されるかもしれません...", + "Show address on hardware device": "ハードウェアデバイスのアドレスを表示する", + "Sign message": "メッセージに署名する", + "Signed transaction": "署名されたトランザクション", + "Since the multisig function like any other account, once created it is available for selection anywhere accounts are used and needs to be funded before use.": "マルチシグは他のアカウントと同様に機能するため、一度作成されたアカウントはどこでも使用可能で、使用する前に資金を調達する必要があります。", + "Society": "ソサエティー", + "Specify the user account to use for this contract call. And fees will be deducted from this account.": "このコントラクトコールで使用するユーザーアカウントを指定します。そして、手数料はこのアカウントから差し引かれます。", + "Specify the user account to use for this deployment. And fees will be deducted from this account.": "このデプロイに使用するユーザー アカウントを指定します。このアカウントから手数料が差し引かれます。", + "Specify the user account to use for this deployment. Any fees will be deducted from this account.": "このデプロイに使用するユーザー アカウントを指定します。料金はこのアカウントから差し引かれます。", + "Start generation": "生成を始める", + "Start recovery": "復元を始める", + "Stop generation": "生成をストップする", + "Storage": "ストレージ", + "Submit (no signature)": "提出する(署名なし)", + "Submit RPC call": "RPCコールを提出する", + "Submit Sudo": "Sudoを提出する", + "Submit Transaction": "トランザクションを提出", + "Submit Unsigned": "「署名なし」を提出", + "Sudo access": "Sudoアクセス", + "Sudo key": "Sudo キー", + "Supply a backed-up JSON file, encrypted with your account-specific password.": "アカウント固有のパスワードで暗号化されたバックアップJSONファイルを提供してください。", + "Technical committee": "テクニカルコミッティー", + "Test account": "テストアカウント", + "The ABI for the WASM code. In this step it is optional, but setting it here simplifies the setup of contract instances.": "WASM コードの ABI。このステップは省略可能ですが、ここで設定すると、コントラクト インスタンスのセットアップが簡単になります。", + "The ABI for the WASM code. Since we will be making a call into the code, the ABI is required and stored for future operations such as sending messages.": "WASMコードのABI。コードを呼び出すことになるので、メッセージの送信などの将来の操作のために ABI が必要であり、保存されています。", + "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.": "アカウントの作成時に指定したアカウント パスワード。これは、バックアップファイルを暗号化し、その後アカウントを復元する際に復号化するために使用されます。", + "The account you want to claim to.": "取得をしたいアカウント", + "The account you will send funds from.": "資金を送るアカウント", + "The address for the deployed contract instance.": "コントラクトのインスタンスをデプロイするために必要なアドレス", + "The addresses that are able to approve multisig transactions. You can select up to {{maxHelpers}} trusted addresses.": "マルチシグのトランザクションを承認できるアドレスです。信頼できるアドレスは{{{maxHelpers}}}つまで選択できます。", + "The addresses that are able to help in recovery. You can select up to {{maxHelpers}} trusted helpers.": "復元に協力できるアドレスです。信頼できるヘルパーは{{maxHelpers}}つまで選択できます。", + "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": "このコントラクトのために割り当てられた寄附金、すなわち、インスタンス化時にコントラクトに転送された金額。", + "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": "このコントラクトに割り当てられた値、すなわち、この呼び出しの一部としてコントラクトに転送された金額。", + "The beneficiary will have access to the transferred fees when the transaction is included in a block.": "受益者は、取引がブロックに含まれている場合、譲渡された手数料にアクセスすることができます。", + "The call hashes that have not been executed as of yet.": "まだ実行されていないコールハッシュ。", + "The code hash for the on-chain deployed code.": "オンチェーンにデプロイされたコードハッシュ", + "The code is not recognized as being in valid WASM format": "コードが有効な WASM 形式であると認識されません。", + "The codeHash is not a valid hex hash": "コードハッシュが有効なhexハッシュではありません", + "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": "デプロイしたいコントラクト用にコンパイルされた WASM。各固有のコードブロブには、新しいインスタンスを作成するために使用できるコードハッシュが添付されます。", + "The compiled runtime WASM for the parachain you wish to register.": "登録したいパラチェーンのコンパイル済みランタイムWASM。", + "The compiled runtime WASM for this parachain.": "このパラチェーンのコンパイル済みランタイム WASM。", + "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.": "以前にデプロイしたコントラクト WASM。内部的には、これはコードのハッシュで識別され、作成されたか添付されたかのどちらかになります。", + "The delay between vouching and the availability of the recovered account.": "記録されたアカウントの可用性と証明間の遅延", + "The deployment constructor information for this contract, as provided by the ABI.": "ABI が提供するこの契約のデプロイメントコンストラクタ情報。", + "The derivation path allows you to create different accounts from the same base mnemonic.": "派生パスでは、同じベースのニーモニックから異なるアカウントを作成することができます。", + "The email address associated with this identity.": "このアイデンティティーに紐づくemailアドレス", + "The existing account password as specified when this account was created or when it was last changed.": "このアカウントが作成されたとき、または最後に変更されたときに指定された既存のアカウントのパスワード。", + "The helpers should be able to verify, via an off-chain mechanism, that the account owner indeed wishes to recover access and as such provide any approvals. In the cases of malicious recovery procedures, they will have the power to stop it.": "ヘルパーは、アカウントの所有者が本当にアクセスの回復を望んでいるかどうかを、オフチェーンのメカニズムを介して検証し、そのような承認を提供することができるはずです。悪意のある回復手順の場合、彼らはそれを止める力を持つことになります。", + "The id number to assign to this parachain.": "このパラチェーンに割り当てるID番号。", + "The id of the parachain to be deregistered.": "登録解除されるパラチェーンのID。", + "The initial head state for the parachain.": "パラチェーンの初期ヘッド状態。", + "The initial head state is invalid.": "初期のヘッド状態は無効です。", + "The legal name for this identity.": "アイデンティティのためのリーガルネーム", + "The local name for this account. Changing this does not affect your on-line identity, so this is only used to indicate the name of the account locally.": "このアカウントのローカル名。これを変更してもオンラインIDには影響しないので、これはローカルでアカウントの名前を示すためにのみ使用されます。", + "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": "この呼び出しで使用できるガスの最大量。コードがそれ以上の量を必要とする場合、この呼び出しは失敗します。", + "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": "このデプロイで使用できるガスの最大量は、コードがそれ以上必要な場合、デプロイは失敗します。", + "The maximum amount of gas to use for this contract call. If the call requires more, it will fail.": "このコントラクトコールで使用するガスの最大量です。それ以上の量を必要とする場合、コールは失敗します。", + "The message to send to this contract. Parameters are adjusted based on the ABI provided.": "このコントラクトに送信するメッセージ。パラメータは、提供されたABIに基づいて調整されます。", + "The minimum amount that an account should have to be deemed active": "アカウントがアクティブであるとみなされるために必要な最低金額。", + "The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.": "このアカウントの名前と、それがあなたのアドレスの下にどのように表示されるか。オンチェーンのIDがあれば、他の人が利用できるようにすることができます。", + "The name is for unique identification of the account in your owner lists.": "この名前は、所有者リストの中でこのアカウントを一意に識別するためのものです。", + "The name that will be displayed in your accounts list.": "アカウントリストに表示される名前です。", + "The new account password. Once set, all future account unlocks will be performed with this new password.": "新しいアカウントのパスワード。一度設定すると、今後のすべてのアカウントのロック解除は、この新しいパスワードで実行されます。", + "The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.": "このアカウントのパスワードとパスワードの確認。これは、行われたすべてのトランザクションを認証し、キーペアを暗号化するために必要です。", + "The password previously used to encrypt this account.": "以前にこのアカウントの暗号化に使用したパスワード。", + "The password to unlock the selected account.": "選択したアカウントのロックを解除するためのパスワード。", + "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.": "アカウントの秘密鍵はこのシードから取得します。このシードを持っている人は誰でもこのアカウントの資金にアクセスできるので、このシードは秘密にしておかなければなりません。検証する場合は、セッションアカウントのシードをノードの\"--key \"パラメータとして使用します。", + "The recoverable account is protected against the loss of seed/access by a social process.": "復元可能なアカウントは、ソーシャルプロセスによってシード/アクセスの損失から保護されています。", + "The scheduling preference for this parachain.": "このパラチェーンのスケジューリング優先順位。", + "The scheduling setting for this parachain.": "このパラチェーンのスケジューリング設定。", + "The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.": "このアカウントの秘密のシード値。安全な場所に保管しておくことを確認し、シードにアクセスすることでアカウントを再作成することができます。", + "The selected account to perform the derivation on.": "派生を実行するために選択されたアカウント。", + "The signatories has the ability to create transactions using the multisig and approve transactions sent by others.Once the threshold is reached with approvals, the multisig transaction is enacted on-chain.": "署名者は、マルチシグを使用してトランザクションを作成し、他の人が送信したトランザクションを承認する機能を持っています。", + "The signatory to send the approval/cancel from": "承認/否認を送るための署名", + "The the Ethereum address you used during the pre-sale (starting by \"0x\")": "プレセールで使用したEthereumのアドレス(0xから始まります)", + "The threshold for approval should be less or equal to the number of signatories for this multisig.": "承認のための閾値は、このマルチシグの署名者数以下でなければならない。", + "The threshold for approvals and the delay is the protection associated with the account. The delay should be such that any colluding recovery attempts does have a window to stop.": "承認の閾値と遅延は、アカウントに関連した保護方法です。遅延は、共謀した復旧の試みが停止するチャンスを持つようなものでなければなりません。", + "The threshold for this multisig": "マルチシグの閾値", + "The threshold of vouches that is to be reached for the account to be recovered.": "アカウントを回収するために到達すべき伝票の閾値", + "The transferred balance will be subtracted (along with fees) from the sender account.": "転送された残高は、送信者アカウントから(手数料とともに)差し引かれます", + "The twitter name for this identity.": "このアイデンティティのtwitter名", + "The unchecked weight as specified for the sudoUncheckedWeight call.": "sudoUncheckedWeightコールで指定されたチェックされていないweight。", + "The value is not in a valid address format": "この値は有効なアドレスフォーマットではありません。", + "There are no registered parachains": "登録されているパラチェーンがありません。", + "These are trusted individuals that can verify and approve any recovery actions. With recovery, once the threshold is reached, the funds associated with the account can be moved to a new destination.": "これらは、任意のリカバリーアクションを確認し、承認することができる信頼できる個人です。リカバリーでは、閾値に達すると、アカウントに関連付けられた資金を新しい宛先に移動することができます。", + "This Ethereum address does not have a claim.": "Ethereumアドレスには取得可能な資金がありません", + "This account is recoverable, with the following friends:": "このアカウントは、次の友人と一緒に、復旧可能です。", + "This is not a valid JSON object.": "無効なJSONオブジェクトです。", + "This operation does not impact the associated on-chain code or any of its contracts.": "この操作は、関連するオンチェーンコードやそのコントラクトには影響しません。", + "This operation does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.": "この操作は、チェーンからアップロードされたコードWASMとABIを削除するものではなく、デプロイされたコントラクトも削除しません。この操作は、このブラウザ上のコードへのアクセスを制限するだけです。", + "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).": "このパスワードは、秘密鍵を暗号化するために使用されます。それは強くてユニークでなければなりません!このパスワードは、あなたの秘密鍵を暗号化するために使用されます。このアカウントでの取引に署名するために必要になります。このパスワードとバックアップファイル(次のステップで生成された)を使用して、このアカウントを復旧することができます。", + "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.": "これは、このブラウザに保存されているこのアカウントの将来の使用に適用されます。この新しいパスワードを安全に保存し、アカウントに固有の強力なパスワードであることを確認してください。", + "Transfer": "送信", + "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.": "ここに、あなたのアドレスを含むようにしたいものを入力してください。このツールはキーを生成し、検索に最も適した関連するアドレスを表示します。文字のワイルドカードとして「?」を使用することができます。", + "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "送金したい金額を入力します。右側の単位を選択することができます。例えば1ミリを送信すると0.001を送信するのと同じです。", + "Type the name of this Asset. This name will be used across all the apps. It can be edited later on.": "このアセットの名前を入力します。この名前はすべてのアプリで使用されます。後で編集することができます。", + "Type the name of your contact. This name will be used across all the apps. It can be edited later on.": "連絡先の名前を入力します。この名前はすべてのアプリで使用されます。後で編集することができます。", + "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.": "アカウント作成時に選択したパスワードを入力します。これは、バックアップファイル内のアカウントの秘密鍵を暗号化するために使用されました。", + "Unable to find deployed contract code at the specified address": "指定されたアドレスにデプロイされたコントラクトコードが見つかりません", + "Unable to find on-chain WASM code for the supplied codeHash": "指定されたアドレスにデプロイされたコントラクトコードが見つかりません", + "Unknown": "Unknown", + "Unlock": "アンロック", + "Updated in the latest block": "最新のブロックをアップデートする", + "Upload": "アップロード", + "Upload WASM": "WASMをアップロードする", + "Vanity generator": "バニティージェネレーター", + "Verify the password entered above.": "上記で入力したパスワードを確認します。", + "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.": "外部ツールからの承認を待っています。インストールした外部ツールを開きアクセスの許可or拒否をしてください。", + "Warning: we did not find any attest statement for {{chain}}": "警告: {{chain}}上でアセットを検出することができませんでした", + "We found a pre-claim with this Polkadot address. However, attesting requires signing with this account. To continue with attesting, please add this account as an owned account first.": "このPolkadotのアドレスで取得前の資産を見つけました。しかし、認証するにはこのアカウントの署名が必要です。認証を継続するには、まずこのアカウントを所有アカウントとして追加してください。", + "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.": "アカウントが作成された後、生成されたバックアップファイルを提供します。アカウントにアクセスしている間は、アカウントセクションの「バックアップ」ボタンをクリックすることで、いつでもこのファイルをダウンロードすることができます。", + "Yes": "はい", + "You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.": "利用可能なコードハッシュのリストからこのコードを削除しようとしています。完了したら、再度アクセスする必要がある場合は、手動でコードハッシュを再度追加する必要があります。", + "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.": "このコードのABIを削除します。削除が完了したら、再度アクセスする必要がある場合は、手動で再アップロードする必要があります。", + "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.": "安全なロケーションから安全ではないWebSocket ({{wsUrl}})に接続しようとしています。ブラウザーのセキュリティー・ポリシーによりこの接続タイプは許可されていません。RPCサーバーを安全な'wss' エンドポイントに変更してください。", + "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.": "ノードに接続していません。ノードが起動していてWebsocketのエンドポイントが接続できることを確認してください。", + "You are using an ABI with an outdated format. Please generate a new one.": "古いフォーマットのABIを使用しています。新しいものを生成してください。", + "You can set a custom derivation path for this account using the following syntax \"//////\". The \"/\" and \"//\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.": "以下の構文\"//////\"を使用して、このアカウントのカスタム派生パスを設定することができます。\"/\" と \"//\" は繰り返しても混合しても構いません`。\"///password \"は任意であり、一度だけ出現させるべきです。", + "You do not have access to the current sudo key": "現在のsudoキーへのアクセスがありません。", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": "アカウントを保有していません。いくつかの特徴が利用できません。アカウントを作成の後利用できるようになります。", + "You have {{claimCount}} accounts that need attestations. Use the Claim Tokens app on the left navigation bar to complete the process. Until you do, your balances for those accounts will not be reflected.": "認証が必要な{{{claimCount}}}アカウントがあります。左のナビゲーションバーのClaim Tokensアプリを使用して、プロセスを完了させてください。このプロセスを完了するまで、これらのアカウントの残高は反映されません。", + "You need to sign an attestation for the following account:": "下記のアカウントの証明書にサインが必要です。", + "You need to sign an attestation for the following accounts:": "下記のアカウントの証明書にサインが必要です。", + "You will no longer have sudo access": "sudoへのアクセスがもうできません", + "Your Ethereum account": "Ethereumアカウント", + "Your custom types have been added": "カスタムタイプが追加されました", + "a riot name linked to this identity": "このアイデンティティーに紐づくriotの名前", + "account forgotten": "アカウントを忘れました", + "account restored": "アカウントが復元されました", + "accounts": "アカウント", + "address": "アドレス", + "address created": "アドレスが生成されました", + "address edited": "アドレスが編集されました", + "address forgotten": "アドレスを忘れました", + "address prefix": "アドレス接頭辞", + "amount": "量", + "approval type": "承認のタイプ", + "asset id": "アセットid", + "available signatories": "利用可能な署名", + "available social recovery helpers": "利用可能なソーシャルリカバリーヘルパー", + "aye: bool": "はい: bool", + "backup file": "バックアップファイル", + "balances": "残高", + "best": "best", + "best #": "best #", + "best block": "best block", + "best hash": "best ハッシュ", + "block hash or number to query": "ブロックハッシュ or キューの番号", + "blocks": "ブロック", + "call from account": "アカウントからコールする", + "case sensitive": "大文字小文字を区別する", + "claim to account": "アカウントを取得する", + "click to copy": "クリックしてコピーする", + "click to select or drag and drop a JSON ABI file": "JSPN ABIファイルをクリックするかドラッグアンドドロップしてください", + "code": "コード", + "code for this contract": "このコントラクトのコード", + "code hash": "コードハッシュ", + "compiled contract WASM": "WASMコントラクトをコンパイルする", + "connected peers": "接続されたピア", + "contacts": "連絡先", + "contract address": "コントラクトアドレス", + "contract to use": "コントラクトを使用する", + "conviction: Conviction": "conviction: Conviction", + "copied": "コピー完了", + "created account": "アカウントを作成する", + "created multisig": "マルチシグを作成する", + "custom endpoint": "カスタムエンドポイント", + "data": "データ", + "default icon theme": "アイコンテーマ", + "default interface language": "言語", + "delay": "遅延", + "deploy": "デプロイ", + "deployment account": "デプロイアカウント", + "deployment constructor": "デプロイコンストラクター", + "deposit": "デポジット", + "derivation path": "派生パス", + "derive root account": "ルートアカウントを派生する", + "details": "詳細", + "development seed": "開発のシード", + "display name": "表示名", + "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": "有効な取得権限を持っているようには見えません。正しいETHアカウントで正しく取引に署名したかどうかを二重に確認してください。", + "email": "email", + "endowment": "寄付金", + "epoch": "エポック", + "era": "エラ", + "events": "イベント", + "everything": "すべて", + "exclude option": "オプションを除外する", + "exclude value": "値を含めない", + "execute": "実行", + "existential deposit": "実存預金", + "extrinsics": "extrinsics", + "filter by name or tags": "名前orタグでフィルターする", + "finalized": "ファイナライズされた", + "forks": "フォーク", + "free balance": "free 残高", + "has a valid claim for": "有効な取得権限をもっています", + "hash": "ハッシュ", + "heads": "ヘッド", + "hex-encoded storage key": "hexエンコードストレージキー", + "identity": "アイデンティティー", + "immortal": "永続的な", + "include option": "オプションを入れる", + "include value": "値を含める", + "index": "インデックス", + "initial head state": "細書のヘッドステート", + "interface operation mode": "機能モード", + "invalid ABI file selected": "有効ではないABIファイルが選択されました", + "invalid/unknown registrar account": "登録アカウントが不正/わかりません", + "judgement": "ジャジメント", + "keypair crypto type": "クリプトタイプキーペアー", + "last block": "最後のブロック", + "legal name": "名前", + "lng.detect": "ブラウザー設定言語(自動反映)", + "logs": "ログ", + "manage hardware connections": "ハードウェアへの接続を管理します", + "matches": "マッチ", + "maximum gas allowed": "gasの最大値", + "message to send": "送信するメッセージ", + "mnemonic seed": "ニーモニックシード", + "multisig": "マルチシグ", + "multisig name": "マルチシグ名", + "name": "名前", + "nav.accounts": "アカウント", + "nav.claims": "トークンを取得する", + "nav.contracts": "コントラクト", + "nav.council": "カウンシル", + "nav.dashboard": "ダッシュボード", + "nav.democracy": "デモクラシー", + "nav.explorer": "エクスプローラー", + "nav.extrinsics": "Extrinsics", + "nav.generic-asset": "ジェネリックアセット", + "nav.i18n": "I18n Translator", + "nav.js": "Javascript", + "nav.parachains": "パラチェーン", + "nav.settings": "設定", + "nav.society": "ソサエティー", + "nav.staking": "ステイキング", + "nav.storage": "チェーン状態", + "nav.sudo": "Sudo", + "nav.tech-comm": "Tech. comm.", + "nav.toolbox": "ツールボックス", + "nav.transfer": "送信", + "nav.treasury": "トレジュリー", + "new account": "新しいアカウント", + "new address": "新しいアドレス", + "next id": "次のID", + "no accounts yet, create or import an existing": "まだ新しいアカウントがありません。作成するか既存のものをイポーとしてください。", + "no addresses saved yet, add any existing address": "アドレスが保存されていません。既存のアドレスを追加してください。", + "no messages": "ノー・メッセージ", + "no tags": "タグなし", + "origin": "オリジン", + "parachain id": "パラチェーン ID", + "parachains": "パラチェーン", + "parent": "親", + "password": "パスワード", + "password (repeat)": "パスワード", + "pending hashes": "ハッシュをペンディングする", + "pending swap id": "ペンディングスワップID", + "recover this account": "このアカウントを復旧する", + "recovery block delay": "ブロックの遅延を復旧する", + "recovery threshold": "出発点を復旧する", + "registrar account": "アカウントを登録する", + "registrar index": "indexを登録する", + "relay dispatch queue": "リレーディスパッチキュー", + "remote node/endpoint to connect to": "リモートノード・エンドポイント", + "riot": "riot", + "riot name": "riot名", + "rpc.arcadia": "Arcadia (Nodle Testnet, hosted by Nodle)", + "rpc.berlin": "Berlin (Edgeware Testnet, hosted by Commonwealth Labs)", + "rpc.custom": "カスタムエンドポイント", + "rpc.edgeware": "Edgeware (Edgeware Mainnet, hosted by Commonwealth Labs)", + "rpc.flamingfir": "Flaming Fir (Substrate Testnet, hosted by Parity)", + "rpc.header.dev": "開発環境", + "rpc.header.live": "メイン環境", + "rpc.header.test": "テストネット", + "rpc.kulupu": "Kulupu (Kulupu Mainnet, hosted by Kulupu)", + "rpc.kusama.ava": "Kusama (Polkadot Canary, user-run public nodes; see https://status.cloud.ava.do/)", + "rpc.kusama.parity": "Kusama (Polkadot Canary, hosted by Parity)", + "rpc.kusama.w3f": "Kusama (Polkadot Canary, hosted by Web3 Foundation)", + "rpc.local": "Local Node (Own, 127.0.0.1:9944)", + "rpc.mandala": "Mandala (Acala Testnet, hosted by Acala)", + "rpc.westend": "Westend (Polkadot Testnet, hosted by Parity)", + "scheduling": "スケジューリング", + "secret": "秘密", + "secret derivation path": "秘密の分岐パス", + "seed (hex or string)": "シード(hexかstring)", + "selected constant query": "選択された定数クエリー", + "selected signatories": "選択された署名", + "selected state query": "選択されたステートクエリー", + "send": "送信", + "send as RPC call": "RPCコールとして送信する", + "send as transaction": "トランザクションを送信する", + "send from account": "アカウントから送信する", + "send to address": "アカウントに送信する", + "signatories": "署名", + "signatory": "署名", + "ss58.centrifuge": "Centrifuge (live)", + "ss58.default": "デフォルト接続ノード", + "ss58.edgeware": "Edgeware (live)", + "ss58.kusama": "Kusama (canary)", + "ss58.polkadot": "Kusama (canary)", + "ss58.substrate": "Substrate (generic)", + "submit the following change": "以下の変更を提出する", + "submit the following extrinsic": "次のextrinsicを提出する", + "sudo key": "sudoキー", + "sudo with unchecked weight parameter": "チェックされていないweightパラメーター付きのsudo", + "sudo without unchecked weight parameter": "チェックされていないweightパラメーター付きではないsudo", + "swap to id": "IDにスワップする", + "tags": "タグ", + "the account to make recoverable": "復元可能にするアカウント", + "the account to recover to": "復元をするアカウント", + "the language to display translations for": "翻訳を表示する言語", + "threshold": "出発点", + "transactions": "トランザクション", + "transfer received": "受信", + "transferrable": "送信可能な", + "trusted social recovery helpers": "信頼できるソーシャルリカバリーヘルパー", + "twitter": "twitter", + "type": "タイプ", + "unchecked weight for this call": "このコールのチェックされていないweight", + "update on #{{index}}": "#{{index}}をアップデートする", + "using the selected account": "選択されたアカウントを使用する", + "value": "値", + "version {{version}}": "バージョン {{version}}", + "web": "web", + "website": "website", + "your current password": "現在のパスワード", + "your new password": "新しいパスワード", + "{{count}} key/value pairs encoded for submission": "エンコードされた {count}} キー/値 のペアー", + "{{d}} days": "{{d}}日", + "{{executionTime}}s execution time": "{{executionTime}} 実行時間", + "{{h}} hrs": "{{h}} 時間", + "{{m}} mins": "{{m}} 分", + "{{percentage}}% of block time": "ブロックタイム {{percentage}} %", + "{{relayDispatchQueueSize}} dispatch messages pending": "{{relayDispatchQueueSize}} ディスパッチメッセージペンディング", + "{{step}}. Sign with your ETH address": "{{step}}. ETHアドレスでサインしてください", + "{{s}} s": "{{s}} 秒" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/ko/translation.json b/pioneer/packages/apps/public/locales/ko/translation.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/pioneer/packages/apps/public/locales/ko/translation.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/pt/translation.json b/pioneer/packages/apps/public/locales/pt/translation.json new file mode 100644 index 0000000000..c8fd8e9592 --- /dev/null +++ b/pioneer/packages/apps/public/locales/pt/translation.json @@ -0,0 +1,1123 @@ +{ + "//hard///password": "//forte///senha", + "//hard/soft": "//forte/fraca", + "//hard/soft///password": "//forte/fraca///senha", + "0.1x voting balance, no lockup period": "0,1 * saldo de voto, sem período de bloqueio", + "1 day": "1 dia", + "1 hr": "1 hora", + "1 min": "1 minuto", + "1 s": "1 s", + "1. Select your {{chain}} account": "1. Selecione sua conta {{chain}}", + "2. Enter the ETH address from the sale.": "2. Digite o endereço ETH da venda.", + "2. Sign ETH transaction": "2. Assinar transação ETH", + "": "", + "": "", + "A controller account should not be set to manages multiple stashes. The selected controller is already controlling {{stashId}}": "Uma conta do controlador não deve ser definida para gerenciar várias reservas. O controlador selecionado já está controlando {{stashId}}", + "A controller account should not map to another stash. This selected controller is a stash, controlled by {{bondedId}}": "Uma conta do controlador não deve ser mapeada para outro esconderijo. Este controlador selecionado é uma reserva, controlado por {{bondedId}}", + "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": "Um contrato implantado que foi implantado ou anexado. O endereço e ABI são usados para construir os parâmetros.", + "A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist": "Uma taxa de {{creationFee}} será deduzida do remetente, pois a conta de destino não existe", + "A reason (to be stored-on-chain) as to why the recipient deserves a tip payout.": "Um motivo (para ser armazenado na cadeia) do motivo pelo qual o destinatário merece um pagamento de gorjeta.", + "ABI": "ABI", + "Acceptance proposal to council": "Proposta de aceitação ao conselho", + "Account actions": "Ações da conta", + "Account balance:": "Saldo da conta:", + "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.": "As contas injetadas em qualquer uma dessas extensões aparecerão neste aplicativo e estarão disponíveis para uso. A lista acima é atualizada à medida que mais extensões com capacidade de assinatura externa se tornam disponíveis.", + "Active nominations ({{count}})": "Nomeações ativas ({{count}})", + "Add a tip to this extrinsic, paying the block author for greater priority": "Adicione uma gorjeta a esse extrínseco, pagando ao autor do bloco por maior prioridade", + "Add account": "Adicionar Conta", + "Add account via Qr": "Adicionar conta via código QR", + "Add an account via seed": "Adicionar uma conta via semente", + "Add an address": "Adicionar um endereço", + "Add an existing code hash": "Adicione um hash de código existente", + "Add an existing contract": "Adicionar um contrato existente", + "Add contact": "Adicionar contato", + "Add identity judgment": "Adicionar julgamento de identidade", + "Add item": "Adicionar Item", + "Add multisig": "Adicionar multi-assinatura", + "Add via Qr": "Adicionar via código QR", + "Add via backup file": "Adicionar via arquivo de backup", + "Adding an optional tip to the transaction could allow for higher priority, especially when the chain is busy.": "Adicionar uma gorjeta opcional à transação pode permitir maior prioridade, especialmente quando a cadeia está ocupada.", + "Additional types as a JSON file (or edit below)": "Tipos adicionais como um arquivo JSON (ou edite abaixo)", + "Address Prefix": "Prefixo do Endereço", + "Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)": "Ajuste o modo de básico (com um número limitado de aplicativos fáceis de usar para iniciantes) a completo (com todos os aplicativos básicos e avançados disponíveis)", + "Advanced creation options": "Opções avançadas de criação", + "All rewards will go towards the selected output destination when a payout is made.": "Todas as recompensas irão para o destino de saída selecionado quando um pagamento for feito.", + "All the listed validators and all their nominators will receive their rewards.": "Todos os validadores listados e todos os seus nominadores receberão suas recompensas.", + "Allocate a suggested tip amount. With enough endorsements, the suggested values are averaged and sent to the beneficiary.": "Aloque um valor de gorjeta sugerido. Com endossos suficientes, os valores sugeridos são calculados em média e enviados ao beneficiário.", + "Amount to add to the currently bonded funds. This is adjusted using the available funds on the account.": "Valor a acrescentar aos fundos atualmente vinculados. Isso é ajustado usando os fundos disponíveis na conta.", + "An URL that is linked to this identity.": "Um URL que está vinculado a essa identidade.", + "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.": "Um arquivo de backup criptografado será criado depois que você pressionar o botão \"Download\". Isso pode ser usado para reimportar sua conta em qualquer outra máquina.", + "Any account can request payout for stakers, this is not limited to accounts that will be rewarded.": "Qualquer conta pode solicitar pagamento para os participantes, isso não se limita às contas que serão recompensadas.", + "Approvals": "Aprovações", + "Approve or reject this defender.": "Aprovar ou rejeitar este defensor.", + "Approve this call hash": "Aprovar este hash de chamada", + "Approve this candidacy.": "Aprovar esta candidatura.", + "Approved": "Aprovado", + "As a council member, you can suggest an initial value for the tip, each other council member can suggest their own.": "Como membro do conselho, você pode sugerir um valor inicial para a gorjeta; cada outro membro do conselho pode sugerir o seu.", + "Asset ID": "ID do recurso", + "Assets": "Ativos", + "Aura": "Aura", + "Authorize transaction": "Autorizar transação", + "Aye": "Sim", + "Aye, I approve": "Sim, eu aprovo", + "Babe": "Babe", + "Backup account": "Conta de backup", + "Block details": "Detalhes do bloco", + "Bond": "Vinculo", + "Bond & Nominate": "Vincular & Nominar", + "Bond & Validate": "Vincular e validar", + "Bond more": "Vincular mais", + "Bond more funds": "Vincular mais fundos", + "Bonding Preferences": "Preferências de vínculação", + "Call": "Chamar", + "Call a contract": "Chamar um contrato", + "Call a method on this contract": "Chamar um método neste contrato", + "Call results": "Chamar resultados", + "Call this message": "Chamar essa mensagem", + "Cancel": "Cancelar", + "Cancel slashes": "Cancelar punições", + "Cancel this call hash": "Cancelar este hash de chamada", + "Chain info": "Informação da cadeia", + "Chain specifications": "Especificações da corrente", + "Change": "Mudança", + "Change account password": "Modificar senha da conta", + "Change controller account": "Alterar conta do controlador", + "Change reward destination": "Alterar destino da recompensa", + "Change session keys": "Alterar chaves da sessão", + "Change this account's password": "Alterar a senha desta conta", + "Change validator preferences": "Alterar preferências do validador", + "Changing the key only takes effect at the start of the next session. The input here is generates from the author_rotateKeys command": "Alterar a tecla só entra em vigor no início da próxima sessão. A entrada aqui é gerada a partir do comando author_rotateKeys", + "Claim": "Reivindicar", + "Claim tokens...": "Reivindicar tokens...", + "Clear all": "Limpar tudo", + "Clear expired democracy locks": "Limpar bloqueios de democracia expirados", + "Close": "Fechar", + "Close proposal": "Fechar proposta", + "Color": "Cor", + "Committee prime member, default voting": "Membro principal do comitê, votação padrão", + "Confirm ABI removal": "Confirme a remoção da ABI", + "Confirm account removal": "Confirmar remoção da conta", + "Confirm address removal": "Confirmar remoção de endereço", + "Confirm claim": "Confirmar reivindicação", + "Confirm code removal": "Confirmar remoção do código", + "Confirm contract removal": "Confirmar remoção do contrato", + "Constants": "Constantes", + "Continue": "Continuar", + "Conviction locks do overlap and is additive, meaning that funds locked during a previous vote can be locked again.": "Os bloqueios por condenação se sobrepõem e são aditivos, o que significa que os fundos bloqueados durante uma votação anterior podem ser bloqueados novamente.", + "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "Copie a sequência acima e assine uma transação do Ethereum com a conta usada durante a pré-venda na carteira de sua escolha, usando a sequência como carga útil e cole o objeto de assinatura da transação abaixo", + "Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:": "Copie a seguinte string e assine-a na conta Ethereum usada durante a pré-venda na carteira de sua escolha, usando a string como carga útil e cole o objeto de assinatura da transação abaixo:", + "Council": "Conselho", + "Council overview": "Visão geral do Conselho", + "Create": "Criar", + "Create a backup file for this account": "Criar um arquivo de backup para esta conta", + "Create an account now.": "Criar uma conta agora.", + "Create and backup account": "Criar e fazer backup da conta", + "Crypto not detected": "Criptografia não detectada", + "Cryptography used to create this signature. It is auto-detected on valid signatures.": "Criptografia usada para criar esta assinatura. É detectado automaticamente em assinaturas válidas.", + "Current account nonce: {{accountNonce}}": "Nonce da conta atual: {{accountNonce}}", + "Current prime member, default voting": "Membro principal atual, votação padrão", + "Current society head, exempt": "Chefe da sociedade atual, isento", + "Decimals": "Decimais", + "Decimals decides the smallest unit of the token, which is 1/10^decimals": "Decimais decide a menor unidade do token, que é 1/10 ^ decimais", + "Delete this custom example": "Excluir este exemplo personalizado", + "Democracy overview": "Visão geral da democracia", + "Deploy": "Implantar", + "Deploy a code hash": "Implantar um hash de código", + "Deploy this code hash as a smart contract": "Implante esse hash de código como um contrato inteligente", + "Deploy with this constructor": "Implante com este construtor", + "Deposit": "Depósito", + "Deregister this parachain": "Cancele o registro desse parachain", + "Derive account from pair": "Derivar conta do par", + "Derive account via derivation path": "Derivar conta via caminho de derivação", + "Details": "Detalhes", + "Detection on the input string to determine if it is hex or non-hex.": "Detecção na sequência de entrada para determinar se é hexadecimal ou não hexadecimal.", + "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".": "Determina qual criptografia será usada para criar esta conta. Observe que, para validar no Polkadot, a conta da sessão deve usar \"ed25519\".", + "Developer": "Desenvolvedor", + "Development": "Desenvolvimento", + "Dismiss all notifications": "Ignorar todas as notificações", + "Display overview information for the selected validator, including blocks produced.": "Exibe informações de visão geral do validador selecionado, incluindo os blocos produzidos.", + "Distinct stash and controller accounts are recommended to ensure fund security. You will be allowed to make the transaction, but take care to not tie up all funds, only use a portion of the available funds during this period.": "Contas reservas e controladoras distintas são recomendadas para garantir a segurança do fundo. Você poderá fazer a transação, mas tome cuidado para não anexar todos os fundos, use apenas uma parte dos fundos disponíveis durante esse período.", + "Do not include a tip for the block author": "Não inclua uma gorjeta para o autor do bloco", + "Download": "Baixar", + "Either approve or reject this call.": "Aprovar ou rejeitar esta chamada.", + "Endorse": "Endossar", + "Endorsements ({{count}})": "Endossos ({{count}})", + "Ensure that not all funds are locked, funds need to be available for fees.": "Certifique-se de que nem todos os fundos estejam bloqueados, os fundos precisam estar disponíveis por taxas.", + "Enter the Asset ID of the token you want to manage.": "Digite o ID do ativo do token que você deseja gerenciar.", + "Enter the Asset ID of the token you want to transfer.": "Digite o ID do ativo do token que você deseja transferir.", + "Erroneous": "Errôneo", + "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)": "Avaliadas {{count}} chaves em {{decorrido}} s ({{avg}} chaves/s)", + "Extensions": "Extensões", + "External": "Externo", + "Extrinsic submission": "Envio extrínseco", + "Fast track": "Faixa rápida", + "Fast track proposal": "Proposta acelerada", + "Filter available candidates based on name, address or short account index.": "Filtre os candidatos disponíveis com base no nome, endereço ou índice abreviado da conta.", + "For fund security, your session key should not match your stash key.": "Para segurança do fundo, sua chave de sessão não deve corresponder à sua chave reserva.", + "Forget": "Esquecer", + "Forget this account": "Esquecer esta conta", + "Forget this address": "Esquecer este endereço", + "Forget this asset": "Esquecer este recurso", + "Forget this code hash": "Esquecer este código hash", + "Forget this contract": "Esquecer este contrato", + "Forks": "Bifurcações", + "General": "Geral", + "Generate {{lng}}/translation.json": "Gerar {{lng}}/translation.json", + "Genesis Hash": "Genesis Hash", + "Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched": "Genesis Hash se refere ao estado inicial da cadeia, não pode ser alterado depois que a cadeia é lançada", + "Grandpa": "Grandpa", + "Hash data": "Dados hash", + "I agree": "Concordo", + "I'm Online": "Eu estou online", + "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.": "Se a conta do destinatário for nova, o saldo precisará ser maior que o depósito de existência. Da mesma forma, se o saldo da conta de envio cair abaixo do mesmo valor, a conta será removida do estado.", + "If this proposal is passed, the changes will be applied via dispatch and the deposit returned.": "Se a proposta for aprovada, as alterações serão aplicadas via despacho e o depósito retornado.", + "If you are moving accounts between applications, ensure that you use the correct type.": "Se você estiver movendo contas entre aplicativos, use o tipo correto.", + "Important notice": "Aviso importante", + "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.": "No cálculo do resultado da eleição, essa ordem de votação priorizada será usada para determinar a pontuação final dos candidatos.", + "Inactive nominations ({{count}})": "Indicações inativas ({{count}})", + "Include an optional tip for faster processing": "Inclua uma gorjeta opcional para um processamento mais rápido", + "Initializing connection": "Inicializando conexão", + "Initiate account recovery": "Iniciar recuperação da conta", + "Initiate recovery for another": "Iniciar recuperação para outro", + "Inject Keys": "Injetar chaves", + "Inject session keys (advanced)": "Injetar chaves de sessão (avançadas)", + "Injected": "Injetado", + "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -": "É recomendável que você crie/armazene suas contas de forma segura e externa a partir do aplicativo. No {{yourBrowser}}, as seguintes extensões de navegador estão disponíveis para uso -", + "Judge": "Juiz", + "Keys from rotateKeys": "\"Keys\" de \"rotateKeys\"", + "Known good": "Bem conhecido", + "Learn more...": "Saber mais...", + "Lifetime (# of blocks)": "Tempo de vida (número de blocos)", + "Loading": "Carregando", + "Locked1x": "Bloqueado1x", + "Locked2x": "Bloqueado2x", + "Locked3x": "Bloqueado3x", + "Locked4x": "Bloqueado4x", + "Locked5x": "Bloqueado5x", + "Locked6x": "Bloqueado6x", + "Low quality": "Baixa qualidade", + "Make Transfer": "Fazer transferência", + "Make recoverable": "Tornar recuperável", + "Manage your connection to Ledger S": "Gerencie sua conexão com o Ledger S", + "Message data": "Dados da mensagem", + "Message origin.": "Origem da mensagem.", + "Messages": "Mensagens", + "Metadata {{count}}": "Metadados {{count}}", + "Mnemonic": "Mnemônico", + "Most recent head data": "Dados de cabeçalho mais recentes", + "Motions ({{count}})": "Moções ({{count}})", + "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "Múltiplas propostas de conselho podem existir, tanto aprovação quanto rejeição. Aplique seu voto à proposta correta do conselho (também disponível na página de moções do conselho)", + "Multisig": "Multi-assinatura", + "Multisig approval with hash (non-final approval)": "Aprovação multi-assinatura com hash (aprovação não final)", + "Multisig approval with hash (not message with call)": "Aprovação multi-assinatura com hash (não mensagem com chamada)", + "Multisig approvals": "Aprovações multi-assinatura", + "Multisig message with call (for final approval)": "Mensagem multi-assinatura com chamada (para aprovação final)", + "Multisig message with call (not approval with hash)": "Mensagem multi-assinatura com chamada (não aprovação com hash)", + "Mutates contract state": "Muda o estado do contrato", + "My accounts": "Minhas contas", + "My contacts": "Meus contatos", + "Name given to this account. You can change it at any point in the future.": "Nome dado a esta conta. Você pode alterá-lo a qualquer momento no futuro.", + "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".": "Nome dado a esta conta. Você pode editá-lo. Para usar a conta para validar ou nomear, é uma boa prática anexar a função da conta no nome, por exemplo, \"nome_desejado - reserva\".", + "Name given to this multisig. You can edit it at any later point in time.": "Nome dado a essa multi-assinatura. Você pode editá-lo a qualquer momento posterior.", + "Name of the network. It is only for display purposes.": "Nome da rede. É apenas para fins de exibição.", + "Name your example": "Dê um nome ao seu exemplo", + "Nay": "Não", + "Nay, I do not approve": "Não, eu não aprovo", + "Network Name": "Nome da rede", + "New stake": "Nova participação", + "No": "Não", + "No active members": "Nenhum membro ativo", + "No active proposals": "Nenhuma proposta ativa", + "No active referendums": "Nenhum referendo ativo", + "No active validators found": "Nenhum validador ativo encontrado", + "No active validators to check": "Não há validadores ativos para verificar", + "No approved proposals": "Nenhuma proposta aprovada", + "No assets found.": "Nenhum recurso encontrado.", + "No bids": "Sem lances", + "No blocks available": "Nenhum bloco disponível", + "No candidates": "Nenhum candidato", + "No candidates found": "Nenhum candidato encontrado", + "No code hashes available": "Nenhum código hashes disponível", + "No committee proposals": "Nenhuma proposta de comitê", + "No contracts available": "Não há contratos disponíveis", + "No council motions": "Nenhuma moção de conselho", + "No documentation provided": "Nenhuma documentação fornecida", + "No events available": "Não há eventos disponíveis", + "No external proposal": "Nenhuma proposta externa", + "No funds staked yet. Bond funds to validate or nominate a validator": "Ainda não há fundos em participação. Fundos de obrigações para validar ou nomear um validador", + "No items": "Nenhum item", + "No judgments": "Sem julgamentos", + "No logs available": "Nenhum registro disponível", + "No matches found": "Nenhuma equivalência encontrada", + "No members found": "Nenhum membro encontrado", + "No open tips": "Não há gorjetas em aberto", + "No pending era payouts from validators": "Não há pagamentos pendentes da era dos validadores", + "No pending extrinsics are in the queue": "Nenhuma extrínseca pendente na fila", + "No pending payouts for your stashes": "Não há pagamentos pendentes para suas reservas", + "No pending proposals": "Nenhuma proposta pendente", + "No runners up found": "Nenhum corredor encontrado", + "No upgradable extensions found": "Nenhuma extensão atualizável encontrada", + "No waiting validators found": "Nenhum validador em espera encontrado", + "Node info": "Informações do nó", + "Nominate": "Nomear", + "Nominate Validators": "Nomear validadores", + "Nominate selected": "Nomear selecionado", + "Nominate validators": "Nomear validadores", + "Nominations ({{count}})": "Nomeações ({{count}})", + "Nominator": "Nominador", + "Nominators can be selected automatically based on the current on-chain conditions or supplied manually as selected from the list of all currently available validators. In both cases, your favorites appear for the selection.": "Os nominadores podem ser selecionados automaticamente com base nas condições atuais da cadeia ou fornecidos manualmente, conforme selecionado na lista de todos os validadores disponíveis no momento. Nos dois casos, seus favoritos aparecem para a seleção.", + "Nonce": "Nonce", + "None": "Nenhum", + "Not updated in the last block": "Não atualizado no último bloco", + "Nothing queued for execution": "Nada na fila para execução", + "Of the beneficiary amount, at least {{bondPercentage}} would need to be put up as collateral. The maximum of this and the minimum bond will be used to secure the proposal, refundable if it passes.": "Do montante do beneficiário, pelo menos {{bondPercentage}} precisaria ser apresentado como garantia. O valor máximo e o vínculo mínimo serão utilizados para garantir a proposta, reembolsável se aprovada.", + "Once bonded, it wil need to be unlocked/withdrawn and will be locked for at least the bonding duration.": "Uma vez vinculado, ele precisará ser desbloqueado/retirado e será bloqueado pelo menos durante a duração do vínculo.", + "Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, the nominations will show as inactive.": "Depois de transmitida, a nova seleção só entrará em vigor em duas épocas, uma vez que os critérios de seleção para a próxima era foram feitos no final da era anterior. Até lá, as indicações serão mostradas como inativas.", + "One of your available nomination accounts, keyed by the stash. The transaction will be sent from the controller.": "Uma das suas contas de nomeação disponíveis, chaveadas pela reserva. A transação será enviada do controlador.", + "Only applicable if the proposal has already passed and is ready for dispatch.": "Aplicável apenas se a proposta já tiver sido aprovada e estiver pronta para envio.", + "Override any applicable values for the specific signed output. These will be used to construct and display the signed transaction.": "Substitua quaisquer valores aplicáveis à saída assinada específica. Eles serão usados para construir e exibir a transação assinada.", + "Override the default identity icon display with a specific theme": "Substitua a exibição do ícone de identidade padrão por um tema específico", + "Override the default ss58 prefix for address generation": "Substitua o prefixo ss58 padrão para geração de endereço", + "Owned": "Possuído", + "Parachains": "parachains", + "Parachains overview": "Visão geral das parachains", + "Paste here the address of the contact you want to add to your address book.": "Cole aqui o endereço do contato que você deseja adicionar ao seu catálogo de endereços.", + "Payout": "Pagamento", + "Payout all": "Todos os pagamentos", + "Payout all stakers": "Pagar todos os participantes", + "Pending call hashes": "Hash de chamada pendente", + "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.": "Salve este arquivo em um local seguro, pois é necessário, juntamente com sua senha, para restaurar sua conta.", + "Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:": "Por favor leia esses termos e condições cuidadosamente. Ao enviar esta declaração, considera-se que você aceitou estes Termos e Condições. Se você não concordar com estes termos, evite acessar ou continuar. Você também pode encontrá-los em:", + "Positive number": "Número positivo", + "Positive number between 1 and {{count}}": "Número positivo entre 1 e {{count}}", + "Positive number between 1 and {{memberCount}}": "Número positivo entre 1 e {{memberCount}}", + "Positive number greater than or equal to {{nextFreeId}}": "Número positivo maior ou igual a {{nextFreeId}}", + "Pre-sale ethereum address": "Endereço ethereum de pré-venda", + "Prefix indicates the ss58 address format in this network, it is a number between 0 ~ 255 that describes the precise format of the bytes of the address": "O prefixo indica o formato do endereço ss58 nesta rede; é um número entre 0 e 255 que descreve o formato preciso dos bytes do endereço", + "Preimage": "Pré-imagem", + "Present the QR code containing the signature to the UI. Once scanned it will be submitted for on-chain processing and execution.": "Apresente o código QR que contém a assinatura para a interface do usuário. Depois de digitalizado, ele será enviado para processamento e execução em cadeia.", + "Proposal can either be to approve or reject this spend. One approved, the change is applied by either removing the proposal or scheduling payout.": "A proposta pode ser para aprovar ou rejeitar esses gastos. Uma vez aprovada, a alteração é aplicada removendo a proposta ou agendando o pagamento.", + "Proposals": "Propostas", + "Proposals ({{count}})": "Propostas ({{count}})", + "Propose": "Ofertas", + "Propose a committee motion": "Propor uma moção de comissão", + "Propose a council motion": "Propor uma moção de conselho", + "Propose external": "Propor externo", + "Propose external (majority)": "Propor externo (maioria)", + "Propose motion": "Propor moção", + "Propose tip": "Propor gorjeta", + "Provide judgement": "Fornecer julgamento", + "Provide the account QR from the module/external application for scanning. One detected as valid, you will be taken to the next step to add the account to your list.": "Forneça o QR da conta no módulo/aplicativo externo para digitalização. Se for detectado como válido, você será levado para a próxima etapa para adicionar a conta à sua lista.", + "Query Ledger": "Livro de consultas", + "RPC calls": "Chamadas RPC", + "Random": "Aleatória", + "Raw seed": "Semente bruta", + "Raw storage": "Armazenamento bruto", + "Reasonable": "Razoável", + "Reassign": "Reatribuir", + "Redeem": "Resgatar", + "Register": "Registro", + "Register Asset": "Registrar ativo", + "Register a parachain": "Registrar uma parachain", + "Register an Asset": "Registrar um ativo", + "Register identity": "Registrar identidade", + "Register the vote for or against the proposal.": "Registre a votação a favor ou contra a proposta.", + "Rejection proposal to council": "Proposta de rejeição ao conselho", + "Rejections": "Rejeições", + "Remove": "Retirar", + "Remove ABI": "Remover ABI", + "Remove item": "Remover item", + "Reset": "Redefinir", + "Restore": "Restaurar", + "Restore JSON": "Restaurar JSON", + "Retrieving data": "Recuperando dados", + "Retrieving info for all applicable eras, this will take some time": "Recuperando informações para todas as eras aplicáveis, isso levará algum tempo", + "Revert": "Reverter", + "Revert pending slashes": "Reverter punições pendentes", + "Rewards (once paid) can be deposited to either the stash or controller, with different effects.": "Recompensas (uma vez pagas) podem ser depositadas no estoque ou no controlador, com efeitos diferentes.", + "Save": "Salve", + "Save & Reload": "Salvar e recarregar", + "Save snippet to local storage": "Salvar snippet no armazenamento local", + "Save the type definitions for your custom structures as key-value pairs in a valid JSON file. The key should be the name of your custom structure and the value an object containing your type definitions.": "Salve as definições de tipo para suas estruturas personalizadas como pares de valor-chave em um arquivo JSON válido. A chave deve ser o nome da sua estrutura personalizada e o valor de um objeto que contém suas definições de tipo.", + "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.": "Salve este arquivo de backup em um local seguro. Além disso, a senha associada a esta conta é necessária junto com este arquivo de backup para restaurar sua conta.", + "Saved": "Salvou", + "Scan Signature Qr": "Digitalizar código QR de Assinatura", + "Scan the QR code with your QR scanner. Once approved, you will be required to present the signed QR back to the UI for submission.": "Digitalize o código QR com o seu scanner QR. Depois de aprovado, você deverá apresentar o QR assinado de volta à interface do usuário para envio.", + "Search for": "Procurar por", + "Second": "Segundo", + "Second proposal": "Segunda proposta", + "Seconding a proposal that indicates your backing for the proposal. Proposals with greater interest moves up the queue for potential next referendums.": "Destacando uma proposta que indica seu apoio à proposta. As propostas com maior interesse aumentam a fila para possíveis próximos referendos.", + "Seconds ({{count}})": "Segundos ({{count}})", + "Select Network": "Selecione rede", + "Select a contact or paste the address you want to send funds to.": "Selecione um contato ou cole o endereço para o qual deseja enviar fundos.", + "Select and order council candidates you wish to vote for.": "Selecione e ordene os candidatos a conselho nos quais deseja votar.", + "Select best": "Selecione melhor", + "Select example": "Selecione exemplo", + "Select targets manually (no auto-selection based on current profitability)": "Selecione destinos manualmente (sem seleção automática com base na rentabilidade atual)", + "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.": "Selecione o arquivo de chave JSON que foi baixado quando você criou a conta. Este arquivo JSON contém sua chave privada criptografada com sua senha.", + "Select the account to use for this action.": "Selecione a conta a ser usada para esta ação.", + "Select the account you wish close the proposal with.": "Selecione a conta com a qual você deseja fechar a proposta.", + "Select the account you wish to make the proposal with.": "Selecione a conta com a qual você deseja fazer a proposta.", + "Select the account you wish to recover into this account.": "Selecione a conta que você deseja recuperar nesta conta.", + "Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected": "Selecione a conta que deseja destacar. Isso bloqueará seus fundos até que a proposta seja aprovada ou rejeitada", + "Select the account you wish to submit for candidacy.": "Selecione a conta que você deseja enviar para a candidatura.", + "Select the account you wish to submit the proposal from.": "Selecione a conta da qual você deseja enviar a proposta.", + "Select the account you wish to submit the tip from.": "Selecione a conta da qual deseja enviar a gorjeta.", + "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.": "Selecione a conta na qual deseja votar. Você pode aprovar \"sim\" ou negar \"não\" a proposta.", + "Select the council account you wish to use to make the proposal.": "Selecione a conta do conselho que você deseja usar para fazer a proposta.", + "Select the remote endpoint, either from the dropdown on manual entered via the custom toggle": "Selecione o endereço remoto, no menu suspenso no manual inserido pela alternância personalizada", + "Send funds": "Enviar fundos", + "Send funds from this account": "Enviar fundos desta conta", + "Send funds to this address": "Enviar fundos para este endereço", + "Send to council": "Enviar para o conselho", + "Sending transaction": "Enviando transação", + "Session Key": "Chave da sessão", + "Set Identity": "Definir identidade", + "Set Session Key": "Definir chave da sessão", + "Set controller": "Definir controlador", + "Set nominees": "Definir candidatos", + "Set on-chain identity": "Definir identidade na cadeia", + "Set reward destination": "Definir destino da recompensa", + "Set sudo key": "Definir \"sudo key\"", + "Set to 0 to make transaction immortal": "Defina como 0 para tornar a transação imortal", + "Set validator preferences": "Definir preferências do validador", + "Setup Nominator {{step}}/{{NUM_STEPS}}": "Configurar Nominador {{step}}/{{NUM_STEPS}}", + "Setup Validator {{step}}/{{NUM_STEPS}}": "Configurar Validador {{step}}/{{NUM_STEPS}}", + "Setup account as recoverable": "Configurar conta como recuperável", + "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...": "Caso a pesquisa faça distinção entre maiúsculas e minúsculas, por exemplo, se você selecionar \"não\", sua pesquisa por \"Algum\" poderá retornar endereços contendo \"alguM\" ou \"alguUm\" ...", + "Show address on hardware device": "Mostrar endereço no dispositivo de hardware", + "Sign (no submission)": "Assinar (sem envio)", + "Sign and Submit": "Assinar e envie", + "Sign for multisig": "Assinar para multi-assinatura", + "Sign message": "Assinar mensagem", + "Sign via Qr": "Assinar via QR", + "Sign via {{hardwareType}}": "Assinar via {{hardwareType}}", + "Signed transaction": "Transação assinada", + "Since the multisig function like any other account, once created it is available for selection anywhere accounts are used and needs to be funded before use.": "Como a multi-assinatura funciona como qualquer outra conta, uma vez criada, está disponível para seleção em qualquer lugar em que as contas sejam usadas e precisam ser financiadas antes do uso.", + "Since this transaction deals with funding, the stash account will be used.": "Como esta transação lida com financiamento, a conta de reserva será usada.", + "Skeptics": "Céticos", + "Society": "Sociedade", + "Society overview": "Visão geral da sociedade", + "Specify the user account to use for this contract call. And fees will be deducted from this account.": "Especifique a conta de usuário a ser usada para esta chamada de contrato. E as taxas serão deduzidas desta conta.", + "Specify the user account to use for this deployment. And fees will be deducted from this account.": "Especifique a conta de usuário a ser usada para esta implantação. E as taxas serão deduzidas desta conta.", + "Specify the user account to use for this deployment. Any fees will be deducted from this account.": "Especifique a conta de usuário a ser usada para esta implantação. Quaisquer taxas serão deduzidas desta conta.", + "Staking overview": "Visão geral de participação", + "Start generation": "Iniciar gerador", + "Start recovery": "Iniciar a recuperação", + "Stash": "Reserva", + "Stop": "Pare", + "Stop generation": "Parar gerador", + "Storage": "Armazenamento", + "Submit": "Enviar", + "Submit (no signature)": "Enviar (sem assinatura)", + "Submit RPC call": "Enviar chamada RPC", + "Submit Sudo": "Enviar Sudo", + "Submit Transaction": "Enviar transação", + "Submit Unsigned": "Enviar não assinado", + "Submit candidacy": "Enviar candidatura", + "Submit key": "Enviar chave", + "Submit preimage": "Enviar pré-imagem", + "Submit proposal": "Enviar proposta", + "Submit signed extrinsic": "Enviar extrínseco assinado", + "Submit tip": "Enviar gorjeta", + "Submit tip endorsement": "Enviar endosso de gorjeta", + "Submit tip request": "Enviar solicitação de gorjeta", + "Submit treasury proposal": "Enviar proposta de tesouraria", + "Submit your council candidacy": "Envie sua candidatura ao conselho", + "Sudo access": "Sudo access", + "Sudo key": "\"Sudo key\"", + "Supply a backed-up JSON file, encrypted with your account-specific password.": "Forneça um arquivo JSON de backup, criptografado com a senha específica da sua conta.", + "Targets": "Alvos", + "Tech. committee": "Comitê técnico", + "Technical committee": "Comitê técnico", + "Test account": "Conta de teste", + "The ABI for the WASM code. In this step it is optional, but setting it here simplifies the setup of contract instances.": "A ABI do código WASM. Nesta etapa, é opcional, mas a configuração aqui simplifica a configuração das instâncias do contrato.", + "The ABI for the WASM code. Since we will be making a call into the code, the ABI is required and stored for future operations such as sending messages.": "A ABI do código WASM. Como faremos uma chamada para o código, a ABI é necessária e armazenada para operações futuras, como o envio de mensagens.", + "The RPC node can be selected from the pre-defined list or manually entered, depending on the chain you wish to connect to.": "O nó RPC pode ser selecionado da lista predefinida ou inserido manualmente, dependendo da cadeia à qual você deseja se conectar.", + "The Threshold must be a positive number": "O mínimo deve ser um número positivo", + "The Threshold must lower than 11": "O mínimo deve ser menor que 11", + "The UI puts a limit of 40 payouts at a time, where each payout is a single validator for a single era.": "A interface do usuário coloca um limite de 40 pagamentos por vez, em que cada pagamento é um validador único para uma única era.", + "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.": "A senha da conta, conforme especificado ao criar a conta. Isso é usado para criptografar o arquivo de backup e, posteriormente, descriptografá-lo ao restaurar a conta.", + "The account that signed the input": "A conta que assinou a entrada", + "The account to which the proposed balance will be transferred if approved": "A conta para a qual o saldo proposto será transferido se aprovado", + "The account to which the tip will be transferred if approved": "A conta para a qual a gorjeta será transferida se aprovada", + "The account you want to claim to.": "A conta que você deseja reivindicar.", + "The account you want to register the preimage from": "A conta da qual você deseja registrar a pré-imagem", + "The account you want to register the proposal from": "A conta da qual você deseja registrar a proposta", + "The account you will send funds from.": "A conta da qual você enviará fundos.", + "The account's password specified at the creation of this account.": "A senha da conta especificada na criação desta conta.", + "The actual JSONRPC module and function to make a call to.": "O módulo JSONRPC e a função reais para os quais fazer uma chamada.", + "The actual proposal to make, based on the selected call and parameters thereof.": "A proposta real a ser feita, com base na chamada selecionada e em seus parâmetros.", + "The address for the deployed contract instance.": "O endereço da instância do contrato implantado.", + "The address to vote from (must be a member)": "O endereço para votar (deve ser um membro)", + "The addresses that are able to approve multisig transactions. You can select up to {{maxHelpers}} trusted addresses.": "Os endereços que podem aprovar transações multi-assinatura. Você pode selecionar até {{maxHelpers}} endereços confiáveis.", + "The addresses that are able to help in recovery. You can select up to {{maxHelpers}} trusted helpers.": "Os endereços que podem ajudar na recuperação. Você pode selecionar até {{maxHelpers}} auxiliares de confiança.", + "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": "A doação alocada para este contrato, ou seja, o valor transferido para o contrato na instanciação.", + "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": "O valor alocado para este contrato, ou seja, o valor transferido para o contrato como parte desta chamada.", + "The amount of funds to unbond, this is adjusted using the bonded funds on the stash account.": "A quantia de fundos a ser desvinculada, é ajustada usando os fundos vinculados na conta reserva.", + "The amount placed at-stake should be no more that 95% of your available amount to protect against slashing events.": "O valor colocado em jogo não deve exceder 95% do valor disponível para proteger contra eventos de corte.", + "The amount that is associated with this vote. This value is is locked for the duration of the vote.": "A quantia associada a este voto. Este valor está bloqueado durante a votação.", + "The amount that will be allocated from the treasury pot": "A quantia que será alocada do pote do tesouro", + "The amount that will be used on a per-validator basis to calculate profits for that validator.": "O valor que será usado por validador para calcular lucros para esse validador.", + "The amount this total can be reduced by to change the referendum outcome. This assumes changes to the convictions of the existing votes, with no additional turnout.": "O valor desse total pode ser reduzido para alterar o resultado do referendo. Isso pressupõe alterações nas convicções dos votos existentes, sem participação adicional.", + "The amount this total should be increased by to change the referendum outcome. This assumes additional turnout with new votes at 1x conviction.": "A quantidade desse total deve ser aumentada para alterar o resultado do referendo. Isso pressupõe participação adicional com novos votos com uma condenação 1x.", + "The associated deposit for this proposal should be more then the minimum on-chain deposit required. It will be locked until the proposal passes.": "O depósito associado a esta proposta deve ser superior ao depósito mínimo exigido na cadeia. Ele ficará bloqueado até que a proposta seja aprovada.", + "The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for the validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.": "A seleção automática é feita com base na rentabilidade atual dos validadores, levando em consideração seus favoritos. É ajustado com base na comissão e na faixa atual de apoio do validador. O cálculo pode e irá mudar ao longo do tempo; portanto, é uma seleção baseada no estado atual da rede, não um preditor de lucratividade futura.", + "The balance associated with the vote will be locked as per the conviction specified and will not be available for transfer during this period.": "O saldo associado à votação será bloqueado de acordo com a condenação especificada e não estará disponível para transferência durante esse período.", + "The beneficiary will have access to the transferred fees when the transaction is included in a block.": "O beneficiário terá acesso às taxas transferidas quando a transação for incluída em um bloco.", + "The beneficiary will receive the full amount if the proposal passes.": "O beneficiário receberá o valor total se a proposta for aprovada.", + "The beneficiary will received the tip as approved by council members.": "O beneficiário receberá a gorjeta conforme aprovado pelos membros do conselho.", + "The blake2b 256-bit hash of the actual input data.": "O hash blake2b de 256 bits dos dados reais de entrada.", + "The bonding duration for any staked funds. After this period needs to be withdrawn.": "A duração do vínculo para quaisquer fundos em participação. Após esse período, é necessário retirar.", + "The bonding duration for any staked funds. Needs to be unlocked and withdrawn to become available.": "A duração do vínculo para quaisquer fundos em participação. Precisa ser desbloqueado e retirado para ficar disponível.", + "The call hashes that have not been executed as of yet.": "Os hashes de chamada que ainda não foram executados.", + "The code hash for the on-chain deployed code.": "O hash de código para o código implantado na cadeia.", + "The code is not recognized as being in valid WASM format": "O código não é reconhecido como estando no formato WASM válido", + "The codeHash is not a valid hex hash": "O codeHash não é um hash hex válido", + "The color used to distinguish this network with others, use color code with 3 or 6 digits, like \"#FFF\" or \"#111111\"": "A cor usada para distinguir esta rede de outras pessoas, use código de cores com 3 ou 6 dígitos, como \"#FFF\" ou \"#111111\"", + "The commission is deducted from all rewards before the remainder is split with nominators.": "A comissão é deduzida de todas as recompensas antes que o restante seja dividido com os nominadores.", + "The committee account that will apply the close for the current round.": "A conta do comitê que aplicará o fechamento da rodada atual.", + "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": "O WASM compilado para o contrato que você deseja implantar. Cada blob de código exclusivo será anexado com um hash de código que pode ser usado para criar novas instâncias.", + "The compiled runtime WASM for the parachain you wish to register.": "O WASM de tempo de execução compilado para a parachain que você deseja registrar.", + "The compiled runtime WASM for this parachain.": "O tempo de execução compilado WASM para essa parachain.", + "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.": "O contrato WASM implementado anteriormente. Internamente, isso é identificado pelo hash do código, como criado ou anexado.", + "The controller is the account that is be used to control any nominating or validating actions. I will sign this transaction.": "O controlador é a conta que deve ser usada para controlar quaisquer ações de nomeação ou validação. Eu assinarei esta transação.", + "The controller is the account that will be used to control any nominating or validating actions. Should not match another stash or controller.": "O controlador é a conta que será usada para controlar quaisquer ações de nomeação ou validação. Não deve corresponder a outro esconderijo ou controlador.", + "The conviction to use for this vote, with an appropriate lock period.": "A condenação de usar para esta votação, com um período de bloqueio apropriado.", + "The council account for the proposal. The selection is filtered by the current members.": "O conselho responde pela proposta. A seleção é filtrada pelos membros atuais.", + "The council account for this vote. The selection is filtered by the current members.": "O conselho responde por esta votação. A seleção é filtrada pelos membros atuais.", + "The council account that will apply the close for the current round.": "A conta do conselho que aplicará o fechamento da rodada atual.", + "The council member that is proposing this, submission equates to an \"aye\" vote.": "O membro do conselho que está propondo isso, a submissão equivale a um voto \"sim\".", + "The council proposal to make the vote on": "A proposta do conselho de votar", + "The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.": "Os dados que foram assinados. Isso é usado em combinação com a assinatura para a verificação. Pode ser hexadecimal ou string.", + "The delay between vouching and the availability of the recovered account.": "O atraso entre o comprovante e a disponibilidade da conta recuperada.", + "The delay period to apply in blocks": "O período de atraso a aplicar em blocos", + "The deployment constructor information for this contract, as provided by the ABI.": "As informações do construtor de implantação deste contrato, conforme fornecidas pela ABI.", + "The deposit is below the {{minimum}} minimum required for the proposal to be evaluated": "O depósito está abaixo do mínimo exigido para que a proposta seja avaliada", + "The deposit of {{deposit}} will be reserved until the proposal is completed": "O depósito de {{deposit}} será reservado até que a proposta seja concluída", + "The derivation path allows you to create different accounts from the same base mnemonic.": "O caminho da derivação permite criar contas diferentes da mesma base mnemônica.", + "The desired threshold. Here set to a default of 50%+1, as applicable for general proposals.": "O mínimo desejado. Defina aqui o padrão de 50% + 1, conforme aplicável a propostas gerais.", + "The destination account for any payments as either a nominator or validator": "A conta de destino para qualquer pagamento como nomeador ou validador", + "The details of the transaction including the type, the description (as available from the chain metadata) as well as any parameters and fee estimations (as available) for the specific type of call.": "Os detalhes da transação, incluindo o tipo, a descrição (conforme disponível nos metadados da cadeia), bem como quaisquer parâmetros e estimativas de taxa (conforme disponíveis) para o tipo específico de chamada.", + "The email address associated with this identity.": "O endereço de email associado a esta identidade.", + "The existing account password as specified when this account was created or when it was last changed.": "A senha da conta existente, conforme especificado quando esta conta foi criada ou quando foi alterada pela última vez.", + "The external proposal to send to the technical committee": "A proposta externa a ser enviada ao comitê técnico", + "The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected": "O saldo do destinatário final é menor ou igual a {{existentialDeposit}} (o valor existencial) e não será refletido", + "The funds will only be available for withdrawal after the unbonding period, however will not be part of the staked amount after the next validator election. You can follow the unlock countdown in the UI.": "Os fundos só estarão disponíveis para retirada após o período de desvínculo, no entanto, não farão parte do valor em participação após a próxima eleição do validador. Você pode acompanhar a contagem regressiva de desbloqueio na interface do usuário.", + "The hash for the proposal this vote applies to": "O hash da proposta a que esta votação se aplica", + "The hash of the preimage for the proposal as previously submitted or intended.": "O hash da pré-imagem da proposta como enviado ou pretendido anteriormente.", + "The hash of the proposal image, either already submitted or valid for the specific call.": "O hash da imagem da proposta, já enviado ou válido para a chamada específica.", + "The hash of the selected proposal, use it for submitting the proposal": "O hash da proposta selecionada, use-o para enviar a proposta", + "The helpers should be able to verify, via an off-chain mechanism, that the account owner indeed wishes to recover access and as such provide any approvals. In the cases of malicious recovery procedures, they will have the power to stop it.": "Os auxiliares devem poder verificar, através de um mecanismo fora da cadeia, se o proprietário da conta deseja realmente recuperar o acesso e, como tal, fornecer quaisquer aprovações. Nos casos de procedimentos de recuperação mal-intencionados, eles poderão detê-lo.", + "The hex output from author_rotateKeys, as executed on the validator node. The keys will show as pending until applied at the start of a new session.": "A saída hexadecimal de author_rotateKeys, conforme executada no nó do validador. As teclas serão exibidas como pendentes até serem aplicadas no início de uma nova sessão.", + "The id number to assign to this parachain.": "O número de identificação a ser atribuído a esta parachain.", + "The id of the parachain to be deregistered.": "O ID da parachain a ser cancelado o registro.", + "The image (proposal) will be stored on-chain against the hash of the contents.": "A imagem (proposta) será armazenada na cadeia contra o hash do conteúdo.", + "The initial head state for the parachain.": "O estado inicial da parachain.", + "The initial head state is invalid.": "O estado inicial do cabeçalho é inválido.", + "The input data to hash. This can be either specified as a hex value (0x-prefix) or as a string.": "Os dados de entrada para o hash. Isso pode ser especificado como um valor hexadecimal (prefixo 0x) ou como uma sequência.", + "The input data to sign. This can be either specified as a hex value (0x-prefix) or as a string.": "Os dados de entrada para assinar. Isso pode ser especificado como um valor hexadecimal (prefixo 0x) ou como uma sequência.", + "The key type and crypto type to use for this key. Be aware that different keys have different crypto requirements. You should be familiar with the type requirements for the different keys.": "O tipo de chave e o tipo de criptografia a serem usados para essa chave. Esteja ciente de que chaves diferentes têm requisitos de criptografia diferentes. Você deve estar familiarizado com os requisitos de tipo para as diferentes chaves.", + "The legal name for this identity.": "O nome legal para esta identidade.", + "The local name for this account. Changing this does not affect your on-line identity, so this is only used to indicate the name of the account locally.": "O nome local desta conta. Alterar isso não afeta sua identidade on-line, portanto, isso é usado apenas para indicar o nome da conta localmente.", + "The locked value for this proposal": "O valor bloqueado para esta proposta", + "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": "A quantidade máxima de gás que pode ser usada por esta chamada. Se o código exigir mais, a chamada falhará.", + "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": "A quantidade máxima de gás que pode ser usada por esta implantação, se o código exigir mais, a implantação falhará.", + "The maximum amount of gas to use for this contract call. If the call requires more, it will fail.": "A quantidade máxima de gás a ser usada para esta chamada de contrato. Se a chamada exigir mais, ela falhará.", + "The message to send to this contract. Parameters are adjusted based on the ABI provided.": "A mensagem a ser enviada para este contrato. Os parâmetros são ajustados com base na ABI fornecida.", + "The minimum amount that an account should have to be deemed active": "O valor mínimo que uma conta deve ter para ser considerada ativa", + "The minimum amount that will be bonded": "O valor mínimo que será vinculado", + "The minimum deposit required": "O depósito mínimo exigido", + "The minimum number of committee votes required to approve this motion": "O número mínimo de votos do comitê necessário para aprovar esta moção", + "The minimum number of council votes required to approve this motion": "O número mínimo de votos do conselho necessário para aprovar esta moção", + "The multisig signatory for this transaction.": "O signatário multi-assinatura para esta transação.", + "The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.": "O nome desta conta e como ela aparecerá em seus endereços. Com uma identidade on-chain, pode ser disponibilizada para outras pessoas.", + "The name is for unique identification of the account in your owner lists.": "O nome é para identificação exclusiva da conta nas suas listas de proprietários.", + "The name that will be displayed in your accounts list.": "O nome que será exibido na sua lista de contas.", + "The new account password. Once set, all future account unlocks will be performed with this new password.": "A nova senha da conta. Depois de definido, todos os futuros desbloqueios de conta serão executados com essa nova senha.", + "The on-chain percentage for the treasury": "A porcentagem na cadeia para o tesouro", + "The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.": "A senha e a confirmação da senha desta conta. Isso é necessário para autenticar todas as transações realizadas e criptografar o par de chaves.", + "The password previously used to encrypt this account.": "A senha usada anteriormente para criptografar esta conta.", + "The password to unlock the selected account.": "A senha para desbloquear a conta selecionada.", + "The percentage reward (0-100) that should be applied for the validator": "A porcentagem de recompensa (0 a 100) que deve ser aplicada ao validador", + "The preimage hash of the proposal": "O hash de pré-imagem da proposta", + "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.": "A chave privada da sua conta é derivada dessa semente. Essa semente deve ser mantida em segredo, pois qualquer pessoa em seu poder tem acesso aos fundos desta conta. Se você validar, use a semente da conta da sessão como o parâmetro \"- chave\" do seu nó.", + "The proposal is in the queue for future referendums. One proposal from this list will move forward to voting.": "A proposta está na fila para futuros referendos. Uma proposta desta lista avançará para a votação.", + "The proposal that is being voted on. It will pass when the threshold is reached.": "A proposta que está sendo votada. Passará quando o mínimo for alcançado.", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted for a subsequent voting round.": "A proposta que será afetada. Uma vez fechado para a atual rodada de votação, ele precisará ser reenviado para uma rodada de votação subsequente.", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted to council for a subsequent voting round.": "A proposta que será afetada. Uma vez fechado para a atual rodada de votação, ele precisará ser reenviado ao conselho para uma próxima rodada de votação.", + "The proposal will be registered from this account and the balance lock will be applied here.": "A proposta será registrada nesta conta e o bloqueio do saldo será aplicado aqui.", + "The reason why this tip should be paid.": "A razão pela qual esta gorjeta deve ser paga.", + "The recoverable account is protected against the loss of seed/access by a social process.": "A conta recuperável é protegida contra a perda de semente/acesso por um processo social.", + "The resulting signature of the input data, as done with the crypto algorithm from the account. (This could be non-deterministic for some types such as sr25519).": "A assinatura resultante dos dados de entrada, como feita com o algoritmo de criptografia da conta. (Isso pode ser não determinístico para alguns tipos, como sr25519).", + "The scheduling preference for this parachain.": "A preferência de agendamento para esta parachain.", + "The scheduling setting for this parachain.": "A configuração de agendamento para esta parachain.", + "The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.": "O valor secreto da semente para esta conta. Certifique-se de manter isso em um local seguro. Com acesso à semente, você pode recriar a conta.", + "The seed and derivation path will be submitted to the validator node. this is an advanced operation, only to be performed when you are sure of the security and connection risks.": "O caminho de origem e derivação será enviado ao nó do validador. esta é uma operação avançada, a ser executada apenas quando você tiver certeza dos riscos de segurança e conexão.", + "The selected account to be unlocked.": "A conta selecionada a ser desbloqueada.", + "The selected account to perform the derivation on.": "A conta selecionada para executar a derivação.", + "The selected controller tied to this stash. Once set, this account will be able to control the actions performed by the stash account.": "O controlador selecionado vinculado a este esconderijo. Depois de definida, esta conta poderá controlar as ações executadas pela conta reserva.", + "The selected validators to nominate, either via the \"currently best algorithm\" or via a manual selection.": "Os validadores selecionados para nomear, através do \"atualmente melhor algoritmo\" ou através de uma seleção manual.", + "The sending account that will be used to send this transaction. Any applicable fees will be paid by this account.": "A conta de envio que será usada para enviar esta transação. Quaisquer taxas aplicáveis serão pagas por esta conta.", + "The signatories has the ability to create transactions using the multisig and approve transactions sent by others. Once the threshold is reached with approvals, the multisig transaction is enacted on-chain.": "Os signatários têm a capacidade de criar transações usando multi-assinatura e aprovar transações enviadas por outras pessoas. Depois que o número mínimo de aprovações é atingido, a transação multi-assinatura é aprovada na cadeia.", + "The signatory is one of the allowed accounts on the multisig, making a recorded approval for the transaction.": "O signatário é uma das contas permitidas na multi-assinatura, fazendo uma aprovação registrada para a transação.", + "The signatory is one of the allowed accounts on the multisig. The transaction could either be the call or an approval for the hash of a call.": "O signatário é uma das contas permitidas na multi-assinatura. A transação pode ser a chamada ou uma aprovação para o hash de uma chamada.", + "The signatory to send the approval/cancel from": "O signatário para enviar a aprovação/cancelar de", + "The signature as by the account being checked, supplied as a hex-formatted string.": "A assinatura conforme a conta está sendo verificada, fornecida como uma sequência de caracteres no formato hexadecimal.", + "The specific eras on which there are unapplied slashes. For each era a separate proposal is to be made.": "As épocas específicas nas quais existem punições não aplicadas. Para cada época, uma proposta separada deve ser feita.", + "The specified value is greater than your free balance. The node will bond the maximum amount available.": "O valor especificado é maior que seu saldo livre. O nó vinculará a quantidade máxima disponível.", + "The stash account that is used. This will allow the controller to perform all non-funds related operations on behalf of the account.": "A conta reserva usada. Isso permitirá que o controlador realize todas as operações não relacionadas a fundos em nome da conta.", + "The stash and controller pair as linked. This operation will be performed via the controller.": "O par de reserva e controlador como vinculados. Esta operação será realizada através do controlador.", + "The stash and controller pair, here the controller will be used to send the transaction.": "O par de reserva e controlador, aqui o controlador será usado para enviar a transação.", + "The stash and controller pair. This transaction, managing preferences, will be sent from the controller.": "O par de reserva e controlador. Esta transação, gerenciando preferências, será enviada do controlador.", + "The stash and controller pair. This transaction, setting the session keys, will be sent from the controller.": "O par de reserva e controlador. Esta transação, configurando as chaves da sessão, será enviada do controlador.", + "The stash that is to be affected. The transaction will be sent from the associated controller account.": "A reserva que deve ser afetado. A transação será enviada da conta do controlador associada.", + "The suggested value for this tip": "O valor sugerido para esta gorjeta", + "The the Ethereum address you used during the pre-sale (starting by \"0x\")": "O endereço do Ethereum que você usou durante a pré-venda (começando por \"0x\")", + "The threshold for approval should be less or equal to the number of signatories for this multisig.": "O mínimo para aprovação deve ser menor ou igual ao número de signatários dessa multi-assinatura.", + "The threshold for approvals and the delay is the protection associated with the account. The delay should be such that any colluding recovery attempts does have a window to stop.": "O mínimo para aprovações e o atraso é a proteção associada à conta. O atraso deve ser tal que qualquer tentativa de recuperação conivente tenha uma janela para parar.", + "The threshold for this multisig": "O mínimo para esta multi-assinatura", + "The threshold of vouches that is to be reached for the account to be recovered.": "O mínimo de atestados que deve ser alcançado para que a conta seja recuperada.", + "The tip amount that should be allocated": "O valor da gorjeta que deve ser alocado", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the free amount available)": "O valor total do saldo reserva que estará em jogo nas próximas rodadas (deve ser menor que o valor livre disponível)", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the total amount available)": "O valor total do saldo reserva que estará em jogo nas próximas rodadas (deve ser menor que o valor total disponível)", + "The transferred balance will be subtracted (along with fees) from the sender account.": "O saldo transferido será subtraído (juntamente com as taxas) da conta do remetente.", + "The twitter name for this identity.": "O nome do twitter para esta identidade.", + "The type of council proposal to submit.": "O tipo de proposta do conselho a ser submetida.", + "The unapplied slashed era to cancel.": "A era de punição não aplicada para cancelar.", + "The unchecked weight as specified for the sudoUncheckedWeight call.": "O peso não verificado, conforme especificado para a chamada \"sudoUncheckedWeight\".", + "The value associated with this vote. The amount will be locked (not available for transfer) and used in all subsequent elections.": "O valor associado a este voto. O valor será bloqueado (não disponível para transferência) e usado em todas as eleições subsequentes.", + "The value is not in a valid address format": "O valor não está em um formato de endereço válido", + "The value is the amount that is being asked for and that will be allocated to the beneficiary if the proposal is approved.": "O valor é o valor solicitado e será atribuído ao beneficiário se a proposta for aprovada.", + "The vote to record for this proposal, either for or against.": "A votação para registrar nesta proposta, a favor ou contra.", + "The vote will be recorded for the selected account.": "O voto será registrado na conta selecionada.", + "The vote will be recorded for this account. If another account delegated to this one, the delegated votes will also be counted.": "O voto será registrado nesta conta. Se outra conta delegada a esta, os votos delegados também serão contados.", + "The votes for the members, runner-ups and candidates. These should be ordered based on your priority.": "Os votos para os membros, finalistas e candidatos. Eles devem ser pedidos com base na sua prioridade.", + "The voting period to apply in blocks": "O período de votação a aplicar em blocos", + "There are no registered parachains": "Não há parachains registradas", + "There is currently an ongoing election for new validator candidates. As such staking operations are not permitted.": "Atualmente, há uma eleição em andamento para novos candidatos ao validador. Como tais operações de participação não são permitidas.", + "These are trusted individuals that can verify and approve any recovery actions. With recovery, once the threshold is reached, the funds associated with the account can be moved to a new destination.": "São indivíduos confiáveis que podem verificar e aprovar qualquer ação de recuperação. Com a recuperação, uma vez atingido o mínimo, os fundos associados à conta podem ser movidos para um novo destino.", + "Think of the stash as your cold wallet and the controller as your hot wallet. Funding operations are controlled by the stash, any other non-funding actions by the controller itself.": "Pense no esconderijo como sua carteira fria e no controlador como sua carteira quente. As operações de financiamento são controladas pelo estoque, quaisquer outras ações que não sejam de financiamento pelo próprio controlador.", + "This Ethereum address does not have a claim.": "Este endereço Ethereum não tem uma reivindicação.", + "This account is recoverable, with the following friends:": "Esta conta é recuperável, com os seguintes amigos:", + "This account that will perform the message signing.": "Esta conta que executará a assinatura da mensagem.", + "This account will appear in the list of candidates. With enough votes in an election, it will become either a runner-up or a council member.": "Esta conta aparecerá na lista de candidatos. Com votos suficientes em uma eleição, ele se tornará um vice-campeão ou um membro do conselho.", + "This account will be use to approve each candidate.": "Esta conta será usada para aprovar cada candidato.", + "This account will make the proposal and be responsible for the bond.": "Esta conta fará a proposta e será responsável pelo vínculo.", + "This account will pay the fees for the preimage, based on the size thereof.": "Esta conta pagará as taxas da pré-imagem, com base no tamanho da mesma.", + "This could either be an approval for the hash or with full call details. The call as last approval triggers execution.": "Pode ser uma aprovação para o hash ou com detalhes completos da chamada. A chamada como última aprovação aciona a execução.", + "This external account cannot be used to sign data. Only Limited support is currently available for signing from any non-internal accounts.": "Esta conta externa não pode ser usada para assinar dados. Atualmente, apenas o suporte limitado está disponível para assinatura em contas não internas.", + "This injected account cannot be used to sign data since the extension does not support raw signing.": "Esta conta injetada não pode ser usada para assinar dados, pois a extensão não suporta assinatura bruta.", + "This is not a valid JSON object.": "Este não é um objeto JSON válido.", + "This operation does not impact the associated on-chain code or any of its contracts.": "Esta operação não afeta o código associado à cadeia ou qualquer um de seus contratos.", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.": "Esta operação não remove o histórico da conta da cadeia, nem quaisquer fundos associados da conta. A operação de esquecer limita apenas o seu acesso à conta neste navegador.", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.": "Esta operação não remove o histórico da conta da cadeia, nem quaisquer fundos associados da conta. A operação de esquecer limita apenas o seu acesso ao endereço neste navegador.", + "This operation does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.": "Esta operação não remove o histórico do contrato da cadeia, nem fundos associados da sua conta. A operação de esquecer limita apenas o seu acesso ao contrato neste navegador.", + "This operation does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.": "Esta operação não remove o código carregado WASM e ABI da cadeia, nem contratos implementados. A operação de esquecer limita apenas o seu acesso ao código neste navegador.", + "This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.": "Esta operação enviará a semente através de uma chamada RPC. Não execute esta operação em um nó RPC público, mas verifique se o nó é local, conectado ao seu validador e seguro.", + "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).": "Essa senha é usada para criptografar sua chave privada. Deve ser forte e único! Você precisará assinar transações com esta conta. Você pode recuperar esta conta usando esta senha junto com o arquivo de backup (gerado na próxima etapa).", + "This pubic key is what will be visible in your queued keys list. It is generated based on the seed and the crypto used.": "Esta chave pública é o que será visível na sua lista de chaves na fila. É gerado com base na semente e na criptografia usada.", + "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.": "Isso se aplicará a qualquer uso futuro desta conta, armazenada neste navegador. Certifique-se de armazenar com segurança essa nova senha e de que ela seja forte e exclusiva para a conta.", + "Tip": "Gorjeta", + "Tip (optional)": "Gorjeta (opcional)", + "To council": "Ao conselho", + "To ensure optimal fund security using the same stash/controller is strongly discouraged, but not forbidden.": "Garantir a segurança ideal do fundo usando o mesmo esconderijo/controlador é fortemente desencorajado, mas não proibido.", + "Transfer": "Transferir", + "Translate": "Traduzir", + "Treasury overview": "Visão geral do Tesouro", + "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.": "Digite aqui o que você gostaria que seu endereço contivesse. Essa ferramenta irá gerar as chaves e mostrar os endereços associados que melhor correspondem à sua pesquisa. Você pode usar \"?\" Como curinga para um caracter.", + "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "Digite o valor que você deseja transferir. Observe que você pode selecionar a unidade à direita, por exemplo, enviar 1 mili é equivalente a enviar 0,001.", + "Type the name of this Asset. This name will be used across all the apps. It can be edited later on.": "Digite o nome deste ativo. Este nome será usado em todos os aplicativos. Pode ser editado mais tarde.", + "Type the name of your contact. This name will be used across all the apps. It can be edited later on.": "Digite o nome do seu contato. Este nome será usado em todos os aplicativos. Pode ser editado mais tarde.", + "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.": "Digite a senha escolhida na criação da conta. Foi usado para criptografar a chave privada da sua conta no arquivo de backup.", + "Unable to find deployed contract code at the specified address": "Não foi possível encontrar o código do contrato implantado no endereço especificado", + "Unable to find on-chain WASM code for the supplied codeHash": "Não foi possível encontrar o código WASM na cadeia para o codeHash fornecido", + "Unbond": "Desvincular", + "Unbond funds": "Desvincular fundos", + "Unbonding {{value}}, ": "Desvinculando {{value}},", + "Uncaught error. Something went wrong with the query and rendering of this component. {{message}}": "Erro não capturado. Ocorreu um problema com a consulta e a renderização deste componente. {{mensagem}}", + "Unit": "Unidade", + "Unit decides the name of 1 unit token, e.g. \"DOT\" for Polkadot": "A unidade decide o nome de 1 token de unidade, por exemplo \"DOT\" para Polkadot", + "Unknown": "Desconhecido", + "Unlock": "Desbloquear", + "Unlock account": "Desbloquear a conta", + "Unlock the account for signing. Once active the signature will be generated based on the content provided.": "Desbloqueie a conta para assinar. Uma vez ativa, a assinatura será gerada com base no conteúdo fornecido.", + "Unlock the sending account to allow signing of this transaction.": "Desbloqueie a conta de envio para permitir a assinatura desta transação.", + "Update metadata": "Atualizar metadados", + "Updated in the latest block": "Atualizado no último bloco", + "Upload": "Envio", + "Upload WASM": "Carregar WASM", + "Use an automatic selection of the currently most profitable validators": "Use uma seleção automática dos validadores atualmente mais lucrativos", + "Use this account to request the tip from. This can be a normal or council account.": "Use esta conta para solicitar a gor. Pode ser uma conta normal ou do conselho.", + "Validate": "Validar", + "Validator": "Validador", + "Validator stats": "Estatísticas do validador", + "Vanity generator": "Gerador de endereço personalizado", + "Verify signature": "Verificar assinatura", + "Verify the password entered above.": "Verifique a senha digitada acima.", + "View this externally": "Veja isso externamente", + "Vote": "Votar", + "Vote Aye": "Vote Sim", + "Vote Nay": "Vote Não", + "Vote for candidate": "Vote no candidato", + "Vote for current candidates": "Vote nos candidatos atuais", + "Vote for defender": "Vote no defensor", + "Vote on proposal": "Votar na proposta", + "Waiting": "Esperando", + "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.": "Aguardando autorização da extensão. Abra a extensão instalada e aprove ou rejeite o acesso.", + "Waiting nominations ({{count}})": "Aguardando indicações ({{count}})", + "Warning: we did not find any attest statement for {{chain}}": "Aviso: não encontramos nenhuma declaração de atestado para {{chain}}", + "We found a pre-claim with this Polkadot address. However, attesting requires signing with this account. To continue with attesting, please add this account as an owned account first.": "Encontramos uma pré-reivindicação com este endereço do Polkadot. No entanto, atestar requer assinatura com esta conta. Para continuar com o atestado, adicione esta conta como uma conta própria primeiro.", + "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.": "Forneceremos um arquivo de backup gerado após a criação da sua conta. Desde que você tenha acesso à sua conta, você sempre poderá fazer o download desse arquivo posteriormente, clicando no botão \"Backup\" na seção Contas.", + "When submitting a proposal the hash needs to be known. Proposals can be submitted with hash-only, but upon dispatch the preimage needs to be available.": "Ao enviar uma proposta, o hash precisa ser conhecido. As propostas podem ser enviadas apenas com hash, mas após o envio, a pré-imagem precisa estar disponível.", + "Withdraw these unbonded funds": "Retirar esses fundos não vinculados", + "Yes": "Sim", + "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.": "Você está prestes a remover esta conta da sua lista de contas disponíveis. Depois de concluído, se você precisar acessá-lo novamente, será necessário recriar a conta via semente ou por um arquivo de backup.", + "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.": "Você está prestes a remover este endereço do seu catálogo de endereços. Depois de concluído, se você precisar acessá-lo novamente, precisará adicionar novamente o endereço.", + "You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.": "Você está prestes a remover este código da sua lista de hashes de código disponíveis. Depois de concluído, se você precisar acessá-lo novamente, será necessário adicionar manualmente o código hash novamente.", + "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.": "Você está prestes a remover a ABI deste código. Depois de concluído, se você precisar acessá-lo novamente, precisará enviá-lo novamente manualmente.", + "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": "Você está prestes a remover este contrato da sua lista de contratos disponíveis. Depois de concluído, se você precisar acessá-lo novamente, precisará adicionar manualmente o endereço do contrato na guia Instanciar.", + "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.": "Você está se conectando de um local seguro a um WebSocket inseguro ({{wsUrl}}). Devido às políticas de segurança de conteúdo misto do navegador, esse tipo de conexão não é permitido. Altere o serviço RPC para um endereço 'wss' seguro.", + "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.": "Você não está conectado a um nó. Verifique se o seu nó está em execução e se o endereço do Websocket está acessível.", + "You are using an ABI with an outdated format. Please generate a new one.": "Você está usando uma ABI com um formato desatualizado. Por favor gere um novo.", + "You are voting with this collective's prime account. The vote will be the default outcome in case of any abstentions.": "Você está votando na conta principal deste coletivo. A votação será o resultado padrão em caso de abstenção.", + "You can set a custom derivation path for this account using the following syntax \"//////\". The \"/\" and \"//\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.": "Você pode definir um caminho de derivação personalizado para esta conta usando a seguinte sintaxe \"//////\". As \"/\" e \"//\" podem ser repetidas e misturadas`. A \"///senha\" é opcional e deve ocorrer apenas uma vez.", + "You do not have access to the current sudo key": "Você não tem acesso à \"sudo key\" atual", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": "Você não tem nenhuma conta. No momento, alguns recursos estão ocultos e só estarão disponíveis quando você tiver contas.", + "You have {{claimCount}} accounts that need attestations. Use the Claim Tokens app on the left navigation bar to complete the process. Until you do, your balances for those accounts will not be reflected.": "Você tem {{claimCount}} contas que precisam de atestados. Use o aplicativo Tokens de reivindicação na barra de navegação esquerda para concluir o processo. Até você, seus saldos para essas contas não serão refletidos.", + "You need to sign an attestation for the following account:": "Você precisa assinar um atestado para a seguinte conta:", + "You need to sign an attestation for the following accounts:": "Você precisa assinar um atestado para as seguintes contas:", + "You need to unlock this account to be able to sign data.": "Você precisa desbloquear esta conta para poder assinar dados.", + "You will no longer have sudo access": "Você não terá mais acesso ao \"sudo\"", + "Your Ethereum account": "Sua conta do Ethereum", + "Your custom types have been added": "Seus tipos personalizados foram adicionados", + "Your endorsement will be applied for this account.": "Seu endosso será aplicado a esta conta.", + "Your stash account. The transaction will be sent from the associated controller.": "Sua conta reserva. A transação será enviada do controlador associado.", + "a riot name linked to this identity": "um nome no riot associado a esta identidade", + "account": "conta", + "account forgotten": "conta esquecida", + "account restored": "conta restaurada", + "accounts": "contas", + "activate": "ativar", + "additional bonded funds": "fundos vinculados adicionais", + "address": "endereço", + "address copied": "endereço copiado", + "address created": "endereço criado", + "address edited": "endereço editado", + "address forgotten": "endereço esquecido", + "address prefix": "prefixo do endereço", + "amount": "montante", + "amount to use for estimation": "quantidade a ser usada para estimativa", + "approval type": "tipo de aprovação", + "approved": "aprovado", + "asset id": "ID do recurso", + "auto-selected targets for nomination": "destinos selecionados automaticamente para nomeação", + "available": "acessível", + "available signatories": "signatários disponíveis", + "available social recovery helpers": "auxiliares de recuperação social disponíveis", + "average": "média", + "aye": "sim", + "aye: bool": "sim: bool", + "backing": "apoio", + "backup file": "arquivo de backup", + "balance": "Saldo", + "balances": "saldos", + "beneficiary": "beneficiário", + "best": "melhor", + "best #": "melhor #", + "best block": "melhor bloco", + "best hash": "melhor hash", + "bids": "lances", + "block hash or number to query": "hash do bloco ou o número da consulta", + "blocks": "blocos", + "bond": "vinculo", + "bonded": "vinculado", + "call from account": "chamar da conta", + "call the selected endpoint": "chamar o endereço selecionado", + "candidate account": "conta candidata", + "candidate accounts": "contas candidatas", + "candidates": "candidatos", + "case sensitive": "sensível a maiúsculas e minúsculas", + "challenge": "desafio", + "claim to account": "reivindicação de conta", + "click to copy": "clique para copiar", + "click to select or drag and drop a JSON ABI file": "clique para selecionar ou arrastar e soltar um arquivo JSON ABI", + "click to select or drag and drop the file here": "clique para selecionar ou arraste e solte o arquivo aqui", + "clipboard": "área de transferência", + "code": "código", + "code for this contract": "código para este contrato", + "code hash": "código hash", + "commission": "comissão", + "compiled contract WASM": "contrato compilado em WASM", + "connected peers": "pares conectados", + "contacts": "Contatos", + "contract address": "endereço do contrato", + "contract to use": "contrato de uso", + "controller": "controlador", + "controller account": "conta do controlador", + "conviction": "convicção", + "conviction: Conviction": "convicção: Convicção", + "copied": "copiado", + "council candidates": "candidatos ao conselho", + "council proposal": "proposta do conselho", + "council proposal type": "tipo de proposta de conselho", + "created account": "criou uma conta", + "created multisig": "criou multi-assinatura", + "crypto type to use": "tipo de criptografia a ser usado", + "custom endpoint": "endereço personalizado", + "data": "dados", + "default icon theme": "tema padrão do ícone", + "default interface language": "idioma padrão da interface", + "defender": "defensor", + "delay": "atraso", + "democracy": "democracia", + "deploy": "implantar", + "deployment account": "conta de implantação", + "deployment constructor": "construtor de implantação", + "deposit": "depósito", + "derivation path": "caminho de derivação", + "derive root account": "derivar conta raiz", + "details": "detalhes", + "development seed": "semente de desenvolvimento", + "dispatch queue": "fila de expedição", + "display name": "Nome em Exibição", + "do not include empty strings in the generated file": "não inclua cadeias vazias no arquivo gerado", + "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": "não parece ter uma reivindicação válida. Verifique se você assinou a transação corretamente na conta ETH correta.", + "ed25519, Edwards": "ed25519, Edwards", + "elected stake": "participação eleita", + "email": "email", + "enact": "decretar", + "endowment": "doação", + "epoch": "epoca", + "era": "era", + "era points": "pontos da era", + "era {{era}}, {{count}} slashes": "era {{era}}, {{count}} punições", + "eras": "eras", + "events": "eventos", + "everything": "tudo", + "exclude option": "excluir opção", + "exclude value": "excluir valor", + "execute": "executar", + "existential deposit": "depósito de existência", + "external": "externo", + "extrinsic hash": "hash extrínseco", + "extrinsics": "extrínseco", + "fee": "taxa", + "filter by name or tags": "filtrar por nome ou tags", + "filter by name, address or index": "filtre por nome, endereço ou índice", + "filter by name, address, or account index": "filtre por nome, endereço ou índice de conta", + "finalized": "finalizado", + "finder": "localizador", + "forks": "bifurcações", + "free balance": "saldo livre", + "from the following data": "a partir dos seguintes dados", + "generated public key": "chave pública gerada", + "has a valid claim for": "tem uma reivindicação válida para", + "hash": "hash", + "heads": "cabeçalhos", + "hex input data": "dados de entrada hexadecimal", + "hex-encoded storage key": "chave de armazenamento codificada em hexadecimal", + "identity": "identidade", + "imminent preimage (proposal already passed)": "pré-imagem iminente (proposta já aprovada)", + "immortal": "imortal", + "include all empty strings in the generated file": "inclui todas as strings vazias no arquivo gerado", + "include option": "incluir opção", + "include value": "inclua valor", + "index": "indíce", + "initial head state": "estado inicial do cabeçalho", + "intentions": "intenções", + "interface operation mode": "modo de operação da interface", + "invalid ABI file selected": "arquivo ABI inválido selecionado", + "invalid/unknown registrar account": "conta de registrador inválida/desconhecida", + "judgement": "julgamento", + "key type to set": "tipo de chave a ser definido", + "keypair crypto type": "tipo de criptografia de par de chaves", + "kind": "tipo", + "last #": "último #", + "last block": "último bloco", + "last reward": "última recompensa", + "launch period": "período de lançamento", + "legal name": "nome legal", + "lifetime": "tempo de vida", + "lng.detect": "Idioma padrão do navegador (detecção automática)", + "lock expired": "bloqueio expirado", + "locked": "bloqueado", + "locked balance": "saldo bloqueado", + "logs": "Registros", + "manage hardware connections": "gerenciar conexões de hardware", + "matches": "combinações", + "maximum gas allowed": "gás máximo permitido", + "members": "membros", + "message to send": "mensagem para enviar", + "minimum bond": "vínculo mínima", + "minimum deposit": "depósito mínimo", + "mnemonic seed": "semente mnemônica", + "mortal, valid from #{{startAt}} to #{{endsAt}}": "mortal, válido de # {{startAt}} a # {{endsAt}}", + "motions": "moções", + "multisig": "multi-assinatura", + "multisig name": "nome multi-assinatura", + "my ordered votes": "meus votos ordenados", + "name": "nome", + "nav.accounts": "Contas", + "nav.claims": "Reivindicar tokens", + "nav.contracts": "Contratos", + "nav.council": "Conselho", + "nav.dashboard": "Painel de controle", + "nav.democracy": "Democracia", + "nav.explorer": "Explorer", + "nav.extrinsics": "Extrínsicas", + "nav.generic-asset": "Ativo genérico", + "nav.github": "GitHub", + "nav.i18n": "Tradutor I18n", + "nav.js": "Javascript", + "nav.parachains": "Parachains", + "nav.settings": "Configurações", + "nav.society": "Sociedade", + "nav.staking": "Participação", + "nav.storage": "Estado da Cadeia", + "nav.sudo": "Sudo", + "nav.tech-comm": "Comitê técnico", + "nav.toolbox": "Ferramentas", + "nav.transfer": "Transferir", + "nav.treasury": "Tesouraria", + "nav.wiki": "Wiki", + "nay": "não", + "new account": "nova conta", + "new address": "novo endereço", + "next": "Próximo", + "next id": "próximo id", + "no": "não", + "no accounts yet, create or import an existing": "ainda não existem contas, crie ou importe uma existente", + "no addresses saved yet, add any existing address": "nenhum endereço salvo ainda, adicione qualquer endereço existente", + "no messages": "sem mensagens", + "no peers connected": "nenhum par conectado", + "no tags": "sem tags", + "no unapplied slashes found": "nenhuma punição não aplicada encontrada", + "nominated accounts": "contas nomeadas", + "nominators": "nominadores", + "not signed": "não assinado", + "on-chain bonding duration": "duração do vínculo em cadeia", + "ongoing referendum": "referendo em curso", + "only this network": "somente esta rede", + "origin": "origem", + "other stake": "outra participação", + "our best": "nosso melhor", + "own stake": "participação própria", + "parachain id": "id da parachain", + "parachains": "parachains", + "parent": "superior", + "password": "senha", + "password (repeat)": "senha (repetir)", + "payment": "Forma de pagamento", + "payment destination": "destino do pagamento", + "payout stakers for (multiple)": "pagar participantes para (vários)", + "payout stakers for (single)": "pagar participantes para (único)", + "payout/stash": "pagamento/reserva", + "payout/validator": "pagamento/validador", + "peer best": "melhor do par", + "pending extrinsics": "extrínsecas pendentes", + "pending hashes": "hashes pendentes", + "pending swap id": "ID de troca pendente", + "points": "pontos", + "pot": "pote", + "preimage hash": "hash de pré-imagem", + "prev": "prev", + "prime member": "membro principal", + "prime voter": "voto principal", + "priority {{index}}": "prioridade {{index}}", + "profit/era est": "lucro/era est", + "proposal": "proposta", + "proposal bond": "vínculo da proposta", + "proposal hash": "hash da proposta", + "proposals": "propostas", + "propose": "ofertas", + "propose from account": "propor da conta", + "proposed by": "proposto por", + "proposer": "proponente", + "queued tx": "transmissão na fila", + "reason": "razão", + "recent blocks": "blocos recentes", + "recent events": "eventos recentes", + "recover this account": "recuperar esta conta", + "recovery block delay": "atraso no bloco de recuperação", + "recovery threshold": "mínimo para recuperação", + "redeemable": "resgatável", + "referenda": "referendo", + "refresh in": "atualizar em", + "registrar account": "conta de registrador", + "registrar index": "índice de registradores", + "relay dispatch queue": "retransmitir fila de envio", + "remaining": "remanescente", + "remote node/endpoint to connect to": "nó/endereço remoto ao qual conectar", + "request payout from": "solicitar pagamento de", + "reserved": "reservado", + "reward commission percentage": "porcentagem de comissão de recompensa", + "rewards": "recompensas", + "rewards & slashes": "recompensas e punições", + "riot": "riot", + "riot name": "nome no riot", + "role": "Função", + "rotation": "rotação", + "rpc.arcadia": "Arcadia (Nodle Testnet, hospedado por Nodle)", + "rpc.berlin": "Berlin (Edgeware Testnet, hospedado pelo Commonwealth Labs)", + "rpc.custom": "Ambiente personalizado", + "rpc.edgeware": "Edgeware (Edgeware Mainnet, hospedado pelo Commonwealth Labs)", + "rpc.flamingfir": "Fir flamejante (Substrate Testnet, hospedado pela Parity)", + "rpc.header.dev": "Desenvolvimento", + "rpc.header.live": "Redes ativas", + "rpc.header.test": "Redes de teste", + "rpc.kulupu": "Grupos (Grupos Mainnet, hospedados por Grupos)", + "rpc.kusama.ava": "Kusama (Polkadot Canary, nós públicos executados por usuários; consulte https://status.cloud.ava.do/)", + "rpc.kusama.parity": "Kusama (Polkadot Canary, hospedado pela Parity)", + "rpc.kusama.w3f": "Kusama (Polkadot Canary, hospedado pela Web3 Foundation)", + "rpc.local": "Nó local (Próprio, 127.0.0.1:9944)", + "rpc.mandala": "Mandala (Acala Testnet, hosted by Acala)", + "rpc.westend": "Westend (Polkadot Testnet, hospedado pela Parity)", + "runners up": "finalistas", + "scheduling": "agendamento", + "seats": "assentos", + "second with account": "segundo com a conta", + "secret": "segredo", + "secret derivation path": "caminho secreto de derivação", + "seed (hex or string)": "semente (hex ou string)", + "select the account you wish to sign data with": "selecione a conta na qual deseja assinar os dados", + "selected constant query": "consulta constante selecionada", + "selected signatories": "signatários selecionados", + "selected state query": "consulta de estado selecionado", + "selected validators": "validadores selecionados", + "send": "enviar", + "send as RPC call": "enviar como chamada RPC", + "send as transaction": "enviar como transação", + "send from account": "enviar da conta", + "send to address": "enviar para o endereço", + "sending account": "enviando conta", + "sending from my account": "enviando da minha conta", + "session": "sessão", + "session keys": "chaves de sessão", + "session next": "sessão seguinte", + "sign data from account": "assinar dados da conta", + "sign the following data": "assinar os seguintes dados", + "signatories": "signatários", + "signatory": "signatário", + "signature crypto type": "tipo de criptografia de assinatura", + "signature of supplied data": "assinatura dos dados fornecidos", + "signer": "signatário", + "society head": "chefe da sociedade", + "spend period": "gastar período", + "sr15519, Schnorrkel": "sr15519, Schnorrkel", + "ss58.centrifuge": "Centrifuge (ativa)", + "ss58.default": "Replicar nó conectado", + "ss58.edgeware": "Edgeware (ativa)", + "ss58.kusama": "Kusama (canário)", + "ss58.polkadot": "Polkadot (ativa)", + "ss58.substrate": "Substrate (genérica)", + "staked": "em participação", + "stash account": "conta reserva", + "stashes": "reservas", + "state": "Estado", + "strikes": "batidas", + "submit the following change": "envie a seguinte alteração", + "submit the following extrinsic": "envie o seguinte extrínseco", + "submit with account": "enviar com conta", + "submit with council account": "enviar com a conta do conselho", + "sudo key": "\"sudo key\"", + "sudo with unchecked weight parameter": "\"sudo\" com parâmetro de peso não verificado", + "sudo without unchecked weight parameter": "\"sudo\" sem parâmetro de peso desmarcado", + "suri (seed & derivation)": "suri (semente e derivação)", + "swap to id": "trocar para id", + "syncing": "sincronizando", + "system events": "eventos do sistema", + "tags": "Tag", + "target": "alvo", + "term progress": "progresso do termo", + "the account to make recoverable": "a conta para recuperar", + "the account to recover to": "a conta para recuperar para", + "the associated controller": "o controlador associado", + "the era to cancel for": "a época para cancelar", + "the language to display translations for": "o idioma para exibir traduções para", + "the module to display strings for": "o módulo para exibir seqüências de caracteres", + "the resulting hash is": "o hash resultante é", + "the stash account to nominate with": "a conta reserva para nomear com", + "the supplied signature": "a assinatura fornecida", + "threshold": "mínimo", + "tip": "gorjeta", + "tip reason": "razão da gorjeta", + "tip value": "valor da gorjeta", + "tips": "gorjetas", + "total": "total", + "total issuance": "emissão total", + "total peers": "total de pares", + "total stake": "participação total", + "total staked": "total em participação", + "transactions": "transações", + "transfer received": "transferência recebida", + "transferrable": "transferível", + "trusted social recovery helpers": "auxiliares confiáveis de recuperação social", + "turnout": "vire para fora", + "twitter": "Twitter", + "type": "tipo", + "unbond amount": "quantidade desvinculada", + "unbonding": "desvinculando", + "unchecked weight for this call": "peso não verificado para esta chamada", + "unlock account with password": "desbloquear conta com senha", + "unstake threshold": "limiar de participação", + "update on #{{index}}": "atualização em # {{index}}", + "upgradable extensions": "extensões atualizáveis", + "use on any network": "use em qualquer rede", + "using my account": "usando minha conta", + "using the following data": "usando os seguintes dados", + "using the selected account": "usando a conta selecionada", + "validator to query": "validador para consulta", + "validators": "validadores", + "validators/nominators": "validadores/nominadores", + "value": "valor", + "value bonded": "valor vinculado", + "verify using address": "verifique usando o endereço", + "version {{version}}": "versão {{version}}", + "vested": "investido", + "via Council/Vote": "via Conselho/Votação", + "via Democracy/Vote": "via Democracia/Votação", + "via Staking/Bond": "via Participação/Vínculo", + "via Vesting": "via Aquisição", + "vote for candidate": "vote no candidato", + "vote for defender": "vote no defensor", + "vote from account": "vote da conta", + "vote value": "valor do voto", + "vote with account": "vote com conta", + "votes": "votos", + "voting account": "conta de voto", + "voting balance": "saldo de votos", + "voting end": "final da votação", + "voting period": "período de votação", + "voting round": "votação", + "waiting": "esperando", + "web": "web", + "website": "website", + "with an index of": "com um índice de", + "wrong password supplied": "senha incorreta fornecida", + "yes": "sim", + "your current password": "Sua senha atual", + "your new password": "Sua nova senha", + "{{blocks}} blocks": "{{blocks}} blocos", + "{{count}} key/value pairs encoded for submission": "{{count}} pares de chave/valor codificados para envio", + "{{count}} own stashes": "{{count}} próprias reservas", + "{{currency}} average": "{{currency}} em média", + "{{currency}} clipped": "{{currency}} recortado", + "{{currency}} rewards": "{{currency}} recompensas", + "{{currency}} slashed": "{{currency}} punições", + "{{currency}} total": "{{currency}} total", + "{{done}}/{{total}}, {{progress}}% done": "{{done}}/{{total}}, {{progress}}% concluído", + "{{d}} days": "{{d}} dias", + "{{executionTime}}s execution timetempo de execução de ": "{{ExecutionTime}}", + "{{h}} hrs": "{{h}} hrs", + "{{m}} mins": "{{m}} min", + "{{name}} ({{size}} bytes)": "{{name}} ({{size}} bytes)", + "{{percentage}} aye": "{{percent}} sim", + "{{percentage}}% of block time": "{{percent}}% de tempo de bloqueio", + "{{relayDispatchQueueSize}} dispatch messages pending": "{{relayDispatchQueueSize}} envia mensagens pendentes", + "{{step}}. Sign with your ETH address": "{{step}}. Assine com seu endereço ETH", + "{{s}} s": "{{s}} s", + "{{threshold}}, not passing": "{{threshold}}, não passando", + "{{threshold}}, passing": "{{threshold}}, passando", + "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}} x saldo de votos, bloqueado por {{lock}} x promulgação ({{period}} dias)" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/ru/translation.json b/pioneer/packages/apps/public/locales/ru/translation.json new file mode 100644 index 0000000000..fc17e6e1d7 --- /dev/null +++ b/pioneer/packages/apps/public/locales/ru/translation.json @@ -0,0 +1,1134 @@ +{ + "//hard///password": "//жесткий///пароль", + "//hard/soft": "//жесткий/мягкий", + "//hard/soft///password": "//жесткий/мягкий///пароль", + "0.1x voting balance, no lockup period": "0,1* умножитель голоса, средства не депонируются", + "1 day": "1 день", + "1 hr": "1 ч", + "1 min": "1 мин", + "1 s": "1 сек", + "1. Select your {{chain}} account": "1. Выберите свой {{chain}} аккаунт", + "2. Enter the ETH address from the sale.": "2. Введите Ваш адрес Ethereum, который использовался при покупке/продаже.", + "2. Sign ETH transaction": "2. Подпишите ETH транзакцию", + "": "<пустой>", + "": "<неизвестный>", + "A controller account should not be set to manages multiple stashes. The selected controller is already controlling {{stashId}}": "Аккаунт контроллера не должен управлять несколькими копилками. Выбранный контроллер уже управляется {{stashId}}", + "A controller account should not map to another stash. This selected controller is a stash, controlled by {{bondedId}}": "Аккаунт контроллера не должен управлять несколькими копилками. Выбранный контроллер уже управляется {{bondedId}}", + "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": "Разворачиваемый контракт либо уже развернут, либо приложен. Адрес и ABI используются для создания параметров.", + "A fee of {{creationFee}} will be deducted from the sender since the destination account does not exist": "Сбор {{creationFee}} будет списан с отправителя, поскольку аккаунт получателя не существует", + "A reason (to be stored-on-chain) as to why the recipient deserves a tip payout.": "Причина (будет сохранена в сети) почему получатель заслуживает чаевые", + "ABI": "ABI", + "Acceptance proposal to council": "Направить предложение Совету с голосом за", + "Account actions": "Функции аккаунта", + "Account balance:": "Баланс аккаунта", + "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.": "Учетные записи, введенные из любого из этих расширений, появятся в этом приложении и будут доступны для использования. Приведенный выше список обновляется по мере появления дополнительных расширений с возможностью внешней подписи.", + "Active nominations ({{count}})": "Активные номинации ({{count}})", + "Add a tip to this extrinsic, paying the block author for greater priority": "Добавыть чаевые для автора блока для большей приоритезации", + "Add account": "Добавить аккаунт", + "Add account via Qr": "Добавить аккаунт с помощью QR-кода", + "Add an account via seed": "Добавить аккаунт с помощью секретной фразы", + "Add an address": "Добавить адрес", + "Add an existing code hash": "Добавить существующий хэш кода", + "Add an existing contract": "Добавить существующий контракт", + "Add contact": "Добавить контакт", + "Add identity judgment": "Добавить оценку персонализации", + "Add item": "Добавить пункт", + "Add multisig": "Добавить мультиподпись", + "Add via Qr": "Добавить с помощью QR-кода", + "Add via backup file": "Добавить с помощью резервного файла", + "Adding an optional tip to the transaction could allow for higher priority, especially when the chain is busy.": "Добавлением чаевых (опционально) в транзакцию Вы можете получить приоритет, особенно когда сеть переполнена", + "Additional types as a JSON file (or edit below)": "Дополнительные типы в виде JSON файла (либо отредактировать ниже)", + "Address Prefix": "Префикс адреса", + "Adjust the mode from basic (with a limited number of beginner-user-friendly apps) to full (with all basic & advanced apps available)": "Поменять режим с базового (с ограниченным количеством приложений для новичков) на полный (со всей базовой и продвинутой функциональностью)", + "Advanced creation options": "Дополнительные опции создания", + "All rewards will go towards the selected output destination when a payout is made.": "Все вознаграждения будут отправлены в выбранное место назначения вывода, когда будет произведена выплата.", + "All the listed validators and all their nominators will receive their rewards.": "Все валидаторы из этого списка и их номинаторы получат вознаграждение.", + "Allocate a suggested tip amount. With enough endorsements, the suggested values are averaged and sent to the beneficiary.": "Предложите сумму чаевых. При достаточном уровне поддержки предложенные суммы усредняются и отправляются получателю.", + "Amount to add to the currently bonded funds. This is adjusted using the available funds on the account.": "Сумма, которую хотите добавить к депонированным средствам. Они вычитаются из доступных средств аккаунта.", + "An URL that is linked to this identity.": "URL, связанный с данной личностью.", + "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.": "Зашифрованный файл резеравной копии будет создан после того, как Вы нажмете кнопку \"Скачать\". Это может быть использовано для повторного импорта Вашей учетной записи на любом другом компьютере.", + "An encrypted backup file will be created once you have pressed the \\Download\\ button. This can be used to re-import your account on any other machine.": "Зашифрованный резервный файл будет создан при нажатии кнопки \\Скачать\\. Он может быть использован для импорта Вашего аккаунта на любом другом компьютере.", + "Any account can request payout for stakers, this is not limited to accounts that will be rewarded.": "Любой аккаунт может требовать платеж для стейкеров, не только аккаунты которые получат вознаграждение.", + "Approvals": "Одобренные", + "Approve or reject this defender.": "Одобрить либо отклонить этого защитника.", + "Approve this call hash": "Подтвердить хэш вызова", + "Approve this candidacy.": "Одобрить эту кандидатуру", + "Approved": "Одобрено", + "As a council member, you can suggest an initial value for the tip, each other council member can suggest their own.": "Как член Совета Вы можете предложить сумму чаевых, каждый член может предложить сумму на свое усмотрение", + "Asset ID": "Идентификатор актива", + "Assets": "Активы", + "Aura": "Aura", + "Authorize transaction": "Авторизовать транзакцию", + "Aye": "За", + "Aye, I approve": "За, я одобряю", + "Babe": "Babe", + "Backup account": "Сохранить резервную копию аккаунта", + "Block details": "Детали блока", + "Bond": "Депонировать", + "Bond & Nominate": "Депонировать и Номинировать", + "Bond & Validate": "Депонировать & Валидировать", + "Bond more": "Депонировать ещё", + "Bond more funds": "Депонировать ещё средств", + "Bonding Preferences": "Настройки депонирования", + "Call": "Вызвать", + "Call a contract": "Вызвать контракт", + "Call a method on this contract": "Вызвать метод этого контракта", + "Call results": "Результаты вызова", + "Call this message": "Вызвать сообщение", + "Cancel": "Отменить", + "Cancel slashes": "Отменить слэши", + "Cancel this call hash": "Отменить этот хэш вызова", + "Chain info": "Информация сети", + "Chain specifications": "Спецификация сети", + "Change": "Изменить", + "Change account password": "Изменить пароль аккаунта", + "Change controller account": "Поменять контроллер", + "Change reward destination": "Поменять получателя вознаграждения", + "Change session keys": "Поменять сессионные ключи", + "Change this account's password": "Поменять пароль этого аккаунта", + "Change validator preferences": "Поменять настройки валидаторов", + "Changing the key only takes effect at the start of the next session. The input here is generates from the author_rotateKeys command": "Ключ меняется только в начале следующей сессии. Это значение генерируется от имени author_rotateKeys команды", + "Claim": "Запросить", + "Claim tokens...": "Запросить токены...", + "Clear all": "Стереть все", + "Clear expired democracy locks": "Освободить истекшие блокировки модуля демократии", + "Close": "Закрыть", + "Close proposal": "Закрыть предложение", + "Color": "Цвет", + "Committee prime member, default voting": "Главный член Комитета, голосование по определению", + "Confirm ABI removal": "Подтвердить удаление ABI", + "Confirm account removal": "Подтвердить удаление аккаунта", + "Confirm address removal": "Подтвердить удаление адреса", + "Confirm claim": "Подтвердить заявку", + "Confirm code removal": "Подтвердить удаление кода", + "Confirm contract removal": "Подтвердить удаление контракта", + "Constants": "Константы", + "Continue": "Продолжить", + "Conviction locks do overlap and is additive, meaning that funds locked during a previous vote can be locked again.": "Голосование возможно также и токенами, которые уже выделены под голосование по другому вопросу.", + "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "Скопируйте строку выше и подпишите ее с помощью транзакции в сети Ethereum с учетной записи, которую Вы использовали в предпродаже,используя строку как \\рayload\\. Затем вставьте обьект подписи транзакции ниже", + "Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:": "Скопируйте следующую строку и подпишите ее учетной записью Ethereum, которую Вы использовали во время предпродажи, используя строку как \\payload\\. Вставьте полученную подпись ниже:", + "Council": "Совет", + "Council overview": "Обзор Совета", + "Create": "Создать", + "Create a backup file for this account": "Создать резервную копиюл для этого аккаунта", + "Create an account now.": "Создать аккаунт сейчас.", + "Create and backup account": "Создать аккаунт и его резервную копию", + "Crypto not detected": "Средства не обнаружены", + "Cryptography used to create this signature. It is auto-detected on valid signatures.": "Шифрование для этой подписи. Для валидных подписей определяется автоматически.", + "Current account nonce": "Nonce этого аккаунта", + "Current account nonce: {{accountNonce}}": "Текущий счет nonce: {{accountNonce}}", + "Current prime member, default voting": "Текущий главный участник, голосование по умолчанию", + "Current society head, exempt": "Нынешний глава общества, освобожденный от должности", + "Decimals": "Десятичные знаки", + "Decimals decides the smallest unit of the token, which is 1/10^decimals": "Десятичные знаки определяют наименьшую единицу токена, т.е. 1/10^decimals", + "Delete this custom example": "Стереть этот пример", + "Democracy overview": "Обзор демократии", + "Deploy": "Развернуть", + "Deploy a code hash": "Развернуть хэш кода", + "Deploy this code hash as a smart contract": "Развернуть этот хэш кода как смарт-контракт", + "Deploy with this constructor": "Развернуть с этим конструктором", + "Deposit": "Внести", + "Deregister this parachain": "Отменить регистрацию этого парачейна", + "Derive account from pair": "Извлечь аккаунт из пары", + "Derive account via derivation path": "Извлечь аккаунт с помощью пути деривации", + "Details": "Детали", + "Detection on the input string to determine if it is hex or non-hex.": "Проверка введнной строки для определения является оно 16-значным или нет.", + "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".": "Определяет какой криптографический алгоритм будет использован для создания этой учетной записи. Для валидации в Polkadot аккаунт должен использовать \"ed25519\".", + "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \\ed25519\\.": "Определяет какой криптографический алгоритм будет использован для создания этого аккаунта. Для валидации в Polkadot аккаунт должен использовать \\ed25519\\.", + "Developer": "Разработчик", + "Development": "В разработке", + "Dismiss all notifications": "Убрать все уведомления", + "Display overview information for the selected validator, including blocks produced.": "Показать информацию о выбранном валидаторе, включая произведенные блоки.", + "Distinct stash and controller accounts are recommended to ensure fund security. You will be allowed to make the transaction, but take care to not tie up all funds, only use a portion of the available funds during this period.": "Для безопасности Ваших средств рекомендуется использовать разные аккаунты для копилки и контроллера. Эта транзакция разрешается, но убедитесь в том, что оставите свободные средства для оплаты будущих комиссий.", + "Do not include a tip for the block author": "Не включать чаевые для автора блока", + "Download": "Скачать", + "Either approve or reject this call.": "Разрешить или отклонить этот вызов.", + "Endorse": "Поддержать", + "Endorsements ({{count}})": "Поддержал ({{count}})", + "Ensure that not all funds are locked, funds need to be available for fees.": "Убедитесь, что оставили свободные средства на платежи", + "Enter the Asset ID of the token you want to manage.": "Введите идентификатор Актива, которым хотите управлять", + "Enter the Asset ID of the token you want to transfer.": "Введите идентификатор Актива, который хотите перевести", + "Erroneous": "Ошибочный", + "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)": "Оценено {{count}} ключей за {{elapsed}}с ({{avg}} ключей/c)", + "Extensions": "Расширения", + "External": "Внешний", + "Extrinsic submission": "Подача экстринзика", + "Fast track": "Ускоренный", + "Fast track proposal": "Ускоренное предложение", + "Filter available candidates based on name, address or short account index.": "Фильтровать кандидатов по имени, адресу или короткому индексу аккаунта", + "For fund security, your session key should not match your stash key.": "В целях безопасности сессионный ключ должен отличаться от ключа копилки", + "Forget": "Забыть", + "Forget this account": "Забыть этот аккаунт", + "Forget this address": "Забыть этот адрес", + "Forget this asset": "Забыть этот актив", + "Forget this code hash": "Забыть этот хэш кода", + "Forget this contract": "Забыть этот контракт", + "Forks": "Форки", + "General": "Общий", + "Generate {{lng}}/translation.json": "Создать {{lng}}/translation.json", + "Genesis Hash": "Хэш Генезиса", + "Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched": "Хэш Генезиса - исходное состояние сети, после запуска его невозможно изменить", + "Grandpa": "Grandpa", + "Hash data": "Данные хэша", + "I agree": "Я согласен", + "I'm Online": "Я в сети", + "If the recipient account is new, the balance needs to be more than the existential deposit. Likewise if the sending account balance drops below the same value, the account will be removed from the state.": "Если получатель - новый аккаунт, то баланс должен быть больше минимального депозита. Аналогично если баланс аккаунта отправителя упадет ниже этого значения, то аккаунт будет исключен из состояния.", + "If this proposal is passed, the changes will be applied via dispatch and the deposit returned.": "Если это предложение будет одобрено, то изменения будут внесены и депозит вернется.", + "If you are moving accounts between applications, ensure that you use the correct type.": "Если Вы перемещаете учетные записи между приложениями, убедитесь, что Вы используете правильный тип.", + "Important notice": "Важное уведомление", + "In calculating the election outcome, this prioritized vote ordering will be used to determine the final score for the candidates.": "Для подсчета результатов голосования будет использовано приоритезированное голосование", + "Inactive nominations ({{count}})": "Неактивные номинации ({{count}})", + "Include an optional tip for faster processing": "Добавить чаевые для приоритетной обработки", + "Initializing connection": "Запуск соединения", + "Initiate account recovery": "Начать восстановление аккаунта", + "Initiate recovery for another": "Начать восстановление другого аккаунта", + "Inject Keys": "Вставить Ключи", + "Inject session keys (advanced)": "Вставить сессионный ключ (для опытных)", + "Injected": "Введеный", + "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -": "Рекомендуется создавать/хранить аккаунты безопасно и отдельно от приложения. В {{yourBrowser}} есть следующие расширения -", + "Judge": "Оценить", + "Keys from rotateKeys": "Ключи из rotateKeys", + "Known good": "Хорошо известен", + "Learn more...": "Узнать больше...", + "Less": "Меньше", + "Lifetime (# of blocks)": "Пожизненно (число блоков)", + "Loading": "Загрузка", + "Locked1x": "Заперто1х", + "Locked2x": "Заперто2х", + "Locked3x": "Заперто3х", + "Locked4x": "Заперто4х", + "Locked5x": "Заперто5х", + "Locked6x": "Заперто6х", + "Low quality": "Низкого качества", + "Make Transfer": "Выполнить Трансфер", + "Make recoverable": "Сделать восстановимым", + "Manage your connection to Ledger S": "Управление соединением с Ledger S", + "Message data": "Данные сообщения", + "Message origin.": "Источник сообщения", + "Messages": "Сообщения", + "Metadata {{count}}": "Метаданные {{count}}", + "Mnemonic": "Мнемонический", + "More": "Ещё", + "Most recent head data": "Последние данные", + "Motions ({{count}})": "Ходатайства ({{count}})", + "Multiple council proposals could exist, both approval and rejection. Apply your vote to the correct council proposal (also available on council motions page)": "Нескольо предложний могут существовать, как одобренных так и отклоненных. Голосуйте в правильном предложении (это можно сделать и на странице Решения Совета)", + "Multisig": "Мультиподпись", + "Multisig approval with hash (non-final approval)": "Разрешить с хэшом мультиподписи (не финальное разрешение)", + "Multisig approval with hash (not message with call)": "Разрешить с хэшом мультиподписи (вызов без сообщения)", + "Multisig approvals": "Разрешения с мультиподписью", + "Multisig message with call (for final approval)": "Сообщение мультиподписи с вызовом (для финального разрешения)", + "Multisig message with call (not approval with hash)": "Сообщение мультиподписи с вызовом (не является разрешением с хэшом)", + "Mutates contract state": "Мутирует состояние контракта", + "My accounts": "Мои аккаунты", + "My contacts": "Мои контакты", + "Name given to this account. You can change it at any point in the future.": "Имя, данное этому аккаунту. Можно изменить в любой момент.", + "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".": "Имя, данное этому аккаунту. Можно изменить в любой момент. Чтобы использовать учетную запись для проверки или номинации, рекомендуется добавить функцию учетной записи в имя, например \"name_you_want-stash\".", + "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \\name_you_want - stash\\.": "Название этого аккаунта. Можно изменить. Чтобы использовать аккаунт для валидации или номинации рекомендуется добавить предназначение аккаунта в его название, например \\любое_название - копилка\\.", + "Name given to this multisig. You can edit it at any later point in time.": "Название этого аккаунта с мультиподписью. Можно изменить в любой момент.", + "Name of the network. It is only for display purposes.": "Название сети,используется только для показа.", + "Name your example": "Назовите Ваш пример", + "Nay": "Против", + "Nay, I do not approve": "Против, я не одобряю", + "Network Name": "Название Сети", + "New stake": "Новый стейк", + "No": "Нет", + "No active members": "Нет активных членов", + "No active proposals": "Нет активных предложений", + "No active referendums": "Нет активных референдумов", + "No active validators found": "Не найдено активных валидаторов", + "No active validators to check": "Нет активных валидаторов для проверки", + "No approved proposals": "Нет одобренных предложений", + "No assets found.": "Не найдено активов", + "No bids": "Нет предложений", + "No blocks available": "Нет доступных блоков", + "No candidates": "Нет кандидатов", + "No candidates found": "Кандидаты не найдены", + "No code hashes available": "Нет доступных хэшей кода", + "No committee proposals": "Нет предложений для Комитета", + "No contracts available": "Нет доступных контрактов", + "No council motions": "Нет ходатайств в Совет", + "No documentation provided": "Нет документации", + "No events available": "Нет доступных событий", + "No external proposal": "Нет внешних предложений", + "No funds staked yet. Bond funds to validate or nominate a validator": "Нет депонированных средств. Депонируйте средства для валидации или номинации", + "No items": "Нет элементов", + "No judgments": "Нет оценок", + "No logs available": "Нет доступных логов", + "No matches found": "Совпадений не найдено", + "No members found": "Участников не найдено", + "No open tips": "Нет запросов на чаевые", + "No pending era payouts from validators": "Нет ожидающих платежей от валидаторов", + "No pending extrinsics are in the queue": "В очереди нет экстринзиков", + "No pending payouts for your stashes": "Для Ваших копилок нет ожидающих платежей", + "No pending proposals": "Нет предложений", + "No runners up found": "Претендентов не найдено", + "No upgradable extensions found": "Не нашлось расширений с обновлениями", + "No waiting validators found": "Нет ожидающих валидаторов", + "Node info": "Информация о ноде", + "Nominate": "Номинировать", + "Nominate Validators": "Номинировать валидаторов", + "Nominate selected": "Номинировать выбранные", + "Nominate validators": "Номинировать валидаторов", + "Nominations ({{count}})": "Номинаций ({{count}})", + "Nominator": "Номинатор", + "Nominators can be selected automatically based on the current on-chain conditions or supplied manually as selected from the list of all currently available validators. In both cases, your favorites appear for the selection.": "Валидаторов можно выбрать автоматически исходя из текущего состояния сети, либо вручную, выбирая из списка текущих валидаторов. В обоих случаях Ваши избранные валидаторы будут в списке.", + "Nonce": "Nonce", + "None": "Никто", + "Not updated in the last block": "Не обновлено в последнем блоке", + "Nothing queued for execution": "Очередь пуста", + "Of the beneficiary amount, at least {{bondPercentage}} would need to be put up as collateral. The maximum of this and the minimum bond will be used to secure the proposal, refundable if it passes.": "Минимум {{bondPercentage}} от запрошенной суммы требуется в качестве депозита. Большее из этого значения и минимального депозита будет использовано для обеспечения, возвращаемого если предложение будет одобрено.", + "Once bonded, it wil need to be unlocked/withdrawn and will be locked for at least the bonding duration.": "Депонируемые средства будут недоступны как минимум на срок депозита, и требуют операции освобождения.", + "Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, the nominations will show as inactive.": "Новый подбор будет действительным через 2 эры. До этого номинации будут показаны как неактивные.", + "One of your available nomination accounts, keyed by the stash. The transaction will be sent from the controller.": "Один из Ваших доступных аккаунтов номинации занят аккаунтом-копилкой. Транзакция будет отправлена с аккаунта-контроллера.", + "Only applicable if the proposal has already passed and is ready for dispatch.": "Валидно только для одобренных предложений, готовых для отправки.", + "Override any applicable values for the specific signed output. These will be used to construct and display the signed transaction.": "Переопределите все применимые значения для данного подписанного вывода. Они будут использоваться для построения и отображения подписанной транзакции.", + "Override the default identity icon display with a specific theme": "Заменить базовый вариант иконки личности", + "Override the default ss58 prefix for address generation": "Заменить базовый ss58 префикс для генерации адреса", + "Owned": "В собственности", + "Parachains": "Парачейны", + "Parachains overview": "Обзор парачейна", + "Paste here the address of the contact you want to add to your address book.": "Вставтье адрес контакта, который хотите добавить в Вашу адресную книгу.", + "Payout": "Выплатить", + "Payout all": "Выплатить всем", + "Payout all stakers": "Выплата всем стейкерам", + "Pending call hashes": "Ожидающие вызова хэши", + "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.": "Пожалуйста, сохраните этот файл в безопасном месте, поскольку он вместе с паролем необходим для восстановления Вашего аккаунта.", + "Please read these terms and conditions carefully. By submitting this statement, you are deemed to have accepted these Terms and Conditions. If you do not agree to these terms, please refrain from accessing or proceeding. You can also find them at:": "Пожалуйста, прочитайте условия соглашения внимательно. Отправляя это заявление, Вы соглашаетесь с данными условиями. Если Вы не согласны, будьте добры, не продолжайте процесс. Вы также можете найти соглашение по адресу:", + "Positive number": "Положительное число", + "Positive number between 1 and {{count}}": "Положительное число между 1 и {{count}}", + "Positive number between 1 and {{memberCount}}": "Положительное число от 1 до {{memberCount}}", + "Positive number greater than or equal to {{nextFreeId}}": "Положительное число большее или равно {{nextFreeId}}", + "Pre-sale ethereum address": "Адрес Ethereum, используемый на предпродаже", + "Prefix indicates the ss58 address format in this network, it is a number between 0 ~ 255 that describes the precise format of the bytes of the address": "Префикс обозначает, что в этой сети используется формат ss58 для адресов. Это число между 0 и 255, оно описивает точный формат байтов адреса", + "Preimage": "Прообраз", + "Present the QR code containing the signature to the UI. Once scanned it will be submitted for on-chain processing and execution.": "Предьявите QR код с подписью. После сканирования транзакция будет запущена для исполнения в сети", + "Proposal can either be to approve or reject this spend. One approved, the change is applied by either removing the proposal or scheduling payout.": "Предложение может быть либо одобрено, либо отклонено. Вы можете либо произвести оплату, либо удалить предложение", + "Proposals": "Предложения", + "Proposals ({{count}})": "Предложений ({{count}})", + "Propose": "Предложить", + "Propose a committee motion": "Сделать предложение комитету", + "Propose a council motion": "Предложить ходатайство Совету", + "Propose external": "Предложить публично", + "Propose external (majority)": "Предложить публично (большинство)", + "Propose motion": "Предложить на голосование", + "Propose tip": "Предложить чаевые", + "Provide judgement": "Сделать оценку", + "Provide the account QR from the module/external application for scanning. One detected as valid, you will be taken to the next step to add the account to your list.": "Предоставьте QR-код аккаунта из модуля/внешнего приложения для сканирования. Когда он будет правильным, Вы перейдете к следующему шагу, чтобы добавить учетную запись в свой список.", + "Query Ledger": "Книга запросов", + "RPC calls": "RPC вызовы", + "Random": "Случайный", + "Raw seed": "Исходная секретная фраза", + "Raw storage": "Хранилище исходных фраз", + "Reasonable": "Обоснованный", + "Reassign": "Переназначить", + "Redeem": "Получить", + "Register": "Зарегистрировать", + "Register Asset": "Зарегистрировать Актив", + "Register a parachain": "Зарегистрировать парачейн", + "Register an Asset": "Зарегистрировать Актив", + "Register identity": "Привязать к аккаунту личность (персональные данные)", + "Register the vote for or against the proposal.": "Голосовать за или против предложения.", + "Rejection proposal to council": "Направить предложение Совету с голосом против", + "Rejections": "Отказы", + "Remove": "Убрать", + "Remove ABI": "Убрать ABI", + "Remove item": "Убрать пункт", + "Reset": "Сбросить", + "Restore": "Восстановить", + "Restore JSON": "Восстановить из JSON файла", + "Retrieving data": "Сбор данных", + "Retrieving info for all applicable eras, this will take some time": "Сбор информации из всех нужных эр, это может занять немало времени", + "Revert": "Вернуться", + "Revert pending slashes": "Вернуть намеченные слэши", + "Rewards (once paid) can be deposited to either the stash or controller, with different effects.": "Вознаграждение (после оплаты) может быть перечислено либо на аккаунт-копилку, либо на контроллер, с разными последствиями", + "Save": "Сохранить", + "Save & Reload": "Сохранить и перезагрузить страницу", + "Save snippet to local storage": "Сохранить фрагмент в локальное хранилище", + "Save the type definitions for your custom structures as key-value pairs in a valid JSON file. The key should be the name of your custom structure and the value an object containing your type definitions.": "Сохраните определения типов для Ваших пользовательских структур в виде пар ключ-значение в файле JSON. Ключ должен быть именем Вашей пользовательской структуры и значением объекта, содержащего Ваши определения типов.", + "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.": "Пожалуйста, сохраните этот резервный файл в безопасном месте, поскольку он вместе с паролем необходим для восстановления Вашего аккаунта.", + "Saved": "Сохранено", + "Scan Signature Qr": "Сохранить подпись QR", + "Scan the QR code with your QR scanner. Once approved, you will be required to present the signed QR back to the UI for submission.": "Сканируйте QR код. После одобрения Вы должны будете предьявить подписанный QR для запуска.", + "Search for": "Искать", + "Second": "Поддерживаю", + "Second proposal": "Поддерживаю предложение", + "Seconding a proposal that indicates your backing for the proposal. Proposals with greater interest moves up the queue for potential next referendums.": "Поддержание предложения указывает на Ваш интерес в нем. Предложения с большим интересом будут продвигаться в очереди на следующий референдум", + "Seconds ({{count}})": "Поддерживают ({{count}})", + "Select Network": "Выберите сеть", + "Select a contact or paste the address you want to send funds to.": "Выберите контакт или вставтье адрес, на который хотите отправить средства", + "Select and order council candidates you wish to vote for.": "Выберите кандидатов в Совет, за которых хотите голосовать", + "Select best": "Выбрать лучшего", + "Select example": "Выберите пример", + "Select targets manually (no auto-selection based on current profitability)": "Выбрать валидаторов вручную (без автоподбора по текущей доходности)", + "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.": "Выберите JSON файл, который был скачан при создании аккаунта. Он содержит Ваш приватный ключ, зашифрованный Вашим паролем.", + "Select the account to use for this action.": "Выберите аккаунт для этого действия", + "Select the account you wish close the proposal with.": "Выберите аккаунт, которым хотите закрыть это предложение", + "Select the account you wish to make the proposal with.": "Выберите аккаунт, которым хотите сделать это предложение", + "Select the account you wish to recover into this account.": "Выберите аккаунт, в который хотите восстановить этот аккаунт", + "Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected": "Выберите аккаунт, которым хотите поддержать это предложение. Ваши средства будут заморожены до того, как предложение одобрено или отклонено", + "Select the account you wish to submit for candidacy.": "Выберите аккаунт, которым хотите предложить кандидатуру", + "Select the account you wish to submit the proposal from.": "Выберите аккаунт, с которого хотите сделать это предложение", + "Select the account you wish to submit the tip from.": "Выберите аккаунт, с которого хотите оплатить чаевые", + "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.": "Выберите аккаунт, с которого хотите голосовать. Вы можете одобрить \"За\" или не поддержать \"Против\" предложение", + "Select the council account you wish to use to make the proposal.": "Выберите аккаунт Совета, с которого хотите сделать это предложение", + "Select the remote endpoint, either from the dropdown on manual entered via the custom toggle": "Выберите удаленный эндпоинт либо из выпадающего меню либо через настраиваемый переключатель", + "Send funds": "Отправить средства", + "Send funds from this account": "Отправить средства с этого аккаунта", + "Send funds to this address": "Отправить средства на этот адрес", + "Send to council": "Отправить в Совет", + "Sending transaction": "Отправка транзакции", + "Session Key": "Ключ сессии", + "Set Identity": "Установить личность", + "Set Session Key": "Установить сессионный ключ", + "Set controller": "Установить контроллер", + "Set nominees": "Выбрать кандидатов", + "Set on-chain identity": "Персонализировать аккаунт на блокчейне", + "Set reward destination": "Аккаунт для получения вознаграждения", + "Set sudo key": "Установить sudo-ключ", + "Set to 0 to make transaction immortal": "Поставить значение 0, чтобы транзакция была вечной", + "Set validator preferences": "Установить настройки валидатора", + "Setup Nominator {{step}}/{{NUM_STEPS}}": "Задать Номинатора {{step}}/{{NUM_STEPS}}", + "Setup Validator {{step}}/{{NUM_STEPS}}": "Задать Валидатора {{step}}/{{NUM_STEPS}}", + "Setup account as recoverable": "Сделать аккаунт восстановляемым", + "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...": "Учитывать ли при поиске регистр. Если Вы выберете «нет», Ваш запрос «Некоторые» может возвращать адреса, содержащие «некоторыЕ» или «некОторые» ...", + "Should the search be case sensitive, e.g if you select \\no\\ your search for \\Some\\ may return addresses containing \\somE\\ or \\sOme\\...": "Поиск должен быть с учетом регистра. Например, если поставить \\нет\\, то Ваш поиск \\кот\\ даст результаты содержащие \\Кот\\ или \\кОт\\...", + "Show address on hardware device": "Показать адрес на устройстве", + "Sign (no submission)": "Подписать (без отправки)", + "Sign and Submit": "Подписать и отправить", + "Sign for multisig": "Подписать мультиподписью", + "Sign message": "Подписать сообщение", + "Sign via Qr": "Подписать QR-кодом", + "Sign via {{hardwareType}}": "Подписать с помощью {{hardwareType}}", + "Signed transaction": "Подписанная транзакция", + "Since the multisig function like any other account, once created it is available for selection anywhere accounts are used and needs to be funded before use.": "Поскольку мультиподпись работает как любой другой эккаунт, то после создания он становится доступным везде где используются аккаунты, и должен иметь средства на балансе", + "Since this transaction deals with funding, the stash account will be used.": "Поскольку это денежная транзакция, то будет использован стэш аккаунт", + "Skeptics": "Скептики", + "Society": "Общество", + "Society overview": "Обзор общества", + "Specify the user account to use for this contract call. And fees will be deducted from this account.": "Укажите пользовательский аккаунт для этого контрактного вызова. Расходы будут сниматься с этого аккаунта.", + "Specify the user account to use for this contract call. Any fees will be deducted from this account.": "Укажите пользовательский аккаунт для этого контрактного вызова. Расходы будут сниматься с этого аккаунта.", + "Specify the user account to use for this deployment. And fees will be deducted from this account.": "Укажите пользовательский аккаунт для этого развертывания. Расходы будут сниматься с этого аккаунта.", + "Specify the user account to use for this deployment. Any fees will be deducted from this account.": "Укажите пользовательский аккаунт для этого развертывания. Расходы будут сниматься с этого аккаунта.", + "Staking overview": "Обзор стейкинга", + "Start generation": "Начать генерацию", + "Start recovery": "Начать восстановление", + "Stash": "Копилка", + "Stop": "Остановить", + "Stop generation": "Остановить генерацию", + "Storage": "Хранилище", + "Submit": "Подать", + "Submit (no signature)": "Отправить (без подписи)", + "Submit RPC call": "Отправить вызов RPC", + "Submit Sudo": "Отправить Sudo", + "Submit Transaction": "Отправить транзакцию", + "Submit Unsigned": "Отправить неподписанную транзакцию", + "Submit candidacy": "Подать кандидатуру", + "Submit key": "Предъявить ключ", + "Submit preimage": "Представить прообраз", + "Submit proposal": "Представить предложение", + "Submit signed extrinsic": "Предъявите подписанный экстринзик", + "Submit tip": "Отправить чаевые", + "Submit tip endorsement": "Отправить поддержку для чаевых", + "Submit tip request": "Отправить запрос на чаевые", + "Submit treasury proposal": "Отправить предложение в сокровищницу", + "Submit your council candidacy": "Подать свою кандидатуру в совет", + "Sudo access": "Sudo-доступ", + "Sudo key": "Ключ администратора", + "Supply a backed-up JSON file, encrypted with your account-specific password.": "Предоставьте резервный файл JSON, зашифрованный с помощью пароля вашей учетной записи.", + "Targets": "Назначение", + "Tech. committee": "Тех. комитет", + "Technical committee": "Технический комитет", + "Test account": "Тестовый аккаунт", + "The RPC node can be selected from the pre-defined list or manually entered, depending on the chain you wish to connect to.": "RPC-ноду можно либо выбрать из списка, либо задать вручную, в зависимости от сети, к которой хотите подключиться", + "The Threshold must be a positive number": "Порог должен быть положительным числом", + "The Threshold must lower than 11": "Порог должен быть меньше 11", + "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.": "Пароль аккаунта устанавливается при его создании. Он используется для шифрования резервного файла и раскодирования его при восстановлении аккаунта.", + "The account that signed the input": "Аккаунт, подписавший входные данные", + "The account to which the proposed balance will be transferred if approved": "Аккаунт для перечисления средств, если предложение бедет одобрено", + "The account to which the tip will be transferred if approved": "Аккаунт для перечисления чаевых, если предложение бедет одобрено", + "The account you want to claim to.": "Аккаунт, на который хотите запросить средства.", + "The account you want to register the preimage from": "Аккаунт, с которого хотите зарегистрировать прообраз", + "The account you want to register the proposal from": "Аккаунт, с которого хотите зарегистрировать предложение", + "The account you will send funds from.": "Аккаунт, с которого хотите отправить средства.", + "The account's password specified at the creation of this account.": "Пароль аккаунта, установленый при его создании", + "The actual JSONRPC module and function to make a call to.": "JSONRPC модуль и функция, которые нужно вызвать", + "The actual proposal to make, based on the selected call and parameters thereof.": "Создание фактического предложение, исходя из выбранного вызова и его параметров.", + "The address for the deployed contract instance.": "Адрес развернутого контракта.", + "The address to vote from (must be a member)": "Адрес с которого хотите голосовать (должен быть членом)", + "The addresses that are able to approve multisig transactions. You can select up to {{maxHelpers}} trusted addresses.": "Адреса, которые могут доказать транзакции мультиподписи. Можете выбрать не более {{maxHelpers}} доверенных адресов", + "The addresses that are able to help in recovery. You can select up to {{maxHelpers}} trusted helpers.": "Адреса, которые могут помочь при восстановлении. Можете выбрать до {{maxHelpers}} доверяемых помощников.", + "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": "Средства, выделенные на этот контракт, то есть сумма, которая будет перечислена при его запуске.", + "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": "Средства, выделенные на этот контракт, то есть сумма, которая будет перечислена этим вызовом.", + "The amount of funds to unbond, this is adjusted using the bonded funds on the stash account.": "Количество средств, которое хотите освободить от привязки со стэш аккаунта", + "The amount placed at-stake should be no more that 95% of your available amount to protect against slashing events.": "Средства для депонирования не должны превышать 95% свободных средств, для защиты от слэшинг событий", + "The amount that is associated with this vote. This value is is locked for the duration of the vote.": "Сумма для голосования, она будет недоступна в течении голосования", + "The amount that will be allocated from the treasury pot": "Сумма, которая будет выделена из казначейства", + "The amount that will be used on a per-validator basis to calculate profits for that validator.": "Сумма для расчета дохода от валидатора", + "The amount this total can be reduced by to change the referendum outcome. This assumes changes to the convictions of the existing votes, with no additional turnout.": "Сумма, на которую эта сумма может быть уменьшена, чтобы изменить исход референдума. Это предполагает внесение изменений в убеждения существующих избирателей, без дополнительной явки.", + "The amount this total should be increased by to change the referendum outcome. This assumes additional turnout with new votes at 1x conviction.": "Сумма, на которую эта сумма должна быть увеличена, чтобы изменить исход референдума. Это предполагает дополнительную явку с новыми голосами при 1х убеждениях.", + "The associated deposit for this proposal should be more then the minimum on-chain deposit required. It will be locked until the proposal passes.": "Депозит для этого предложения должен быть больше установленного минимума. Депозит будет возвращен только после одобрения предложения.", + "The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for the validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.": "Автоподбор учитывает доходность валидаторов и Ваши предпочтения. Он меняется в зависимости от комиссий и стейка валидаторов. Расчет постоянно меняется, и является отражением текущего состояния а не предсказанием будущей доходности", + "The balance associated with the vote will be locked as per the conviction specified and will not be available for transfer during this period.": "Остаток средств, связанный с голосованием, будет заблокирован в соответствии с указанным обвинительным приговором и не будет доступен для передачи в течение этого периода.", + "The beneficiary will have access to the transferred fees when the transaction is included in a block.": "Получатель будет иметь доступ к отправляемым средствам когда транзакция попадет в блок.", + "The beneficiary will receive the full amount if the proposal passes.": "Получатель получит полную сумму в случае одобрения членами совета", + "The beneficiary will received the tip as approved by council members.": "Получатель получит чаевые в случае одобрения членами совета", + "The blake2b 256-bit hash of the actual input data.": "Blake2b 256-битовый хэш вводных данных.", + "The bonding duration for any staked funds. After this period needs to be withdrawn.": "Время выделенное для освобождения номинированных средств. После этого периода требуется операция вывода средств", + "The bonding duration for any staked funds. Needs to be unlocked and withdrawn to become available.": "Время выделенное для освобождения номинированных средств. После этого периода требуется операция вывода средств для свободного распоряжения ими", + "The call hashes that have not been executed as of yet.": "Хэши невыполненных вызовов.", + "The code hash for the on-chain deployed code.": "Хэш кода для развернутого кода на блокчейне.", + "The code is not recognized as being in valid WASM format": "Код не соответствует WASM формату", + "The codeHash is not a valid hex hash": "codeHash не соответствует валидному 16-ричному хэшу", + "The color used to distinguish this network with others, use color code with 3 or 6 digits, like \"#FFF\" or \"#111111\"": "Цвет, используемый для различения этой сети с другими, используйте цветовой код с 3 или 6 цифрами, например \"#FFF \" или \" #111111\"", + "The color used to distinguish this network with others, use color code with 3 or 6 digits, like \\#FFF\\ or \\#111111\\": "Цвет для обозначения сети, используйте коды цвета с 3 или 6 знаками, например \\#FFF\\ или \\111111\\", + "The commission is deducted from all rewards before the remainder is split with nominators.": "Комиссионные вычитаются из всех вознаграждений прежде чем остаток распределяется между номинаторами", + "The committee account that will apply the close for the current round.": "Счет Комитета, который будет применять закрытие для текущего раунда.", + "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": "Компилированный WASM контракта, который хотите развернуть. Каждый блок кода получит свой хэш, который можно будет использовать для создания новых копий.", + "The compiled runtime WASM for the parachain you wish to register.": "Скомпилированная среда выполнения WASM для парачейна, которую Вы хотите зарегистрировать.", + "The compiled runtime WASM for this parachain.": "Скомпилированная среда выполнения была создана для этого парачейна.", + "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.": "Контракт был заключен еще раньше. Внутренне это определяется хэшем кода, как созданным так и присоединенным.", + "The controller is the account that is be used to control any nominating or validating actions. I will sign this transaction.": "Контроллер - это аккаунт, который используется для контроля любых действий валиации или номинации. Подписываю эту транакцию.", + "The controller is the account that will be used to control any nominating or validating actions. Should not match another stash or controller.": "Контроллер - это аккаунт, который используется для контроля любых действий валиации или номинации. Должен отличаться от другого стэша или контроллера.", + "The conviction to use for this vote, with an appropriate lock period.": "Убеждение, которое хотите использовать для этого голосования. Определяет срок депонирования средств", + "The council account for the proposal. The selection is filtered by the current members.": "Аккаунт совета для предложения. Селекция фильтрует текущих членов", + "The council account for this vote. The selection is filtered by the current members.": "Аккаунт совета для голосования. Селекция фильтрует текущих членов", + "The council account that will apply the close for the current round.": "Аккаунт совета который может закрыть текущий раунд", + "The council member that is proposing this, submission equates to an \"aye\" vote.": "Член совета который делает предложение, автоматически голусует \"За\".", + "The council proposal to make the vote on": "Предложение совета для голосования", + "The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.": "Подписанные данные. Используется вместе с подписью для проверки. Может быть hex или string", + "The delay between vouching and the availability of the recovered account.": "Задержка от поручения.", + "The delay period to apply in blocks": "Период задержки для применения в блоках", + "The deployment constructor information for this contract, as provided by the ABI.": "Информация о конструкторе запуска для этого контракта, получается из ABI.", + "The deposit is below the {{minimum}} minimum required for the proposal to be evaluated": "Депозит меньше чем {{minimum}}, минимума для оценки предложения", + "The deposit of {{deposit}} will be reserved until the proposal is completed": "Депозит в размере {{deposit}} будет зарезрвирован до окончания голосования", + "The derivation path allows you to create different accounts from the same base mnemonic.": "Путь деривации позволяет создавать различные учетные записи из одной и той же базовой мнемоники.", + "The desired threshold. Here set to a default of 50%+1, as applicable for general proposals.": "Желаемый порог. По определению он 50%+1, как для всех общих предложений", + "The destination account for any payments as either a nominator or validator": "Целевой аккаунт для платежей от номинации или валидации", + "The details of the transaction including the type, the description (as available from the chain metadata) as well as any parameters and fee estimations (as available) for the specific type of call.": "Детали транзакции включая тип, описание (из метаданных сети) а так же параметры и оценка сборов (если есть) для этого типа вызова", + "The email address associated with this identity.": "Email связанный с этой личностью.", + "The existing account password as specified when this account was created or when it was last changed.": "Текущий пароль, заданный при создании аккаунта или при последнем изменении.", + "The external proposal to send to the technical committee": "Внешнее предложение для отправки техническому комитету", + "The final recipient balance is less or equal to {{existentialDeposit}} (the existential amount) and will not be reflected": "Баланс финального получателя меньше или равно {{existentialDeposit}} (минимальному балансу) и не будет отражен", + "The funds will only be available for withdrawal after the unbonding period, however will not be part of the staked amount after the next validator election. You can follow the unlock countdown in the UI.": "Средства будут свободны для вывода только после периода блокировки, но после следующего выбора валидатора уже не будут номинировать. Прогресс разблокировки отображается в UI", + "The hash for the proposal this vote applies to": "Хэш предложения к которому относится голосование", + "The hash of the preimage for the proposal as previously submitted or intended.": "Хэш прообраза для предложения как изначально предложен", + "The hash of the proposal image, either already submitted or valid for the specific call.": "Хэш образа предложения, либо уже предложенного либо валидного для этого вызова", + "The hash of the selected proposal, use it for submitting the proposal": "Хэш выбранного предложения, используйте для запуска предложения", + "The helpers should be able to verify, via an off-chain mechanism, that the account owner indeed wishes to recover access and as such provide any approvals. In the cases of malicious recovery procedures, they will have the power to stop it.": "Помощники должны иметь возможность проверить механизмами вне сети, что владелец аккаунта действительно желает восстановить доступ и одобрить эти действия. В случае злонамереных попыток востановления они будут иметь возможность остановить их.", + "The hex output from author_rotateKeys, as executed on the validator node. The keys will show as pending until applied at the start of a new session.": "Шестнадцатеричный вывод из author_rotateKeys, выполненный на узле валидатора. Ключи будут отображаться как отложенные до тех пор, пока они не будут применены в начале нового сеанса.", + "The id number to assign to this parachain.": "п/п № для этого парачейна", + "The id of the parachain to be deregistered.": "п/п № парачейна для снятия регистрации", + "The image (proposal) will be stored on-chain against the hash of the contents.": "Образ (предложение) сохраняется в сети вместе с хэшом содержания", + "The initial head state for the parachain.": "Изначальное главное состояние парачейна", + "The initial head state is invalid.": "Изначальное главное состояние не действительно", + "The input data to hash. This can be either specified as a hex value (0x-prefix) or as a string.": "Входные данные для хэширования. Это может быть указано либо в виде шестнадцатеричного значения (0x-префикс), либо в виде строки.", + "The input data to sign. This can be either specified as a hex value (0x-prefix) or as a string.": "Входные данные для подписи. Это может быть указано либо в виде шестнадцатеричного значения (0x-префикс), либо в виде строки.", + "The key type and crypto type to use for this key. Be aware that different keys have different crypto requirements. You should be familiar with the type requirements for the different keys.": "Тип ключа и шифрования для этого ключа. Обратите внимание, что разные ключи имеют разные требования по шифрованию, вам должны быть известны какие именно вам нужны.", + "The legal name for this identity.": "Юридическое имя этой личности.", + "The local name for this account. Changing this does not affect your on-line identity, so this is only used to indicate the name of the account locally.": "Локальное имя для этой учетной записи. Изменение этого параметра не влияет на Вашу онлайн-идентификацию, поэтому он используется только для указания имени учетной записи локально.", + "The locked value for this proposal": "Заблокированная сумма для этого предложения", + "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": "Максимум газа для этого вызова. Если исполнение кода потребует больше, вызов не исполнится.", + "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": "Максимум газа для этого развертывания. Если исполнение кода потребует больше, развертывание не исполнится.", + "The message to send to this contract. Parameters are adjusted based on the ABI provided.": "Сообщение для этого контракта. Параметры зависят от ABI.", + "The minimum amount that an account should have to be deemed active": "Минимум средств, необходимый для того, чтобы аккаунт считался активным", + "The minimum amount that will be bonded": "Минимум средств для привязки", + "The minimum deposit required": "Минимальное значение депозита", + "The minimum number of committee votes required to approve this motion": "Минимальное количество голосов Совета для одобрения", + "The minimum number of council votes required to approve this motion": "Минимальное количество голосов членов совета для одобрения ходатайства", + "The multisig signatory for this transaction.": "Аккаунт, подписивающий мультиподпись для этой транзакции", + "The name for this account and how it will appear under your addresses. With an on-chain identity, it can be made available to others.": "Имя этой учетной записи и то, как она будет отображаться под Вашими адресами. С личностью в цепи оно может быть доступно другим.", + "The name is for unique identification of the account in your owner lists.": "Это имя используется для уникальной идентификации учетной записи в Ваших списках владельцев.", + "The name that will be displayed in your accounts list.": "Название, которое будет отображаться в списке Ваших аккаунтов.", + "The new account password. Once set, all future account unlocks will be performed with this new password.": "Новый пароль. Задай новый пароль. Он будет использоваться в будущем для разблокирования аккаунта.", + "The on-chain percentage for the treasury": "Процент для казначейства", + "The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.": "Пароль и подтверждение пароля для этой учетной записи. Это необходимо для проверки подлинности всех совершенных транзакций и шифрования ключевой пары.", + "The password previously used to encrypt this account.": "Пароль, который ранее использовался для шифрования этой учетной записи.", + "The password to unlock the selected account.": "Пароль для разблокирования выбранного аккаунта.", + "The percentage reward (0-100) that should be applied for the validator": "Процент вознаграждения (от 0 до 100) для валидатора", + "The preimage hash of the proposal": "Прообраз хэша предложения", + "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.": "Закрытый ключ для Вашей учетной записи является производным от этого сида. Этот сид должен храниться в тайне, так как любой человек, находящийся в его распоряжении, имеет доступ к средствам этого счета. Если Вы проверяете, используйте сид учетной записи сеанса в качестве параметра\" --key \" Вашего узла.", + "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \\--key\\ parameter of your node.": "Приватный ключ для Вашего аккаунта получается из этой секретной фразы. Эта фраза должна держаться в секрете, так как любой кто ей обладает, может получить доступ к средствам на Вашем аккаунте. Если Вы используете этот аккаунт для валидации, используйте сид-фразу аккаунта сессии в качестве \\--key\\ параметра Вашей ноды.", + "The proposal is in the queue for future referendums. One proposal from this list will move forward to voting.": "Предложение находится в очереди для будущих референдумов. Одно из предлоежений из этого списка будет выдвинуто на голосование", + "The proposal that is being voted on. It will pass when the threshold is reached.": "Предложение, по которому проходит голосование. Будет одобрено при достижении порога", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted for a subsequent voting round.": "Предложение, которое будет затронуто. Как только он будет закрыт для текущего раунда голосования, его необходимо будет повторно представить для последующего раунда голосования.", + "The proposal that will be affected. Once closed for the current voting round, it would need to be re-submitted to council for a subsequent voting round.": "Предложение, которого это касается. Закрыв его, нужно будет его подать повторно для последующего раунда голосования", + "The proposal will be registered from this account and the balance lock will be applied here.": "Предложение будет зарегистрировано с этого аккаунта и блокировка баланса будет на нем", + "The reason why this tip should be paid.": "Причина по которой чаевые должны быть оплачены", + "The recoverable account is protected against the loss of seed/access by a social process.": "Восстанавливаемый аккаунт защищается от потери тайной фразы/доступа социальным процессом", + "The resulting signature of the input data, as done with the crypto algorithm from the account. (This could be non-deterministic for some types such as sr25519).": "ГолосоватьРезультирующая сигнатура входных данных, как это сделано с крипто-алгоритмом от учетной записи. (Это может быть недетерминированным для некоторых типов, таких как sr25519).", + "The scheduling preference for this parachain.": "Предпочтения по расписанию для этого парачейна", + "The scheduling setting for this parachain.": "Настройки расписания для этого парачейна", + "The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.": "Секретный сид для этой учетной записи. Убедитесь, что вы храните его в безопасном месте, имея доступ к сиду можно повторно создать учетную запись.", + "The seed and derivation path will be submitted to the validator node. this is an advanced operation, only to be performed when you are sure of the security and connection risks.": "Тайная фраза (seed) и путь деривации будут предьявлены ноде валидатора. Это операция для продвинутых пользователей, исполняйте ее только если вам известны риски безопасности и соединения", + "The selected account to be unlocked.": "Выбранный аккаунт для разблокировки", + "The selected account to perform the derivation on.": "Выбранный аккаунт для деривации", + "The selected controller tied to this stash. Once set, this account will be able to control the actions performed by the stash account.": "Выбранный контроллер для этого стэша. После установки этот аккаунт сможет контролировать активность стэш аккаунта", + "The selected validators to nominate, either via the \"currently best algorithm\" or via a manual selection.": "Выбранные валидаторы для номинации. Выберите либо с помощью \"Лучший алгоритм\", либо вручную", + "The sending account that will be used to send this transaction. Any applicable fees will be paid by this account.": "Аккаунт отправителя, с которого будет сделана транзакция. Все сборы будут оплачиваться с этого аккаунта", + "The signatories has the ability to create transactions using the multisig and approve transactions sent by others.Once the threshold is reached with approvals, the multisig transaction is enacted on-chain.": "Подписанты имеют возможность создавать транзакции с помощью мультподписи и одобрять транзакции других. По достижении порога одобрений транзакция мультиподписи будет исполнена в сети.", + "The signatory is one of the allowed accounts on the multisig, making a recorded approval for the transaction.": "Подписант - один из разрешенных аккаунтов на мультиподписи, совершает регистрируемые одобрения транзакций", + "The signatory is one of the allowed accounts on the multisig. The transaction could either be the call or an approval for the hash of a call.": "Подписант - один из разрешенных аккаунтов на мультиподписи. Транзакия может быть либо вызов, либо одобрение хэша вызова", + "The signatory to send the approval/cancel from": "Кому отправить запрос на подпись/отмену", + "The signature as by the account being checked, supplied as a hex-formatted string.": "Подпись как проверяемая учетная запись, поставляемая в виде строки шестнадцатеричного формата.", + "The specific eras on which there are unapplied slashes. For each era a separate proposal is to be made.": "Эра, по которой есть не исполненные слэши. Для каждой эры должно быть отдельное предложение", + "The specified value is greater than your free balance. The node will bond the maximum amount available.": "Указанная сумма больше, чем Ваш свободный баланс. Нода привяжет максимальную свободную сумму.", + "The stash account that is used. This will allow the controller to perform all non-funds related operations on behalf of the account.": "Используемый стэш аккаунт. Это действие позволит контроллер аккаунту выполнять все недежные операции для стэша.", + "The stash and controller pair as linked. This operation will be performed via the controller.": "Связанная пара стэша и контроллера. Эта операция будет выполнена через контроллер", + "The stash and controller pair, here the controller will be used to send the transaction.": "Связанная пара стэша и контроллера. Эта операция будет выполнена через контроллер", + "The stash and controller pair. This transaction, managing preferences, will be sent from the controller.": "Связанная пара стэша и контроллера. Эта операция, меняющая настройки, будет выполнена через контроллер", + "The stash and controller pair. This transaction, setting the session keys, will be sent from the controller.": "Связанная пара стэша и контроллера. Эта операция, устанавливающая ключи сессии, будет выполнена через контроллер", + "The stash that is to be affected. The transaction will be sent from the associated controller account.": "Используемый стэш аккаунт. Транзакция будет выполнена через привязанный контроллер аккаунт.", + "The suggested value for this tip": "Предложенная сумма для чаевых", + "The the Ethereum address you used during the pre-sale (starting by \"0x\")": "Адрес Ethereum, который Вы использовали на предпродаже (начинается с \"0x\")", + "The threshold for approval should be less or equal to the number of signatories for this multisig.": "Порог одобрения должна быть меньше или равна количеству подписантов этой мультиподписи", + "The threshold for approvals and the delay is the protection associated with the account. The delay should be such that any colluding recovery attempts does have a window to stop.": "Порог одобрения и заддержка служат защите аккаунта. Заддержка должна быть достаточной для предотвращения нелегитимных процедур восстановления", + "The threshold for this multisig": "Порог для этого мультисига", + "The threshold of vouches that is to be reached for the account to be recovered.": "Порог поручительства для восстановления аккаунта.", + "The tip amount that should be allocated": "Чаевые которые должны быть оплачены", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the free amount available)": "Общая сумма остатка копилки, которая будет номинирована в предстоящих раундах (должна быть меньше доступной свободной суммы)", + "The total amount of the stash balance that will be at stake in any forthcoming rounds (should be less than the total amount available)": "Полная сумма баланса копилки, которая будет номинирована в предстоящих раундах (должна быть меньше свободной суммы баланса)", + "The transferred balance will be subtracted (along with fees) from the sender account.": "Отправляемый баланс будет вычтен (вместе с затратами) из аккаунта отправителя", + "The twitter name for this identity.": "Twitter для этой личности", + "The type of council proposal to submit.": "Тип предложения Совету", + "The unapplied slashed era to cancel.": "Эра, для которой хотите отменить слэш", + "The unchecked weight as specified for the sudoUncheckedWeight call.": "Непроверенный вес, указанный для вызова sudoUncheckedWeight.", + "The value associated with this vote. The amount will be locked (not available for transfer) and used in all subsequent elections.": "Сумма для этого голосования. Эта сумма будет заблокирована (ее невозможно будет переводить) и использоваться для всех последующих голосований.", + "The value is not in a valid address format": "Неверный формат адреса", + "The value is the amount that is being asked for and that will be allocated to the beneficiary if the proposal is approved.": "Сумма, заправшиваемая и переводимая бенефициару в случае одобрения предложения.", + "The vote to record for this proposal, either for or against.": "Голосовать по этому предложению, за или против", + "The vote will be recorded for the selected account.": "Голосование будет сохранено для выбранного аккаунта", + "The vote will be recorded for this account. If another account delegated to this one, the delegated votes will also be counted.": "Голосование будет сохранено для этого аккаунта. Если у этого аккаунта есть делегаты, то их голоса тоже будут учитываться.", + "The votes for the members, runner-ups and candidates. These should be ordered based on your priority.": "Голоса за членов Совета, финалистов и кандидатов, по Вашим приоритетам", + "The voting period to apply in blocks": "Период голосования в блоках", + "There are no registered parachains": "Нет зарегистрированных парачейнов", + "There is currently an ongoing election for new validator candidates. As such staking operations are not permitted.": "В настоящее время идет выбор новых валидаторов. На данный момент стейкинг не разрешен.", + "These are trusted individuals that can verify and approve any recovery actions. With recovery, once the threshold is reached, the funds associated with the account can be moved to a new destination.": "Это доверенные вами лица, которые могут подтвердить любые востановительные действия. При востановлении средства с аккаунта могут быть выведены из аккаунта при достижении порога", + "Think of the stash as your cold wallet and the controller as your hot wallet. Funding operations are controlled by the stash, any other non-funding actions by the controller itself.": "Стэш аккаунт это примерно как холодный кошелек, а контроллен - горячий. Операции со средствами контролируются стэшом, в а любые другие контроллером.", + "This Ethereum address does not have a claim.": "Данный адрес Ethereum не имеет заявок.", + "This account is recoverable, with the following friends:": "Этот аккаунт возможно восстановить с помощью этих друзей:", + "This account that will perform the message signing.": "Этот аккаунт будет подписивать сообщения", + "This account will appear in the list of candidates. With enough votes in an election, it will become either a runner-up or a council member.": "Этот аккаунт будет показан в списке кандидатов. С достаточным количеством голосов он может стать либо финалистом, либо членом совета.", + "This account will be use to approve each candidate.": "Этот аккаунт будет исполтзоваться для одобрения каждого кандидата.", + "This account will make the proposal and be responsible for the bond.": "Этот аккаунт сделает предложение и с него будут выделены средства для привязки", + "This account will pay the fees for the preimage, based on the size thereof.": "Этот аккаунт будет оплачивать расходы на прообраз", + "This could either be an approval for the hash or with full call details. The call as last approval triggers execution.": "Это может быть либо одобрение хэша либо полный вызов со всеми деталями. Вызов является последним перед выполнением.", + "This external account cannot be used to sign data. Only Limited support is currently available for signing from any non-internal accounts.": "Этот внешний аккаунт не может использоваться для подписи данных. Поддержка внешних аккаунтов на данный момент ограничена.", + "This injected account cannot be used to sign data since the extension does not support raw signing.": "Встроенный аккаунт не может использоваться для подписи данных, так как расширение не поддерживает сырую подпись", + "This is not a valid JSON object.": "Это не валидный JSON объект.", + "This operation does not impact the associated on-chain code or any of its contracts.": "Эта операция не влияет на ассоциированный код в блокчейне или какие-либо его контракты.", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the account on this browser.": "Эта операция не стирает историю аккаунта из сети или средства из него. Операция удаления лишь убирает доступ к аккаунту из этого браузера", + "This operation does not remove the history of the account from the chain, nor any associated funds from the account. The forget operation only limits your access to the address on this browser.": "Эта операция не стирает историю аккаунта из сети или средства из него. Операция удаления лишь убирает доступ к аккаунту из этого браузера", + "This operation does not remove the history of the contract from the chain, nor any associated funds from its account. The forget operation only limits your access to the contract on this browser.": "Эта операция не стирает историю аккаунта из сети или средства из него. Операция удаления лишь убирает доступ к аккаунту из этого браузера", + "This operation does not remove the uploaded code WASM and ABI from the chain, nor any deployed contracts. The forget operation only limits your access to the code on this browser.": "Эта операция не стирает запущенный код WASM и ABI из сети, а так же запущенные контракты. Операция удаления лишь убирает доступ к аккаунту из этого браузера", + "This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.": "Эта операция отправит секретную фразу через RPC вызов. Не производите эту операцию через публичную RPC ноду. Убедитесь что нода локальная, связана с Вашим валидатором и безопасна.", + "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).": "Этот пароль используется для шифрования Вашего приватного ключа. Он должен быть сложным и уникальным! Вы должны будете подписывать транзакции этим паролем. Вы можете восстановить этот аккаунт с помощью этого пароля и резервного файла (создается на следующем шаге).", + "This pubic key is what will be visible in your queued keys list. It is generated based on the seed and the crypto used.": "Этот публичный ключ будет видимым в Вашем списке. Он создается на основании seed и метода шифрования", + "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.": "Это будет применяться к любому будущему использованию этого аккаунта, хранящемуся в этом браузере. Убедитесь в безопасном хранении этого пароля, а также что он сложный и уникальный.", + "Tip": "чаевые", + "Tip (optional)": "Чаевые (необязательно)", + "To council": "Совету", + "To ensure optimal fund security using the same stash/controller is strongly discouraged, but not forbidden.": "Для обеспечения оптимальной безопасности фонда использование одного и того же тайника/контроллера категорически не рекомендуется, но и не запрещено.", + "Transfer": "Перевести", + "Translate": "Перевод", + "Treasury overview": "Обзор сокровищницы", + "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.": "Введите здесь то, что Вы хотели бы, чтобы Ваш адрес содержал. Этот инструмент будет генерировать ключи и показывать соответствующие адреса, которые лучше всего соответствуют Вашему поиску. Вы можете использовать \"?\" как подстановочный символ.", + "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \\?\\ as a wildcard for a character.": "Впишите здесь то, что должен содержать Ваш адрес. Этот инструмент генерирует ключи и покажет соответствующие адреса, которые больше всего подходят Вашему поиску. Используйте \\?\\ в качестве любого символа.", + "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "Впишите сумму, которую хотите перевести. Справа можете выбрать единицу измерения. Например 1 milli - это 0,001.", + "Type the name of this Asset. This name will be used across all the apps. It can be edited later on.": "Введите название этого Имущества. Это название будет использоваться во всех приложениях. Его потом можно изменить.", + "Type the name of your contact. This name will be used across all the apps. It can be edited later on.": "Впишите имя Вашего контакта. Это имя будет использоваться в всех приложениях. Его можно менять.", + "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.": "Впишите пароль выбранный при создании аккаунта. Он был использован для шифрования приватного ключа в резервном файле.", + "Unable to find deployed contract code at the specified address": "Не найден код развернутого контракта по этому адресу", + "Unable to find on-chain WASM code for the supplied codeHash": "Не найден WASM код для этого codeHash", + "Unbond": "Освободить", + "Unbond funds": "Освободить средства", + "Unbonding {{value}}, ": "Разблокирование {{value}}", + "Uncaught error. Something went wrong with the query and rendering of this component. {{message}}": "Неизвестная ошибка. Что-то пошло не так с запросом и показом этого компонента {{message}}", + "Unit": "Единица", + "Unit decides the name of 1 unit token, e.g. \"DOT\" for Polkadot": "Единица измерения определяет имя 1 единицы токена, например, \"DOT\" для Polkadot", + "Unit decides the name of 1 unit token, e.g. \\DOT\\ for Polkadot": "Единица измерения определяет название 1 единицы токена, например 1 DOT для Polkadot", + "Unknown": "Неизвестный", + "Unlock": "Разблокировать", + "Unlock account": "Разблокировать аккаунт", + "Unlock the account for signing. Once active the signature will be generated based on the content provided.": "Открыть аккаунт для подписи. После открытия подпись будет сгенерирована на основании предоставленного соддержания", + "Unlock the sending account to allow signing of this transaction.": "Открыть аккаунт отправки для того, чтобы разрешить подпись этой транзакции", + "Update metadata": "Обновить метаданные", + "Updated in the latest block": "Обновлено в последнем блоке", + "Upload": "Загрузить", + "Upload WASM": "Загрузить WASM", + "Use an automatic selection of the currently most profitable validators": "Использовать автоселекцию наиболее доходных в данный момент валидаторов", + "Use this account to request the tip from. This can be a normal or council account.": "Использовать этот аккаунт чтобы запросить чаевые. Это может быть обычный или аккаунт члена Совета", + "Validate": "Валидировать", + "Validator": "Валидатор", + "Validator stats": "Статистика валидатора", + "Vanity generator": "Генератор адреса с определенными символами", + "Verify signature": "Подтвердить подпись", + "Verify the password entered above.": "Подтвердите пароль, введенный выше.", + "View this externally": "Вид снаружи", + "Vote": "Голосовать", + "Vote Aye": "Голосовать За", + "Vote Nay": "Голосовать Против", + "Vote for candidate": "Голосовать за кандидата", + "Vote for current candidates": "Голосовать за текущих кандидатов", + "Vote for defender": "Голосовать за защитника", + "Vote on proposal": "Голосовать по предложению", + "Waiting": "Ожидание", + "Waiting for API to be connected and ready.": "Подождать соединения API.", + "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.": "Ожидается авторизация от расширения. Пожалуйста, откройте установленное расширение и разрешите или отклоните доступ.", + "Waiting nominations ({{count}})": "Ожидание номинаций ({{count}})", + "Warning: we did not find any attest statement for {{chain}}": "Внимание! Мы не нашли никакого соглашения для {{chain}}", + "We found a pre-claim with this Polkadot address. However, attesting requires signing with this account. To continue with attesting, please add this account as an owned account first.": "Мы нашли предварительную заявку для данного адреса Polkadot. Для аттестации необходима подпись, сделанная этой учётной записью. Чтобы продолжить проверку, сначала добавьте эту учетную запись в \"Аккаунты\".", + "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.": "Мы предоставим вам сгенерированный файл резервной копии после создания Вашей учетной записи. Пока у вас есть доступ к Вашей учетной записи, Вы всегда можете загрузить этот файл позже, нажав на кнопку \"резервное копирование\" в разделе Учетные записи.", + "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \\Backup\\ button from the Accounts section.": "Мы предоставим Вам резервный файл после того, как аккаунт будет создан. Если у Вас есть доступ к аккаунту, то этот файл Вы всегда можете скачать в секции аккаунты нажатием на кнопку \\Создать резервный файл\\.", + "When submitting a proposal the hash needs to be known. Proposals can be submitted with hash-only, but upon dispatch the preimage needs to be available.": "При отправке предложения должен быть известен хэш и доступен прообраз.", + "With the Firefox browser connecting to insecure WebSockets ({{wsUrl}}) will fail due to the browser not allowing localhost access from a secure site.": "Соединение Firefox браузера с небезопасным веб сокетами ({{wsUrl}}) не будет работать, т.к. браузер не разрешает доступ к localhost с безопасного сайта.", + "Withdraw these unbonded funds": "Вывести освободившиеся средства", + "Yes": "Да", + "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.": "Вы собираетесь удалить этот аккаунт из списка доступных. По завершении операции если вам опять нужен доступ к этому аккаунту, то вам нужно будет его восстановить с помощью seed фразы или с помощью резервного файла.", + "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.": "Вы собираетесь удалить этот аккаунт из списка доступных. По завершении операции если вам опять нужен доступ к этому аккаунту, в то вам нужно будет его восстановить.", + "You are about to remove this code from your list of available code hashes. Once completed, should you need to access it again, you will have to manually add the code hash again.": "Вы собираетесь удалить этот код из списка доступных хэшов кода. По завершении операции если вам опять нужен доступ к нему, то вам нужно будет хэш кода вручную восстановить.", + "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.": "Вы собираетесь удалить ABI этого кода. По завершении операции если вам опять нужен доступ к нему, то вам нужно будет его вручную загрузить.", + "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": "Вы собираетесь удалить этот контракт из списка доступных. По завершении операции если вам опять нужен доступ к этому контракту, в то вам нужно будет его восстановить вручную во вкладке Инстанцировать.", + "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.": "Вы соединяетесь с безопасного сайта на небезопасный WebSocket ({{wsUrl}}). Из-за политик безопасности браузера смешанные соединения не разрешены. Поменяйте RPC сервис на безопасный 'wss' эндпоинт.", + "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.": "Нет подключения к ноде. Убедитесь, что Ваша нода работает и эндпоинт веб сокета доступен.", + "You are using an ABI with an outdated format. Please generate a new one.": "Используете ABI устаревшего формата. Пожалуйста, обновите его.", + "You are voting with this collective's prime account. The vote will be the default outcome in case of any abstentions.": "Вы голосуете с Главного аккаунта этого коллективах . Если будут воздержанные от голосования, то голос с Главного аккаунта будет считаться голосом по определению для них", + "You can set a custom derivation path for this account using the following syntax \"//////\". The \"/\" and \"//\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.": "Вы можете задать пользовательский путь вывода для этой учетной записи, используя следующий синтаксис \"// / / / / < password>\". \"/ \" и \"// \" могут повторяться и смешиваться`. \"///password\" является необязательным и должен выполняться только один раз.", + "You can set a custom derivation path for this account using the following syntax \\//////\\. The \\/\\ and \\//\\ may be repeated and mixed`. The \\///password\\ is optional and should only occur once.": "Вы можете установить собственный путь деривации для этой учетной записи, используя следующий синтаксис: \\/<мягкий ключ>//<твердый ключ>///<пароль>\\. \\/<мягкий ключ>\\ и \\//<твердый ключ>\\ можно повторять и менять местами. \\///пароль\\ не обязателен, но использовать его можно только 1 раз.", + "You do not have access to the current sudo key": "У вас нет доступа к текущему sudo-ключу", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": "У вас нет аккаунтов. Некоторые функции спрятаны и появятся только при наличии аккаунтов.", + "You have {{claimCount}} accounts that need attestations. Use the Claim Tokens app on the left navigation bar to complete the process. Until you do, your balances for those accounts will not be reflected.": "У вас есть учетные записи {{claimCount}}, которые нуждаются в аттестации. Для завершения процесса используйте приложение \"Claim Tokens\" на левой панели навигации. Пока Вы этого не сделаете, Ваши балансы по этим счетам не будут отражены.", + "You need to sign an attestation for the following account:": "Вам необходимо подписать аттестацию для следующего аккаунта:", + "You need to sign an attestation for the following accounts:": "Вам необходимо подписать аттестацию для следующих аккаунтов:", + "You need to unlock this account to be able to sign data.": "Вам необходимо разблокировать аккаунт для возможность подписать данные.", + "You will no longer have sudo access": "У Вас больше не будет sudo-доступа", + "Your Ethereum account": "Ваш аккаунт в сети Ethereum", + "Your custom types have been added": "Пользовательские типы добавлены", + "Your endorsement will be applied for this account.": "Ваша поддержка будет по отношению к этому аккаунту", + "Your stash account. The transaction will be sent from the associated controller.": "Ваш стэш аккаунт. Транзакции будут отправляться с его контроллера.", + "a riot name linked to this identity": "Riot имя этой личности", + "account": "аккаунт", + "account forgotten": "аккаунт забыт", + "account restored": "аккаунт восстановлен", + "accounts": "аккаунты", + "activate": "активировать", + "additional bonded funds": "дополнительные депонированные средства", + "address": "адрес", + "address copied": "адрес скопирован", + "address created": "адрес создан", + "address edited": "адрес отредактирован", + "address forgotten": "адрес забыт", + "address prefix": "префикс адреса", + "amount": "сумма", + "amount to use for estimation": "Сумма для этой оценки", + "approval type": "Тип одобрения", + "approved": "одобрено", + "asset id": "№ актива", + "auto-selected targets for nomination": "автоматически выбранные валидаторы", + "available": "доступный", + "available signatories": "доступные подписанты", + "available social recovery helpers": "доступные помощники для социального востановления", + "average": "средний", + "aye": "За", + "aye: bool": "aye: bool", + "backing": "поддержка", + "backup file": "резервный файл", + "balance": "баланс", + "balances": "балансы", + "beneficiary": "бенефициар", + "best": "последний", + "best #": "№ последнего", + "best block": "последний блок", + "best hash": "хэш последнего", + "bids": "заявки", + "block hash or number to query": "поиск хэша или номера блока", + "blocks": "блоки", + "bond": "депонировать", + "bonded": "депонировано", + "call from account": "вызов с аккаунта", + "call the selected endpoint": "вызвать выбранный эндпоинт", + "candidate account": "аккаунт кандидата", + "candidate accounts": "аккаунты кандидатов", + "candidates": "кандидаты", + "case sensitive": "с учетом регистра", + "challenge": "вызов", + "claim to account": "запросить на аккаунт", + "click to copy": "кликните для копирования", + "click to select or drag and drop a JSON ABI file": "выберите или перетащите JSON ABI файл", + "click to select or drag and drop the file here": "нажмите для выбора, либо сбростье файл сюда", + "clipboard": "буфер обмена", + "code": "код", + "code for this contract": "код этого контракта", + "code hash": "хэш кода", + "commission": "комиссия", + "compiled contract WASM": "скомпилированный WASM контракта", + "connected peers": "соединенния с другими нодами", + "contacts": "контакты", + "contract address": "адрес контракта", + "contract to use": "использовать контракт", + "controller": "контроллер", + "controller account": "аккаунт контроллера", + "conviction": "убеждение", + "conviction: Conviction": "conviction: Conviction", + "copied": "скопировано", + "council candidates": "кандидаты в Совет", + "council proposal": "предложение Совету", + "council proposal type": "тип предложения Совету", + "created account": "создан аккаунт", + "created multisig": "мультиподпись создана", + "crypto type to use": "тип шифрования", + "custom endpoint": "пользовательский эндпоинт", + "data": "данные", + "default icon theme": "тема иконок по умолчанию", + "default interface language": "язык интерфейса по умолчанию", + "defender": "защитник", + "delay": "задержка", + "democracy": "демократия", + "deploy": "развернуть", + "deployment account": "аккаунт для развертывания", + "deployment constructor": "конструктор для развертывания", + "deposit": "депозит", + "derivation path": "путь деривации", + "derive root account": "извлечь исходный аккаунт", + "details": "детали", + "development seed": "seed разработки", + "dispatch queue": "очередь неисполненных", + "display name": "экранное имя", + "do not include empty strings in the generated file": "не включать пробелы в созданном файле", + "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": "Не найдено действительного запроса. Перепровертье, что Вы правильно подписали транзакцию правильным адресом на сети Ethereum.", + "ed25519, Edwards": "ed25519, Edwards", + "elected stake": "выбранный стейк", + "email": "email", + "enact": "принять", + "endowment": "фонд", + "epoch": "эпоха", + "era": "эра", + "era points": "очки за эру", + "era {{era}}, {{count}} slashes": "эра {era}}, {{count}} слэши", + "eras": "эры", + "events": "события", + "everything": "все", + "exclude option": "исключить опцию", + "exclude value": "исключить значение", + "execute": "выполнить", + "existential deposit": "существующий депозит", + "external": "внешний", + "extrinsic hash": "хэш экстринзика", + "extrinsics": "экстринзики", + "fee": "сбор", + "filter by name or tags": "фильтровать по имени или тэгам", + "filter by name, address or index": "фильтровать по имени, адресу или индексу", + "filter by name, address, or account index": "фильтровать по имени, адресу или индексу аккаунта", + "finalized": "финализирован", + "finder": "искатель", + "forks": "форки", + "free balance": "свободный баланс", + "from the following data": "из следуюющих данных", + "generated public key": "сгенерированный публичный ключ", + "has a valid claim for": "имеет действительный запрос на", + "hash": "хэш", + "heads": "руководители", + "hex input data": "шестнадцатеричные входные данные", + "hex-encoded storage key": "ключ хранения в шестнадцатеричном кодировании", + "identity": "личность", + "imminent preimage (proposal already passed)": "неизбежный прообраз (предложение уже одобрено)", + "immortal": "бессмертный", + "include all empty strings in the generated file": "включить пробелы в генерируемом файле", + "include option": "включить опцию", + "include value": "включить значение", + "index": "индекс", + "initial head state": "начальное состояние главы", + "intentions": "намерения", + "interface operation mode": "рабочий режим интерфейса", + "invalid ABI file selected": "выбран не валидный ABI файл", + "invalid/unknown registrar account": "неправильный/неизвестный аккаунт регистратора", + "judgement": "суждение", + "key type to set": "тип ключа", + "keypair crypto type": "тип криптопары", + "kind": "сорт", + "last #": "последний №", + "last block": "последний блок", + "last reward": "последнее вознаграждение", + "launch period": "период запуска", + "legal name": "юридическое название", + "lifetime": "продолжительность жизни", + "lng.detect": "Язык браузера по умолчанию (автоматическое определение)", + "lock expired": "блокировка истекла", + "locked": "заблокировано", + "locked balance": "заблокированный баланс", + "logs": "логи", + "manage hardware connections": "управление соединений оборудования", + "matches": "соответствует", + "maximum gas allowed": "максимальные затраты газа", + "members": "участники", + "message to send": "сообщение для отправки", + "minimum bond": "минимум для привязки", + "minimum deposit": "минимальный депозит", + "mnemonic seed": "мнемоническая фраза", + "mortal, valid from #{{startAt}} to #{{endsAt}}": "смертный, действителен с #{{startAt}} до #{{endsAt}}", + "motions": "ходатайства", + "multisig": "мультиподпись", + "multisig name": "название мультиподписи", + "my ordered votes": "мои голосования", + "name": "имя", + "nav.accounts": "Аккаунты", + "nav.claims": "Права на токены", + "nav.contracts": "Контракты", + "nav.council": "Совет", + "nav.dashboard": "Панель", + "nav.democracy": "Демократия", + "nav.explorer": "Эксплорер", + "nav.extrinsics": "Экстринзики", + "nav.generic-asset": "generic-asset", + "nav.github": "GitHub", + "nav.i18n": "I18n Переводчик", + "nav.js": "javascript", + "nav.parachains": "Парачейны", + "nav.settings": "Настройки", + "nav.society": "Общество", + "nav.staking": "Стейкинг", + "nav.storage": "Хранилище", + "nav.sudo": "sudo", + "nav.tech-comm": "Тех.Ком", + "nav.toolbox": "Инструменты", + "nav.transfer": "Трансфер", + "nav.treasury": "Сокровищница", + "nav.wiki": "Wiki", + "nay": "Против", + "new account": "аккаунт", + "new address": "новый адрес", + "next": "следующий", + "next id": "следующий №", + "no": "нет", + "no accounts yet, create or import an existing": "пока нет аккаунтов, создайте новый или импортируйте существующий", + "no addresses saved yet, add any existing address": "у вас нет сохраненных адресов, добавьте любой сущействующий адрес", + "no messages": "нет сообщений", + "no peers connected": "нет соединений с другими нодами", + "no tags": "нет тэгов", + "no unapplied slashes found": "не нашлось непримененных слэшов", + "nominated accounts": "номинированные аккаунты", + "nominators": "номинаторы", + "not signed": "не подписаны", + "on-chain bonding duration": "время привязки в сети", + "ongoing referendum": "идущий референдум", + "only this network": "только эта сеть", + "or": "или", + "origin": "источник", + "other stake": "другой стейк", + "our best": "наш последний", + "own stake": "свой стейк", + "parachain id": "№ парачейна", + "parachains": "парачейны", + "parent": "родитель", + "password": "пароль", + "password (repeat)": "пароль (повторно)", + "payment": "платеж", + "payment destination": "место назначения платежа", + "payout stakers for (multiple)": "выплаты стейкеров для (нескольких)", + "payout stakers for (single)": "выплаты стейкеров для (одиночных)", + "payout/stash": "выплата/копилка", + "payout/validator": "выплата/валидатор", + "peer best": "последний у Ваших соединений", + "pending extrinsics": "экстринзики в обработке", + "pending hashes": "хэши в обработке", + "pending swap id": "в ожидании своп id", + "points": "очки", + "pot": "горшок", + "preimage hash": "хэш прообраза", + "prev": "предыдущий", + "prime member": "главный член", + "prime voter": "главный голосующий", + "priority {{index}}": "приоритет {{index}}", + "profit/era est": "доход/эра (оценка)", + "proposal": "предложение", + "proposal bond": "предложенное депонирование", + "proposal hash": "хэш предложения", + "proposals": "предложения", + "propose": "предложить", + "propose from account": "предложить с аккаунта", + "proposed by": "предложенный", + "proposer": "автор предложения", + "queued tx": "транзакции в очереди", + "reason": "причина", + "recent blocks": "последние блоки", + "recent events": "последние события", + "recover this account": "восстановить этот аккаунт", + "recovery block delay": "задержка блока восстановление", + "recovery threshold": "порог восстановления", + "redeemable": "выкупаемые", + "referenda": "референдумы", + "refresh in": "обновление через", + "registrar account": "аккаунт регистратора", + "registrar index": "индекс регистратора", + "relay dispatch queue": "реле очереди", + "remaining": "осталось", + "remote node/endpoint to connect to": "удаленная нода/эндпоинт к которой нужно подсоединиться", + "request payout from": "запросить выплату от", + "reserved": "резервировано", + "reward commission percentage": "процент комиссионного вознаграждения", + "rewards": "вознаграждения", + "rewards & slashes": "вознаграждения и слэши", + "riot": "riot", + "riot name": "имя в riot", + "role": "роль", + "rotation": "ротация", + "rpc.arcadia": "Arcadia (Nodle Testnet, hosted by Nodle)", + "rpc.berlin": "Berlin (Edgeware Testnet, hosted by Commonwealth Labs)", + "rpc.custom": "Пользовательская среда", + "rpc.edgeware": "Edgeware (Edgeware Mainnet, hosted by Commonwealth Labs)", + "rpc.flamingfir": "Flaming Fir (Substrate Testnet, hosted by Parity)", + "rpc.header.dev": "Разработка", + "rpc.header.live": "Рабочие сети", + "rpc.header.test": "Тестовые сети", + "rpc.kulupu": "Kulupu (Kulupu Mainnet, hosted by Kulupu)", + "rpc.kusama.ava": "Kusama (Polkadot Canary, пользовательские публичные ноды; смотреть https://status.cloud.ava.do/)", + "rpc.kusama.parity": "Kusama (Polkadot Canary, hosted by Parity)", + "rpc.kusama.w3f": "Kusama (Polkadot Canary, hosted by Web3 Foundation)", + "rpc.local": "Локальная нода (Own, 127.0.0.1:9944)", + "rpc.mandala": "Mandala (Acala Testnet, hosted by Acala)", + "rpc.westend": "Westend (Polkadot Testnet, hosted by Parity)", + "runners up": "претенденты", + "scheduling": "планирование", + "seats": "места", + "second with account": "поддерживаю с аккаунта", + "secret": "секрет", + "secret derivation path": "секретный путь деривации", + "seed (hex or string)": "порождающее значение (seed) (16-ричное число или строка)", + "select the account you wish to sign data with": "выберите аккаунт, с которого хотите подписать данные", + "selected constant query": "запрос выбранной константы", + "selected signatories": "выбранные подписанты", + "selected state query": "выбранный запрос состояний", + "selected validators": "выбранные валидатори", + "send": "отправить", + "send as RPC call": "отправить как RPC вызов", + "send as transaction": "отправить как транзакцию", + "send from account": "отправить с аккаунта", + "send to address": "отправить на адрес", + "sending account": "аккаунт отправителя", + "sending from my account": "отправляется с моего аккаунта", + "session": "сессия", + "session keys": "сессионные ключи", + "session next": "следующая сессия", + "sign data from account": "подписать данные с аккаунта", + "sign the following data": "подписать следующие данные", + "signatories": "подписанты", + "signatory": "подписант", + "signature crypto type": "тип шифрования подписи", + "signature of supplied data": "подпись предоставленных данных", + "signer": "подписавший", + "society head": "глава сообщества", + "spend period": "период трат", + "sr15519, Schnorrkel": "sr15519, Schnorrkel", + "ss58.centrifuge": "Centrifuge (рабочая)", + "ss58.default": "Значение по умолчанию для подключенной ноды", + "ss58.edgeware": "Edgeware (рабочая)", + "ss58.kusama": "Kusama (canary)", + "ss58.polkadot": "Polkadot (рабочая)", + "ss58.substrate": "Substrate (generic)", + "staked": "застейкано", + "stash account": "аккаунт-копилка", + "stashes": "копилки", + "state": "состояние", + "strikes": "удары", + "submit the following change": "внести следующее изменение", + "submit the following extrinsic": "отправить следущий экстринзик", + "submit with account": "отправить с аккаунта", + "submit with council account": "отправить с аккаунта Совета", + "sudo key": "ключ sudo", + "sudo with unchecked weight parameter": "sudo с непроверенным параметром веса", + "sudo without unchecked weight parameter": "sudo без непроверенного параметра веса", + "suri (seed & derivation)": "suri (seed & derivation)", + "swap to id": "поменять на №", + "syncing": "синхронизация", + "system events": "системные события", + "tags": "тэги", + "target": "цель", + "term progress": "прогресс срока", + "the account to make recoverable": "аккаунт, который хотите сделать восстанавливаемым", + "the account to recover to": "аккаунт, куда хотите восстановить", + "the associated controller": "соответствующий контроллер", + "the era to cancel for": "эра, для которой хотите отменить", + "the language to display translations for": "язык для которого показать перевод", + "the module to display strings for": "модуль, для которого показать строки", + "the resulting hash is": "результирующий хэш", + "the stash account to nominate with": "стэш аккаунт с которого номинировать", + "the supplied signature": "предоставленная подпись", + "threshold": "порог", + "tip": "чаевые", + "tip reason": "причина для чаевых", + "tip value": "сумма чаевых", + "tips": "чаевые", + "total": "всего", + "total issuance": "всего выпущено", + "total peers": "всего соединений с другими нодами", + "total stake": "всего стейк", + "total staked": "всего застейкано", + "transactions": "транзакции", + "transfer received": "трансферов получено", + "transferrable": "передаваемые", + "trusted social recovery helpers": "доверяемые помощники для социального восстановления", + "turnout": "оказаться", + "twitter": "twitter", + "type": "тип", + "unbond amount": "освободить сумму", + "unbonding": "освобождение", + "unchecked weight for this call": "непроверенный вес для этого вызова", + "unlock account with password": "разблокировать аккаунт с помощью пароля", + "unstake threshold": "порог освобождения от стейка", + "update on #{{index}}": "обновление #{{index}}", + "upgradable extensions": "обновляемые расширения", + "use on any network": "использовать на любой сети", + "using my account": "использовать мой аккаунт", + "using the following data": "используя следующие данные", + "using the selected account": "используя выбранный аккаунт", + "validator to query": "поиск по валидатору", + "validators": "валидаторы", + "validators/nominators": "валидаторы/номираторы", + "value": "значение", + "value bonded": "привязанная сумма", + "verify using address": "проверить с помощью адреса", + "version {{version}}": "версия {{version}}", + "vested": "vested", + "via Council/Vote": "через Совет/Голосование", + "via Democracy/Vote": "через Демократию/Голосование", + "via Staking/Bond": "Через Стейкинг/Привязку", + "via Vesting": "через Vesting", + "vote for candidate": "голосовать за кандидата", + "vote for defender": "голосовать за защитника", + "vote from account": "голосовать с аккаунта", + "vote value": "ценность голоса", + "vote with account": "голосовать с аккаунта", + "votes": "голоса", + "voting account": "голосующий аккаунт", + "voting balance": "баланс голосования", + "voting end": "голосование заканчивается", + "voting period": "период голосования", + "voting round": "круг голосования", + "waiting": "ожидание", + "web": "интернет", + "website": "веб-сайт", + "with an index of": "с индексом", + "wrong password supplied": "неправильный пароль", + "yes": "да", + "your current password": "Ваш текущий пароль", + "your new password": "Ваш новый пароль", + "{{blocks}} blocks": "{{blocks}} блоков", + "{{count}} key/value pairs encoded for submission": "{{count}} ключ/сумма пары для запуска", + "{{count}} own stashes": "{{count}} моих стэшов", + "{{currency}} average": "{{currency}} среднее", + "{{currency}} clipped": "{{currency}} урезано", + "{{currency}} rewards": "{{currency}} вознаграждения", + "{{currency}} slashed": "{{currency}} слэш", + "{{currency}} total": "{{currency}} всего", + "{{done}}/{{total}}, {{progress}}% done": "{{done}}/{{total}}, {{progress}}% закончено", + "{{d}} days": "{{d}} д.", + "{{h}} hrs": "{{h}} ч", + "{{m}} mins": "{{m}} мин.", + "{{name}} ({{size}} bytes)": "{{name}} ({{size}} bytes)", + "{{percentage}} aye": "{{percentage}} да", + "{{relayDispatchQueueSize}} dispatch messages pending": "{{relayDispatchQueueSize}} сообщений в очереди", + "{{step}}. Sign with your ETH address": "{{step}}. Подпишите Вашим адресом Ethereum", + "{{s}} s": "{{s}} с", + "{{threshold}}, not passing": "{{threshold}}, не проходит", + "{{threshold}}, passing": "{{threshold}}, проходит", + "{{value}}x voting balance, locked for {{lock}}x enactment ({{period}} days)": "{{value}}x баланс голосования, заблокировано на {{lock}}x ({{period}} дней)" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/locales/zh/translation.json b/pioneer/packages/apps/public/locales/zh/translation.json new file mode 100644 index 0000000000..4f7ef34c4a --- /dev/null +++ b/pioneer/packages/apps/public/locales/zh/translation.json @@ -0,0 +1,450 @@ +{ + "//hard///password": "//硬///密码", + "//hard/soft": "//硬/软", + "//hard/soft///password": "//硬/软///密码", + "1. Select your {{chain}} account": "1. 选择你的 {{chain}} 账户", + "2. Sign ETH transaction": "2. 签名 ETH 交易", + "A controller account should not map to another stash. This selected controller is a stash, controlled by {{bondedId}}": "控制账户不能映射成其他的存储账户. 当前选择的控制账户是存储账户,受控于 {{bondedId}}", + "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": "已经部署的合约要么是附加的要么是部署的. 地址和ABI用于构造参数.", + "ABI": "ABI", + "Account actions": "账户操作", + "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.": "从这些扩展程序中的任何一个注入的帐户都将出现在此应用程序中并可供使用。 随着更多具有外部签名功能的扩展可用,上面列表将会自动更新.", + "Active nominations ({{count}})": "活跃提名 ({{count}})", + "Add account": "添加账号", + "Add account via Qr": "通过二维码添加账户", + "Add an account via seed": "使用种子创建账号", + "Add an address": "添加一个地址", + "Add an existing code hash": "添加一个已经存在的代码哈希", + "Add an existing contract": "添加一个已存在的合约", + "Add contact": "添加联系人", + "Add via Qr": "二维码添加", + "Add via backup file": "通过备份文件添加", + "Additional types as a JSON file (or edit below)": "附加类型为一个JSON文件(或者在下面编辑)", + "Address Prefix": "地址前缀", + "Advanced creation options": "高级创建选项", + "Amount to add to the currently bonded funds. This is adjusted using the available funds on the account.": "当前抵押金额的总量. 使用帐户中的可用资金进行调整.", + "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.": "一个加密的备份文件将会被创建 一旦你按下 \"下载\" 按钮. 这将会在你用另外一台设备重新导入账户时使用", + "Approved": "赞成", + "Aura": "Aura", + "Authorize transaction": "交易授权", + "Aye, I approve": "是, 我赞成", + "Babe": "Babe", + "Backup account": "备份账户", + "Block details": "区块详情", + "Bond": "抵押", + "Bond & Nominate": "抵押 & 提名", + "Bond & Validate": "抵押 & 验证", + "Bond more": "抵押更多", + "Bond more funds": "抵押更多资金", + "Bonding Preferences": "抵押偏好设置", + "Call": "调用", + "Call a contract": "调用一个合约", + "Call a method on this contract": "在合约上调用一个方法", + "Call results": "调用结果", + "Cancel": "取消", + "Chain info": "链信息", + "Chain specifications": "生成含网络参数的二维码", + "Change": "更改", + "Change account password": "更改账户密码", + "Change controller account": "更改控制账户", + "Change reward destination": "更改收益地址", + "Change this account's password": "更改账户密码", + "Clear all": "清空所有", + "Color": "颜色", + "Confirm ABI removal": "确认 ABI 清除", + "Confirm account removal": "确定账户被移除", + "Confirm address removal": "确定地址被移除", + "Confirm claim": "确定认领", + "Confirm code removal": "确定代码被移除", + "Confirm contract removal": "确定合约被移除", + "Continue": "继续", + "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "在钱包中选择预售阶段你使用的以太坊账户,复制上面的字符串,并且使用你的以太坊账户把字符串作为内容签名交易, 并且粘贴交易签名对象到下面", + "Council overview": "议会概览", + "Create": "创建", + "Create a backup file for this account": "给这个账号创建一个备份文件", + "Create an account now.": "马上创建账户.", + "Create and backup account": "创建并且备份账户", + "Decimals": "小数", + "Decimals decides the smallest unit of the token, which is 1/10^decimals": "小数代表每一单位token最小可以精确到多少位小数", + "Deploy": "部署", + "Deploy a code hash": "部署一个代码哈希", + "Deploy this code hash as a smart contract": "将此代码哈希部署为智能合约", + "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".": "确定将使用哪种加密方法创建此帐户. 注意,要在Polkadot上进行验证, 会话账户必须使用 \"ed25519\".", + "Developer": "开发者", + "Development": "开发", + "Dismiss all notifications": "禁用所有通知", + "Do not include a tip for the block author": "不包含区块打包者费用", + "Download": "下载", + "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)": "在 {{elapsed}}秒, 生成 {{count}} 个 key (平均{{avg}} keys/秒)", + "Extensions": "浏览器插件", + "Forget": "忘记", + "Forget this account": "删除这个账号", + "Forget this address": "忘记这个地址", + "Forget this code hash": "忘记这个代码哈希", + "Forget this contract": "忘记该合约", + "Forks": "(Forks)分叉", + "General": "通用", + "Genesis Hash": "创世区块哈希", + "Genesis Hash refers to initial state of the chain, it cannot be changed once the chain is launched": "创世区块哈希代表网络的初始状态,在网络启动后不可改变", + "Grandpa": "Grandpa", + "I'm Online": "我在线", + "Important notice": "重要提示", + "Inactive nominations ({{count}})": "非活跃提名 ({{count}})", + "Include an optional tip for faster processing": "为了尽快处理可以额外添加费用", + "Initializing connection": "正在初始化连接", + "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -": "强烈建议你安全的创建/存储你的账户并且放在应用外部. 在 {{yourBrowser}} 下面的的浏览器扩展可用 -", + "Keys from rotateKeys": "Keys 来自 rotateKeys", + "Learn more...": "了解更多...", + "Make Transfer": "添加交易", + "Manage your connection to Ledger S": "管理你的连接到 Ledger S", + "Messages": "消息", + "Mnemonic": "助记词", + "Motions ({{count}})": "Motions ({{count}})", + "My accounts": "我的账户", + "My contacts": "我的账户", + "Name given to this account. You can change it at any point in the future.": "给该账户命名. 将来你可以随时更改.", + "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".": "给该账户命名,你可以随时编辑,使用帐户进行验证或提名, 最好的做法是在该账户名后面附加上账户的功能, 例如 \"账户名 - 暂存\".", + "Name of the network. It is only for display purposes.": "网络名称,仅作为显示用", + "Nay, I do not approve": "否, 我反对", + "Network Name": "网络名称", + "No approved proposals": "没有赞成的提案", + "No candidates found": "没有候选人", + "No code hashes available": "没有代码哈希可用", + "No contracts available": "没有合约可用", + "No council motions": "没有议会议案", + "No events available": "没有事件可用", + "No funds staked yet. Bond funds to validate or nominate a validator": "尚无抵押资金,抵押资金来验证或者提名一个验证者", + "No items": "没有选项", + "No members found": "没有成员", + "No pending extrinsics are in the queue": "队列中没有等待的交易", + "No pending payouts for your stashes": "没有待支付的存储账户", + "No pending proposals": "没有进行中的提案", + "No runners up found": "No runners up found", + "No upgradable extensions found": "没有需要升级的浏览器插件", + "Node info": "节点信息", + "Nominate": "提名", + "Nominate Validators": "提名验证者", + "Nominate selected": "提名选中的", + "Nominate validators": "提名验证者", + "Nominator": "提名者", + "Override the default identity icon display with a specific theme": "用一个指定主题重来载默认的身份图标显示", + "Override the default ss58 prefix for address generation": "生成地址时重载 ss58 前缀", + "Parachains": "平行链", + "Paste here the address of the contact you want to add to your address book.": "把你想要添加到地址簿的联系人地址粘贴到这里.", + "Payout all": "支付所有", + "Payouts": "支付", + "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.": "请确保将此文件和你的秘密保存在安全的位置,因为它是必需的, 恢复账户使用.", + "Positive number": "正数", + "Positive number between 1 and {{memberCount}}": "整数在1和{{memberCount}}之间", + "Prefix indicates the ss58 address format in this network, it is a number between 0 ~ 255 that describes the precise format of the bytes of the address": "地址前缀用0~255的数来来表示SS58地址的编码格式", + "Proposals": "提案", + "Propose a council motion": "提出一个一会议案", + "Query Ledger": "查询账本", + "Random": "随机生成颜色", + "Raw seed": "原始种子", + "Redeem": "赎回", + "Remove": "移除", + "Reset": "重置", + "Restore": "恢复", + "Restore JSON": "使用JSON恢复", + "Retrieving data": "正在读取数据", + "Retrieving info for all applicable eras, this will take some time": "检索所有可用 era 的信息,需要花点时间", + "Retrieving info for last {{numEras}} eras, this will take some time": "获取最近 {{numEras}} 个 era 的信息, 需要花点时间", + "Save": "保存", + "Save & Reload": "保存并重载", + "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.": "把这个安全文件保存到一个安全的位置. 另外, 与此帐户关联的密码以及此备份文件需要一起才能还原您的帐户.", + "Search for": "搜索", + "Select Network": "选择网络", + "Select a contact or paste the address you want to send funds to.": "选择一个联系人或者粘贴一个你想要发送资金的地址.", + "Select best": "选择最优", + "Select example": "选择实例", + "Select targets manually (no auto-selection based on current profitability)": "手动选择(不是根据当前收益自动选择)", + "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.": "选择创建帐户时下载的JSON密钥文件。 此JSON文件包含使用密码加密的私钥.", + "Select the remote endpoint, either from the dropdown on manual entered via the custom toggle": "选择远程终端,或者通过自定义切换输入的手动下拉菜单中的任意一个", + "Send funds": "发送资金", + "Send funds from this account": "从这个账号里面发送资金", + "Send funds to this address": "发送资金到这个地址", + "Sending transaction": "正在发送交易", + "Set Session Key": "设置会话key", + "Set controller": "设置控制账户", + "Set nominees": "设置被提名人", + "Set reward destination": "设置收益地址", + "Setup Nominator {{step}}/{{NUM_STEPS}}": "设置提名者 {{step}}/{{NUM_STEPS}}", + "Setup Validator {{step}}/{{NUM_STEPS}}": "设置验证者 {{step}}/{{NUM_STEPS}}", + "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...": "搜索是否大小写敏感, 例如: 如果你选择 \"否\" 你搜索 \"Dsy\" 返回的地址可能包括 \"dsY\" 或者 \"DSy\"...", + "Sign (no submission)": "签名(不提交)", + "Sign and Submit": "签名并提交", + "Specify the user account to use for this contract call. And fees will be deducted from this account.": "指定用于此合约调用的用户帐户。费用将从该帐户中扣除.", + "Specify the user account to use for this deployment. And fees will be deducted from this account.": "指定用于部署的用户帐户。 费用将从该帐户中扣除.", + "Staking overview": "质押概览", + "Start generation": "开始生成", + "Stash": "存储账户", + "Stop": "停止", + "Stop generation": "停止生成", + "Submit": "提交", + "Submit candidacy": "提交候选人", + "Submit key": "提交key", + "Submit signed extrinsic": "提交签名交易", + "Submit your council candidacy": "提交你的一会候选人", + "Targets": "目标", + "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.": "创建账户的时候密码需要被指定 用来加密备份文件并且恢复账户的时候用来解密.", + "The account you want to claim to.": "你想要认领的账户.", + "The account you will send funds from.": "从哪个账户发送资金.", + "The address for the deployed contract instance.": "部署合同实例的地址.", + "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": "分配给这个合约的捐赠, 即 the amount transferred to the contract upon instantiation.", + "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": "此合约的分配值,即转移给合约的总量作为调用的一部分.", + "The code hash for the on-chain deployed code.": "链上部署代码的哈希.", + "The code is not recognized as being in valid WASM format": "该代码未被识别为有效的WASM格式", + "The codeHash is not a valid hex hash": "codeHash不是有效的十六进制哈希", + "The color used to distinguish this network with others, use color code with 3 or 6 digits, like \"#FFF\" or \"#111111\"": "颜色用来和其他网络相区别,用3位或者6位的16进制码表示,比如#FFF或#111111", + "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": "您希望部署的合约已编译为WASM. 每个唯一的代码blob都将附加一个代码哈希,可用于创建新实例", + "The controller does no have sufficient funds available to cover transaction fees. Ensure that a funded controller is used.": "控制账户没有足够的资金支付交易费用. 确保使用的控制账户有足够的资金.", + "The deployment constructor information for this contract, as provided by the ABI.": "由ABI提供的此合约的部署构造函数信息.", + "The existing account password as specified when this account was created or when it was last changed.": "当账户被创建或者最后一个被修改的时候需要给已存在的账号指定密码.", + "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": "用于调用使用gas的最大量. 如果超过这个量将会调用失败.", + "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": "此部署可以使用gas的最大量,如果代码需要更多gas,部署将失败.", + "The message to send to this contract. Parameters are adjusted based on the ABI provided.": "发送给合约的消息. 根据提供的ABI调整参数.", + "The minimum number of council votes required to approve this motion": "The minimum number of council votes required to approve this motion", + "The new account password. Once set, all future account unlocks will be performed with this new password.": "新账户的密码. 一旦设置, 后面所有账号解锁都将使用新的密码.", + "The password to unlock the selected account.": "密码用来解锁当前选择的账户.", + "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.": "你的账户的私钥是从这个种子派生出来的. 该种子必须保密,因为其拥有的任何人都可以使用此帐户的资金. 如果你验证过了, 你的节点使用会话账户的种子作为 \"--key\" 参数.", + "The selected account to perform the derivation on.": "选择的账号正在执行派生.", + "The value is not in a valid address format": "该值的地址格式无效", + "This is not a valid JSON object.": "不是一个有效的JSON对象.", + "This operation does not impact the associated on-chain code or any of its contracts.": "This operation does not impact the associated on-chain code or any of its contracts.", + "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).": "这个秘密用来加密你的私钥. 必须健壮唯一! 你将会和你的账号一起用来签名交易. 你可以用你的密码和你的备份账号一起来恢复你账户 (下一步生成的).", + "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.": "此帐户在浏览器中存储以供将来使用。 确保您安全地存储了新密码,该密码是该帐户的唯一密码.", + "Tip (optional)": "小费(可选)", + "Treasury overview": "财政概览", + "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.": "在此处输入您希望包含的地址。 该工具将生成密钥并显示与您的搜索最匹配的关联地址. 你可以使用 \"?\" 最为通配符.", + "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "输入你要发送的总量. 注意 你可以从右边选择单位 例如 发送 1 milli 相当于发送 0.001.", + "Type the name of your contact. This name will be used across all the apps. It can be edited later on.": "输入联系人的名字. 这个名字在整个app内可用. 稍后课编辑.", + "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.": "输入在创建帐户时选择的密码。 它用于加密备份文件中帐户的私钥。", + "Unable to find deployed contract code at the specified address": "在指定的地址找不到部署的合约代码", + "Unable to find on-chain WASM code for the supplied codeHash": "找不到所提供codeHash的链上WASM代码", + "Unbond": "解除抵押", + "Unbond funds": "解除抵押资金", + "Unit": "单位", + "Unit decides the name of 1 unit token, e.g. \"DOT\" for Polkadot": "每一个token的单位,比如Polkadot的单位是DOT", + "Unlock": "解锁", + "Update metadata": "升级Metadata", + "Upload": "上传", + "Upload WASM": "上传 WASM", + "Use an automatic selection of the currently most profitable validators": "自动选择当前收益最高的验证者", + "Validate": "验证", + "Validator": "验证者", + "Validator stats": "验证者统计", + "Vanity generator": "地址美化", + "Vote": "投票", + "Vote for current candidates": "给当前候选人投票", + "Waiting": "等待中", + "Waiting for API to be connected and ready.": "等待API连接成功。", + "Waiting for authorization from the extension. Please open the installed extension and approve or reject access.": "扩展等待认证. 打开已经安装的扩展同意或者拒绝他.", + "Waiting nominations ({{count}})": "等待被提名 ({{count}})", + "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.": "创建帐户后,我们将为您提供一个生成的备份文件。 只要您有权访问帐户,以后就可以通过单击\"帐户\"中的\"备份\"按钮来下载此文件.", + "With the Firefox browser connecting to insecure WebSockets ({{wsUrl}}) will fail due to the browser not allowing localhost access from a secure site.": "用Firefox连接不安全的 WebSockets ({{wsUrl}}) 由于浏览器不允许从一个安全的站点访问本地服务, 可能访问失败.", + "You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure 'wss' endpoint.": "你正在从一个安全的连接访问不安全的 WebSocket ({{wsUrl}}). 由于浏览器最大安全策略这个连接将不被允许访问. 更改RPC服务到一个安全的 'wss' 终端.", + "You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.": "没有连接任何节点. 确保你的节点正在运行并且保证websocket终端可访问.", + "You are using an ABI with an outdated format. Please generate a new one.": "你正在使用的ABI格式已过期. 请生成一个新的.", + "You can set a custom derivation path for this account using the following syntax \"//////\". The \"/\" and \"//\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.": "你可以对该账户自定义派生路径根据下面的语法 \"//////<密码>\". \"/\" 和 \"//\" 可以重复和混合`. \"///密码\" 是可选的只能出现一次.", + "You don't have any accounts. Some features are currently hidden and will only become available once you have accounts.": "你还没有账户. 很多功能对你隐藏了,赶紧添加一个账号试试.", + "Your Ethereum account": "你的以太坊账户", + "Your custom types have been added": "你的自定义类型被添加了", + "account forgotten": "账号忘记了", + "account restored": "账户已恢复", + "additional bonded funds": "附加抵押更多资金", + "address": "地址", + "address copied": "地址已经复制", + "address created": "地址被创建了", + "address edited": "地址被编辑了", + "address forgotten": "地址忘记了", + "address prefix": "地址前缀", + "amount": "总量", + "amount to use for estimation": "估算金额", + "approved": "赞成", + "auto-selected targets for nomination": "自动选择提名的对象", + "backing": "backing", + "backup file": "备份文件", + "balance": "余额", + "beneficiary": "受益者", + "best": "最好", + "best #": "最好 #", + "best hash": "最好哈希", + "block hash or number to query": "区块哈希或查询号", + "blocks": "区块", + "bond": "抵押", + "bonded": "抵押", + "call from account": "从账户调用", + "candidate accounts": "候选人账户", + "candidates": "候选人", + "case sensitive": "大小写敏感", + "claim to account": "认领到账户", + "click to copy": "点击复制", + "click to select or drag and drop a JSON ABI file": "点击选择或者拖拽一个JSON格式的ABI文件", + "clipboard": "剪贴板", + "code for this contract": "该合约的代码", + "code hash": "代码哈希", + "comm.": "佣金", + "commission": "佣金", + "compiled contract WASM": "编译过的合约 WASM", + "connected peers": "连接的节点", + "contract address": "合约地址", + "contract to use": "要使用的合约", + "controller": "控制账户", + "controller account": "控制账户", + "copied": "已经复制", + "created account": "账号已创建", + "crypto type to use": "使用的加密类型", + "custom endpoint": "自定义终端", + "default icon theme": "默认图标主题", + "default interface language": "默认接口语言", + "deploy": "部署", + "deployment account": "部署账户", + "deposit": "存入", + "derivation path": "派生路径", + "derive root account": "获取跟账户", + "development seed": "开发种子", + "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": "一个合法的认领没有出现. 请再次确认你签名的交易是否在正确的以太坊账户上.", + "elected stake": "选定的质押", + "endowment": "捐赠", + "epoch": "时期", + "era": "时代", + "era points": "era 得分", + "events": "事件", + "execute": "执行", + "extrinsic hash": "交易哈希", + "extrinsics": "交易", + "filter by name or tags": "通过名字或者标签过滤", + "filter by name, address or index": "使用名字,地址或者索引筛选", + "filter by name, address, or account index": "使用名字、地址、账户索引过滤", + "finalized": "最终确定", + "forks": "(forks)分叉", + "generated public key": "生成的公钥", + "has a valid claim for": "有一个有效的声明", + "index": "索引", + "intentions": "意向", + "interface operation mode": "接口操作模式", + "invalid ABI file selected": "非法 ABI 文件被选择", + "key type to set": "设置key类型", + "keypair crypto type": "密钥对加密类型", + "last #": "最后 #", + "last block": "最后的区块", + "last reward": "最终奖励", + "lifetime": "生命周期", + "limit to elected": "限于选定", + "lng.detect": "默认浏览器语言 (自动检测)", + "locked": "锁定", + "logs": "日志", + "manage hardware connections": "管理硬件连接", + "maximum gas allowed": "允许的最大gas", + "members": "成员", + "message to send": "要发送的消息", + "mnemonic seed": "助记词", + "name": "名称", + "nav.accounts": "账户", + "nav.claims": "认领", + "nav.contracts": "合约", + "nav.council": "议会", + "nav.dashboard": "仪表盘", + "nav.democracy": "民主权利", + "nav.explorer": "浏览", + "nav.extrinsics": "交易", + "nav.generic-asset": "通用资产", + "nav.js": "javascript", + "nav.parachains": "平行链", + "nav.settings": "设置", + "nav.staking": "质押", + "nav.storage": "链状态", + "nav.sudo": "撤销", + "nav.tech-comm": "技术委员会", + "nav.toolbox": "工具箱", + "nav.transfer": "转账", + "nav.treasury": "财政", + "new account": "新建账户", + "new address": "新建地址", + "next": "下一步", + "no": "否", + "no accounts yet, create or import an existing": "还没有账户, 创建或者导入一个已经存在的账户", + "no peers connected": "没有节点连接", + "no tags": "没有标签", + "nominated accounts": "提名账户", + "nominators": "提名者", + "on-chain bonding duration": "链上抵押时长", + "only query most recent {{partialEras}} of {{historyDepth}} eras": "只查询最近 {{historyDepth}} 中的 {{partialEras}} 个 ear", + "only this network": "仅在这个网络", + "only with an identity": "只显示经过认证的验证者", + "or": "或", + "other stake": "提名抵押金额", + "our best": "我们最好的", + "own stake": "自抵押金额", + "password": "密码", + "payment destination": "支付账户", + "payout/stash": "支付/储值", + "peer best": "最好节点", + "pending extrinsics": "等待的交易", + "points": "得分", + "prev": "上一步", + "profit/era": "盈利/era", + "proposal": "提案", + "proposals": "提案", + "queued tx": "队列中的交易", + "recent blocks": "最新区块", + "recent events": "最新事件", + "redeemable": "可赎回", + "refresh in": "刷新", + "remote node/endpoint to connect to": "远程节点/终端连接到", + "reserved": "保留", + "reward commission percentage": "奖励佣金百分比", + "rewards": "奖励", + "rewards & slashes": "奖励 & 削减", + "role": "角色", + "runners up": "runners up", + "seats": "座位", + "secret derivation path": "加密派生路径", + "seed (hex or string)": "发送 (16进制 或 字符串)", + "selected validators": "选择的验证者", + "send": "发送", + "send as RPC call": "作为RPC调用发送", + "send as transaction": "作为交易发送", + "send from account": "发送地址", + "send to address": "发送到的地址", + "sending from my account": "从我的账户发送", + "session": "session", + "session keys": "会话key", + "staked": "质押的", + "stash account": "储值账户", + "stashes": "存储账户", + "syncing": "同步中", + "target": "目标", + "term progress": "term progress", + "the associated controller": "相关的控制账户", + "the stash account to nominate with": "用来提名的存储账户", + "threshold": "阀值", + "tip": "提示", + "total": "总共", + "total issuance": "总发行量", + "total peers": "节点总数", + "total stake": "质押总额", + "total staked": "质押总量", + "transactions": "交易", + "transfer received": "转账接收成功", + "transferrable": "可转移", + "type": "类型", + "unbonding": "解除抵押", + "unlock account with password": "使用密码解锁", + "unstake threshold": "取消抵押阈值", + "update on #{{index}}": "更新 #{{index}}", + "upgradable extensions": "升级浏览器插件", + "use on any network": "在任何网络使用", + "using my account": "正在使用我的账户", + "validator to query": "要查询的验证者", + "validators": "验证者", + "validators/nominators": "验证者/提名者", + "value": "值", + "value bonded": "抵押金额", + "voting round": "投票轮次", + "waiting": "等候中", + "yes": "是", + "your current password": "你当前的密码", + "your new password": "你的新密码" +} \ No newline at end of file diff --git a/pioneer/packages/apps/public/manifest.json b/pioneer/packages/apps/public/manifest.json new file mode 100644 index 0000000000..274491d22b --- /dev/null +++ b/pioneer/packages/apps/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "Polkadot Apps", + "name": "Apps for interacting with a Polkadot node", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": "index.html", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/pioneer/packages/apps/src/Apps.tsx b/pioneer/packages/apps/src/Apps.tsx new file mode 100644 index 0000000000..c331f9f976 --- /dev/null +++ b/pioneer/packages/apps/src/Apps.tsx @@ -0,0 +1,238 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { BareProps as Props } from '@polkadot/react-components/types'; + +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import store from 'store'; +import styled from 'styled-components'; +import AccountSidebar from '@polkadot/app-accounts/Sidebar'; +import { getSystemChainColor } from '@polkadot/apps-config/ui'; +import { defaultColor } from '@polkadot/apps-config/ui/general'; +import GlobalStyle from '@polkadot/react-components/styles'; +import { useApi } from '@polkadot/react-hooks'; +import Signer from '@polkadot/react-signer'; + +import ConnectingOverlay from './overlays/Connecting'; +import { SideBarTransition, SIDEBAR_MENU_THRESHOLD } from './constants'; +import Content from './Content'; +import SideBar from './SideBar'; +import WarmUp from './WarmUp'; +import { WindowDimensionsCtx } from './WindowDimensions'; + +/* Joystream-specific */ +import TopBar from './JoyTopBar/TopBar'; + +interface SidebarState { + isCollapsed: boolean; + isMenu: boolean; + isMenuOpen: boolean; + transition: SideBarTransition; +} + +export const PORTAL_ID = 'portals'; + +function saveSidebar (sidebar: SidebarState): SidebarState { + return store.set('sidebar', sidebar) as SidebarState; +} + +function Apps ({ className = '' }: Props): React.ReactElement { + const windowDimensions = useContext(WindowDimensionsCtx); + const { systemChain, systemName } = useApi(); + const [sidebar, setSidebar] = useState({ + isCollapsed: false, + isMenuOpen: false, + transition: SideBarTransition.COLLAPSED, + ...store.get('sidebar', {}), + isMenu: window.innerWidth < SIDEBAR_MENU_THRESHOLD + }); + const uiHighlight = useMemo( + (): string | undefined => getSystemChainColor(systemChain, systemName), + [systemChain, systemName] + ); + + const _collapse = useCallback( + (): void => setSidebar((sidebar: SidebarState) => saveSidebar({ ...sidebar, isCollapsed: !sidebar.isCollapsed })), + [] + ); + const _toggleMenu = useCallback( + (): void => setSidebar((sidebar: SidebarState) => saveSidebar({ ...sidebar, isCollapsed: false, isMenuOpen: true })), + [] + ); + const _handleResize = useCallback( + (): void => { + const transition = window.innerWidth < SIDEBAR_MENU_THRESHOLD + ? SideBarTransition.MINIMISED_AND_EXPANDED + : SideBarTransition.EXPANDED_AND_MAXIMISED; + + setSidebar((sidebar: SidebarState) => saveSidebar({ + ...sidebar, + isMenu: transition === SideBarTransition.MINIMISED_AND_EXPANDED, + isMenuOpen: false, + transition + })); + }, + [] + ); + + useEffect((): void => { + _handleResize(); + }, [_handleResize, windowDimensions]); + + const { isCollapsed, isMenu, isMenuOpen } = sidebar; + + return ( + <> + +
+ +
+ + +
+ + +
+
+ +
+ +
+ + + ); +} + +export default React.memo(styled(Apps)` + box-sizing: border-box; + display: flex; + flex-direction: row; + height: 100vh; + + &.theme--default { + a.apps--SideBar-Item-NavLink { + color: #f5f4f3; + display: block; + padding: 0.75em 0.75em; + white-space: nowrap; + + &:hover { + background: #5f5f5f; + border-radius: 0.28571429rem 0 0 0.28571429rem; + color: #eee; + margin-right: 0.25rem; + } + } + + a.apps--SideBar-Item-NavLink-active { + background: #f5f4f3; + border-radius: 0.28571429rem 0 0 0.28571429rem; + /* border-bottom: 2px solid transparent; */ + color: #3f3f3f; + + &:hover { + background: #f5f4f3; + color: #3f3f3f; + margin-right: 0; + } + } + } + + &.collapsed .apps--SideBar { + text-align: center; + + .divider { + display: none; + } + + .apps--SideBar-Item { + margin-left: 5px; + + .text { + display: none; + } + } + + .apps--SideBar-logo { + .apps--SideBar-logo-inner { + margin: auto; + padding: 0; + width: 3rem; + + img { + margin: 0 0.4rem; + } + + > div.info { + display: none; + } + } + } + + .apps--SideBar-collapse .ui.basic.secondary.button { + left: 0.66rem; + } + } + + &.expanded .apps--SideBar { + text-align: left; + + .apps--SideBar-Scroll { + padding-left: 0.75rem; + } + } + + &.fixed { + .apps--SideBar-Wrapper { + position: absolute; + width: 0px; + + .apps--SideBar { + padding-left: 0; + } + } + } + + &.menu-open { + .apps--SideBar-Wrapper { + width: 12rem; + } + } + + .apps--Menu-bg { + background: transparent; + height: 100%; + left: 0; + position: absolute; + top: 0; + transition: opacity 0.2s; + width: 100%; + z-index: 299; + + &.closed { + opacity: 0; + width: 0; + } + + &.open { + opacity: 1; + } + } + + .apps--Main { + flex-grow: 1; + min-height: 100vh; + overflow-x: hidden; + overflow-y: auto; + } +`); diff --git a/pioneer/packages/apps/src/Content/NotFound.tsx b/pioneer/packages/apps/src/Content/NotFound.tsx new file mode 100644 index 0000000000..f2c58785c9 --- /dev/null +++ b/pioneer/packages/apps/src/Content/NotFound.tsx @@ -0,0 +1,14 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import React from 'react'; +import { Redirect } from 'react-router'; + +function NotFound (): React.ReactElement { + return ( + + ); +} + +export default React.memo(NotFound); diff --git a/pioneer/packages/apps/src/Content/Status.tsx b/pioneer/packages/apps/src/Content/Status.tsx new file mode 100644 index 0000000000..ffcc5f3547 --- /dev/null +++ b/pioneer/packages/apps/src/Content/Status.tsx @@ -0,0 +1,82 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { EventRecord } from '@polkadot/types/interfaces'; +import { KeyringOptions } from '@polkadot/ui-keyring/options/types'; +import { ActionStatus } from '@polkadot/react-components/Status/types'; + +import React, { useContext, useEffect } from 'react'; +import { Status as StatusDisplay, StatusContext } from '@polkadot/react-components'; +import { useAccounts, useApi, useCall } from '@polkadot/react-hooks'; +import { stringToU8a } from '@polkadot/util'; +import { xxhashAsHex } from '@polkadot/util-crypto'; + +import { useTranslation } from '../translate'; + +interface Props { + optionsAll?: KeyringOptions; +} + +let prevEventHash: string; + +function filterEvents (allAccounts: string[], t: (key: string, opts?: Record) => T, optionsAll?: KeyringOptions, events?: EventRecord[]): ActionStatus[] | null { + const eventHash = xxhashAsHex(stringToU8a(JSON.stringify(events))); + + if (!optionsAll || !events || eventHash === prevEventHash) { + return null; + } + + prevEventHash = eventHash; + + return events + .map(({ event: { data, method, section } }): ActionStatus | null => { + if (section === 'balances' && method === 'Transfer') { + const account = data[1].toString(); + + if (allAccounts.includes(account)) { + return { + account, + action: `${section}.${method}`, + message: t('transfer received'), + status: 'event' + }; + } + } else if (section === 'democracy') { + const index = data[0].toString(); + + return { + action: `${section}.${method}`, + message: t('update on #{{index}}', { + replace: { + index + } + }), + status: 'event' + }; + } + + return null; + }) + .filter((item): item is ActionStatus => !!item); +} + +function Status ({ optionsAll }: Props): React.ReactElement { + const { queueAction } = useContext(StatusContext); + const { api, isApiReady } = useApi(); + const { allAccounts } = useAccounts(); + const { t } = useTranslation(); + const events = useCall(isApiReady && api.query.system?.events, []); + + useEffect((): void => { + const filtered = filterEvents(allAccounts, t, optionsAll, events); + + filtered && queueAction(filtered); + }, [allAccounts, events, optionsAll, queueAction, t]); + + return ( + + ); +} + +export default React.memo(Status); diff --git a/pioneer/packages/apps/src/Content/index.tsx b/pioneer/packages/apps/src/Content/index.tsx new file mode 100644 index 0000000000..63f0238535 --- /dev/null +++ b/pioneer/packages/apps/src/Content/index.tsx @@ -0,0 +1,94 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Route } from '@polkadot/apps-routing/types'; + +import React, { Suspense, useContext, useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; +import styled from 'styled-components'; +import createRoutes from '@polkadot/apps-routing'; +import { ErrorBoundary, Spinner, StatusContext } from '@polkadot/react-components'; +import { useApi } from '@polkadot/react-hooks'; + +import { findMissingApis } from '../endpoint'; +import { useTranslation } from '../translate'; +import NotFound from './NotFound'; +import Status from './Status'; + +interface Props { + className?: string; +} + +const NOT_FOUND: Route = { + Component: NotFound, + display: { + needsApi: undefined + }, + icon: 'times', + isIgnored: false, + name: 'unknown', + text: 'Unknown' +}; + +function Content ({ className }: Props): React.ReactElement { + const location = useLocation(); + const { t } = useTranslation(); + const { api, isApiConnected, isApiReady } = useApi(); + const { queueAction } = useContext(StatusContext); + const { Component, display: { needsApi }, name } = useMemo( + (): Route => { + const app = location.pathname.slice(1) || ''; + + return createRoutes(t).find((route) => !!(route && app.startsWith(route.name))) || NOT_FOUND; + }, + [location, t] + ); + + return ( +
+ {needsApi && (!isApiReady || !isApiConnected) + ? ( +
+ ('Initializing connection')} /> +
+ ) + : ( + <> + + {findMissingApis(api, needsApi).length + ? + : ( + + + + ) + } + + + + ) + } +
+ ); +} + +export default React.memo(styled(Content)` + background: rgba(250, 250, 250); + padding: 0 1.5rem; + position: relative; + width: 100%; + height: 100%; + + @media(max-width: 768px) { + padding: 0 0.5rem; + } + + .connecting { + padding: 3.5rem 0; + } +`); diff --git a/pioneer/packages/apps/src/JoyTopBar/TopBar.tsx b/pioneer/packages/apps/src/JoyTopBar/TopBar.tsx new file mode 100644 index 0000000000..807141b73c --- /dev/null +++ b/pioneer/packages/apps/src/JoyTopBar/TopBar.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { useMyMembership } from '@polkadot/joy-utils/react/hooks'; +import { InputAddress } from '@polkadot/react-components'; +import { Available } from '@polkadot/react-query'; +import styled from 'styled-components'; +import { useApi } from '@polkadot/react-hooks'; + +const StyledTopBar = styled.div` + padding: 0.75rem; + background-color: #3f3f3f; + border-bottom: 1px solid #d4d4d5; + text-align: right; + margin: 0; + + &.NoMyAddress { + background-color: #ffeb83; + color: #000; + text-align: center; + } + + .ui--InputAddress { + display: inline-block; + } +`; + +function JoyTopBar () { + const { + allAccounts, + myAddress + } = useMyMembership(); + + const { isApiReady } = useApi(); + + if (!isApiReady) { + return null; + } + + const balance = Balance: ; + const labelExtra = myAddress + ? + : 'No key selected'; + + return Object.keys(allAccounts || {}).length ? ( + + + + ) : null; +} + +export default JoyTopBar; diff --git a/pioneer/packages/apps/src/SideBar/ChainInfo.tsx b/pioneer/packages/apps/src/SideBar/ChainInfo.tsx new file mode 100644 index 0000000000..737bed301b --- /dev/null +++ b/pioneer/packages/apps/src/SideBar/ChainInfo.tsx @@ -0,0 +1,95 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RuntimeVersion } from '@polkadot/types/interfaces'; + +import React from 'react'; +import styled from 'styled-components'; +import { ChainImg, Icon } from '@polkadot/react-components'; +import { useCall, useApi } from '@polkadot/react-hooks'; +import { BestNumber, Chain } from '@polkadot/react-query'; + +import { useTranslation } from '../translate'; + +interface Props { + className?: string; + onClick?: () => void; +} + +function ChainInfo ({ className = '', onClick }: Props): React.ReactElement { + const { t } = useTranslation(); + const { api } = useApi(); + const runtimeVersion = useCall(api.rpc.state.subscribeRuntimeVersion, []); + + return ( +
+
+ +
+ + {runtimeVersion && ( +
{t('version {{version}}', { replace: { version: runtimeVersion.specVersion.toNumber() } })}
+ )} + +
+ +
+
+ ); +} + +export default React.memo(styled(ChainInfo)` + border-top: 0.5rem solid transparent; + box-sizing: border-box; + cursor: pointer; + padding: 0.75rem; + margin: 0 0 0.5rem -1rem; + + .apps--SideBar-logo-inner { + display: flex; + align-items: center; + justify-content: space-between; + width: 10.5rem; + + img { + flex: 0; + height: 2.75rem; + width: 2.75rem; + } + + .ui--Icon.dropdown, + > div.info { + color: white; + opacity: 0.75; + text-align: right; + vertical-align: middle; + } + + .ui--Icon.dropdown { + flex: 0; + margin: 0; + } + + > div.info { + flex: 1; + padding-right: 0.5rem; + + > div.chain { + font-size: 0.9rem; + line-height: 1rem; + } + + > div.runtimeVersion { + font-size: 0.75rem; + line-height: 1rem; + } + } + } +`); diff --git a/pioneer/packages/apps/src/SideBar/Item.tsx b/pioneer/packages/apps/src/SideBar/Item.tsx new file mode 100644 index 0000000000..abcd801144 --- /dev/null +++ b/pioneer/packages/apps/src/SideBar/Item.tsx @@ -0,0 +1,135 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ApiProps } from '@polkadot/react-api/types'; +import { Route } from '@polkadot/apps-routing/types'; +import { AccountId } from '@polkadot/types/interfaces'; + +import React, { useEffect, useState } from 'react'; +import { NavLink } from 'react-router-dom'; +import { Badge, Icon, Menu, Tooltip } from '@polkadot/react-components'; +import { useAccounts, useApi, useCall } from '@polkadot/react-hooks'; + +import { findMissingApis } from '../endpoint'; + +const DUMMY_COUNTER = (): null => null; + +interface Props { + isCollapsed: boolean; + onClick: () => void; + route: Route; +} + +const disabledLog = new Map(); +const TOOLTIP_OFFSET = { right: -4 }; + +function logDisabled (route: string, message: string): void { + if (!disabledLog.get(route)) { + disabledLog.set(route, message); + + console.warn(`Disabling ${route}: ${message}`); + } +} + +function checkVisible (name: string, { api, isApiConnected, isApiReady }: ApiProps, hasAccounts: boolean, hasSudo: boolean, { isHidden, needsAccounts, needsApi, needsSudo }: Route['display']): boolean { + if (isHidden) { + return false; + } else if (needsAccounts && !hasAccounts) { + return false; + } else if (!needsApi) { + return true; + } else if (!isApiReady || !isApiConnected) { + return false; + } else if (needsSudo && !hasSudo) { + logDisabled(name, 'Sudo key not available'); + + return false; + } + + const notFound = findMissingApis(api, needsApi); + + if (notFound.length !== 0) { + logDisabled(name, `API not available: ${notFound.toString()}`); + } + + return notFound.length === 0; +} + +function Item ({ isCollapsed, onClick, route }: Props): React.ReactElement | null { + const { allAccounts, hasAccounts } = useAccounts(); + const apiProps = useApi(); + const sudoKey = useCall(apiProps.isApiReady && apiProps.api.query.sudo?.key, []); + const [hasSudo, setHasSudo] = useState(false); + const [isVisible, setIsVisible] = useState(false); + const count = (route.useCounter || DUMMY_COUNTER)(); + + useEffect((): void => { + setHasSudo(!!sudoKey && allAccounts.some((address): boolean => sudoKey.eq(address))); + }, [allAccounts, sudoKey]); + + useEffect((): void => { + const isVisible = checkVisible(route.name, apiProps, hasAccounts, hasSudo, route.display); + + route.isIgnored = !isVisible; + setIsVisible(isVisible); + }, [apiProps, hasAccounts, hasSudo, route]); + + if (!isVisible) { + return null; + } + + const { Modal, icon, name, text } = route; + + const body = ( + <> + + {text} + {!!count && ( + + )} + + + ); + + return ( + + {Modal + ? ( + + {body} + + ) + : ( + + {body} + + ) + } + + ); +} + +export default React.memo(Item); diff --git a/pioneer/packages/apps/src/SideBar/NodeInfo.tsx b/pioneer/packages/apps/src/SideBar/NodeInfo.tsx new file mode 100644 index 0000000000..787c3a2900 --- /dev/null +++ b/pioneer/packages/apps/src/SideBar/NodeInfo.tsx @@ -0,0 +1,52 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { BareProps as Props } from '@polkadot/react-components/types'; + +import React from 'react'; +import styled from 'styled-components'; +import { useApi } from '@polkadot/react-hooks'; +import { NodeName, NodeVersion } from '@polkadot/react-query'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const pkgJson = require('../../package.json') as { version: string }; + +const uiInfo = `apps v${pkgJson.version}`; + +function NodeInfo ({ className }: Props): React.ReactElement { + const { api, isApiReady } = useApi(); + + return ( +
+ {isApiReady && ( +
+   + +
+ )} +
{api.libraryInfo.replace('@polkadot/', '')}
+
{uiInfo}
+
+ ); +} + +export default React.memo(styled(NodeInfo)` + background: transparent; + color: white; + font-size: 0.75rem; + opacity: 0.5; + padding: 0 1.5rem 0 1.5rem; + text-align: right; + + > div { + margin-bottom: -0.125em; + > div { + display: inline-block; + } + + &.spacer { + margin-bottom: 0.5rem; + } + } +`); diff --git a/pioneer/packages/apps/src/SideBar/index.tsx b/pioneer/packages/apps/src/SideBar/index.tsx new file mode 100644 index 0000000000..5496e93255 --- /dev/null +++ b/pioneer/packages/apps/src/SideBar/index.tsx @@ -0,0 +1,259 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Routes } from '@polkadot/apps-routing/types'; + +import React, { useCallback, useMemo, useState } from 'react'; +import styled from 'styled-components'; +import createRoutes from '@polkadot/apps-routing'; +import { Button, ChainImg, Menu, media } from '@polkadot/react-components'; + +import { SIDEBAR_MENU_THRESHOLD } from '../constants'; +import NetworkModal from '../modals/Network'; +import { useTranslation } from '../translate'; +import ChainInfo from './ChainInfo'; +import Item from './Item'; +import NodeInfo from './NodeInfo'; + +interface Props { + className?: string; + collapse: () => void; + handleResize: () => void; + isCollapsed: boolean; + isMenuOpen: boolean; + toggleMenu: () => void; +} + +function SideBar ({ className = '', collapse, handleResize, isCollapsed, isMenuOpen, toggleMenu }: Props): React.ReactElement { + const { t } = useTranslation(); + const [modals, setModals] = useState>( + createRoutes(t).reduce((result: Record, route): Record => { + if (route && route.Modal) { + result[route.name] = false; + } + + return result; + }, { network: false }) + ); + + const routing = useMemo( + () => createRoutes(t), + [t] + ); + + const _toggleModal = useCallback( + (name: string): () => void => + (): void => setModals((modals: Record) => ({ + ...modals, + [name]: !modals[name] + })), + [] + ); + + return ( +
+ + {routing.map((route): React.ReactNode => ( + route?.Modal + ? route.Modal && modals[route.name] + ? ( + + ) + :
+ : null + ))} + {modals.network && ( + + )} +
+ +
+ + {routing.map((route, index): React.ReactNode => ( + route + ? ( + + ) + : ( +
+
+
+
+
+
+
+ ); +} + +const sideBorderWidth = '0.65rem'; + +export default React.memo(styled(SideBar)` + display: flex; + position: relative; + z-index: 300; + + &.collapsed { + width: 4.2rem; + } + + &.expanded { + width: 12rem; + } + + .apps--SideBar { + align-items: center; + background: #3f3f3f; + box-sizing: border-box; + display: flex; + flex-flow: column; + height: auto; + position: relative; + transition: left 0.3s linear; + width: 100%; + + .apps--SideBar-border { + border-top: ${sideBorderWidth} solid transparent; + position: absolute; + left: 0; + right: 0; + top: 0; + } + + .ui.vertical.menu { + display: flex; + height: 100vh; + margin: 0; + top: 0; + width: 100%; + position: sticky; + } + + .apps--SideBar-Scroll { + align-items: center; + display: flex; + flex-direction: column; + height: 100vh; + overflow-y: auto; + width: 100%; + scrollbar-width: none; + + &::-webkit-scrollbar { + display: none; + width: 0px; + } + } + + .apps--SideBar-Item { + align-self: flex-end; + flex-grow: 0; + padding: 0 !important; + position: relative; + width: inherit; + + .text { + padding-left: 0.5rem; + } + + .ui--Badge { + margin: 0; + position: absolute; + right: 0.5rem; + top: 0.55rem; + z-index: 1; + } + } + + .apps--SideBar-collapse { + background: #3f3f3f; + bottom: 0; + left: 0; + padding: 0.75rem 0 .75rem 0.65rem; + position: sticky; + right: 0; + text-align: left; + width: 100%; + } + + .apps--SideBar-toggle { + height: 100%; + position: absolute; + right: 0px; + top: 0px; + transition: all 0.2s; + width: 6px; + + &:hover { + background: rgba(255,255,255,0.15); + cursor: pointer; + } + } + } + + .toggleImg { + cursor: pointer; + height: 2.75rem; + left: 0.9rem; + opacity: 0; + position: absolute; + top: 0px; + transition: opacity 0.2s ease-in, top 0.2s ease-in; + width: 2.75rem; + + &.delayed { + transition-delay: 0.4s; + } + + &.open { + opacity: 1; + top: 0.9rem; + } + + ${media.DESKTOP` + opacity: 0 !important; + top: -2.9rem !important; + `} + } + + @media only screen and (max-width: ${SIDEBAR_MENU_THRESHOLD}px) { + .apps--SideBar-collapse, + .apps--Sidebar-toggle { + display: none; + } + } +`); diff --git a/pioneer/packages/apps/src/TopBar.tsx b/pioneer/packages/apps/src/TopBar.tsx new file mode 100644 index 0000000000..a6845160ce --- /dev/null +++ b/pioneer/packages/apps/src/TopBar.tsx @@ -0,0 +1,56 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// Unused atm, experiment as a replacement for NodeInfo on the SideBar + +import React from 'react'; +import styled from 'styled-components'; +import { BestNumber, Chain, NodeName, NodeVersion } from '@polkadot/react-query'; + +interface Props { + className?: string; +} + +function TopBar ({ className }: Props): React.ReactElement { + return ( +
+
+   + +
+
+   + +
+
+ ); +} + +export default React.memo(styled(TopBar)` + background: #f2f2f2; + font-size: 0.85rem; + line-height: 1rem; + overflow: hidden; + padding: 0.5rem 1rem; + position: fixed; + right: 0; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + top: 0; + + div { + display: inline-block; + vertical-align: middle; + } + + > div { + border-left: 1px solid #ccc; + padding: 0 0.5rem; + + &:first-child { + border-width: 0; + } + } +`); diff --git a/pioneer/packages/apps/src/WarmUp.tsx b/pioneer/packages/apps/src/WarmUp.tsx new file mode 100644 index 0000000000..478045592d --- /dev/null +++ b/pioneer/packages/apps/src/WarmUp.tsx @@ -0,0 +1,27 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import React, { useEffect, useState } from 'react'; +import { useApi, useCall } from '@polkadot/react-hooks'; + +function WarmUp (): React.ReactElement { + const { api, isApiReady } = useApi(); + const indexes = useCall(isApiReady && api.derive.accounts?.indexes, []); + const registrars = useCall(isApiReady && api.query.identity?.registrars, []); + const staking = null; // useCall(isApiReady && api.derive.staking?.overview, []); + const issuance = useCall(isApiReady && api.query.balances?.totalIssuance, []); + const historyDepth = useCall(api.query.staking?.historyDepth, []); + const [hasValues, setHasValues] = useState(false); + + useEffect((): void => { + setHasValues(!!historyDepth || !!indexes || !!issuance || !!registrars || !!staking); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ ); +} + +export default React.memo(WarmUp); diff --git a/pioneer/packages/apps/src/WindowDimensions.tsx b/pioneer/packages/apps/src/WindowDimensions.tsx new file mode 100644 index 0000000000..339a4f8aab --- /dev/null +++ b/pioneer/packages/apps/src/WindowDimensions.tsx @@ -0,0 +1,42 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// Adapted from https://hackernoon.com/simplifying-responsive-layouts-with-react-hooks-19db73893a7a + +import React, { useEffect, useState } from 'react'; + +interface Dimensions { + windowHeight: number; + windowWidth: number; +} + +interface Props { + children: React.ReactNode; +} + +const WindowDimensionsCtx = React.createContext({ windowHeight: window.innerHeight, windowWidth: window.innerWidth }); + +function WindowDimensionsProvider ({ children }: Props): React.ReactElement { + const [dimensions, setDimensions] = useState({ windowHeight: window.innerHeight, windowWidth: window.innerWidth }); + + useEffect(() => { + const handleResize = () => setDimensions({ windowHeight: window.innerHeight, windowWidth: window.innerWidth }); + + window.addEventListener('resize', handleResize); + + return (): void => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + return ( + + {children} + + ); +} + +export default React.memo(WindowDimensionsProvider); + +export { WindowDimensionsCtx }; diff --git a/pioneer/packages/apps/src/constants.ts b/pioneer/packages/apps/src/constants.ts new file mode 100644 index 0000000000..0a938803ed --- /dev/null +++ b/pioneer/packages/apps/src/constants.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ScreenSizes } from '@polkadot/react-components/constants'; + +export enum SideBarTransition { + COLLAPSED = 'COLLAPSED', + EXPANDED = 'EXPANDED', + EXPANDED_AND_MAXIMISED = 'EXPANDED_AND_MAXIMISED', + MINIMISED_AND_EXPANDED = 'MINIMISED_AND_EXPANDED' +} + +export const SIDEBAR_MENU_THRESHOLD = ScreenSizes.DESKTOP; +export const SIDEBAR_TRANSITION_DURATION = 300; diff --git a/pioneer/packages/apps/src/endpoint.ts b/pioneer/packages/apps/src/endpoint.ts new file mode 100644 index 0000000000..1bacd58763 --- /dev/null +++ b/pioneer/packages/apps/src/endpoint.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ApiPromise } from '@polkadot/api'; +import { isFunction } from '@polkadot/util'; + +function hasEndpoint (api: ApiPromise, endpoint: string): boolean { + const [area, section, method] = endpoint.split('.'); + + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + return isFunction((api as any)[area][section][method]); + } catch (error) { + return false; + } +} + +export function findMissingApis (api: ApiPromise, needsApi?: (string | string[])[]): (string | string[])[] { + if (!needsApi) { + return []; + } + + return needsApi.filter((endpoint: string | string[]): boolean => { + const hasApi = Array.isArray(endpoint) + ? endpoint.reduce((hasApi, endpoint) => hasApi || hasEndpoint(api, endpoint), false) + : hasEndpoint(api, endpoint); + + return !hasApi; + }); +} diff --git a/pioneer/packages/apps/src/index.tsx b/pioneer/packages/apps/src/index.tsx new file mode 100644 index 0000000000..df91ca7b15 --- /dev/null +++ b/pioneer/packages/apps/src/index.tsx @@ -0,0 +1,64 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// setup these right at front +import './initSettings'; +import 'semantic-ui-css/semantic.min.css'; +import '@polkadot/react-components/i18n'; + +import React, { Suspense } from 'react'; +import ReactDOM from 'react-dom'; +import { HashRouter } from 'react-router-dom'; +import store from 'store'; +import { ThemeProvider } from 'styled-components'; +import { Api } from '@polkadot/react-api'; +import Queue from '@polkadot/react-components/Status/Queue'; +import { BlockAuthors, Events } from '@polkadot/react-query'; +import settings from '@polkadot/ui-settings'; + +import Apps from './Apps'; +import WindowDimensions from './WindowDimensions'; + +/* Joystream-specific */ +import { MyMembershipProvider, MyAccountProvider } from '@polkadot/joy-utils/react/context'; + +const rootId = 'root'; +const rootElement = document.getElementById(rootId); +const theme = { theme: settings.uiTheme }; + +if (!rootElement) { + throw new Error(`Unable to find element with id '${rootId}'`); +} + +// cleanups for old/unused storage items +store.each((_, key): void => { + if (key.startsWith('hooks:sessionSlashes:')) { + store.remove(key); + } +}); + +ReactDOM.render( + + + + + + + + + + + + + + + + + + + + + , + rootElement +); diff --git a/pioneer/packages/apps/src/initSettings.ts b/pioneer/packages/apps/src/initSettings.ts new file mode 100644 index 0000000000..0c1cafb675 --- /dev/null +++ b/pioneer/packages/apps/src/initSettings.ts @@ -0,0 +1,54 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import queryString from 'query-string'; +import store from 'store'; +import { createEndpoints } from '@polkadot/apps-config/settings'; +import { extractIpfsDetails } from '@polkadot/react-hooks/useIpfs'; +import settings from '@polkadot/ui-settings'; + +function getApiUrl (): string { + // we split here so that both these forms are allowed + // - http://localhost:3000/?rpc=wss://substrate-rpc.parity.io/#/explorer + // - http://localhost:3000/#/explorer?rpc=wss://substrate-rpc.parity.io + const urlOptions = queryString.parse(location.href.split('?')[1]); + + // if specified, this takes priority + if (urlOptions.rpc) { + if (Array.isArray(urlOptions.rpc)) { + throw new Error('Invalid WS endpoint specified'); + } + + return urlOptions.rpc.split('#')[0]; // https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9944#/explorer; + } + + const endpoints = createEndpoints((): T => ('' as unknown as T)); + const { ipnsChain } = extractIpfsDetails(); + + // check against ipns domains (could be expanded to others) + if (ipnsChain) { + const option = endpoints.find(({ dnslink }) => dnslink === ipnsChain); + + if (option) { + return option.value as string; + } + } + + const stored = store.get('settings') as Record || {}; + const fallbackUrl = endpoints.find(({ value }) => !!value); + + // via settings, or the default chain + return [stored.apiUrl, process.env.WS_URL].includes(settings.apiUrl) + ? settings.apiUrl // keep as-is + : fallbackUrl + ? fallbackUrl.value as string // grab the fallback + : 'ws://127.0.0.1:9944'; // nothing found, go local +} + +const apiUrl = getApiUrl(); + +// set the default as retrieved here +settings.set({ apiUrl }); + +console.log('WS endpoint=', apiUrl); diff --git a/pioneer/packages/apps/src/modals/Network.tsx b/pioneer/packages/apps/src/modals/Network.tsx new file mode 100644 index 0000000000..7e1a2ed169 --- /dev/null +++ b/pioneer/packages/apps/src/modals/Network.tsx @@ -0,0 +1,35 @@ +// Copyright 2017-2020 @polkadot/app-accounts authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import React from 'react'; +import { Modal } from '@polkadot/react-components'; +import General from '@polkadot/app-settings/General'; + +import { useTranslation } from '../translate'; + +interface Props { + className?: string; + onClose: () => void; +} + +function NetworkModal ({ className = '', onClose }: Props): React.ReactElement { + const { t } = useTranslation(); + + return ( + ('Select Network')} + size='large' + > + + + + + ); +} + +export default React.memo(NetworkModal); diff --git a/pioneer/packages/apps/src/overlays/Base.tsx b/pioneer/packages/apps/src/overlays/Base.tsx new file mode 100644 index 0000000000..d1351b6131 --- /dev/null +++ b/pioneer/packages/apps/src/overlays/Base.tsx @@ -0,0 +1,95 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import React from 'react'; +import styled from 'styled-components'; +import { IconName } from '@fortawesome/fontawesome-svg-core'; +import { Icon } from '@polkadot/react-components'; +import { useToggle } from '@polkadot/react-hooks'; + +interface Props { + children: React.ReactNode; + className?: string; + icon: IconName; + type: 'error' | 'info'; +} + +function BaseOverlay ({ children, className = '', icon, type }: Props): React.ReactElement | null { + const [isHidden, toggleHidden] = useToggle(); + + if (isHidden) { + return null; + } + + return ( +
+
+ +
+ {children} +
+ +
+
+ ); +} + +export default React.memo(styled(BaseOverlay)` + border-bottom: 1px solid transparent; + left: 0; + line-height: 1.5em; + padding: 0 2rem; + position: fixed; + right: 0; + top: 0; + z-index: 500; + + &.isError { + background: #ffe6e6; + border-color: #c00; + color: #4d0000; + } + + &.isInfo { + background: #fff6cb; + border-color: #e7c000; + color: #6b5900; + } + + .content { + display: flex; + margin: 0 auto; + max-width: 50rem; + padding: 1em 2rem; + position: relative; + + .contentIcon { + flex: 0; + } + + .contentItem { + flex: 1; + padding: 0 1rem; + + > div+div { + margin-top: 0.5rem; + } + } + } + + .closeIcon { + cursor: pointer; + position: absolute; + right: 0.75em; + top: 0.75em; + } +`); diff --git a/pioneer/packages/apps/src/overlays/Connecting.tsx b/pioneer/packages/apps/src/overlays/Connecting.tsx new file mode 100644 index 0000000000..3bcd1a1b81 --- /dev/null +++ b/pioneer/packages/apps/src/overlays/Connecting.tsx @@ -0,0 +1,55 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import React from 'react'; +import { useApi } from '@polkadot/react-hooks'; +import settings from '@polkadot/ui-settings'; + +import { useTranslation } from '../translate'; +import BaseOverlay from './Base'; + +const wsUrl = settings.apiUrl; +const isWs = typeof wsUrl === 'string' && wsUrl.startsWith('ws://'); +const isWsLocal = typeof wsUrl === 'string' && wsUrl.includes('127.0.0.1'); +const isHttps = window.location.protocol.startsWith('https:'); + +interface Props { + className?: string; +} + +function Connecting ({ className }: Props): React.ReactElement | null { + const { t } = useTranslation(); + const { isApiConnected, isWaitingInjected } = useApi(); + + if (isWaitingInjected) { + return ( + +
{t('Waiting for authorization from the extension. Please open the installed extension and approve or reject access.')}
+
+ ); + } else if (!isApiConnected) { + return ( + +
{t('You are not connected to a node. Ensure that your node is running and that the Websocket endpoint is reachable.')}
+ { + isWs && !isWsLocal && isHttps + ?
{t('You are connecting from a secure location to an insecure WebSocket ({{wsUrl}}). Due to browser mixed-content security policies this connection type is not allowed. Change the RPC service to a secure \'wss\' endpoint.', { replace: { wsUrl } })}
+ : undefined + } +
+ ); + } + + return null; +} + +export default React.memo(Connecting); diff --git a/pioneer/packages/apps/src/translate.ts b/pioneer/packages/apps/src/translate.ts new file mode 100644 index 0000000000..c016d62e8b --- /dev/null +++ b/pioneer/packages/apps/src/translate.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { useTranslation as useTranslationBase, UseTranslationResponse } from 'react-i18next'; + +export function useTranslation (): UseTranslationResponse { + return useTranslationBase(['apps', 'apps-routing']); +} diff --git a/pioneer/packages/apps/src/wp-gif.d.ts b/pioneer/packages/apps/src/wp-gif.d.ts new file mode 100644 index 0000000000..c036fb8490 --- /dev/null +++ b/pioneer/packages/apps/src/wp-gif.d.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +declare module '*.gif' { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const content: any; + export default content; +} diff --git a/pioneer/packages/apps/src/wp-md.d.ts b/pioneer/packages/apps/src/wp-md.d.ts new file mode 100644 index 0000000000..c0c194f852 --- /dev/null +++ b/pioneer/packages/apps/src/wp-md.d.ts @@ -0,0 +1,8 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +declare module '*.md' { + const content: any; + export default content; +} diff --git a/pioneer/packages/apps/src/wp-png.d.ts b/pioneer/packages/apps/src/wp-png.d.ts new file mode 100644 index 0000000000..4d9bc55bc3 --- /dev/null +++ b/pioneer/packages/apps/src/wp-png.d.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +declare module '*.png' { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const content: any; + export default content; +} diff --git a/pioneer/packages/apps/src/wp-svg.d.ts b/pioneer/packages/apps/src/wp-svg.d.ts new file mode 100644 index 0000000000..9eb2438d3e --- /dev/null +++ b/pioneer/packages/apps/src/wp-svg.d.ts @@ -0,0 +1,8 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +declare module '*.svg' { + const content: any; + export default content; +} diff --git a/pioneer/packages/apps/webpack.base.config.js b/pioneer/packages/apps/webpack.base.config.js new file mode 100644 index 0000000000..c0f79a4010 --- /dev/null +++ b/pioneer/packages/apps/webpack.base.config.js @@ -0,0 +1,193 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/* eslint-disable camelcase */ + +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const { WebpackPluginServe } = require('webpack-plugin-serve'); +const findPackages = require('../../scripts/findPackages'); + +function mapChunks (name, regs, inc) { + return regs.reduce((result, test, index) => ({ + ...result, + [`${name}${index}`]: { + chunks: 'initial', + enforce: true, + name: `${name}.${`0${index + (inc || 0)}`.slice(-2)}`, + test + } + }), {}); +} + +function createWebpack (ENV, context) { + const pkgJson = require(path.join(context, 'package.json')); + const isProd = ENV === 'production'; + const hasPublic = fs.existsSync(path.join(context, 'public')); + const plugins = hasPublic + ? [new CopyWebpackPlugin({ patterns: [{ from: 'public' }] })] + : []; + + !isProd && plugins.push( + new WebpackPluginServe({ + hmr: false, // switch off, Chrome WASM memory leak + liveReload: false, // explict off, overrides hmr + port: 3000, + progress: false, // since we have hmr off, disable + static: path.join(process.cwd(), '/build') + }) + ); + + const alias = findPackages().reduce((alias, { dir, name }) => { + alias[name] = path.resolve(context, `../${dir}/src`); + + return alias; + }, {}); + + return { + context, + entry: ['@babel/polyfill', './src/index.tsx'], + mode: ENV, + module: { + rules: [ + { + exclude: /(node_modules)/, + test: /\.css$/, + use: [ + isProd + ? MiniCssExtractPlugin.loader + : require.resolve('style-loader'), + { + loader: require.resolve('css-loader'), + options: { + importLoaders: 1 + } + } + ] + }, + { + include: /node_modules/, + test: /\.css$/, + use: [ + isProd + ? MiniCssExtractPlugin.loader + : require.resolve('style-loader'), + require.resolve('css-loader') + ] + }, + { + exclude: /(node_modules)/, + test: /\.(js|ts|tsx)$/, + use: [ + require.resolve('thread-loader'), + { + loader: require.resolve('babel-loader'), + options: require('@polkadot/dev/config/babel') + } + ] + }, + { + test: /\.md$/, + use: [ + require.resolve('html-loader'), + require.resolve('markdown-loader') + ] + }, + { + // Original config had "exclude: [/semantic-ui-css/]" + test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], + use: [ + { + loader: require.resolve('url-loader'), + options: { + esModule: false, + limit: 10000, + name: 'static/[name].[hash:8].[ext]' + } + } + ] + }, + { + // Original config had "exclude: [/semantic-ui-css/]", because Semantic UI Icons + // are not used in polkadot-js/apps repository, but they are used in ours + test: [/\.eot$/, /\.ttf$/, /\.svg$/, /\.woff$/, /\.woff2$/], + use: [ + { + loader: require.resolve('file-loader'), + options: { + esModule: false, + name: 'static/[name].[hash:8].[ext]' + } + } + ] + } + ] + }, + node: { + child_process: 'empty', + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty' + }, + optimization: { + runtimeChunk: 'single', + splitChunks: { + cacheGroups: { + ...mapChunks('polkadot', [ + /* 00 */ /node_modules\/@polkadot\/(wasm)/, + /* 01 */ /node_modules\/(@polkadot\/(api|metadata|rpc|types))/, + /* 02 */ /node_modules\/(@polkadot\/(extension|keyring|react|ui|util|vanitygen)|@acala-network|@edgeware|@ledgerhq|@open-web3|@zondax|edgeware)/ + ]), + ...mapChunks('react', [ + /* 00 */ /node_modules\/(@fortawesome)/, + /* 01 */ /node_modules\/(@emotion|@semantic-ui-react|@stardust|classnames|chart\.js|codeflask|copy-to-clipboard|file-selector|file-saver|hoist-non-react|i18next|jdenticon|keyboard-key|mini-create-react|popper\.js|prop-types|qrcode-generator|react|remark-parse|semantic-ui|styled-components)/ + ]), + ...mapChunks('other', [ + /* 00 */ /node_modules\/(@babel|ansi-styles|asn1|browserify|buffer|history|html-parse|inherit|lodash|memoizee|object|path-|parse-asn1|pbkdf2|process|public-encrypt|query-string|readable-stream|regenerator-runtime|repeat|rtcpeerconnection-shim|safe-buffer|stream-browserify|store|tslib|unified|unist-util|util|vfile|vm-browserify|webrtc-adapter|whatwg-fetch)/, + /* 01 */ /node_modules\/(attr|brorand|camelcase|core|chalk|color|create|cuint|decode-uri|deep-equal|define-properties|detect-browser|es|event|evp|ext|function-bind|has-symbols|ieee754|ip|is|lru|markdown|minimalistic-|moment|next-tick|node-libs-browser|random|regexp|resolve|rxjs|scheduler|sdp|setimmediate|timers-browserify|trough)/, + /* 03 */ /node_modules\/(base-x|base64-js|blakejs|bip|bn\.js|cipher-base|crypto|des\.js|diffie-hellman|elliptic|hash|hmac|js-sha3|md5|miller-rabin|ripemd160|secp256k1|sha\.js|xxhashjs)/ + ]) + } + } + }, + output: { + chunkFilename: '[name].[chunkhash:8].js', + filename: '[name].[hash:8].js', + globalObject: '(typeof self !== \'undefined\' ? self : this)', + path: path.join(context, 'build'), + publicPath: '' + }, + performance: { + hints: false + }, + plugins: plugins.concat([ + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(ENV), + VERSION: JSON.stringify(pkgJson.version), + WS_URL: JSON.stringify(process.env.WS_URL) + } + }), + new webpack.optimize.SplitChunksPlugin(), + new MiniCssExtractPlugin({ + filename: '[name].[contenthash:8].css' + }) + ]).filter((plugin) => plugin), + resolve: { + alias, + extensions: ['.js', '.jsx', '.ts', '.tsx'] + }, + watch: !isProd, + watchOptions: { + ignored: ['.yarn', /build/, /node_modules/] + } + }; +} + +module.exports = createWebpack; diff --git a/pioneer/packages/apps/webpack.config.js b/pioneer/packages/apps/webpack.config.js new file mode 100644 index 0000000000..041f8f8356 --- /dev/null +++ b/pioneer/packages/apps/webpack.config.js @@ -0,0 +1,30 @@ +// Copyright 2017-2020 @polkadot/apps authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/* eslint-disable camelcase */ + +const fs = require('fs'); +const path = require('path'); +const merge = require('webpack-merge'); +const baseConfig = require('./webpack.base.config'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + +const ENV = process.env.NODE_ENV || 'development'; +const context = __dirname; +const hasPublic = fs.existsSync(path.join(context, 'public')); + +module.exports = merge( + baseConfig(ENV, context), + { + devtool: process.env.BUILD_ANALYZE ? 'source-map' : false, + plugins: [ + new HtmlWebpackPlugin({ + IS_PROD: ENV === 'production', + PAGE_TITLE: 'Joystream Network Portal', + inject: true, + template: path.join(context, `${hasPublic ? 'public/' : ''}index.html`) + }) + ] + } +); diff --git a/pioneer/packages/joy-election/.skip-build b/pioneer/packages/joy-election/.skip-build new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/joy-election/README.md b/pioneer/packages/joy-election/README.md new file mode 100644 index 0000000000..10aedc6a3e --- /dev/null +++ b/pioneer/packages/joy-election/README.md @@ -0,0 +1 @@ +# Election module for Joystream node diff --git a/pioneer/packages/joy-election/package.json b/pioneer/packages/joy-election/package.json new file mode 100644 index 0000000000..2bee08d3ff --- /dev/null +++ b/pioneer/packages/joy-election/package.json @@ -0,0 +1,15 @@ +{ + "name": "@polkadot/joy-election", + "version": "0.1.1", + "description": "Election module for Joystream node", + "main": "index.js", + "scripts": {}, + "author": "Joystream contributors", + "maintainers": [], + "dependencies": { + "@babel/runtime": "^7.7.1", + "@polkadot/react-components": "0.37.0-beta.63", + "@polkadot/react-query": "0.37.0-beta.63", + "@polkadot/joy-utils": "^0.1.1" + } +} diff --git a/pioneer/packages/joy-election/src/Applicant.tsx b/pioneer/packages/joy-election/src/Applicant.tsx new file mode 100644 index 0000000000..b48c3291f3 --- /dev/null +++ b/pioneer/packages/joy-election/src/Applicant.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Table } from 'semantic-ui-react'; + +import { I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls } from '@polkadot/react-api/with'; +import { AccountId } from '@polkadot/types/interfaces'; +import { formatBalance } from '@polkadot/util'; +import CandidatePreview from './CandidatePreview'; + +import translate from './translate'; +import { calcTotalStake } from '@polkadot/joy-utils/index'; +import { ElectionStake } from '@joystream/types/council'; + +type Props = ApiProps & I18nProps & { + index: number; + accountId: AccountId; + stake?: ElectionStake; +}; + +class Applicant extends React.PureComponent { + render () { + const { index, accountId, stake } = this.props; + const voteUrl = `/council/votes?applicantId=${accountId.toString()}`; + + return ( + + {index + 1} + + + + + {formatBalance(calcTotalStake(stake))} + + + Vote + + + ); + } +} + +// inject the actual API calls automatically into props +export default translate( + withCalls( + ['query.councilElection.applicantStakes', + { paramName: 'accountId', propName: 'stake' }] + )(Applicant) +); diff --git a/pioneer/packages/joy-election/src/Applicants.tsx b/pioneer/packages/joy-election/src/Applicants.tsx new file mode 100644 index 0000000000..e84c85f3d5 --- /dev/null +++ b/pioneer/packages/joy-election/src/Applicants.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { Table } from 'semantic-ui-react'; +import BN from 'bn.js'; + +import { I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls } from '@polkadot/react-api/with'; +import { AccountId } from '@polkadot/types/interfaces'; +import { formatNumber } from '@polkadot/util'; + +import translate from './translate'; +import Applicant from './Applicant'; +import ApplyForm from './ApplyForm'; +import Section from '@polkadot/joy-utils/Section'; +import { queryToProp } from '@polkadot/joy-utils/index'; +import { withMyAccount, MyAccountProps } from '@polkadot/joy-utils/MyAccount'; + +type Props = ApiProps & I18nProps & MyAccountProps & { + candidacyLimit?: BN; + applicants?: Array; +}; + +class Applicants extends React.PureComponent { + private renderTable = (applicants: Array) => ( + + + + # + Applicant + Total stake + Actions + + + {applicants.map((accountId, index) => ( + + ))} +
+ ) + + render () { + const { myAddress, applicants = [], candidacyLimit = new BN(0) } = this.props; + const title = Applicants {applicants.length}/{formatNumber(candidacyLimit)}; + + return <> +
+ +
+
+ {!applicants.length + ? No applicants yet + : this.renderTable(applicants) + } +
+ ; + } +} + +// inject the actual API calls automatically into props +export default translate( + withCalls( + queryToProp('query.councilElection.candidacyLimit'), + queryToProp('query.councilElection.applicants') + )(withMyAccount(Applicants)) +); diff --git a/pioneer/packages/joy-election/src/ApplyForm.tsx b/pioneer/packages/joy-election/src/ApplyForm.tsx new file mode 100644 index 0000000000..f058fc992c --- /dev/null +++ b/pioneer/packages/joy-election/src/ApplyForm.tsx @@ -0,0 +1,95 @@ +import BN from 'bn.js'; +import React from 'react'; + +import { I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls, withMulti } from '@polkadot/react-api/with'; +import { Labelled } from '@polkadot/react-components/index'; +import { Balance } from '@polkadot/types/interfaces'; + +import translate from './translate'; +import TxButton from '@polkadot/joy-utils/TxButton'; +import InputStake from '@polkadot/joy-utils/InputStake'; +import { ElectionStake } from '@joystream/types/council'; +import { calcTotalStake, ZERO } from '@polkadot/joy-utils/index'; +import { MyAddressProps, withOnlyMembers } from '@polkadot/joy-utils/MyAccount'; + +type Props = ApiProps & I18nProps & MyAddressProps & { + minStake?: Balance; + alreadyStaked?: ElectionStake; + myBalance?: Balance; +}; + +type State = { + stake: BN; + isStakeValid: boolean; +}; + +const DEFAULT_STATE: State = { + stake: ZERO, + isStakeValid: false +}; + +class ApplyForm extends React.PureComponent { + state = DEFAULT_STATE; + + render () { + const { stake, isStakeValid } = this.state; + const hasAlreadyStakedEnough = this.alreadyStaked().gte(this.minStake()); + const minStake = hasAlreadyStakedEnough ? ZERO : this.minStake(); + const buttonLabel = hasAlreadyStakedEnough + ? 'Add to my stake' + : 'Apply to council'; + + return ( +
+ + + + +
+ ); + } + + private alreadyStaked = (): BN => { + return calcTotalStake(this.props.alreadyStaked); + } + + private minStake = (): BN => { + return this.props.minStake || new BN(1); + } + + private onChangeStake = (stake?: BN): void => { + stake = stake || ZERO; + const { myBalance = ZERO } = this.props; + const isStakeLteBalance = stake.lte(myBalance); + const isStakeGteMinStake = stake.add(this.alreadyStaked()).gte(this.minStake()); + const isStakeValid = !stake.isZero() && isStakeGteMinStake && isStakeLteBalance; + this.setState({ stake, isStakeValid }); + } +} + +// inject the actual API calls automatically into props +export default withMulti( + ApplyForm, + translate, + withOnlyMembers, + withCalls( + ['query.councilElection.minCouncilStake', + { propName: 'minStake' }], + ['query.councilElection.applicantStakes', + { paramName: 'myAddress', propName: 'alreadyStaked' }], + ['query.balances.freeBalance', + { paramName: 'myAddress', propName: 'myBalance' }] + ) +); diff --git a/pioneer/packages/joy-election/src/CandidatePreview.tsx b/pioneer/packages/joy-election/src/CandidatePreview.tsx new file mode 100644 index 0000000000..9e6571efb6 --- /dev/null +++ b/pioneer/packages/joy-election/src/CandidatePreview.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import AddressMini from '@polkadot/react-components/AddressMiniJoy'; +import MemberByAccount from '@polkadot/joy-utils/MemberByAccountPreview'; +import { AccountId } from '@polkadot/types/interfaces'; + +import styled from 'styled-components'; + +const StyledCouncilCandidate = styled.div` + display: flex; + flex-wrap: wrap; + padding: 1rem; +`; + +const CandidateMembership = styled.div``; + +const CandidateAddress = styled.div` + margin-left: auto; +`; + +type CouncilCandidateProps = { + accountId: AccountId | string; +}; + +const CouncilCandidate: React.FunctionComponent = ({ accountId }) => ( + + + + + + + + +); + +export default CouncilCandidate; diff --git a/pioneer/packages/joy-election/src/Council.tsx b/pioneer/packages/joy-election/src/Council.tsx new file mode 100644 index 0000000000..b81c144883 --- /dev/null +++ b/pioneer/packages/joy-election/src/Council.tsx @@ -0,0 +1,68 @@ +import React from 'react'; + +import { ApiProps } from '@polkadot/react-api/types'; +import { I18nProps } from '@polkadot/react-components/types'; +import { withCalls } from '@polkadot/react-api/with'; +import { Table } from 'semantic-ui-react'; +import { formatBalance } from '@polkadot/util'; +import CouncilCandidate from './CandidatePreview'; + +import { calcBackersStake } from '@polkadot/joy-utils/index'; +import { Seat } from '@joystream/types/council'; +import translate from './translate'; +import Section from '@polkadot/joy-utils/Section'; + +type Props = ApiProps & +I18nProps & { + council?: Seat[]; +}; + +type State = {}; + +class Council extends React.PureComponent { + state: State = {}; + + private renderTable (council: Seat[]) { + return ( + + + + # + Council member + Own stake + {'Backers\' stake'} + Backers count + + + + {council.map((seat, index) => ( + + {index + 1} + + + + {formatBalance(seat.stake)} + {formatBalance(calcBackersStake(seat.backers))} + {seat.backers.length} + + ))} + +
+ ); + } + + render () { + const { council = [] } = this.props; + // console.log({ council }); + return ( +
+ {!council.length ? Council is not elected yet : this.renderTable(council)} +
+ ); + } +} + +// inject the actual API calls automatically into props +export default translate( + withCalls(['query.council.activeCouncil', { propName: 'council' }])(Council) +); diff --git a/pioneer/packages/joy-election/src/Dashboard.tsx b/pioneer/packages/joy-election/src/Dashboard.tsx new file mode 100644 index 0000000000..c7f64ef811 --- /dev/null +++ b/pioneer/packages/joy-election/src/Dashboard.tsx @@ -0,0 +1,165 @@ +import BN from 'bn.js'; +import React from 'react'; + +import { ApiProps } from '@polkadot/react-api/types'; +import { I18nProps } from '@polkadot/react-components/types'; +import { withCalls } from '@polkadot/react-api/with'; +import { Option } from '@polkadot/types'; +import { BlockNumber, Balance } from '@polkadot/types/interfaces'; +import { Bubble } from '@polkadot/react-components/index'; +import { formatNumber, formatBalance } from '@polkadot/util'; + +import Section from '@polkadot/joy-utils/Section'; +import { queryToProp } from '@polkadot/joy-utils/index'; +import { ElectionStage, Seat } from '@joystream/types/council'; +import translate from './translate'; + +type Props = ApiProps & I18nProps & { + bestNumber?: BN; + + activeCouncil?: Seat[]; + termEndsAt?: BlockNumber; + + autoStart?: boolean; + newTermDuration?: BN; + candidacyLimit?: BN; + councilSize?: BN; + minCouncilStake?: Balance; + minVotingStake?: Balance; + announcingPeriod?: BlockNumber; + votingPeriod?: BlockNumber; + revealingPeriod?: BlockNumber; + + round?: BN; + stage?: Option; +}; + +type State = {}; + +class Dashboard extends React.PureComponent { + state: State = {}; + + renderCouncil () { + const p = this.props; + const { activeCouncil = [] } = p; + const title = `Council ${activeCouncil.length > 0 ? '' : '(not elected)'}`; + + return
+ + {activeCouncil.length} + + + {formatNumber(p.termEndsAt)} + +
; + } + + renderElection () { + const { bestNumber, round, stage } = this.props; + + let stageName: string | undefined; + let stageEndsAt: BlockNumber | undefined; + if (stage && stage.isSome) { + const stageValue = stage.value as ElectionStage; + stageEndsAt = stageValue.value as BlockNumber; // contained value + stageName = stageValue.type; // name of Enum variant + } + + let leftBlocks: BN | undefined; + if (stageEndsAt && bestNumber) { + leftBlocks = stageEndsAt.sub(bestNumber); + } + + const isRunning: boolean = stage !== undefined && stage.isSome; + const stateClass = `JoyElection--${isRunning ? '' : 'Not'}Running`; + const stateText = `is ${isRunning ? '' : 'not'} running`; + const title = <>Election ({stateText}); + + return
+ + {formatNumber(round)} + + {isRunning && <> + + {stageName} + + + {formatNumber(leftBlocks)} + + + {formatNumber(stageEndsAt)} + + } +
; + } + + renderConfig () { + const p = this.props; + const isAutoStart = (p.autoStart || false).valueOf(); + + return
+ + {isAutoStart ? 'Yes' : 'No'} + + + {formatNumber(p.newTermDuration)} + + + {formatNumber(p.candidacyLimit)} + + + {formatNumber(p.councilSize)} + + + {formatBalance(p.minCouncilStake)} + + + {formatBalance(p.minVotingStake)} + + + {formatNumber(p.announcingPeriod)} blocks + + + {formatNumber(p.votingPeriod)} blocks + + + {formatNumber(p.revealingPeriod)} blocks + +
; + } + + render () { + return ( +
+ {this.renderCouncil()} + {this.renderElection()} + {this.renderConfig()} +
+ ); + } +} + +// inject the actual API calls automatically into props +export default translate( + withCalls( + queryToProp('derive.chain.bestNumber'), + + queryToProp('query.council.activeCouncil'), + queryToProp('query.council.termEndsAt'), + + queryToProp('query.councilElection.autoStart'), + queryToProp('query.councilElection.newTermDuration'), + queryToProp('query.councilElection.candidacyLimit'), + queryToProp('query.councilElection.councilSize'), + + queryToProp('query.councilElection.minCouncilStake'), + queryToProp('query.councilElection.minVotingStake'), + + queryToProp('query.councilElection.announcingPeriod'), + queryToProp('query.councilElection.votingPeriod'), + queryToProp('query.councilElection.revealingPeriod'), + + queryToProp('query.councilElection.stage'), + queryToProp('query.councilElection.round') + )(Dashboard) +); diff --git a/pioneer/packages/joy-election/src/Reveals.tsx b/pioneer/packages/joy-election/src/Reveals.tsx new file mode 100644 index 0000000000..6a20dad6f9 --- /dev/null +++ b/pioneer/packages/joy-election/src/Reveals.tsx @@ -0,0 +1,117 @@ +import React from 'react'; + +import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls, withMulti } from '@polkadot/react-api/with'; +import { AccountId } from '@polkadot/types/interfaces'; +import { Input, Labelled, InputAddress } from '@polkadot/react-components/index'; + +import translate from './translate'; +import { nonEmptyStr, queryToProp, getUrlParam } from '@polkadot/joy-utils/index'; +import { accountIdsToOptions, hashVote } from './utils'; +import TxButton from '@polkadot/joy-utils/TxButton'; +import { findVoteByHash } from './myVotesStore'; +import { withOnlyMembers } from '@polkadot/joy-utils/MyAccount'; + +// AppsProps is needed to get a location from the route. +type Props = AppProps & ApiProps & I18nProps & { + applicantId?: string | null; + applicants?: AccountId[]; + location: any; +}; + +type State = { + applicantId?: string | null; + salt?: string; + hashedVote?: string | null; +}; + +class RevealVoteForm extends React.PureComponent { + constructor (props: Props) { + super(props); + let { applicantId, location } = this.props; + applicantId = applicantId || getUrlParam(location, 'applicantId'); + const hashedVote = getUrlParam(location, 'hashedVote'); + + this.state = { + applicantId, + salt: '', // TODO show salts from local storage in a dropdown + hashedVote + }; + } + + render () { + let { applicantId, salt, hashedVote } = this.state; + const applicantOpts = accountIdsToOptions(this.props.applicants || []); + + const myVote = hashedVote ? findVoteByHash(hashedVote) : undefined; + if (myVote) { + // Try to substitue applicantId and salt from local sotre: + if (!applicantId) applicantId = myVote.applicantId; + if (!salt) salt = myVote.salt; + } + + const hasHash = nonEmptyStr(hashedVote); + const isVoteRevealed = hasHash && hashedVote === hashVote(applicantId, salt); + + return ( +
+
+ +
+ {hasHash &&
+ +
} + {hasHash &&
+ +
} + + + +
+ ); + } + + private onChangeApplicant = (applicantId: string | null) => { + this.setState({ applicantId }); + } + + private onChangeSalt = (salt?: string) => { + this.setState({ salt }); + } + + private onChangeHash = (hashedVote?: string) => { + this.setState({ hashedVote }); + } +} + +export default withMulti( + RevealVoteForm, + translate, + withOnlyMembers, + withCalls( + queryToProp('query.councilElection.applicants') + ) +); diff --git a/pioneer/packages/joy-election/src/SealedVote.tsx b/pioneer/packages/joy-election/src/SealedVote.tsx new file mode 100644 index 0000000000..54650328c1 --- /dev/null +++ b/pioneer/packages/joy-election/src/SealedVote.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Table } from 'semantic-ui-react'; + +import { I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls } from '@polkadot/react-api/with'; +import { Hash } from '@polkadot/types/interfaces'; +import { formatBalance } from '@polkadot/util'; + +import translate from './translate'; +import { calcTotalStake } from '@polkadot/joy-utils/index'; +import { SealedVote } from '@joystream/types/council'; +import AddressMini from '@polkadot/react-components/AddressMiniJoy'; +import CandidatePreview from './CandidatePreview'; +import { findVoteByHash } from './myVotesStore'; + +type Props = ApiProps & I18nProps & { + hash: Hash; + sealedVote?: SealedVote; +}; + +class Comp extends React.PureComponent { + renderCandidateOrAction () { + const { hash, sealedVote } = this.props; + if (!sealedVote) { + return Unknown hashed vote: {hash.toHex()}; + } + + if (sealedVote.vote.isSome) { + const candidateId = sealedVote.vote.unwrap(); + return ; + } else { + const revealUrl = `/council/reveals?hashedVote=${hash.toHex()}`; + return Reveal this vote; + } + } + + render () { + const { hash, sealedVote } = this.props; + const myVote = findVoteByHash(hash.toHex()); + + return !sealedVote ? null : ( + + + + Hash + {hash.toHex()} + + {myVote && + Salt + {myVote.salt} + } + + Stake + {formatBalance(calcTotalStake(sealedVote.stake))} + + {myVote && + Voted on + {new Date(myVote.votedOnTime).toLocaleString()} + } + + Voter + + + + Candidate + {this.renderCandidateOrAction()} + + +
+ ); + } +} + +// inject the actual API calls automatically into props +export default translate( + withCalls( + ['query.councilElection.votes', + { paramName: 'hash', propName: 'sealedVote' }] + )(Comp) +); diff --git a/pioneer/packages/joy-election/src/SealedVotes.tsx b/pioneer/packages/joy-election/src/SealedVotes.tsx new file mode 100644 index 0000000000..1f03b66ac0 --- /dev/null +++ b/pioneer/packages/joy-election/src/SealedVotes.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Button } from 'semantic-ui-react'; + +import { I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls } from '@polkadot/react-api/with'; +import { Hash } from '@polkadot/types/interfaces'; + +import translate from './translate'; +import SealedVote from './SealedVote'; +import { queryToProp } from '@polkadot/joy-utils/index'; +import { MyAddressProps } from '@polkadot/joy-utils/MyAccount'; +import { SavedVote } from './myVotesStore'; +import Section from '@polkadot/joy-utils/Section'; + +type Props = ApiProps & I18nProps & MyAddressProps & { + myVotes?: SavedVote[]; + commitments?: Hash[]; +}; + +class Comp extends React.PureComponent { + private filterVotes = (myVotesOnly: boolean): Hash[] => { + const { myVotes = [], commitments = [] } = this.props; + const isMyVote = (hash: string): boolean => { + return myVotes.find(x => x.hash === hash) !== undefined; + }; + return commitments.filter(x => myVotesOnly === isMyVote(x.toHex())); + } + + private renderVotes = (votes: Hash[]) => { + return votes.map((hash, index) => + + ); + } + + render () { + const myVotes = this.filterVotes(true); + const otherVotes = this.filterVotes(false); + + return <> +
{ + !myVotes.length + ? No votes by the current account found on the current browser. + : this.renderVotes(myVotes) + }
+
+ + { + !otherVotes.length + ? No votes submitted by other accounts yet. + : this.renderVotes(otherVotes) + } +
+ ; + } +} + +// inject the actual API calls automatically into props +export default translate( + withCalls( + queryToProp('query.councilElection.commitments') + )(Comp) +); diff --git a/pioneer/packages/joy-election/src/VoteForm.tsx b/pioneer/packages/joy-election/src/VoteForm.tsx new file mode 100644 index 0000000000..63fbb78b17 --- /dev/null +++ b/pioneer/packages/joy-election/src/VoteForm.tsx @@ -0,0 +1,227 @@ +import BN from 'bn.js'; +import uuid from 'uuid/v4'; + +import React from 'react'; +import { Message, Table } from 'semantic-ui-react'; + +import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls, withMulti } from '@polkadot/react-api/with'; +import { AccountId, Balance } from '@polkadot/types/interfaces'; +import { Button, Input, Labelled } from '@polkadot/react-components/index'; +import { SubmittableResult } from '@polkadot/api'; +import { formatBalance } from '@polkadot/util'; + +import translate from './translate'; +import { hashVote } from './utils'; +import { queryToProp, ZERO, getUrlParam, nonEmptyStr } from '@polkadot/joy-utils/index'; +import TxButton from '@polkadot/joy-utils/TxButton'; +import InputStake from '@polkadot/joy-utils/InputStake'; +import CandidatePreview from './CandidatePreview'; +import { MyAccountProps, withOnlyMembers } from '@polkadot/joy-utils/MyAccount'; +import MembersDropdown from '@polkadot/joy-utils/MembersDropdown'; +import { saveVote, NewVote } from './myVotesStore'; +import { TxFailedCallback } from '@polkadot/react-components/Status/types'; + +// TODO use a crypto-prooven generator instead of UUID 4. +function randomSalt () { + return uuid().replace(/-/g, ''); +} + +// AppsProps is needed to get a location from the route. +type Props = AppProps & ApiProps & I18nProps & MyAccountProps & { + applicantId?: string | null; + minVotingStake?: Balance; + applicants?: AccountId[]; + location?: any; +}; + +type State = { + applicantId?: string | null; + stake?: BN; + salt?: string; + isStakeValid?: boolean; + isFormSubmitted: boolean; +}; + +class Component extends React.PureComponent { + constructor (props: Props) { + super(props); + + let { applicantId, location } = this.props; + applicantId = applicantId || getUrlParam(location, 'applicantId'); + + this.state = { + applicantId, + stake: ZERO, + salt: randomSalt(), + isFormSubmitted: false + }; + } + + render () { + const { myAddress } = this.props; + const { applicantId, stake, salt, isStakeValid, isFormSubmitted } = this.state; + const isFormValid = nonEmptyStr(applicantId) && isStakeValid; + const hashedVote = hashVote(applicantId, salt); + + const buildNewVote = (): Partial => ({ + voterId: myAddress, + applicantId: applicantId || undefined, + stake: (stake || ZERO).toString(), + salt: salt, + hash: hashedVote || undefined + }); + + return ( + <>{isFormSubmitted + + // Summary of submitted vote: + ?
+ + Your vote has been sent + + + + + Applicant + + { applicantId && } + + + + Stake + {formatBalance(stake)} + + + Salt + {salt} + + + Hashed vote + {hashedVote} + + +
+ +
+ + // New vote form: + :
+
+ this.onChangeApplicant(data.value as string) } + accounts={this.props.applicants || []} + value={applicantId || ''} + placeholder="Select an applicant you support" + /> +
+ +
+ +
+ + +
+
+
+ +
+ + this.onTxSuccess(buildNewVote() as NewVote, txResult)} + /> + +
} + + ); + } + + private resetForm = (): void => { + this.onChangeStake(ZERO); + this.newRandomSalt(); + this.setState({ isFormSubmitted: false }); + } + + private onFormSubmitted = (): void => { + this.setState({ isFormSubmitted: true }); + } + + private onTxFailed: TxFailedCallback = (_txResult: SubmittableResult | null): void => { + // TODO Possible UX improvement: tell a user that his vote hasn't been accepted. + } + + private onTxSuccess = (vote: NewVote, txResult: SubmittableResult): void => { + let hasVotedEvent = false; + txResult.events.forEach((event, i) => { + const { section, method } = event.event; + if (section === 'councilElection' && method === 'Voted') { + hasVotedEvent = true; + } + }); + if (hasVotedEvent) { + saveVote(vote); + this.setState({ isFormSubmitted: true }); + } + } + + private newRandomSalt = (): void => { + this.setState({ salt: randomSalt() }); + } + + private minStake = (): BN => { + return this.props.minVotingStake || new BN(1); + } + + private onChangeStake = (stake?: BN) => { + const isStakeValid = stake && stake.gte(this.minStake()); + this.setState({ stake, isStakeValid }); + } + + private onChangeApplicant = (applicantId?: string | null) => { + this.setState({ applicantId }); + } + + private onChangeSalt = (salt?: string) => { + // TODO check that salt is unique by checking Substrate store. + this.setState({ salt }); + } +} + +export default withMulti( + Component, + translate, + withOnlyMembers, + withCalls( + queryToProp('query.councilElection.minVotingStake'), + queryToProp('query.councilElection.applicants') + ) +); diff --git a/pioneer/packages/joy-election/src/Votes.tsx b/pioneer/packages/joy-election/src/Votes.tsx new file mode 100644 index 0000000000..a1558406e6 --- /dev/null +++ b/pioneer/packages/joy-election/src/Votes.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; + +import translate from './translate'; +import SealedVotes from './SealedVotes'; +import Section from '@polkadot/joy-utils/Section'; +import { withMyAccount, MyAccountProps } from '@polkadot/joy-utils/MyAccount'; +import { getVotesByVoter } from './myVotesStore'; +import VoteForm from './VoteForm'; + +type Props = AppProps & ApiProps & I18nProps & MyAccountProps & {}; + +class Component extends React.PureComponent { + render () { + const { myAddress } = this.props; + const myVotes = myAddress ? getVotesByVoter(myAddress) : []; + + return <> +
+ +
+ + ; + } +} + +export default translate( + withMyAccount(Component) +); diff --git a/pioneer/packages/joy-election/src/index.css b/pioneer/packages/joy-election/src/index.css new file mode 100644 index 0000000000..5ad24c8384 --- /dev/null +++ b/pioneer/packages/joy-election/src/index.css @@ -0,0 +1,28 @@ + +.JoyElection--NotRunning { + /* nothing yet */ +} +.JoyElection--Running { + font-style: italic; + color: green; +} +.SealedVoteTable { + -webkit-box-shadow: 0 1px 2px 0 rgba(34,36,38,.15) !important; + box-shadow: 0 1px 2px 0 rgba(34,36,38,.15) !important; + tr td:first-child { + color: #999 !important; + font-weight: normal !important; + } +} + +.SidebarSubtitle { + &.Announcing { + color: #4caf50; /* green */ + } + &.Voting { + color: #2196f3; /* blue */ + } + &.Revealing { + color: #ff5722; /* red */ + } +} \ No newline at end of file diff --git a/pioneer/packages/joy-election/src/index.tsx b/pioneer/packages/joy-election/src/index.tsx new file mode 100644 index 0000000000..c4206c3aeb --- /dev/null +++ b/pioneer/packages/joy-election/src/index.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { Route, Switch } from 'react-router'; + +import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls } from '@polkadot/react-api/with'; +import { AccountId, Hash } from '@polkadot/types/interfaces'; +import Tabs, { TabItem } from '@polkadot/react-components/Tabs'; + +// our app-specific styles +import './index.css'; + +// local imports and components +import translate from './translate'; +import Dashboard from './Dashboard'; +import Council from './Council'; +import Applicants from './Applicants'; +import Votes from './Votes'; +import Reveals from './Reveals'; +import { queryToProp } from '@polkadot/joy-utils/index'; +import { Seat } from '@joystream/types/council'; + +// define out internal types +type Props = AppProps & ApiProps & I18nProps & { + activeCouncil?: Seat[]; + applicants?: AccountId[]; + commitments?: Hash[]; +}; + +type State = {}; + +class App extends React.PureComponent { + state: State = {}; + + private buildTabs (): TabItem[] { + const { t, activeCouncil = [], applicants = [], commitments = [] } = this.props; + return [ + { + isRoot: true, + name: 'council', + text: t('Dashboard') + }, + { + name: 'members', + text: t('Council members') + ` (${activeCouncil.length})` + }, + { + name: 'applicants', + text: t('Applicants') + ` (${applicants.length})` + }, + { + name: 'votes', + text: t('Votes') + ` (${commitments.length})` + } + ]; + } + + render () { + const { basePath } = this.props; + const tabs = this.buildTabs(); + return ( +
+
+ +
+ + + + + + + +
+ ); + } +} + +export default translate( + withCalls( + queryToProp('query.council.activeCouncil'), + queryToProp('query.councilElection.applicants'), + queryToProp('query.councilElection.commitments') + )(App) +); diff --git a/pioneer/packages/joy-election/src/myVotesStore.ts b/pioneer/packages/joy-election/src/myVotesStore.ts new file mode 100644 index 0000000000..46a500780f --- /dev/null +++ b/pioneer/packages/joy-election/src/myVotesStore.ts @@ -0,0 +1,54 @@ +import store from 'store'; +import { nonEmptyArr } from '@polkadot/joy-utils/index'; + +const MY_VOTES = 'joy.myVotes'; + +export type NewVote = { + voterId: string; + applicantId: string; + stake: string; // Actually this is a BN serialized to string. + salt: string; + hash: string; +}; + +export type SavedVote = NewVote & { + isRevealed: boolean; + votedOnTime: number; + revealedOnTime?: number; +}; + +/** Get all votes that are stored in a local sotrage. */ +export const getAllVotes = (): SavedVote[] => { + const votes = store.get(MY_VOTES); + return nonEmptyArr(votes) ? votes as SavedVote[] : []; +}; + +export const getVotesByVoter = (voterId: string): SavedVote[] => { + return getAllVotes().filter(v => v.voterId === voterId); +}; + +export const findVoteByHash = (hash: string): SavedVote | undefined => { + return getAllVotes().find(v => v.hash === hash); +}; + +export const saveVote = (vote: NewVote): void => { + const votes = getAllVotes(); + const similarVote = votes.find(v => v.hash === vote.hash); + if (similarVote) { + console.log('There is a vote with the same hash in a storage:', similarVote); + return; + } + + votes.push({ ...vote, votedOnTime: Date.now(), isRevealed: false }); + store.set(MY_VOTES, votes); +}; + +export const revealVote = (hash: string): void => { + const votes = getAllVotes(); + const savedVote = votes.find(v => v.hash === hash); + if (savedVote && !savedVote.isRevealed) { + savedVote.isRevealed = true; + savedVote.revealedOnTime = Date.now(); + store.set(MY_VOTES, votes); + } +}; diff --git a/pioneer/packages/joy-election/src/translate.ts b/pioneer/packages/joy-election/src/translate.ts new file mode 100644 index 0000000000..4f0ab6348a --- /dev/null +++ b/pioneer/packages/joy-election/src/translate.ts @@ -0,0 +1,3 @@ +import { withTranslation } from 'react-i18next'; + +export default withTranslation(['election', 'ui']); diff --git a/pioneer/packages/joy-election/src/utils.tsx b/pioneer/packages/joy-election/src/utils.tsx new file mode 100644 index 0000000000..1a82754ee0 --- /dev/null +++ b/pioneer/packages/joy-election/src/utils.tsx @@ -0,0 +1,53 @@ +import { AccountId } from '@polkadot/types/interfaces'; + +// Keyring / identicon / address +// ----------------------------------- + +import createItem from '@polkadot/ui-keyring/options/item'; +import { findNameByAddress } from '@polkadot/joy-utils/index'; + +// Hash +// ----------------------------------- + +import { decodeAddress } from '@polkadot/keyring'; +import { stringToU8a } from '@polkadot/util'; +import { blake2AsHex } from '@polkadot/util-crypto'; + +export type HashedVote = { + applicantId: string; + salt: string; + hash: string; +}; + +const createAddressOption = (address: string) => { + const name = findNameByAddress(address); + return createItem(address, name); +}; + +export const accountIdsToOptions = (applicants: Array): any => { + if (applicants && applicants.length) { + return applicants.map(a => { + const addr = a.toString(); + return createAddressOption(addr); + }); + } + return []; +}; + +/** hash(accountId + salt) */ +export const hashVote = (accountId?: string | null, salt?: string): string | null => { + if (!accountId || !salt) { + // console.log('Cannot hash a vote: either accountId or salt is undefined', { accountId, salt }); + return null; + } + + const accountU8a = decodeAddress(accountId); + const saltU8a = stringToU8a(salt); + const voteU8a = new Uint8Array(accountU8a.length + saltU8a.length); + voteU8a.set(accountU8a); + voteU8a.set(saltU8a, accountU8a.length); + + const hash = blake2AsHex(voteU8a, 256); + // console.log('Vote hash:', hash, 'for', { accountId, salt }); + return hash; +}; diff --git a/pioneer/packages/joy-forum/.skip-build b/pioneer/packages/joy-forum/.skip-build new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/joy-forum/README.md b/pioneer/packages/joy-forum/README.md new file mode 100644 index 0000000000..7f2f1d04cb --- /dev/null +++ b/pioneer/packages/joy-forum/README.md @@ -0,0 +1,3 @@ +# Forum module for Joystream node + +Forum allows to create categories and subcategories, start discussion threads and reply to others. diff --git a/pioneer/packages/joy-forum/package.json b/pioneer/packages/joy-forum/package.json new file mode 100644 index 0000000000..77cc47c4a2 --- /dev/null +++ b/pioneer/packages/joy-forum/package.json @@ -0,0 +1,16 @@ +{ + "name": "@polkadot/joy-forum", + "version": "0.1.1", + "description": "Forum module for Joystream node", + "main": "index.js", + "scripts": {}, + "author": "Joystream contributors", + "maintainers": [], + "dependencies": { + "@babel/runtime": "^7.7.1", + "@polkadot/joy-utils": "^0.1.1", + "@polkadot/react-components": "0.37.0-beta.63", + "@polkadot/react-query": "0.37.0-beta.63", + "lodash": "^4.17.15" + } +} diff --git a/pioneer/packages/joy-forum/src/CategoryList.tsx b/pioneer/packages/joy-forum/src/CategoryList.tsx new file mode 100644 index 0000000000..d2a1fb7d1d --- /dev/null +++ b/pioneer/packages/joy-forum/src/CategoryList.tsx @@ -0,0 +1,398 @@ +import React, { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; +import ReactMarkdown from 'react-markdown'; +import { Table, Dropdown, Button, Segment, Label } from 'semantic-ui-react'; +import styled from 'styled-components'; +import orderBy from 'lodash/orderBy'; +import BN from 'bn.js'; + +import { Option, bool } from '@polkadot/types'; +import { ThreadId } from '@joystream/types/common'; +import { CategoryId, Category, Thread } from '@joystream/types/forum'; +import { ViewThread } from './ViewThread'; +import { MutedSpan } from '@polkadot/joy-utils/MutedText'; +import { UrlHasIdProps, CategoryCrumbs, Pagination, ThreadsPerPage, usePagination } from './utils'; +import Section from '@polkadot/joy-utils/Section'; +import { JoyWarn } from '@polkadot/joy-utils/JoyStatus'; +import { withForumCalls } from './calls'; +import { withMulti, withApi } from '@polkadot/react-api'; +import { ApiProps } from '@polkadot/react-api/types'; +import { bnToStr, isEmptyArr } from '@polkadot/joy-utils/index'; +import TxButton from '@polkadot/joy-utils/TxButton'; +import { IfIAmForumSudo } from './ForumSudo'; +import { MemberPreview } from '@polkadot/joy-members/MemberPreview'; + +type CategoryActionsProps = { + id: CategoryId; + category: Category; +}; + +function CategoryActions (props: CategoryActionsProps) { + const { id, category } = props; + const className = 'ui button ActionButton'; + + type BtnProps = { + label: string; + icon?: string; + archive?: boolean; + delete?: boolean; + }; + + const UpdateCategoryButton = (btnProps: BtnProps) => { + return {btnProps.label}} + params={[id, new Option(bool, btnProps.archive), new Option(bool, btnProps.delete)]} + tx={'forum.updateCategory'} + />; + }; + + if (category.archived) { + return ( + + + + ); + } + + if (category.deleted) { + return ( + + ; + + ); + } + + return + + + New thread + + + + + {/* TODO show 'Edit' if I am moderator_id */} + {/* + + Edit + */} + + + }> + + + + Add subcategory + + + + + + + + + ; +} + +type InnerViewCategoryProps = { + category?: Category; + preview?: boolean; +}; + +type ViewCategoryProps = InnerViewCategoryProps & { + id: CategoryId; +}; + +const CategoryPreviewRow = styled(Table.Row)` + height: 55px; +`; + +function InnerViewCategory (props: InnerViewCategoryProps) { + const { category, preview = false } = props; + + if (!category) { + return Loading...; + } + + if (category.isEmpty) { + return preview ? null : Category not found; + } + + const { id } = category; + + const renderCategoryActions = () => { + return ; + }; + + if (preview) { + return ( + + + + {category.archived + ? {category.title} + : category.title + } + + + + {category.num_direct_unmoderated_threads.toString()} + + + {category.num_direct_subcategories.toString()} + + + {category.description} + + + ); + } + + const renderSubCategoriesAndThreads = () => <> + {category.archived && + + No new subcategories, threads and posts can be added to it. + + } + + +
+ +
+
+ +
+
+ + {category.hasSubcategories && +
+ +
+ } + +
+ +
+ ; + + return (<> + +

+ {category.title} + {renderCategoryActions()} +

+ + {category.deleted + ? + : renderSubCategoriesAndThreads() + } + ); +} + +const ViewCategory = withForumCalls( + ['categoryById', { propName: 'category', paramName: 'id' }] +)(InnerViewCategory); + +type InnerCategoryThreadsProps = { + category: Category; +}; + +type CategoryThreadsProps = ApiProps & InnerCategoryThreadsProps & { + nextThreadId?: ThreadId; +}; + +function InnerCategoryThreads (props: CategoryThreadsProps) { + const { api, category, nextThreadId } = props; + const [currentPage, setCurrentPage] = usePagination(); + + if (!category.hasUnmoderatedThreads) { + return No threads in this category; + } + + const threadCount = category.num_threads_created.toNumber(); + const [loaded, setLoaded] = useState(false); + const [threads, setThreads] = useState(new Array()); + + useEffect(() => { + const loadThreads = async () => { + if (!nextThreadId || threadCount === 0) return; + + const newId = (id: number | BN) => new ThreadId(id); + const apiCalls: Promise[] = []; + let id = newId(1); + while (nextThreadId.gt(id)) { + apiCalls.push(api.query.forum.threadById(id) as Promise); + id = newId(id.add(newId(1))); + } + + const allThreads = await Promise.all(apiCalls); + const threadsInThisCategory = allThreads.filter(item => + !item.isEmpty && + item.category_id.eq(category.id) + ); + const sortedThreads = orderBy( + threadsInThisCategory, + // TODO UX: Replace sort by id with sort by blocktime of the last reply. + [ + x => x.moderated, + // x => x.pinned, + x => x.nr_in_category.toNumber() + ], + [ + 'asc', + // 'desc', + 'desc' + ] + ); + + setThreads(sortedThreads); + setLoaded(true); + }; + + loadThreads(); + }, [bnToStr(category.id), bnToStr(nextThreadId)]); + + // console.log({ nextThreadId: bnToStr(nextThreadId), loaded, threads }); + + if (!loaded) { + return Loading threads...; + } + + if (isEmptyArr(threads)) { + return No threads in this category; + } + + const itemsPerPage = ThreadsPerPage; + const minIdx = (currentPage - 1) * itemsPerPage; + const maxIdx = minIdx + itemsPerPage - 1; + + const pagination = + ; + + const pageOfItems = threads + .filter((_thread, i) => i >= minIdx && i <= maxIdx) + .map((thread, i) => ); + + return <> + {pagination} + + + + Thread + Replies + Creator + Created + + + + {pageOfItems} + +
+ {pagination} + ; +} + +export const CategoryThreads = withMulti( + InnerCategoryThreads, + withApi, + withForumCalls( + ['nextThreadId', { propName: 'nextThreadId' }] + ) +); + +type ViewCategoryByIdProps = UrlHasIdProps & { + match: { + params: { + id: string; + }; + }; +}; + +export function ViewCategoryById (props: ViewCategoryByIdProps) { + const { match: { params: { id } } } = props; + try { + return ; + } catch (err) { + return Invalid category ID: {id}; + } +} + +type CategoryListProps = ApiProps & { + nextCategoryId?: CategoryId; + parentId?: CategoryId; +}; + +function InnerCategoryList (props: CategoryListProps) { + const { api, parentId, nextCategoryId } = props; + const [loaded, setLoaded] = useState(false); + const [categories, setCategories] = useState(new Array()); + + useEffect(() => { + const loadCategories = async () => { + if (!nextCategoryId) return; + + const newId = (id: number | BN) => new CategoryId(id); + const apiCalls: Promise[] = []; + let id = newId(1); + while (nextCategoryId.gt(id)) { + apiCalls.push(api.query.forum.categoryById(id) as Promise); + id = newId(id.add(newId(1))); + } + + const allCats = await Promise.all(apiCalls); + const filteredCats = allCats.filter(cat => + !cat.isEmpty && + !cat.deleted && // TODO show deleted categories if current user is forum sudo + (parentId ? parentId.eq(cat.parent_id) : cat.isRoot) + ); + + setCategories(filteredCats); + setLoaded(true); + }; + + loadCategories(); + }, [bnToStr(parentId), bnToStr(nextCategoryId)]); + + // console.log({ nextCategoryId: bnToStr(nextCategoryId), loaded, categories }); + + if (!loaded) { + return Loading categories...; + } + + if (isEmptyArr(categories)) { + return Forum is empty; + } + + return ( + + + + Category + Threads + Subcategories + Description + + + {categories.map((category, i) => ( + + ))} +
+ ); +} + +export const CategoryList = withMulti( + InnerCategoryList, + withApi, + withForumCalls( + ['nextCategoryId', { propName: 'nextCategoryId' }] + ) +); diff --git a/pioneer/packages/joy-forum/src/Context.tsx b/pioneer/packages/joy-forum/src/Context.tsx new file mode 100644 index 0000000000..02d1495960 --- /dev/null +++ b/pioneer/packages/joy-forum/src/Context.tsx @@ -0,0 +1,337 @@ + +// NOTE: The purpose of this context is to immitate a Substrate storage for the forum until it's implemented as a substrate runtime module. + +import React, { useReducer, createContext, useContext } from 'react'; +import { Category, Thread, Reply, ModerationAction } from '@joystream/types/forum'; +import { BlockAndTime } from '@joystream/types/common'; +import { Option, Text, GenericAccountId } from '@polkadot/types'; + +type CategoryId = number; +type ThreadId = number; +type ReplyId = number; + +export type ForumState = { + sudo?: string; + + nextCategoryId: CategoryId; + categoryById: Map; + rootCategoryIds: CategoryId[]; + categoryIdsByParentId: Map; + + nextThreadId: ThreadId; + threadById: Map; + threadIdsByCategoryId: Map; + + nextReplyId: ReplyId; + replyById: Map; + replyIdsByThreadId: Map; +}; + +const initialState: ForumState = { + sudo: undefined, + + nextCategoryId: 1, + categoryById: new Map(), + rootCategoryIds: [], + categoryIdsByParentId: new Map(), + + nextThreadId: 1, + threadById: new Map(), + threadIdsByCategoryId: new Map(), + + nextReplyId: 1, + replyById: new Map(), + replyIdsByThreadId: new Map() +}; + +type SetForumSudo = { + type: 'SetForumSudo'; + sudo?: string; +}; + +type NewCategoryAction = { + type: 'NewCategory'; + category: Category; + onCreated?: (newId: number) => void; +}; + +type UpdateCategoryAction = { + type: 'UpdateCategory'; + category: Category; + id: CategoryId; +}; + +type NewThreadAction = { + type: 'NewThread'; + thread: Thread; + onCreated?: (newId: number) => void; +}; + +type UpdateThreadAction = { + type: 'UpdateThread'; + thread: Thread; + id: ThreadId; +}; + +type ModerateThreadAction = { + type: 'ModerateThread'; + id: ThreadId; + moderator: string; + rationale: string; +}; + +type NewReplyAction = { + type: 'NewReply'; + reply: Reply; + onCreated?: (newId: number) => void; +}; + +type UpdateReplyAction = { + type: 'UpdateReply'; + reply: Reply; + id: ReplyId; +}; + +type ModerateReplyAction = { + type: 'ModerateReply'; + id: ReplyId; + moderator: string; + rationale: string; +}; + +type ForumAction = + SetForumSudo | + NewCategoryAction | + UpdateCategoryAction | + NewThreadAction | + UpdateThreadAction | + ModerateThreadAction | + NewReplyAction | + UpdateReplyAction | + ModerateReplyAction; + +function reducer (state: ForumState, action: ForumAction): ForumState { + switch (action.type) { + case 'SetForumSudo': { + const { sudo } = action; + return { + ...state, + sudo + }; + } + + case 'NewCategory': { + const { category, onCreated } = action; + const { parent_id } = category; + + let { + nextCategoryId, + categoryById, + rootCategoryIds, + categoryIdsByParentId + } = state; + + if (parent_id) { + let childrenIds = categoryIdsByParentId.get(parent_id.toNumber()); + if (!childrenIds) { + childrenIds = []; + } + childrenIds.push(nextCategoryId); + categoryIdsByParentId.set(parent_id.toNumber(), childrenIds); + } else { + if (!rootCategoryIds) { + rootCategoryIds = []; + } + rootCategoryIds.push(nextCategoryId); + } + + const newId = nextCategoryId; + categoryById.set(newId, category); + nextCategoryId = nextCategoryId + 1; + + if (onCreated) onCreated(newId); + + return { + ...state, + nextCategoryId, + categoryById, + rootCategoryIds, + categoryIdsByParentId + }; + } + + case 'UpdateCategory': { + const { category, id } = action; + const { categoryById } = state; + + categoryById.set(id, category); + + return { + ...state, + categoryById + }; + } + + case 'NewThread': { + const { thread, onCreated } = action; + const { category_id } = thread; + + let { + nextThreadId, + threadById, + threadIdsByCategoryId + } = state; + + let threadIds = threadIdsByCategoryId.get(category_id.toNumber()); + if (!threadIds) { + threadIds = []; + threadIdsByCategoryId.set(category_id.toNumber(), threadIds); + } + threadIds.push(nextThreadId); + + const newId = nextThreadId; + threadById.set(newId, thread); + nextThreadId = nextThreadId + 1; + + if (onCreated) onCreated(newId); + + return { + ...state, + nextThreadId, + threadById, + threadIdsByCategoryId + }; + } + + case 'UpdateThread': { + const { thread, id } = action; + const { threadById } = state; + + threadById.set(id, thread); + + return { + ...state, + threadById + }; + } + + case 'ModerateThread': { + const { id, moderator, rationale } = action; + const { threadById } = state; + + const thread = threadById.get(id) as Thread; + const moderation = new ModerationAction({ + moderated_at: BlockAndTime.newEmpty(), + moderator_id: new GenericAccountId(moderator), + rationale: new Text(rationale) + }); + const threadUpd = new Thread(Object.assign( + thread.cloneValues(), + { moderation: new Option(ModerationAction, moderation) } + )); + threadById.set(id, threadUpd); + + return { + ...state, + threadById + }; + } + + case 'NewReply': { + const { reply, onCreated } = action; + const { thread_id } = reply; + + let { + nextReplyId, + replyById, + replyIdsByThreadId + } = state; + + let replyIds = replyIdsByThreadId.get(thread_id.toNumber()); + if (!replyIds) { + replyIds = []; + replyIdsByThreadId.set(thread_id.toNumber(), replyIds); + } + replyIds.push(nextReplyId); + + const newId = nextReplyId; + replyById.set(newId, reply); + nextReplyId = nextReplyId + 1; + + if (onCreated) onCreated(newId); + + return { + ...state, + nextReplyId, + replyById, + replyIdsByThreadId + }; + } + + case 'UpdateReply': { + const { reply, id } = action; + const { replyById } = state; + + replyById.set(id, reply); + + return { + ...state, + replyById + }; + } + + case 'ModerateReply': { + const { id, moderator, rationale } = action; + const { replyById } = state; + + const reply = replyById.get(id) as Reply; + const moderation = new ModerationAction({ + moderated_at: BlockAndTime.newEmpty(), + moderator_id: new GenericAccountId(moderator), + rationale: new Text(rationale) + }); + const replyUpd = new Reply(Object.assign( + reply.cloneValues(), + { moderation: new Option(ModerationAction, moderation) } + )); + replyById.set(id, replyUpd); + + return { + ...state, + replyById + }; + } + + default: + throw new Error('Unexptected action: ' + JSON.stringify(action)); + } +} + +function functionStub () { + throw new Error('Function needs to be set in ForumProvider'); +} + +export type ForumContextProps = { + state: ForumState; + dispatch: React.Dispatch; +}; + +const contextStub: ForumContextProps = { + state: initialState, + dispatch: functionStub +}; + +export const ForumContext = createContext(contextStub); + +export function ForumProvider (props: React.PropsWithChildren<{}>) { + const [state, dispatch] = useReducer(reducer, initialState); + return ( + + {props.children} + + ); +} + +export function useForum () { + return useContext(ForumContext); +} diff --git a/pioneer/packages/joy-forum/src/EditCategory.tsx b/pioneer/packages/joy-forum/src/EditCategory.tsx new file mode 100644 index 0000000000..96216f2040 --- /dev/null +++ b/pioneer/packages/joy-forum/src/EditCategory.tsx @@ -0,0 +1,272 @@ +import React from 'react'; +import { Button, Message } from 'semantic-ui-react'; +import { Form, Field, withFormik, FormikProps } from 'formik'; +import * as Yup from 'yup'; +import { History } from 'history'; + +import TxButton from '@polkadot/joy-utils/TxButton'; +import { SubmittableResult } from '@polkadot/api'; +import { withMulti } from '@polkadot/react-api/with'; + +import * as JoyForms from '@polkadot/joy-utils/forms'; +import { Text } from '@polkadot/types'; +import { Option } from '@polkadot/types/codec'; +import { CategoryId, Category } from '@joystream/types/forum'; +import Section from '@polkadot/joy-utils/Section'; +import { useMyAccount } from '@polkadot/joy-utils/MyAccountContext'; +import { UrlHasIdProps, CategoryCrumbs } from './utils'; +import { withOnlyForumSudo } from './ForumSudo'; +import { withForumCalls } from './calls'; +import { ValidationProps, withCategoryValidation } from './validation'; +import { TxFailedCallback, TxCallback } from '@polkadot/react-components/Status/types'; + +const buildSchema = (props: ValidationProps) => { + const { + categoryTitleConstraint, + categoryDescriptionConstraint + } = props; + + if (!categoryTitleConstraint || !categoryDescriptionConstraint) { + throw new Error('Missing some validation constraints'); + } + + const minTitle = categoryTitleConstraint.min.toNumber(); + const maxTitle = categoryTitleConstraint.max.toNumber(); + const minDescr = categoryDescriptionConstraint.min.toNumber(); + const maxDescr = categoryDescriptionConstraint.max.toNumber(); + + return Yup.object().shape({ + + title: Yup.string() + .min(minTitle, `Category title is too short. Minimum length is ${minTitle} chars.`) + .max(maxTitle, `Category title is too long. Maximum length is ${maxTitle} chars.`) + .required('Category title is required'), + + description: Yup.string() + .min(minDescr, `Category description is too short. Minimum length is ${minDescr} chars.`) + .max(maxDescr, `Category description is too long. Maximum length is ${maxDescr} chars.`) + .required('Category description is required') + }); +}; + +type OuterProps = ValidationProps & { + history?: History; + id?: CategoryId; + parentId?: CategoryId; + struct?: Category; +}; + +type FormValues = { + title: string; + description: string; +}; + +type FormProps = OuterProps & FormikProps; + +const LabelledField = JoyForms.LabelledField(); + +const LabelledText = JoyForms.LabelledText(); + +const InnerForm = (props: FormProps) => { + const { + history, + id, + parentId, + struct, + values, + dirty, + isValid, + isSubmitting, + setSubmitting, + resetForm + } = props; + + const { + title, + description + } = values; + + const onSubmit = (sendTx: () => void) => { + if (isValid) sendTx(); + }; + + const onTxFailed: TxFailedCallback = (txResult: SubmittableResult | null) => { + setSubmitting(false); + if (txResult == null) { + // Tx cancelled. + + } + }; + + const onTxSuccess: TxCallback = (_txResult: SubmittableResult) => { + setSubmitting(false); + if (!history) return; + + // Get id of newly created category: + let _id = id; + if (!_id) { + _txResult.events.find(event => { + const { event: { data, method } } = event; + if (method === 'CategoryCreated') { + _id = data.toArray()[0] as CategoryId; + } + return true; + }); + } + + // Redirect to category view: + if (_id) { + history.push('/forum/categories/' + _id.toString()); + } + }; + + const isNew = struct === undefined; + const isSubcategory = parentId !== undefined; + + const buildTxParams = () => { + if (!isValid) return []; + + if (isNew) { + return [ + new Option(CategoryId, parentId), + new Text(title), + new Text(description) + ]; + } else { + // NOTE: currently update_category doesn't support title and description updates. + return []; + } + }; + + const categoryWord = isSubcategory ? 'subcategory' : 'category'; + + const form = +
+ + + + + + + + + +
+ { + !isNew && ( + + } + + + + +
+ + ; + }; + + const replyLinkSearch = new URLSearchParams(search); + replyLinkSearch.set(ReplyIdxQueryParam, reply.nr_in_thread.toString()); + + return ( + + + + + + + + + #{reply.nr_in_thread.toNumber()} + + + + + + {reply.moderated + ? renderModerationRationale() + : renderReplyDetails() + } + + + + {renderActions()} + {showModerateForm && + setShowModerateForm(false)} /> + } + + + ); +}); diff --git a/pioneer/packages/joy-forum/src/ViewThread.tsx b/pioneer/packages/joy-forum/src/ViewThread.tsx new file mode 100644 index 0000000000..95130a6cd5 --- /dev/null +++ b/pioneer/packages/joy-forum/src/ViewThread.tsx @@ -0,0 +1,482 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Link } from 'react-router-dom'; +import ReactMarkdown from 'react-markdown'; +import styled from 'styled-components'; +import { Table, Button, Label, Icon } from 'semantic-ui-react'; +import BN from 'bn.js'; + +import { ThreadId, PostId } from '@joystream/types/common'; +import { Category, Thread, Post } from '@joystream/types/forum'; +import { Pagination, RepliesPerPage, CategoryCrumbs, TimeAgoDate, usePagination, useQueryParam, ReplyIdxQueryParam, ReplyEditIdQueryParam } from './utils'; +import { ViewReply } from './ViewReply'; +import { Moderate } from './Moderate'; +import { MutedSpan } from '@polkadot/joy-utils/MutedText'; +import { JoyWarn } from '@polkadot/joy-utils/JoyStatus'; +import { withForumCalls } from './calls'; +import { withApi, withMulti } from '@polkadot/react-api'; +import { ApiProps } from '@polkadot/react-api/types'; +import { orderBy } from 'lodash'; +import { bnToStr } from '@polkadot/joy-utils/index'; +import { IfIAmForumSudo } from './ForumSudo'; +import { MemberPreview } from '@polkadot/joy-members/MemberPreview'; +import { formatDate } from '@polkadot/joy-utils/functions/date'; +import { NewReply, EditReply } from './EditReply'; + +type ThreadTitleProps = { + thread: Thread; + className?: string; +}; + +function ThreadTitle (props: ThreadTitleProps) { + const { thread, className } = props; + return + {/* {thread.pinned && } */} + {thread.title} + ; +} + +const ThreadHeader = styled.div` + margin: 1rem 0; + + h1 { + margin: 0; + } +`; + +const ThreadInfoAndActions = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + + margin-top: .3rem; + + h1 { + margin: 0; + } +`; + +const ThreadInfo = styled.span` + display: inline-flex; + align-items: center; + + font-size: .85rem; + color: rgba(0, 0, 0, 0.5); +`; + +const ThreadInfoMemberPreview = styled(MemberPreview)` + && { + margin: 0 .2rem; + + .PrefixLabel { + color: inherit; + margin-right: .2rem; + } + } +`; + +const ReplyEditContainer = styled.div` + margin-top: 30px; + padding-bottom: 60px; +`; + +type ThreadPreviewProps = { + thread: Thread; + repliesCount: number; +} + +const ThreadPreview: React.FC = ({ thread, repliesCount }) => { + const title = ; + + return ( + + + + { + thread.moderated + ? ( + + {title} + + ) + : title + } + + + + {repliesCount} + + + + + + {formatDate(thread.created_at.momentDate)} + + + ); +}; + +type InnerViewThreadProps = { + category: Category; + thread: Thread; + preview?: boolean; +}; + +type ViewThreadProps = ApiProps & InnerViewThreadProps & { + nextPostId?: ThreadId; +}; + +function InnerViewThread (props: ViewThreadProps) { + const [showModerateForm, setShowModerateForm] = useState(false); + const [displayedPosts, setDisplayedPosts] = useState([]); + const [quotedPost, setQuotedPost] = useState(null); + + const postsRefs = useRef>>({}); + const replyFormRef = useRef(null); + + const [rawSelectedPostIdx, setSelectedPostIdx] = useQueryParam(ReplyIdxQueryParam); + const [rawEditedPostId, setEditedPostId] = useQueryParam(ReplyEditIdQueryParam); + const [currentPage, setCurrentPage] = usePagination(); + + const parsedSelectedPostIdx = rawSelectedPostIdx ? parseInt(rawSelectedPostIdx) : null; + const selectedPostIdx = (parsedSelectedPostIdx && !Number.isNaN(parsedSelectedPostIdx)) ? parsedSelectedPostIdx : null; + + const { category, thread, preview = false } = props; + + const editedPostId = rawEditedPostId && new PostId(rawEditedPostId); + + if (!thread) { + return Loading thread details...; + } + + const renderThreadNotFound = () => ( + preview ? null : Thread not found + ); + + if (thread.isEmpty) { + return renderThreadNotFound(); + } + + const { id } = thread; + const totalPostsInThread = thread.num_posts_ever_created.toNumber(); + + const changePageAndClearSelectedPost = (page?: number | string) => { + setSelectedPostIdx(null); + setCurrentPage(page, [ReplyIdxQueryParam]); + }; + + if (!category) { + return {'Thread\'s category was not found.'}; + } else if (category.deleted) { + return renderThreadNotFound(); + } + + if (preview) { + return ; + } + + const { api, nextPostId } = props; + const [loaded, setLoaded] = useState(false); + const [posts, setPosts] = useState(new Array()); + + // fetch posts + useEffect(() => { + const loadPosts = async () => { + if (!nextPostId || totalPostsInThread === 0) return; + + const newId = (id: number | BN) => new PostId(id); + const apiCalls: Promise[] = []; + let id = newId(1); + while (nextPostId.gt(id)) { + apiCalls.push(api.query.forum.postById(id) as Promise); + id = newId(id.add(newId(1))); + } + + const allPosts = await Promise.all(apiCalls); + const postsInThisThread = allPosts.filter(item => + !item.isEmpty && + item.thread_id.eq(thread.id) + ); + const sortedPosts = orderBy( + postsInThisThread, + [x => x.nr_in_thread.toNumber()], + ['asc'] + ); + + // initialize refs for posts + postsRefs.current = sortedPosts.reduce((acc, reply) => { + const refKey = reply.nr_in_thread.toNumber(); + acc[refKey] = React.createRef(); + return acc; + }, postsRefs.current); + + setPosts(sortedPosts); + setLoaded(true); + }; + + loadPosts(); + }, [bnToStr(thread.id), bnToStr(nextPostId)]); + + // handle selected post + useEffect(() => { + if (!selectedPostIdx) return; + + const selectedPostPage = Math.ceil(selectedPostIdx / RepliesPerPage); + if (currentPage !== selectedPostPage) { + setCurrentPage(selectedPostPage); + } + + if (!loaded) return; + if (selectedPostIdx > posts.length) { + // eslint-disable-next-line no-console + console.warn(`Tried to open nonexistent reply with idx: ${selectedPostIdx}`); + return; + } + + const postRef = postsRefs.current[selectedPostIdx]; + + // postpone scrolling for one render to make sure the ref is set + setTimeout(() => { + if (postRef.current) { + postRef.current.scrollIntoView(); + } else { + // eslint-disable-next-line no-console + console.warn('Ref for selected post empty'); + } + }); + }, [loaded, selectedPostIdx, currentPage]); + + // handle displayed posts based on pagination + useEffect(() => { + if (!loaded) return; + const minIdx = (currentPage - 1) * RepliesPerPage; + const maxIdx = minIdx + RepliesPerPage - 1; + const postsToDisplay = posts.filter((_id, i) => i >= minIdx && i <= maxIdx); + setDisplayedPosts(postsToDisplay); + }, [loaded, posts, currentPage]); + + const scrollToReplyForm = () => { + if (!replyFormRef.current) return; + replyFormRef.current.scrollIntoView(); + }; + + const clearEditedPost = () => { + setEditedPostId(null); + }; + + const onThreadReplyClick = () => { + clearEditedPost(); + setQuotedPost(null); + scrollToReplyForm(); + }; + + const onPostEditSuccess = async () => { + if (!editedPostId) { + // eslint-disable-next-line no-console + console.error('editedPostId not set!'); + return; + } + + const updatedPost = await api.query.forum.postById(editedPostId) as Post; + const updatedPosts = posts.map(post => post.id.eq(editedPostId) ? updatedPost : post); + + setPosts(updatedPosts); + clearEditedPost(); + }; + + // console.log({ nextPostId: bnToStr(nextPostId), loaded, posts }); + + const renderPageOfPosts = () => { + if (!loaded) { + return Loading posts...; + } + + const pagination = + ; + + const renderedReplies = displayedPosts.map((reply) => { + const replyIdx = reply.nr_in_thread.toNumber(); + + const onReplyEditClick = () => { + setEditedPostId(reply.id.toString()); + scrollToReplyForm(); + }; + + const onReplyQuoteClick = () => { + setQuotedPost(reply); + scrollToReplyForm(); + }; + + return ( + + ); + }); + + return <> + {pagination} + {renderedReplies} + {pagination} + ; + }; + + const renderActions = () => { + if (thread.moderated || category.archived || category.deleted) { + return null; + } + return + + + {/* TODO show 'Edit' button only if I am owner */} + {/* + + Edit + */} + + + + + ); + } + + private resetForm = () => { + const { cancelSource } = this.state; + this.setState({ + ...defaultState(), + cancelSource + }); + } + + private renderUploading () { + const { file, newContentId, progress, error } = this.state; + if (!file || !file.name) return ; + + const success = !error && progress >= 100; + const { history, match: { params: { channelId } } } = this.props; + + return
+ {this.renderProgress()} + {success && + + } +
; + } + + private renderDiscovering () { + return Contacting storage provider.; + } + + private renderProgress () { + const { progress, error } = this.state; + const active = !error && progress < 100; + const success = !error && progress >= 100; + + let label = ''; + if (active) { + label = 'Your file is uploading. Please keep this page open until it\'s done.'; + } else if (success) { + label = 'Uploaded! Click "Publish" button to make your file live.'; + } + + return ; + } + + private renderFileInput () { + const { file } = this.state; + const file_size = file ? file.size : 0; + const file_name = file ? file.name : ''; + + return
+ +
+
{file_name + ? `${file_name} (${formatNumber(file_size)} bytes)` + : <> +
Drag and drop either video or audio file here.
+
Your file should not be more than {MAX_FILE_SIZE_MB} MB.
+ + }
+
+ } + onChange={this.onFileSelected} + /> + {file_name &&
+ +
} +
; + } + + private onFileSelected = (file: File) => { + if (!file.size) { + this.setState({ error: 'You cannot upload an empty file.' }); + } else if (file.size > MAX_FILE_SIZE_BYTES) { + this.setState({ + error: + `You can't upload files larger than ${MAX_FILE_SIZE_MB} MBytes in size.` + }); + } else { + this.setState({ file, computingHash: true }); + this.startComputingHash(); + } + } + + private async startComputingHash () { + const { file } = this.state; + + if (!file) { + return this.hashComputationComplete(undefined, 'No file passed to hasher'); + } + + try { + const iterableFile = new IterableFile(file, { chunkSize: 65535 }); + const ipfs_cid = await IpfsHash.of(iterableFile); + + this.hashComputationComplete(ipfs_cid); + } catch (err) { + return this.hashComputationComplete(undefined, err); + } + } + + private hashComputationComplete (ipfs_cid: string | undefined, error?: string) { + if (!error) { + console.log('Computed IPFS hash:', ipfs_cid); + } + + this.setState({ + computingHash: false, + ipfs_cid, + error + }); + } + + private renderComputingHash () { + return ; + } + + private buildTxParams = () => { + const { file, newContentId, ipfs_cid } = this.state; + if (!file || !ipfs_cid) return []; + + // TODO get corresponding data type id based on file content + const dataObjectTypeId = new BN(1); + const { myMemberId } = this.props; + return [myMemberId, newContentId, dataObjectTypeId, new BN(file.size), ipfs_cid]; + } + + private onDataObjectCreated = async (_txResult: SubmittableResult) => { + this.setState({ discovering: true }); + + const { api } = this.props; + const { newContentId } = this.state; + let dataObject: Option; + try { + dataObject = await api.query.dataDirectory.dataObjectByContentId(newContentId) as Option; + } catch (err) { + this.setState({ + error: err, + discovering: false + }); + return; + } + + const { discovering } = this.state; + + if (!discovering) { + return; + } + + if (dataObject.isSome) { + const storageProvider = dataObject.unwrap().liaison; + this.uploadFileTo(storageProvider); + } else { + this.setState({ + error: new Error('No Storage Provider assigned to process upload'), + discovering: false + }); + } + } + + private uploadFileTo = async (storageProvider: StorageProviderId) => { + const { file, newContentId, cancelSource } = this.state; + if (!file || !file.size) { + this.setState({ + error: new Error('No file to upload!'), + discovering: false + }); + return; + } + + const contentId = newContentId.encode(); + const config = { + headers: { + // TODO uncomment this once the issue fixed: + // https://github.com/Joystream/storage-node-joystream/issues/16 + // 'Content-Type': file.type + 'Content-Type': '' // <-- this is a temporary hack + }, + cancelToken: cancelSource.token, + onUploadProgress: (progressEvent: any) => { + const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); + this.setState({ + progress: percentCompleted + }); + } + }; + + const { discoveryProvider } = this.props; + let url: string; + try { + url = await discoveryProvider.resolveAssetEndpoint(storageProvider, contentId, cancelSource.token); + } catch (err) { + return this.setState({ + error: new Error(`Failed to contact storage provider: ${err.message}`), + discovering: false + }); + } + + const { discovering } = this.state; + + if (!discovering) { + return; + } + + // TODO: validate url .. must start with http + + this.setState({ discovering: false, uploading: true, progress: 0 }); + + try { + await axios.put<{ message: string }>(url, file, config); + } catch (err) { + this.setState({ progress: 0, error: err, uploading: false }); + if (axios.isCancel(err)) { + return; + } + if (!err.response || (err.response.status >= 500 && err.response.status <= 504)) { + // network connection error + discoveryProvider.reportUnreachable(storageProvider); + } + } + } +} + +export const UploadWithRouter = withMulti( + Upload, + translate, + withApi, + withOnlyMembers, + withDiscoveryProvider +); diff --git a/pioneer/packages/joy-media/src/channels/ChannelAvatar.tsx b/pioneer/packages/joy-media/src/channels/ChannelAvatar.tsx new file mode 100644 index 0000000000..f1416df962 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelAvatar.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { BgImg } from '../common/BgImg'; +import { DEFAULT_THUMBNAIL_URL } from '@polkadot/joy-utils/images'; + +const defaultSizePx = 75; + +export type ChannelAvatarSize = 'big' | 'default' | 'small'; + +type Props = { + channel: ChannelEntity; + size?: ChannelAvatarSize; +} + +function sizeToPx (size: ChannelAvatarSize): number { + switch (size) { + case 'big': return 100; + case 'small': return 35; + case 'default': return defaultSizePx; + default: return defaultSizePx; + } +} + +export function ChannelAvatar (props: Props) { + const { channel, size = 'default' } = props; + + return ( + + + + ); +} diff --git a/pioneer/packages/joy-media/src/channels/ChannelAvatarAndName.tsx b/pioneer/packages/joy-media/src/channels/ChannelAvatarAndName.tsx new file mode 100644 index 0000000000..ca553c6b1f --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelAvatarAndName.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelAvatar } from './ChannelAvatar'; +import { ChannelNameAsLink } from './ChannelNameAsLink'; + +type Props = { + channel: ChannelEntity; +} + +export const ChannelAvatarAndName = (props: Props) => { + const { channel } = props; + return ( +
+ +
+

+ +

+
+
+ ); +}; diff --git a/pioneer/packages/joy-media/src/channels/ChannelHeader.tsx b/pioneer/packages/joy-media/src/channels/ChannelHeader.tsx new file mode 100644 index 0000000000..291be1e860 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelHeader.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { BgImg } from '../common/BgImg'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelPreview } from './ChannelPreview'; + +type Props = { + channel: ChannelEntity; +} + +export function ChannelHeader (props: Props) { + const { channel } = props; + const { banner } = channel; + + return ( +
+ {banner && } + +
+ ); +} diff --git a/pioneer/packages/joy-media/src/channels/ChannelHelpers.ts b/pioneer/packages/joy-media/src/channels/ChannelHelpers.ts new file mode 100644 index 0000000000..9e3558e5bf --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelHelpers.ts @@ -0,0 +1,34 @@ +import { AccountId } from '@polkadot/types/interfaces'; +import { ChannelType } from '../schemas/channel/Channel'; +import { ChannelPublicationStatusAllValues } from '@joystream/types/content-working-group'; + +export const ChannelPublicationStatusDropdownOptions = + ChannelPublicationStatusAllValues + .map(x => ({ key: x, value: x, text: x })); + +export const isVideoChannel = (channel: ChannelType) => { + return channel.content === 'Video'; +}; + +export const isMusicChannel = (channel: ChannelType) => { + return channel.content === 'Music'; +}; + +export const isAccountAChannelOwner = (channel?: ChannelType, account?: AccountId | string): boolean => { + return (channel && account) ? channel.roleAccount.eq(account) : false; +}; + +export function isPublicChannel (channel: ChannelType): boolean { + return ( + channel.publicationStatus === 'Public' && + channel.curationStatus !== 'Censored' + ); +} + +export function isCensoredChannel (channel: ChannelType): boolean { + return channel.curationStatus === 'Censored'; +} + +export function isVerifiedChannel (channel: ChannelType): boolean { + return channel.verified; +} diff --git a/pioneer/packages/joy-media/src/channels/ChannelNameAsLink.tsx b/pioneer/packages/joy-media/src/channels/ChannelNameAsLink.tsx new file mode 100644 index 0000000000..d794b57602 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelNameAsLink.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { ChannelEntity } from '../entities/ChannelEntity'; + +type Props = { + channel: ChannelEntity; + className?: string; + style?: React.CSSProperties; +} + +export const ChannelNameAsLink = (props: Props) => { + const { channel, className, style } = props; + return ( + + {channel.title || channel.handle} + + ); +}; diff --git a/pioneer/packages/joy-media/src/channels/ChannelPreview.tsx b/pioneer/packages/joy-media/src/channels/ChannelPreview.tsx new file mode 100644 index 0000000000..7c0e19372a --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelPreview.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import ReactMarkdown from 'react-markdown'; +import { Icon, Label, SemanticICONS, SemanticCOLORS } from 'semantic-ui-react'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelAvatar, ChannelAvatarSize } from './ChannelAvatar'; +import { isPublicChannel, isMusicChannel, isVideoChannel, isAccountAChannelOwner, isVerifiedChannel } from './ChannelHelpers'; + +import { useMyMembership } from '@polkadot/joy-utils/MyMembershipContext'; +import { nonEmptyStr } from '@polkadot/joy-utils/index'; +import { CurationPanel } from './CurationPanel'; +import { ChannelNameAsLink } from './ChannelNameAsLink'; + +type ChannelPreviewProps = { + channel: ChannelEntity; + size?: ChannelAvatarSize; + withSubtitle?: boolean; + withDescription?: boolean; +}; + +export const ChannelPreview = (props: ChannelPreviewProps) => { + const { myAccountId } = useMyMembership(); + const { channel, size, withSubtitle = true, withDescription } = props; + + let subtitle: string | undefined; + let icon: 'music' | 'film' | undefined; + + if (isMusicChannel(channel)) { + subtitle = 'Music channel'; + icon = 'music'; + } else if (isVideoChannel(channel)) { + subtitle = 'Video channel'; + icon = 'film'; + } + + let visibilityIcon: SemanticICONS = 'eye'; + let visibilityColor: SemanticCOLORS = 'green'; + let visibilityText = 'Public'; + + if (!isPublicChannel(channel)) { + visibilityIcon = 'eye slash'; + visibilityColor = 'orange'; + visibilityText = 'Unlisted'; + } + + return <> +
+ + + +
+

+ + + + + {isAccountAChannelOwner(channel, myAccountId) && +
+ + + + Edit + + + + + Upload {channel.content} + + +
+ } +

+ +
+ + {withSubtitle && subtitle && + + {icon && } + {subtitle} + + } + + + + {channel.curationStatus !== 'Normal' && + + } + + {isVerifiedChannel(channel) && + + } +
+ + {withDescription && nonEmptyStr(channel.description) && + + } +
+ + {/* // TODO uncomment when we calculate reward and count of videos in channel: */} + {/* */} + +
+ ; +}; diff --git a/pioneer/packages/joy-media/src/channels/ChannelPreviewStats.tsx b/pioneer/packages/joy-media/src/channels/ChannelPreviewStats.tsx new file mode 100644 index 0000000000..cee1016479 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelPreviewStats.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Statistic } from 'semantic-ui-react'; + +import { ChannelEntity } from '../entities/ChannelEntity'; +import { formatNumber } from '@polkadot/util'; + +type Props = { + channel: ChannelEntity; +}; + +export const ChannelPreviewStats = (props: Props) => { + const { channel } = props; + const statSize = 'tiny'; + + let itemsPublishedLabel = ''; + if (channel.content === 'Video') { + itemsPublishedLabel = 'Videos'; + } else if (channel.content === 'Music') { + itemsPublishedLabel = 'Music tracks'; + } + + return ( +
+
+ + Reward earned + + {formatNumber(channel.rewardEarned)} +  JOY + + +
+ +
+ + {itemsPublishedLabel} + {formatNumber(channel.contentItemsCount)} + +
+
+ ); +}; diff --git a/pioneer/packages/joy-media/src/channels/ChannelsByOwner.tsx b/pioneer/packages/joy-media/src/channels/ChannelsByOwner.tsx new file mode 100644 index 0000000000..4e29d3420f --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelsByOwner.tsx @@ -0,0 +1,77 @@ +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; +import { Segment, Tab } from 'semantic-ui-react'; +import { AccountId } from '@polkadot/types/interfaces'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { YouHaveNoChannels } from './YouHaveNoChannels'; +import { ChannelContentTypeValue } from '@joystream/types/content-working-group'; +import { ChannelPreview } from './ChannelPreview'; + +export type ChannelsByOwnerProps = { + accountId: AccountId; + suspended?: boolean; + channels?: ChannelEntity[]; +}; + +const TabsAndChannels = (props: ChannelsByOwnerProps) => { + const { channels: allChannels = [] } = props; + const [channels, setChannels] = useState(allChannels); + + let videoChannelsCount = 0; + let musicChannelsCount = 0; + allChannels.forEach(x => { + if (x.content === 'Video') { + videoChannelsCount++; + } else if (x.content === 'Music') { + musicChannelsCount++; + } + }); + + const panes = [ + { menuItem: `All channels (${allChannels.length})` }, + { menuItem: `Video channels (${videoChannelsCount})` }, + { menuItem: `Music channels (${musicChannelsCount})` } + ]; + + const contentTypeByTabIndex: Array = + [undefined, 'Video', 'Music']; + + const switchTab = (activeIndex: number) => { + const activeContentType = contentTypeByTabIndex[activeIndex]; + if (activeContentType === undefined) { + setChannels(allChannels); + } else { + setChannels(allChannels.filter( + (x) => x.content === activeContentType) + ); + } + }; + + return <> + switchTab(data.activeIndex as number)} + /> + + + Create Channel + + {channels.map((channel) => + + + + )} + ; +}; + +export function ChannelsByOwner (props: ChannelsByOwnerProps) { + const { suspended = false, channels = [] } = props; + + return
+ {!channels.length + ? + : + }
; +} diff --git a/pioneer/packages/joy-media/src/channels/ChannelsByOwner.view.tsx b/pioneer/packages/joy-media/src/channels/ChannelsByOwner.view.tsx new file mode 100644 index 0000000000..9200d6d778 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ChannelsByOwner.view.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { RouteComponentProps } from 'react-router'; + +import { GenericAccountId } from '@polkadot/types'; +import { MediaView } from '../MediaView'; +import { ChannelsByOwnerProps, ChannelsByOwner } from './ChannelsByOwner'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +type Props = ChannelsByOwnerProps; + +export const ChannelsByOwnerView = MediaView({ + component: ChannelsByOwner, + resolveProps: async (props) => { + const { transport, accountId } = props; + const channels = await transport.channelsByAccount(accountId); + return { channels }; + } +}); + +export const ChannelsByOwnerWithRouter = (props: Props & RouteComponentProps) => { + const { match: { params: { account } } } = props; + + if (account) { + try { + return ; + } catch (err) { + console.log('ChannelsByOwnerWithRouter failed:', err); + } + } + + return {account}; +}; diff --git a/pioneer/packages/joy-media/src/channels/CurationPanel.tsx b/pioneer/packages/joy-media/src/channels/CurationPanel.tsx new file mode 100644 index 0000000000..ce2279a537 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/CurationPanel.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { isVerifiedChannel, isCensoredChannel } from './ChannelHelpers'; +import { useMyMembership } from '@polkadot/joy-utils/MyMembershipContext'; +import TxButton from '@polkadot/joy-utils/TxButton'; +import { ChannelCurationStatus } from '@joystream/types/content-working-group'; +import { AccountId } from '@polkadot/types/interfaces'; + +type ChannelCurationPanelProps = { + channel: ChannelEntity; +}; + +export const CurationPanel = (props: ChannelCurationPanelProps) => { + const { curationActor, allAccounts } = useMyMembership(); + const { channel } = props; + + const canUseAccount = (account: AccountId) => { + if (!allAccounts || !Object.keys(allAccounts).length) { + return false; + } + + const ix = Object.keys(allAccounts).findIndex((key) => { + return account.eq(allAccounts[key].json.address); + }); + + return ix !== -1; + }; + + const renderToggleCensorshipButton = () => { + if (!curationActor) { return null; } + + const [curation_actor, role_account] = curationActor; + const accountAvailable = canUseAccount(role_account); + + const isCensored = isCensoredChannel(channel); + + const new_curation_status = new ChannelCurationStatus( + isCensored ? 'Normal' : 'Censored' + ); + + return ; + }; + + const renderToggleVerifiedButton = () => { + if (!curationActor) { return null; } + + const [curation_actor, role_account] = curationActor; + const accountAvailable = canUseAccount(role_account); + const isVerified = isVerifiedChannel(channel); + + return ; + }; + + return <> +
+ {renderToggleCensorshipButton()} + {renderToggleVerifiedButton()} +
+ ; +}; diff --git a/pioneer/packages/joy-media/src/channels/EditChannel.tsx b/pioneer/packages/joy-media/src/channels/EditChannel.tsx new file mode 100644 index 0000000000..b0f1b4f068 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/EditChannel.tsx @@ -0,0 +1,220 @@ +import React from 'react'; +import { Button } from 'semantic-ui-react'; +import { Form, withFormik } from 'formik'; +import { History } from 'history'; + +import { Text, Option } from '@polkadot/types'; +import TxButton from '@polkadot/joy-utils/TxButton'; +import { onImageError } from '@polkadot/joy-utils/images'; +import { withMediaForm, MediaFormProps } from '../common/MediaForms'; +import { ChannelType, ChannelClass as Fields, buildChannelValidationSchema, ChannelFormValues, ChannelToFormValues, ChannelGenericProp } from '../schemas/channel/Channel'; +import { MediaDropdownOptions } from '../common/MediaDropdownOptions'; +import { ChannelId, ChannelContentType, ChannelPublicationStatus, OptionalText } from '@joystream/types/content-working-group'; +import { newOptionalText, findFirstParamOfSubstrateEvent } from '@polkadot/joy-utils/index'; +import { useMyMembership } from '@polkadot/joy-utils/MyMembershipContext'; +import { ChannelPublicationStatusDropdownOptions, isAccountAChannelOwner } from './ChannelHelpers'; +import { TxCallback } from '@polkadot/react-components/Status/types'; +import { SubmittableResult } from '@polkadot/api'; +import { ChannelValidationConstraints } from '../transport'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; +import Section from '@polkadot/joy-utils/Section'; + +export type OuterProps = { + history?: History; + id?: ChannelId; + entity?: ChannelType; + constraints?: ChannelValidationConstraints; + opts?: MediaDropdownOptions; +}; + +type FormValues = ChannelFormValues; + +const InnerForm = (props: MediaFormProps) => { + const { + // React components for form fields: + MediaText, + MediaDropdown, + LabelledField, + + // Callbacks: + onSubmit, + // onTxSuccess, + onTxFailed, + + history, + id: existingId, + entity, + isFieldChanged, + + // Formik stuff: + values, + dirty, + isValid, + isSubmitting, + setSubmitting, + resetForm + } = props; + + const { myAccountId, myMemberId } = useMyMembership(); + + if (entity && !isAccountAChannelOwner(entity, myAccountId)) { + return ; + } + + const { avatar } = values; + const isNew = !entity; + + // if user is not the channel owner don't render the edit form + // return null + + const onTxSuccess: TxCallback = (txResult: SubmittableResult) => { + setSubmitting(false); + if (!history) return; + + const id = existingId || findFirstParamOfSubstrateEvent(txResult, 'ChannelCreated'); + + console.log('Channel id:', id?.toString()); + + if (id) { + history.push('/media/channels/' + id.toString()); + } + }; + + const buildTxParams = () => { + if (!isValid) return []; + + // TODO get value from the form: + const publicationStatus = new ChannelPublicationStatus('Public'); + + if (!entity) { + // Create a new channel + + const channelOwner = myMemberId; + const roleAccount = myAccountId; + const contentType = new ChannelContentType(values.content); + + return [ + channelOwner, + roleAccount, + contentType, + new Text(values.handle), + newOptionalText(values.title), + newOptionalText(values.description), + newOptionalText(values.avatar), + newOptionalText(values.banner), + publicationStatus + ]; + } else { + // Update an existing channel + + const updOptText = (field: ChannelGenericProp): Option => { + return new Option(OptionalText, + isFieldChanged(field) + ? newOptionalText(values[field.id]) + : null + ); + }; + + const updHandle = new Option(Text, + isFieldChanged(Fields.handle) + ? values[Fields.handle.id] + : null + ); + + const updPublicationStatus = new Option(ChannelPublicationStatus, + isFieldChanged(Fields.publicationStatus) + ? new ChannelPublicationStatus(values[Fields.publicationStatus.id] as any) + : null + ); + + return [ + new ChannelId(entity.id), + updHandle, + updOptText(Fields.title), + updOptText(Fields.description), + updOptText(Fields.avatar), + updOptText(Fields.banner), + updPublicationStatus + ]; + } + }; + + const formFields = () => <> + + + + + + + + ; + + const renderMainButton = () => + ; + + return
+
+ {avatar && } +
+ +
+
+ + {formFields()} + + + {renderMainButton()} +
+
; +}; + +export const EditForm = withFormik({ + + // Transform outer props into form values + mapPropsToValues: (props): FormValues => { + const { entity } = props; + return ChannelToFormValues(entity); + }, + + validationSchema: (props: OuterProps): any => { + const { constraints } = props; + if (!constraints) return null; + + return buildChannelValidationSchema(constraints); + }, + + handleSubmit: () => { + // do submitting things + } +})(withMediaForm(InnerForm) as any); + +export default EditForm; diff --git a/pioneer/packages/joy-media/src/channels/EditChannel.view.tsx b/pioneer/packages/joy-media/src/channels/EditChannel.view.tsx new file mode 100644 index 0000000000..b05582a965 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/EditChannel.view.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { RouteComponentProps } from 'react-router'; +import { MediaView } from '../MediaView'; +import { OuterProps, EditForm } from './EditChannel'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +type Props = OuterProps; + +export const EditChannelView = MediaView({ + component: EditForm, + membersOnly: true, + triggers: ['id'], + resolveProps: async (props) => { + const { transport, id } = props; + const entity = id && await transport.channelById(id); + const constraints = await transport.channelValidationConstraints(); + return { entity, constraints }; + } +}); + +type WithRouterProps = Props & RouteComponentProps + +export const EditChannelWithRouter = (props: WithRouterProps) => { + const { match: { params: { id } } } = props; + + if (id) { + try { + return ; + } catch (err) { + console.log('EditChannelWithRouter failed:', err); + } + } + + return {id}; +}; diff --git a/pioneer/packages/joy-media/src/channels/ViewChannel.tsx b/pioneer/packages/joy-media/src/channels/ViewChannel.tsx new file mode 100644 index 0000000000..f55af16abf --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ViewChannel.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { VideoType } from '../schemas/video/Video'; +import { MusicAlbumPreviewProps } from '../music/MusicAlbumPreview'; +import { MusicTrackReaderPreviewProps } from '../music/MusicTrackReaderPreview'; +import { ViewVideoChannel } from './ViewVideoChannel'; +import { ViewMusicChannel } from './ViewMusicChannel'; +import { toVideoPreviews } from '../video/VideoPreview'; +import { isVideoChannel, isMusicChannel } from './ChannelHelpers'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +export type ViewChannelProps = { + id: ChannelId; + channel?: ChannelEntity; + + // Video channel specific: + videos?: VideoType[]; + + // Music channel specific: + albums?: MusicAlbumPreviewProps[]; + tracks?: MusicTrackReaderPreviewProps[]; +} + +export function ViewChannel (props: ViewChannelProps) { + const { channel, videos = [], albums = [], tracks = [] } = props; + + if (!channel) { + return ; + } + + if (isVideoChannel(channel)) { + const previews = toVideoPreviews(videos); + return ; + } else if (isMusicChannel(channel)) { + return ; + } else { + return {channel.content}; + } +} diff --git a/pioneer/packages/joy-media/src/channels/ViewChannel.view.tsx b/pioneer/packages/joy-media/src/channels/ViewChannel.view.tsx new file mode 100644 index 0000000000..d44432c73a --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ViewChannel.view.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { RouteComponentProps } from 'react-router'; +import { MediaView } from '../MediaView'; +import { ViewChannelProps, ViewChannel } from './ViewChannel'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +type Props = ViewChannelProps; + +export const ViewChannelView = MediaView({ + component: ViewChannel, + triggers: ['id'], + resolveProps: async (props) => { + const { transport, id } = props; + const channel = await transport.channelById(id); + const videos = await transport.videosByChannelId(id); + return { channel, videos }; + } +}); + +export const ViewChannelWithRouter = (props: Props & RouteComponentProps) => { + const { match: { params: { id } } } = props; + + if (id) { + try { + return ; + } catch (err) { + console.log('ViewChannelWithRouter failed:', err); + } + } + + return {id}; +}; diff --git a/pioneer/packages/joy-media/src/channels/ViewMusicChannel.tsx b/pioneer/packages/joy-media/src/channels/ViewMusicChannel.tsx new file mode 100644 index 0000000000..f3526e7b49 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ViewMusicChannel.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import Section from '@polkadot/joy-utils/Section'; +import { ChannelHeader } from './ChannelHeader'; +import { MusicAlbumPreviewProps, MusicAlbumPreview } from '../music/MusicAlbumPreview'; +import { MusicTrackReaderPreview, MusicTrackReaderPreviewProps } from '../music/MusicTrackReaderPreview'; +import NoContentYet from '../common/NoContentYet'; + +type Props = { + channel: ChannelEntity; + albums?: MusicAlbumPreviewProps[]; + tracks?: MusicTrackReaderPreviewProps[]; +}; + +function NoAlbums () { + return Channel has no music albums yet.; +} + +function NoTracks () { + return Channel has no music tracks yet.; +} + +export function ViewMusicChannel (props: Props) { + const { channel, albums = [], tracks = [] } = props; + + const renderAlbumsSection = () => ( + !albums.length + ? + :
+ {albums.map(x => )} +
+ ); + + const renderTracksSection = () => ( + !tracks.length + ? + :
+ {tracks.map(x => )} +
+ ); + + return
+ + {renderAlbumsSection()} + {renderTracksSection()} +
; +} diff --git a/pioneer/packages/joy-media/src/channels/ViewVideoChannel.tsx b/pioneer/packages/joy-media/src/channels/ViewVideoChannel.tsx new file mode 100644 index 0000000000..0d330f7810 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/ViewVideoChannel.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import Section from '@polkadot/joy-utils/Section'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelHeader } from './ChannelHeader'; +import { VideoPreview, VideoPreviewProps } from '../video/VideoPreview'; +import NoContentYet from '../common/NoContentYet'; + +type Props = { + channel: ChannelEntity; + videos?: VideoPreviewProps[]; +}; + +function NoVideosYet () { + return Channel has no videos yet.; +} + +export function ViewVideoChannel (props: Props) { + const { channel, videos = [] } = props; + + const renderVideosSection = () => ( + !videos.length + ? + :
+ {videos.map((x) => + + )} +
+ ); + + return
+ + {renderVideosSection()} +
; +} diff --git a/pioneer/packages/joy-media/src/channels/YouHaveNoChannels.tsx b/pioneer/packages/joy-media/src/channels/YouHaveNoChannels.tsx new file mode 100644 index 0000000000..e0d6d8f7f1 --- /dev/null +++ b/pioneer/packages/joy-media/src/channels/YouHaveNoChannels.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Message } from 'semantic-ui-react'; + +type Props = { + suspended?: boolean; +}; + +export function YouHaveNoChannels (props: Props) { + const { suspended = false } = props; + + const renderSuspendedAlert = () => ( + + ); + + const renderCreateButton = () => ( + + + + ); + + return <> +

+ Build your following on Joystream +

+ +

+ A channel is a way to organize related content for the benefit + of both the publisher and the audience. +

+ + {suspended + ? renderSuspendedAlert() + : renderCreateButton() + } + ; +} diff --git a/pioneer/packages/joy-media/src/common/BgImg.tsx b/pioneer/packages/joy-media/src/common/BgImg.tsx new file mode 100644 index 0000000000..6913c7e2ec --- /dev/null +++ b/pioneer/packages/joy-media/src/common/BgImg.tsx @@ -0,0 +1,43 @@ +import React, { CSSProperties } from 'react'; + +type Props = { + url: string; + size?: number; + width?: number; + height?: number; + circle?: boolean; + className?: string; + style?: CSSProperties; +}; + +export function BgImg (props: Props) { + let { url, width, height, size, circle, className, style } = props; + + const fullClass = 'JoyBgImg ' + className; + + let fullStyle: CSSProperties = { + backgroundImage: `url(${url})` + }; + + if (!width || !height) { + width = size; + height = size; + } + + fullStyle = Object.assign(fullStyle, { + width, + height, + minWidth: width, + minHeight: height + }); + + if (circle) { + fullStyle = Object.assign(fullStyle, { + borderRadius: '50%' + }); + } + + fullStyle = Object.assign(fullStyle, style); + + return
+ ; + }; + + return
{ + !showSecondScreen + ? renderAllTracks() + : renderReorderTracks() + }
; +} diff --git a/pioneer/packages/joy-media/src/music/ReorderableTracks.tsx b/pioneer/packages/joy-media/src/music/ReorderableTracks.tsx new file mode 100644 index 0000000000..258885b955 --- /dev/null +++ b/pioneer/packages/joy-media/src/music/ReorderableTracks.tsx @@ -0,0 +1,89 @@ +import React, { useState } from 'react'; +import { DragDropContext, Droppable, Draggable, DropResult } from 'react-beautiful-dnd'; +import { EditableMusicTrackPreviewProps, MusicTrackPreview } from './MusicTrackPreview'; + +// A little function to help us with reordering the result +const reorder = (list: OrderableItem[], startIndex: number, endIndex: number) => { + const result = Array.from(list); + const [removed] = result.splice(startIndex, 1); + result.splice(endIndex, 0, removed); + + return result; +}; + +type Props = { + tracks: EditableMusicTrackPreviewProps[]; + onRemove?: (track: EditableMusicTrackPreviewProps) => void; + noTracksView?: React.ReactElement; +} + +type OrderableItem = EditableMusicTrackPreviewProps; + +export const ReorderableTracks = (props: Props) => { + const { tracks = [], onRemove = () => { /* do nothing */ }, noTracksView = null } = props; + + const [items, setItems] = useState(tracks); + + if (!items.length) { + return noTracksView; + } + + const onDragEnd = (result: DropResult) => { + // Dropped outside the list + if (!result.destination) { + return; + } + + const reorderedItems = reorder( + items, + result.source.index, + result.destination.index + ); + + setItems(reorderedItems); + }; + + // Normally you would want to split things out into separate components. + // But in this example everything is just done in one place for simplicity + return ( + + + {(provided, _snapshot) => ( +
+ {items.map((item, index) => ( + + {(provided, snapshot) => ( +
+ { + onRemove(item); + const lessItems = items.filter(x => x.id !== item.id); + setItems(lessItems); + }} + /> +
+ )} +
+ ))} + {provided.placeholder} +
+ )} +
+
+ ); +}; diff --git a/pioneer/packages/joy-media/src/schemas/channel/Channel.ts b/pioneer/packages/joy-media/src/schemas/channel/Channel.ts new file mode 100644 index 0000000000..26820320fa --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/channel/Channel.ts @@ -0,0 +1,166 @@ + +import * as Yup from 'yup'; +import { BlockNumber, AccountId } from '@polkadot/types/interfaces'; +import { ChannelContentTypeValue, PrincipalId, Channel, ChannelId, ChannelPublicationStatusValue, ChannelCurationStatusValue } from '@joystream/types/content-working-group'; +import { MemberId } from '@joystream/types/members'; +import { ChannelValidationConstraints } from '@polkadot/joy-media/transport'; +import { ValidationConstraint } from '@polkadot/joy-utils/ValidationConstraint'; + +function textValidation (constraint?: ValidationConstraint) { + if (!constraint) { + return Yup.string(); + } + + const { min, max } = constraint; + return Yup.string() + .min(min, `Text is too short. Minimum length is ${min} chars.`) + .max(max, `Text is too long. Maximum length is ${max} chars.`); +} +export const buildChannelValidationSchema = (constraints?: ChannelValidationConstraints) => + Yup.object().shape({ + handle: textValidation(constraints?.handle).required('This field is required'), + title: textValidation(constraints?.title), + description: textValidation(constraints?.description), + avatar: textValidation(constraints?.avatar), + banner: textValidation(constraints?.banner) + }); + +export type ChannelFormValues = { + content: ChannelContentTypeValue; + handle: string; + title: string; + description: string; + avatar: string; + banner: string; + publicationStatus: ChannelPublicationStatusValue; +}; + +export type ChannelType = { + id: number; + verified: boolean; + handle: string; + title?: string; + description?: string; + avatar?: string; + banner?: string; + content: ChannelContentTypeValue; + owner: MemberId; + roleAccount: AccountId; + publicationStatus: ChannelPublicationStatusValue; + curationStatus: ChannelCurationStatusValue; + created: BlockNumber; + principalId: PrincipalId; +}; + +export class ChannelCodec { + static fromSubstrate (id: ChannelId, sub: Channel): ChannelType { + return { + id: id.toNumber(), + verified: sub.getBoolean('verified'), + handle: sub.getString('handle'), + title: sub.getOptionalString('title'), + description: sub.getOptionalString('description'), + avatar: sub.getOptionalString('avatar'), + banner: sub.getOptionalString('banner'), + content: sub.getEnumAsString('content'), + owner: sub.getField('owner'), + roleAccount: sub.getField('role_account'), + publicationStatus: sub.getEnumAsString('publication_status'), + curationStatus: sub.getEnumAsString('curation_status'), + created: sub.getField('created'), + principalId: sub.getField('principal_id') + }; + } +} + +export function ChannelToFormValues (entity?: ChannelType): ChannelFormValues { + return { + content: (entity && entity.content) || 'Video', + handle: (entity && entity.handle) || '', + title: (entity && entity.title) || '', + description: (entity && entity.description) || '', + avatar: (entity && entity.avatar) || '', + banner: (entity && entity.banner) || '', + publicationStatus: (entity && entity.publicationStatus) || 'Public' + }; +} + +export type ChannelPropId = + 'content' | + 'handle' | + 'title' | + 'description' | + 'avatar' | + 'banner' | + 'publicationStatus' + ; + +export type ChannelGenericProp = { + id: ChannelPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type ChannelClassType = { + [id in ChannelPropId]: ChannelGenericProp +}; + +export const ChannelClass: ChannelClassType = { + content: { + id: 'content', + name: 'Content', + description: 'The type of channel.', + type: 'Text', + required: true, + maxTextLength: 100 + }, + handle: { + id: 'handle', + name: 'Handle', + description: 'Unique URL handle of channel.', + type: 'Text', + required: true, + maxTextLength: 40 + }, + title: { + id: 'title', + name: 'Title', + description: 'Human readable title of channel.', + type: 'Text', + maxTextLength: 100 + }, + description: { + id: 'description', + name: 'Description', + description: 'Human readable description of channel purpose and scope.', + type: 'Text', + maxTextLength: 4000 + }, + avatar: { + id: 'avatar', + name: 'Avatar', + description: 'URL to avatar (logo) iamge: NOTE: Should be an https link to a square image.', + type: 'Text', + maxTextLength: 1000 + }, + banner: { + id: 'banner', + name: 'Banner', + description: 'URL to banner image: NOTE: Should be an https link to a rectangular image, between 1400x1400 and 3000x3000 pixels, in JPEG or PNG format.', + type: 'Text', + maxTextLength: 1000 + }, + publicationStatus: { + id: 'publicationStatus', + name: 'Publication Status', + description: 'The publication status of the channel.', + required: true, + type: 'Internal', + classId: 'Publication Status' + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/general/ContentLicense.ts b/pioneer/packages/joy-media/src/schemas/general/ContentLicense.ts new file mode 100644 index 0000000000..9d3dd9c6ad --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/general/ContentLicense.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const ContentLicenseValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(200, 'Text is too long. Maximum length is 200 chars.') +}); + +export type ContentLicenseFormValues = { + value: string; +}; + +export type ContentLicenseType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class ContentLicenseCodec extends EntityCodec { } + +export function ContentLicenseToFormValues (entity?: ContentLicenseType): ContentLicenseFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type ContentLicensePropId = + 'value' + ; + +export type ContentLicenseGenericProp = { + id: ContentLicensePropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type ContentLicenseClassType = { + [id in ContentLicensePropId]: ContentLicenseGenericProp +}; + +export const ContentLicenseClass: ContentLicenseClassType = { + value: { + id: 'value', + name: 'Value', + description: 'The license of which the content is originally published under.', + type: 'Text', + required: true, + maxTextLength: 200 + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/general/CurationStatus.ts b/pioneer/packages/joy-media/src/schemas/general/CurationStatus.ts new file mode 100644 index 0000000000..efe8ec0383 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/general/CurationStatus.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const CurationStatusValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.') +}); + +export type CurationStatusFormValues = { + value: string; +}; + +export type CurationStatusType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class CurationStatusCodec extends EntityCodec { } + +export function CurationStatusToFormValues (entity?: CurationStatusType): CurationStatusFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type CurationStatusPropId = + 'value' + ; + +export type CurationStatusGenericProp = { + id: CurationStatusPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type CurationStatusClassType = { + [id in CurationStatusPropId]: CurationStatusGenericProp +}; + +export const CurationStatusClass: CurationStatusClassType = { + value: { + id: 'value', + name: 'Value', + description: 'The curator publication status of the content in the content directory.', + required: true, + type: 'Text', + maxTextLength: 255 + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/general/FeaturedContent.ts b/pioneer/packages/joy-media/src/schemas/general/FeaturedContent.ts new file mode 100644 index 0000000000..a6e55ca28c --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/general/FeaturedContent.ts @@ -0,0 +1,83 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; +import { VideoType } from '../video/Video'; +import { MusicAlbumType } from '../music/MusicAlbum'; + +export const FeaturedContentValidationSchema = Yup.object().shape({ + // No validation rules. +}); + +export type FeaturedContentFormValues = { + topVideo: number; + featuredVideos: number[]; + featuredAlbums: number[]; +}; + +export type FeaturedContentType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + topVideo?: VideoType; + featuredVideos?: VideoType[]; + featuredAlbums?: MusicAlbumType[]; +}; + +export class FeaturedContentCodec extends EntityCodec { } + +export function FeaturedContentToFormValues (entity?: FeaturedContentType): FeaturedContentFormValues { + return { + topVideo: (entity && entity.topVideo?.id) || 0, + featuredVideos: (entity && entity.featuredVideos?.map(x => x.id)) || [], + featuredAlbums: (entity && entity.featuredAlbums?.map(x => x.id)) || [] + }; +} + +export type FeaturedContentPropId = + 'topVideo' | + 'featuredVideos' | + 'featuredAlbums' + ; + +export type FeaturedContentGenericProp = { + id: FeaturedContentPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type FeaturedContentClassType = { + [id in FeaturedContentPropId]: FeaturedContentGenericProp +}; + +export const FeaturedContentClass: FeaturedContentClassType = { + topVideo: { + id: 'topVideo', + name: 'Top Video', + description: 'The video that has the most prominent position(s) on the platform.', + type: 'Internal', + classId: 'Video' + }, + featuredVideos: { + id: 'featuredVideos', + name: 'Featured Videos', + description: 'Videos featured in the Video tab.', + type: 'InternalVec', + maxItems: 12, + classId: 'Video' + }, + featuredAlbums: { + id: 'featuredAlbums', + name: 'Featured Albums', + description: 'Music albums featured in the Music tab.', + type: 'InternalVec', + maxItems: 12, + classId: 'Music Album' + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/general/Language.ts b/pioneer/packages/joy-media/src/schemas/general/Language.ts new file mode 100644 index 0000000000..dffc6b9486 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/general/Language.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const LanguageValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(2, 'Text is too long. Maximum length is 2 chars.') +}); + +export type LanguageFormValues = { + value: string; +}; + +export type LanguageType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class LanguageCodec extends EntityCodec { } + +export function LanguageToFormValues (entity?: LanguageType): LanguageFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type LanguagePropId = + 'value' + ; + +export type LanguageGenericProp = { + id: LanguagePropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type LanguageClassType = { + [id in LanguagePropId]: LanguageGenericProp +}; + +export const LanguageClass: LanguageClassType = { + value: { + id: 'value', + name: 'Value', + description: 'Language code following the ISO 639-1 two letter standard.', + type: 'Text', + required: true, + maxTextLength: 2 + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/general/MediaObject.ts b/pioneer/packages/joy-media/src/schemas/general/MediaObject.ts new file mode 100644 index 0000000000..4675e40ae3 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/general/MediaObject.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const MediaObjectValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(66, 'Text is too long. Maximum length is 66 chars.') +}); + +export type MediaObjectFormValues = { + value: string; +}; + +export type MediaObjectType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class MediaObjectCodec extends EntityCodec { } + +export function MediaObjectToFormValues (entity?: MediaObjectType): MediaObjectFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type MediaObjectPropId = + 'value' + ; + +export type MediaObjectGenericProp = { + id: MediaObjectPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type MediaObjectClassType = { + [id in MediaObjectPropId]: MediaObjectGenericProp +}; + +export const MediaObjectClass: MediaObjectClassType = { + value: { + id: 'value', + name: 'Value', + description: 'Content id of object in the data directory.', + type: 'Text', + required: true, + maxTextLength: 48 + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/general/PublicationStatus.ts b/pioneer/packages/joy-media/src/schemas/general/PublicationStatus.ts new file mode 100644 index 0000000000..65334874b6 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/general/PublicationStatus.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const PublicationStatusValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(50, 'Text is too long. Maximum length is 50 chars.') +}); + +export type PublicationStatusFormValues = { + value: string; +}; + +export type PublicationStatusType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class PublicationStatusCodec extends EntityCodec { } + +export function PublicationStatusToFormValues (entity?: PublicationStatusType): PublicationStatusFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type PublicationStatusPropId = + 'value' + ; + +export type PublicationStatusGenericProp = { + id: PublicationStatusPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type PublicationStatusClassType = { + [id in PublicationStatusPropId]: PublicationStatusGenericProp +}; + +export const PublicationStatusClass: PublicationStatusClassType = { + value: { + id: 'value', + name: 'Value', + description: 'The publication status of the content in the content directory.', + required: true, + type: 'Text', + maxTextLength: 50 + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/music/MusicAlbum.ts b/pioneer/packages/joy-media/src/schemas/music/MusicAlbum.ts new file mode 100644 index 0000000000..78f8881a18 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/music/MusicAlbum.ts @@ -0,0 +1,308 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; +import moment from 'moment'; +import { MusicGenreType } from './MusicGenre'; +import { MusicMoodType } from './MusicMood'; +import { MusicThemeType } from './MusicTheme'; +import { MusicTrackType } from './MusicTrack'; +import { LanguageType } from '../general/Language'; +import { PublicationStatusType } from '../general/PublicationStatus'; +import { CurationStatusType } from '../general/CurationStatus'; +import { ContentLicenseType } from '../general/ContentLicense'; +import { ChannelEntity } from '@polkadot/joy-media/entities/ChannelEntity'; + +export const MusicAlbumValidationSchema = Yup.object().shape({ + title: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.'), + artist: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.'), + thumbnail: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.'), + description: Yup.string() + .required('This field is required') + .max(4000, 'Text is too long. Maximum length is 4000 chars.'), + firstReleased: Yup.string() + .required('This field is required') + .test('valid-date', 'Invalid date. Valid date formats are yyyy-mm-dd or yyyy-mm or yyyy.', (val?: any) => { + return moment(val).isValid(); + }), + lyrics: Yup.string() + .max(255, 'Text is too long. Maximum length is 255 chars.'), + composerOrSongwriter: Yup.string() + .max(255, 'Text is too long. Maximum length is 255 chars.'), + attribution: Yup.string() + .max(255, 'Text is too long. Maximum length is 255 chars.') +}); + +export type MusicAlbumFormValues = { + title: string; + artist: string; + thumbnail: string; + description: string; + firstReleased: string; + genre: number; + mood: number; + theme: number; + tracks: number[]; + language: number; + links: string[]; + lyrics: string; + composerOrSongwriter: string; + reviews: string[]; + publicationStatus: number; + curationStatus: number; + explicit: boolean; + license: number; + attribution: string; + channelId: number; +}; + +export type MusicAlbumType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + title: string; + artist: string; + thumbnail: string; + description: string; + firstReleased: number; + genre?: MusicGenreType; + mood?: MusicMoodType; + theme?: MusicThemeType; + tracks?: MusicTrackType[]; + language?: LanguageType; + links?: string[]; + lyrics?: string; + composerOrSongwriter?: string; + reviews?: string[]; + publicationStatus: PublicationStatusType; + curationStatus?: CurationStatusType; + explicit: boolean; + license: ContentLicenseType; + attribution?: string; + channelId?: number; + channel?: ChannelEntity; +}; + +export class MusicAlbumCodec extends EntityCodec { } + +export function MusicAlbumToFormValues (entity?: MusicAlbumType): MusicAlbumFormValues { + return { + title: (entity && entity.title) || '', + artist: (entity && entity.artist) || '', + thumbnail: (entity && entity.thumbnail) || '', + description: (entity && entity.description) || '', + firstReleased: (entity && moment(entity.firstReleased * 1000).format('YYYY-MM-DD')) || '', + genre: (entity && entity.genre?.id) || 0, + mood: (entity && entity.mood?.id) || 0, + theme: (entity && entity.theme?.id) || 0, + tracks: (entity && entity.tracks?.map(x => x.id)) || [], + language: (entity && entity.language?.id) || 0, + links: (entity && entity.links) || [], + lyrics: (entity && entity.lyrics) || '', + composerOrSongwriter: (entity && entity.composerOrSongwriter) || '', + reviews: (entity && entity.reviews) || [], + publicationStatus: (entity && entity.publicationStatus.id) || 0, + curationStatus: (entity && entity.curationStatus?.id) || 0, + explicit: (entity && entity.explicit) || false, + license: (entity && entity.license?.id) || 0, + attribution: (entity && entity.attribution) || '', + channelId: (entity && entity.channelId) || 0 + }; +} + +export type MusicAlbumPropId = + 'title' | + 'artist' | + 'thumbnail' | + 'description' | + 'firstReleased' | + 'genre' | + 'mood' | + 'theme' | + 'tracks' | + 'language' | + 'links' | + 'lyrics' | + 'composerOrSongwriter' | + 'reviews' | + 'publicationStatus' | + 'curationStatus' | + 'explicit' | + 'license' | + 'attribution' | + 'channelId' + ; + +export type MusicAlbumGenericProp = { + id: MusicAlbumPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type MusicAlbumClassType = { + [id in MusicAlbumPropId]: MusicAlbumGenericProp +}; + +export const MusicAlbumClass: MusicAlbumClassType = { + title: { + id: 'title', + name: 'Title', + description: 'The title of the album', + type: 'Text', + required: true, + maxTextLength: 255 + }, + artist: { + id: 'artist', + name: 'Artist', + description: 'The artist, composer, band or group that published the album.', + type: 'Text', + required: true, + maxTextLength: 255 + }, + thumbnail: { + id: 'thumbnail', + name: 'Thumbnail', + description: 'URL to album cover art thumbnail: NOTE: Should be an https link to a square image, between 1400x1400 and 3000x3000 pixels, in JPEG or PNG format.', + required: true, + type: 'Text', + maxTextLength: 255 + }, + description: { + id: 'description', + name: 'Description', + description: 'Information about the album and artist.', + required: true, + type: 'Text', + maxTextLength: 4000 + }, + firstReleased: { + id: 'firstReleased', + name: 'First Released', + description: 'When the album was first released', + required: true, + type: 'Int64' + }, + genre: { + id: 'genre', + name: 'Genre', + description: 'The genre of the album.', + type: 'Internal', + classId: 'Music Genre' + }, + mood: { + id: 'mood', + name: 'Mood', + description: 'The mood of the album.', + type: 'Internal', + classId: 'Music Mood' + }, + theme: { + id: 'theme', + name: 'Theme', + description: 'The theme of the album.', + type: 'Internal', + classId: 'Music Theme' + }, + tracks: { + id: 'tracks', + name: 'Tracks', + description: 'The tracks of the album.', + type: 'InternalVec', + maxItems: 100, + classId: 'Music Track' + }, + language: { + id: 'language', + name: 'Language', + description: 'The language of the song lyrics in the album.', + required: false, + type: 'Internal', + classId: 'Language' + }, + links: { + id: 'links', + name: 'Links', + description: 'Links to the artist or album site, or social media pages.', + type: 'TextVec', + maxItems: 5, + maxTextLength: 255 + }, + lyrics: { + id: 'lyrics', + name: 'Lyrics', + description: 'Link to the album tracks lyrics.', + type: 'Text', + maxTextLength: 255 + }, + composerOrSongwriter: { + id: 'composerOrSongwriter', + name: 'Composer or songwriter', + description: 'The composer(s) and/or songwriter(s) of the album.', + type: 'Text', + maxTextLength: 255 + }, + reviews: { + id: 'reviews', + name: 'Reviews', + description: 'Links to reviews of the album.', + type: 'TextVec', + maxItems: 5, + maxTextLength: 255 + }, + publicationStatus: { + id: 'publicationStatus', + name: 'Publication Status', + description: 'The publication status of the album.', + required: true, + type: 'Internal', + classId: 'Publication Status' + }, + curationStatus: { + id: 'curationStatus', + name: 'Curation Status', + description: 'The publication status of the album set by the a content curator on the platform.', + type: 'Internal', + classId: 'Curation Status' + }, + explicit: { + id: 'explicit', + name: 'Explicit', + description: 'Indicates whether the album contains explicit material.', + required: true, + type: 'Bool' + }, + license: { + id: 'license', + name: 'License', + description: 'The license of which the album is released under.', + required: true, + type: 'Internal', + classId: 'Content License' + }, + attribution: { + id: 'attribution', + name: 'Attribution', + description: 'If the License requires attribution, add this here.', + type: 'Text', + maxTextLength: 255 + }, + channelId: { + id: 'channelId', + name: 'Channel Id', + description: 'Id of the channel this album is published under.', + type: 'Uint64' + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/music/MusicGenre.ts b/pioneer/packages/joy-media/src/schemas/music/MusicGenre.ts new file mode 100644 index 0000000000..4d28af1f75 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/music/MusicGenre.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const MusicGenreValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(100, 'Text is too long. Maximum length is 100 chars.') +}); + +export type MusicGenreFormValues = { + value: string; +}; + +export type MusicGenreType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class MusicGenreCodec extends EntityCodec { } + +export function MusicGenreToFormValues (entity?: MusicGenreType): MusicGenreFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type MusicGenrePropId = + 'value' + ; + +export type MusicGenreGenericProp = { + id: MusicGenrePropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type MusicGenreClassType = { + [id in MusicGenrePropId]: MusicGenreGenericProp +}; + +export const MusicGenreClass: MusicGenreClassType = { + value: { + id: 'value', + name: 'Value', + description: 'Genres for music.', + required: true, + type: 'Text', + maxTextLength: 100 + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/music/MusicMood.ts b/pioneer/packages/joy-media/src/schemas/music/MusicMood.ts new file mode 100644 index 0000000000..cb99140fe9 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/music/MusicMood.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const MusicMoodValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(100, 'Text is too long. Maximum length is 100 chars.') +}); + +export type MusicMoodFormValues = { + value: string; +}; + +export type MusicMoodType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class MusicMoodCodec extends EntityCodec { } + +export function MusicMoodToFormValues (entity?: MusicMoodType): MusicMoodFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type MusicMoodPropId = + 'value' + ; + +export type MusicMoodGenericProp = { + id: MusicMoodPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type MusicMoodClassType = { + [id in MusicMoodPropId]: MusicMoodGenericProp +}; + +export const MusicMoodClass: MusicMoodClassType = { + value: { + id: 'value', + name: 'Value', + description: 'Moods for music.', + required: true, + type: 'Text', + maxTextLength: 100 + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/music/MusicTheme.ts b/pioneer/packages/joy-media/src/schemas/music/MusicTheme.ts new file mode 100644 index 0000000000..e1f9d79239 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/music/MusicTheme.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const MusicThemeValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(100, 'Text is too long. Maximum length is 100 chars.') +}); + +export type MusicThemeFormValues = { + value: string; +}; + +export type MusicThemeType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class MusicThemeCodec extends EntityCodec { } + +export function MusicThemeToFormValues (entity?: MusicThemeType): MusicThemeFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type MusicThemePropId = + 'value' + ; + +export type MusicThemeGenericProp = { + id: MusicThemePropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type MusicThemeClassType = { + [id in MusicThemePropId]: MusicThemeGenericProp +}; + +export const MusicThemeClass: MusicThemeClassType = { + value: { + id: 'value', + name: 'Value', + description: 'Themes for music.', + required: true, + type: 'Text', + maxTextLength: 100 + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/music/MusicTrack.ts b/pioneer/packages/joy-media/src/schemas/music/MusicTrack.ts new file mode 100644 index 0000000000..7298a083bc --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/music/MusicTrack.ts @@ -0,0 +1,292 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; +import moment from 'moment'; +import { LanguageType } from '../general/Language'; +import { MusicGenreType } from './MusicGenre'; +import { MusicMoodType } from './MusicMood'; +import { MusicThemeType } from './MusicTheme'; +import { MediaObjectType } from '../general/MediaObject'; +import { PublicationStatusType } from '../general/PublicationStatus'; +import { CurationStatusType } from '../general/CurationStatus'; +import { ContentLicenseType } from '../general/ContentLicense'; +import { ChannelEntity } from '@polkadot/joy-media/entities/ChannelEntity'; + +export const MusicTrackValidationSchema = Yup.object().shape({ + title: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.'), + artist: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.'), + thumbnail: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.'), + description: Yup.string() + .max(255, 'Text is too long. Maximum length is 255 chars.'), + firstReleased: Yup.string() + .required('This field is required') + .test('valid-date', 'Invalid date. Valid date formats are yyyy-mm-dd or yyyy-mm or yyyy.', (val?: any) => { + return moment(val).isValid(); + }), + composerOrSongwriter: Yup.string() + .max(255, 'Text is too long. Maximum length is 255 chars.'), + lyrics: Yup.string() + .max(255, 'Text is too long. Maximum length is 255 chars.'), + attribution: Yup.string() + .max(255, 'Text is too long. Maximum length is 255 chars.') +}); + +export type MusicTrackFormValues = { + title: string; + artist: string; + thumbnail: string; + description: string; + language: number; + firstReleased: string; + genre: number; + mood: number; + theme: number; + links: string[]; + composerOrSongwriter: string; + lyrics: string; + object: number; + publicationStatus: number; + curationStatus: number; + explicit: boolean; + license: number; + attribution: string; + channelId: number; +}; + +export type MusicTrackType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + title: string; + artist: string; + thumbnail: string; + description?: string; + language?: LanguageType; + firstReleased: number; + genre?: MusicGenreType; + mood?: MusicMoodType; + theme?: MusicThemeType; + links?: string[]; + composerOrSongwriter?: string; + lyrics?: string; + object?: MediaObjectType; + publicationStatus: PublicationStatusType; + curationStatus?: CurationStatusType; + explicit: boolean; + license: ContentLicenseType; + attribution?: string; + channelId?: number; + channel?: ChannelEntity; +}; + +export class MusicTrackCodec extends EntityCodec { } + +export function MusicTrackToFormValues (entity?: MusicTrackType): MusicTrackFormValues { + return { + title: (entity && entity.title) || '', + artist: (entity && entity.artist) || '', + thumbnail: (entity && entity.thumbnail) || '', + description: (entity && entity.description) || '', + language: (entity && entity.language?.id) || 0, + firstReleased: (entity && moment(entity.firstReleased * 1000).format('YYYY-MM-DD')) || '', + genre: (entity && entity.genre?.id) || 0, + mood: (entity && entity.mood?.id) || 0, + theme: (entity && entity.theme?.id) || 0, + links: (entity && entity.links) || [], + composerOrSongwriter: (entity && entity.composerOrSongwriter) || '', + lyrics: (entity && entity.lyrics) || '', + object: (entity && entity.object?.id) || 0, + publicationStatus: (entity && entity.publicationStatus?.id) || 0, + curationStatus: (entity && entity.curationStatus?.id) || 0, + explicit: (entity && entity.explicit) || false, + license: (entity && entity.license?.id) || 0, + attribution: (entity && entity.attribution) || '', + channelId: (entity && entity.channelId) || 0 + }; +} + +export type MusicTrackPropId = + 'title' | + 'artist' | + 'thumbnail' | + 'description' | + 'language' | + 'firstReleased' | + 'genre' | + 'mood' | + 'theme' | + 'links' | + 'composerOrSongwriter' | + 'lyrics' | + 'object' | + 'publicationStatus' | + 'curationStatus' | + 'explicit' | + 'license' | + 'attribution' | + 'channelId' + ; + +export type MusicTrackGenericProp = { + id: MusicTrackPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type MusicTrackClassType = { + [id in MusicTrackPropId]: MusicTrackGenericProp +}; + +export const MusicTrackClass: MusicTrackClassType = { + title: { + id: 'title', + name: 'Title', + description: 'The title of the track', + type: 'Text', + required: true, + maxTextLength: 255 + }, + artist: { + id: 'artist', + name: 'Artist', + description: 'The artist, composer, band or group that published the track.', + type: 'Text', + required: true, + maxTextLength: 255 + }, + thumbnail: { + id: 'thumbnail', + name: 'Thumbnail', + description: 'URL to track cover art: NOTE: Should be an https link to a square image, between 1400x1400 and 3000x3000 pixels, in JPEG or PNG format.', + required: true, + type: 'Text', + maxTextLength: 255 + }, + description: { + id: 'description', + name: 'Description', + description: 'Information about the track.', + type: 'Text', + maxTextLength: 255 + }, + language: { + id: 'language', + name: 'Language', + description: 'The language of the lyrics in the track.', + type: 'Internal', + classId: 'Language' + }, + firstReleased: { + id: 'firstReleased', + name: 'First Released', + description: 'When the track was first released', + required: true, + type: 'Int64' + }, + genre: { + id: 'genre', + name: 'Genre', + description: 'The genre of the track.', + type: 'Internal', + classId: 'Music Genre' + }, + mood: { + id: 'mood', + name: 'Mood', + description: 'The mood of the track.', + type: 'Internal', + classId: 'Music Mood' + }, + theme: { + id: 'theme', + name: 'Theme', + description: 'The theme of the track.', + type: 'Internal', + classId: 'Music Theme' + }, + links: { + id: 'links', + name: 'Links', + description: 'Links to the artist site or social media pages.', + type: 'TextVec', + maxItems: 5, + maxTextLength: 255 + }, + composerOrSongwriter: { + id: 'composerOrSongwriter', + name: 'Composer or songwriter', + description: 'The composer(s) and/or songwriter(s) of the track.', + type: 'Text', + maxTextLength: 255 + }, + lyrics: { + id: 'lyrics', + name: 'Lyrics', + description: 'Link to the track lyrics.', + type: 'Text', + maxTextLength: 255 + }, + object: { + id: 'object', + name: 'Object', + description: 'The entityId of the object in the data directory.', + type: 'Internal', + classId: 'Media Object' + }, + publicationStatus: { + id: 'publicationStatus', + name: 'Publication Status', + description: 'The publication status of the track.', + required: true, + type: 'Internal', + classId: 'Publication Status' + }, + curationStatus: { + id: 'curationStatus', + name: 'Curation Status', + description: 'The publication status of the track set by the a content curator on the platform.', + type: 'Internal', + classId: 'Curation Status' + }, + explicit: { + id: 'explicit', + name: 'Explicit', + description: 'Indicates whether the track contains explicit material.', + required: true, + type: 'Bool' + }, + license: { + id: 'license', + name: 'License', + description: 'The license of which the track is released under.', + required: true, + type: 'Internal', + classId: 'Content License' + }, + attribution: { + id: 'attribution', + name: 'Attribution', + description: 'If the License requires attribution, add this here.', + type: 'Text', + maxTextLength: 255 + }, + channelId: { + id: 'channelId', + name: 'Channel Id', + description: 'Id of the channel this track is published under.', + type: 'Uint64' + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/video/Video.ts b/pioneer/packages/joy-media/src/schemas/video/Video.ts new file mode 100644 index 0000000000..7131d3fde7 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/video/Video.ts @@ -0,0 +1,230 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; +import moment from 'moment'; +import { LanguageType } from '../general/Language'; +import { VideoCategoryType } from './VideoCategory'; +import { MediaObjectType } from '../general/MediaObject'; +import { PublicationStatusType } from '../general/PublicationStatus'; +import { CurationStatusType } from '../general/CurationStatus'; +import { ContentLicenseType } from '../general/ContentLicense'; +import { ChannelEntity } from '@polkadot/joy-media/entities/ChannelEntity'; + +export const VideoValidationSchema = Yup.object().shape({ + title: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.'), + thumbnail: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.'), + description: Yup.string() + .required('This field is required') + .max(4000, 'Text is too long. Maximum length is 4000 chars.'), + firstReleased: Yup.string() + .required('This field is required') + .test('valid-date', 'Invalid date. Valid date formats are yyyy-mm-dd or yyyy-mm or yyyy.', (val?: any) => { + return moment(val).isValid(); + }), + attribution: Yup.string() + .max(255, 'Text is too long. Maximum length is 255 chars.') +}); + +export type VideoFormValues = { + title: string; + thumbnail: string; + description: string; + language: number; + firstReleased: string; + category: number; + links: string[]; + object: number; + publicationStatus: number; + curationStatus: number; + explicit: boolean; + license: number; + attribution: string; + channelId: number; +}; + +export type VideoType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + title: string; + thumbnail: string; + description: string; + language: LanguageType; + firstReleased: number; + category?: VideoCategoryType; + links?: string[]; + object?: MediaObjectType; + publicationStatus: PublicationStatusType; + curationStatus?: CurationStatusType; + explicit: boolean; + license: ContentLicenseType; + attribution?: string; + channelId?: number; + channel?: ChannelEntity; +}; + +export class VideoCodec extends EntityCodec { } + +export function VideoToFormValues (entity?: VideoType): VideoFormValues { + return { + title: (entity && entity.title) || '', + thumbnail: (entity && entity.thumbnail) || '', + description: (entity && entity.description) || '', + language: (entity && entity.language?.id) || 0, + firstReleased: (entity && moment(entity.firstReleased * 1000).format('YYYY-MM-DD')) || '', + category: (entity && entity.category?.id) || 0, + links: (entity && entity.links) || [], + object: (entity && entity.object?.id) || 0, + publicationStatus: (entity && entity.publicationStatus?.id) || 0, + curationStatus: (entity && entity.curationStatus?.id) || 0, + explicit: (entity && entity.explicit) || false, + license: (entity && entity.license?.id) || 0, + attribution: (entity && entity.attribution) || '', + channelId: (entity && entity.channelId) || 0 + }; +} + +export type VideoPropId = + 'title' | + 'thumbnail' | + 'description' | + 'language' | + 'firstReleased' | + 'category' | + 'links' | + 'object' | + 'publicationStatus' | + 'curationStatus' | + 'explicit' | + 'license' | + 'attribution' | + 'channelId' + ; + +export type VideoGenericProp = { + id: VideoPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type VideoClassType = { + [id in VideoPropId]: VideoGenericProp +}; + +export const VideoClass: VideoClassType = { + title: { + id: 'title', + name: 'Title', + description: 'The title of the video', + type: 'Text', + required: true, + maxTextLength: 255 + }, + thumbnail: { + id: 'thumbnail', + name: 'Thumbnail', + description: 'URL to video thumbnail: NOTE: Should be an https link to an image of ratio 16:9, ideally 1280 pixels wide by 720 pixels tall, with a minimum width of 640 pixels, in JPEG or PNG format.', + required: true, + type: 'Text', + maxTextLength: 255 + }, + description: { + id: 'description', + name: 'Description', + description: 'Information about the video.', + required: true, + type: 'Text', + maxTextLength: 4000 + }, + language: { + id: 'language', + name: 'Language', + description: 'The main language used in the video.', + required: true, + type: 'Internal', + classId: 'Language' + }, + firstReleased: { + id: 'firstReleased', + name: 'First Released', + description: 'When the video was first released', + required: true, + type: 'Int64' + }, + category: { + id: 'category', + name: 'Category', + description: 'The category of the video.', + type: 'Internal', + classId: 'Video Category' + }, + links: { + id: 'links', + name: 'Link', + description: 'A link to the creators page.', + type: 'TextVec', + maxItems: 5, + maxTextLength: 255 + }, + object: { + id: 'object', + name: 'Object', + description: 'The entityId of the object in the data directory.', + type: 'Internal', + classId: 'Media Object' + }, + publicationStatus: { + id: 'publicationStatus', + name: 'Publication Status', + description: 'The publication status of the video.', + required: true, + type: 'Internal', + classId: 'Publication Status' + }, + curationStatus: { + id: 'curationStatus', + name: 'Curation Status', + description: 'The publication status of the video set by the a content curator on the platform.', + type: 'Internal', + classId: 'Curation Status' + }, + explicit: { + id: 'explicit', + name: 'Explicit', + description: 'Indicates whether the video contains explicit material.', + required: true, + type: 'Bool' + }, + license: { + id: 'license', + name: 'License', + description: 'The license of which the video is released under.', + required: true, + type: 'Internal', + classId: 'Content License' + }, + attribution: { + id: 'attribution', + name: 'Attribution', + description: 'If the License requires attribution, add this here.', + type: 'Text', + maxTextLength: 255 + }, + channelId: { + id: 'channelId', + name: 'Channel Id', + description: 'Id of the channel this video is published under.', + type: 'Uint64' + } +}; diff --git a/pioneer/packages/joy-media/src/schemas/video/VideoCategory.ts b/pioneer/packages/joy-media/src/schemas/video/VideoCategory.ts new file mode 100644 index 0000000000..b713e90ca0 --- /dev/null +++ b/pioneer/packages/joy-media/src/schemas/video/VideoCategory.ts @@ -0,0 +1,60 @@ + +/** This file is generated based on JSON schema. Do not modify. */ + +import * as Yup from 'yup'; +import { EntityCodec } from '@joystream/types/versioned-store/EntityCodec'; + +export const VideoCategoryValidationSchema = Yup.object().shape({ + value: Yup.string() + .required('This field is required') + .max(255, 'Text is too long. Maximum length is 255 chars.') +}); + +export type VideoCategoryFormValues = { + value: string; +}; + +export type VideoCategoryType = { + classId: number; + inClassSchemaIndexes: number[]; + id: number; + value: string; +}; + +export class VideoCategoryCodec extends EntityCodec { } + +export function VideoCategoryToFormValues (entity?: VideoCategoryType): VideoCategoryFormValues { + return { + value: (entity && entity.value) || '' + }; +} + +export type VideoCategoryPropId = + 'value' + ; + +export type VideoCategoryGenericProp = { + id: VideoCategoryPropId; + type: string; + name: string; + description?: string; + required?: boolean; + maxItems?: number; + maxTextLength?: number; + classId?: any; +}; + +type VideoCategoryClassType = { + [id in VideoCategoryPropId]: VideoCategoryGenericProp +}; + +export const VideoCategoryClass: VideoCategoryClassType = { + value: { + id: 'value', + name: 'Value', + description: 'Categories for videos.', + type: 'Text', + required: true, + maxTextLength: 255 + } +}; diff --git a/pioneer/packages/joy-media/src/stories/ExploreContent.stories.tsx b/pioneer/packages/joy-media/src/stories/ExploreContent.stories.tsx new file mode 100644 index 0000000000..f446771f75 --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/ExploreContent.stories.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import '../common/index.css'; + +import { ExploreContent } from '../explore/ExploreContent'; +import { withMockTransport } from './withMockTransport'; + +export default { + title: 'Media | Explore', + decorators: [withMockTransport] +}; + +export const DefaultState = () => + ; diff --git a/pioneer/packages/joy-media/src/stories/MusicAlbumTracks.stories.tsx b/pioneer/packages/joy-media/src/stories/MusicAlbumTracks.stories.tsx new file mode 100644 index 0000000000..f45d00a1c6 --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/MusicAlbumTracks.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import '../common/index.css'; + +import { EditForm } from '../music/EditMusicAlbum'; +import { MyMusicTracks } from '../music/MyMusicTracks'; +import { MusicAlbumSamples } from './data/MusicAlbumSamples'; +import { albumTracks, AllMusicTrackSamples } from './data/MusicTrackSamples'; +import { withMockTransport } from './withMockTransport'; +import { EditMusicAlbumView } from '../music/EditMusicAlbum.view'; +import EntityId from '@joystream/types/versioned-store/EntityId'; + +export default { + title: 'Media | My music tracks', + decorators: [withMockTransport] +}; + +export const DefaultState = () => + ; + +export const MockEditAlbumView = () => + ; + +export const MyMusicTracksStory = () => + ; diff --git a/pioneer/packages/joy-media/src/stories/MusicChannel.stories.tsx b/pioneer/packages/joy-media/src/stories/MusicChannel.stories.tsx new file mode 100644 index 0000000000..eff4f344e7 --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/MusicChannel.stories.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import '../common/index.css'; + +import { MockMusicChannel } from './data/ChannelSamples'; +import { ViewMusicChannel } from '../channels/ViewMusicChannel'; +import { MusicAlbumSamples } from './data/MusicAlbumSamples'; +import { AllMusicTrackSamples } from './data/MusicTrackSamples'; +import { withMockTransport } from './withMockTransport'; + +export default { + title: 'Media | Music channel', + decorators: [withMockTransport] +}; + +export const EmptyMusicChannel = () => + ; + +export const MusicChannelWithAlbumsOnly = () => + ; + +export const MusicChannelWithAlbumAndTracks = () => + ; diff --git a/pioneer/packages/joy-media/src/stories/MyChannels.stories.tsx b/pioneer/packages/joy-media/src/stories/MyChannels.stories.tsx new file mode 100644 index 0000000000..d3ab3fe063 --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/MyChannels.stories.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import '../common/index.css'; + +import { GenericAccountId } from '@polkadot/types'; +import { ChannelsByOwner } from '../channels/ChannelsByOwner'; +import { AllMockChannels } from './data/ChannelSamples'; +import { withMockTransport } from './withMockTransport'; +import EditForm from '../channels/EditChannel'; +import { EditChannelView } from '../channels/EditChannel.view'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { AccountIdSamples } from './data/AccountIdSamples'; + +export default { + title: 'Media | My channels', + decorators: [withMockTransport] +}; + +// TODO pass to mocked MyMembershipContext provider via Stories decorators: +const accountId = new GenericAccountId(AccountIdSamples.Alice); + +export const DefaultState = () => + ; + +export const ChannelCreationSuspended = () => + ; + +export const YouHaveChannels = () => + ; + +export const DefaultEditForm = () => + ; + +export const MockEditFormView = () => + ; diff --git a/pioneer/packages/joy-media/src/stories/MyMusicAlbums.stories.tsx b/pioneer/packages/joy-media/src/stories/MyMusicAlbums.stories.tsx new file mode 100644 index 0000000000..90e711f10e --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/MyMusicAlbums.stories.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import '../common/index.css'; + +import { MyMusicAlbums } from '../music/MyMusicAlbums'; +import { MusicAlbumSamples } from './data/MusicAlbumSamples'; +import { withMockTransport } from './withMockTransport'; + +export default { + title: 'Media | My music albums', + decorators: [withMockTransport] +}; + +export const DefaultState = () => + ; + +export const WithState = () => + ; diff --git a/pioneer/packages/joy-media/src/stories/Playback.stories.tsx b/pioneer/packages/joy-media/src/stories/Playback.stories.tsx new file mode 100644 index 0000000000..d6f2efe5bd --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/Playback.stories.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import '../common/index.css'; + +import { PlayContent } from '../explore/PlayContent'; +import { PlayVideo } from '../video/PlayVideo'; +import { FeaturedAlbums } from './data/MusicAlbumSamples'; +import { Album1TrackSamples } from './data/MusicTrackSamples'; +import { MockMusicChannel, MockVideoChannel } from './data/ChannelSamples'; +import { withMockTransport } from './withMockTransport'; +import { Video } from '../mocks'; +import { EntityId } from '@joystream/types/versioned-store'; + +export default { + title: 'Media | Playback', + decorators: [withMockTransport] +}; + +export const PlayVideoStory = () => + ; + +export const PlayAlbumStory = () => + ; diff --git a/pioneer/packages/joy-media/src/stories/UploadAudio.stories.tsx b/pioneer/packages/joy-media/src/stories/UploadAudio.stories.tsx new file mode 100644 index 0000000000..a770d2d711 --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/UploadAudio.stories.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { EditForm } from '../upload/UploadAudio'; +import '../index.css'; + +import { ContentId } from '@joystream/types/media'; +import EntityId from '@joystream/types/versioned-store/EntityId'; +import { UploadAudioView } from '../upload/UploadAudio.view'; +import { withMockTransport } from './withMockTransport'; + +export default { + title: 'Media | Upload audio', + decorators: [withMockTransport] +}; + +const contentId = ContentId.generate(); + +export const DefaultState = () => + ; + +export const MockEditFormView = () => + ; diff --git a/pioneer/packages/joy-media/src/stories/UploadVideo.stories.tsx b/pioneer/packages/joy-media/src/stories/UploadVideo.stories.tsx new file mode 100644 index 0000000000..5c2913b575 --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/UploadVideo.stories.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { EditForm } from '../upload/UploadVideo'; +import '../index.css'; + +import { ContentId } from '@joystream/types/media'; +import { withMockTransport } from './withMockTransport'; +import EditVideoView from '../upload/EditVideo.view'; +import EntityId from '@joystream/types/versioned-store/EntityId'; + +export default { + title: 'Media | Upload video', + decorators: [withMockTransport] +}; + +const contentId = ContentId.generate(); + +export const DefaultState = () => + ; + +export const MockEditFormView = () => + ; diff --git a/pioneer/packages/joy-media/src/stories/data/AccountIdSamples.ts b/pioneer/packages/joy-media/src/stories/data/AccountIdSamples.ts new file mode 100644 index 0000000000..d10e2a4fdd --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/data/AccountIdSamples.ts @@ -0,0 +1,7 @@ +import { GenericAccountId } from '@polkadot/types'; + +export const AccountIdSamples = { + Alice: new GenericAccountId('5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY'), + Bob: new GenericAccountId('5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'), + Charlie: new GenericAccountId('5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y') +}; diff --git a/pioneer/packages/joy-media/src/stories/data/ChannelSamples.ts b/pioneer/packages/joy-media/src/stories/data/ChannelSamples.ts new file mode 100644 index 0000000000..8afa50661d --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/data/ChannelSamples.ts @@ -0,0 +1,60 @@ +import BN from 'bn.js'; +import { ChannelEntity } from '@polkadot/joy-media/entities/ChannelEntity'; +import { u32 } from '@polkadot/types'; +import { AccountIdSamples } from './AccountIdSamples'; +import { MemberId } from '@joystream/types/members'; +import { PrincipalId } from '@joystream/types/content-working-group'; + +let id = 0; +const nextId = () => ++id; + +export const MockMusicChannel: ChannelEntity = +{ + id: nextId(), + verified: true, + content: 'Music', + handle: 'easy_notes', + title: 'Easy Notes', + description: 'A fortepiano is an early piano. In principle, the word "fortepiano" can designate any piano dating from the invention of the instrument by Bartolomeo Cristofori around 1700 up to the early 19th century. Most typically, however, it is used to refer to the late-18th to early-19th century instruments for which Haydn, Mozart, and the younger Beethoven wrote their piano music.', + + avatar: 'https://images.unsplash.com/photo-1485561222814-e6c50477491b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + banner: 'https://images.unsplash.com/photo-1514119412350-e174d90d280e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=80', + + publicationStatus: 'Unlisted', + curationStatus: 'Censored', + owner: new MemberId(1), + roleAccount: AccountIdSamples.Alice, + principalId: new PrincipalId(1), + created: new u32(123456), + + rewardEarned: new BN('4587'), + contentItemsCount: 57 +}; + +export const MockVideoChannel: ChannelEntity = +{ + id: nextId(), + verified: true, + content: 'Video', + handle: 'bicycles_rocknroll', + title: 'Bicycles and Rock-n-Roll', + description: 'A bicycle, also called a cycle or bike, is a human-powered or motor-powered, pedal-driven, single-track vehicle, having two wheels attached to a frame, one behind the other. A is called a cyclist, or bicyclist.', + + avatar: 'https://images.unsplash.com/photo-1485965120184-e220f721d03e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + banner: 'https://images.unsplash.com/photo-1494488802316-82250d81cfcc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=900&q=60', + + publicationStatus: 'Public', + curationStatus: 'Normal', + owner: new MemberId(1), + roleAccount: AccountIdSamples.Alice, + principalId: new PrincipalId(1), + created: new u32(123456), + + rewardEarned: new BN('1820021'), + contentItemsCount: 1529 +}; + +export const AllMockChannels: ChannelEntity[] = [ + MockVideoChannel, + MockMusicChannel +]; diff --git a/pioneer/packages/joy-media/src/stories/data/MusicAlbumSamples.ts b/pioneer/packages/joy-media/src/stories/data/MusicAlbumSamples.ts new file mode 100644 index 0000000000..7609ee10fb --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/data/MusicAlbumSamples.ts @@ -0,0 +1,60 @@ +import { MusicAlbumPreviewProps } from '@polkadot/joy-media/music/MusicAlbumPreview'; + +let id = 0; +const nextId = (): string => `${++id}`; + +export const MusicAlbumSample: MusicAlbumPreviewProps = { + id: nextId(), + title: 'Sound of the cold leaves', + artist: 'Man from the Woods', + cover: 'https://images.unsplash.com/photo-1477414348463-c0eb7f1359b6?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=60', + tracksCount: 8 +}; + +export const MusicAlbumSamples = [ + MusicAlbumSample, + { + id: nextId(), + title: 'Riddle', + artist: 'Liquid Stone', + cover: 'https://images.unsplash.com/photo-1484352491158-830ef5692bb3?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60', + tracksCount: 1 + }, + { + id: nextId(), + title: 'Habitants of the silver water', + artist: 'Heavy Waves and Light Shells', + cover: 'https://images.unsplash.com/photo-1543467091-5f0406620f8b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + tracksCount: 12 + }, + { + id: nextId(), + title: 'Fresh and Green', + artist: 'Oldest Trees', + cover: 'https://images.unsplash.com/photo-1526749837599-b4eba9fd855e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + tracksCount: 9 + }, + { + id: nextId(), + title: 'Under the Sun, close to the Ground', + artist: 'Sunflower', + cover: 'https://images.unsplash.com/photo-1504567961542-e24d9439a724?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + tracksCount: 16 + }, + { + id: nextId(), + title: 'Concrete Jungle', + artist: 'Polis', + cover: 'https://images.unsplash.com/photo-1543716091-a840c05249ec?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + tracksCount: 21 + }, + { + id: nextId(), + title: 'Feeed the Bird', + artist: 'Smally', + cover: 'https://images.unsplash.com/photo-1444465693019-aa0b6392460d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + tracksCount: 5 + } +]; + +export const FeaturedAlbums = MusicAlbumSamples.slice(0, 3); diff --git a/pioneer/packages/joy-media/src/stories/data/MusicTrackSamples.ts b/pioneer/packages/joy-media/src/stories/data/MusicTrackSamples.ts new file mode 100644 index 0000000000..eb3aadc857 --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/data/MusicTrackSamples.ts @@ -0,0 +1,83 @@ +import { MusicAlbumSample } from './MusicAlbumSamples'; +import { TracksOfMyMusicAlbumProps } from '@polkadot/joy-media/music/MusicAlbumTracks'; +import { MusicAlbumEntity } from '@polkadot/joy-media/entities/MusicAlbumEntity'; + +export const trackArtists = [ + 'Man from the Woods', + 'Liquid Stone' +]; + +export const trackThumbnails = [ + 'https://images.unsplash.com/photo-1477414348463-c0eb7f1359b6?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=60', + 'https://images.unsplash.com/photo-1484352491158-830ef5692bb3?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60' +]; + +export const trackNames = [ + 'Arborvitae (Thuja occidentalis)', + 'Black Ash (Fraxinus nigra)', + 'White Ash (Fraxinus americana)', + 'Bigtooth Aspen (Populus grandidentata)', + 'Quaking Aspen (Populus tremuloides)', + 'Basswood (Tilia americana)', + 'American Beech (Fagus grandifolia)', + 'Black Birch (Betula lenta)', + 'Gray Birch (Betula populifolia)', + 'Paper Birch (Betula papyrifera)', + 'Yellow Birch (Betula alleghaniensis)', + 'Butternut (Juglans cinerea)', + 'Black Cherry (Prunus serotina)', + 'Pin Cherry (Prunus pensylvanica)' +]; + +export const albumTracks = trackNames.map((title, i) => ({ + id: `${i}`, + title, + artist: trackArtists[0], + thumbnail: trackThumbnails[0] +})); + +export const Album1TrackSamples = trackNames + .slice(0, trackNames.length / 2) + .map((title, i) => ({ + id: `${100 + i}`, + title, + artist: trackArtists[0], + thumbnail: trackThumbnails[0] + })); + +export const Album2TrackSamples = trackNames + .slice(trackNames.length / 2) + .map((title, i) => ({ + id: `${200 + i}`, + title, + artist: trackArtists[1], + thumbnail: trackThumbnails[1] + })); + +export const AllMusicTrackSamples = + Album1TrackSamples + .concat(Album2TrackSamples); + +export const AlbumWithTracksProps: TracksOfMyMusicAlbumProps = { + album: MusicAlbumSample, + tracks: albumTracks +}; + +export const MusicAlbumExample: MusicAlbumEntity = { + title: 'Requiem (Mozart)', + description: 'The Requiem in D minor, K. 626, is a requiem mass by Wolfgang Amadeus Mozart (1756–1791). Mozart composed part of the Requiem in Vienna in late 1791, but it was unfinished at his death on 5 December the same year. A completed version dated 1792 by Franz Xaver Süssmayr was delivered to Count Franz von Walsegg, who commissioned the piece for a Requiem service to commemorate the anniversary of his wifes death on 14 February.', + thumbnail: 'https://assets.classicfm.com/2017/36/mozart-1504532179-list-handheld-0.jpg', + year: 2019, + + // visibility: 'Public', + // album: 'Greatest Collection of Mozart', + + // Additional: + artist: 'Berlin Philharmonic', + composer: 'Wolfgang Amadeus Mozart', + genre: 'Classical Music', + mood: 'Relaxing', + theme: 'Dark', + explicit: false, + license: 'Public Domain' +}; diff --git a/pioneer/packages/joy-media/src/stories/withMockTransport.tsx b/pioneer/packages/joy-media/src/stories/withMockTransport.tsx new file mode 100644 index 0000000000..d032721b4f --- /dev/null +++ b/pioneer/packages/joy-media/src/stories/withMockTransport.tsx @@ -0,0 +1,5 @@ +import React from 'react'; +import { MockTransportProvider } from '../TransportContext'; + +export const withMockTransport = (storyFn: () => React.ReactElement) => + {storyFn()}; diff --git a/pioneer/packages/joy-media/src/translate.ts b/pioneer/packages/joy-media/src/translate.ts new file mode 100644 index 0000000000..fe2a214153 --- /dev/null +++ b/pioneer/packages/joy-media/src/translate.ts @@ -0,0 +1,3 @@ +import { withTranslation } from 'react-i18next'; + +export default withTranslation(['media', 'ui']); diff --git a/pioneer/packages/joy-media/src/transport.mock.ts b/pioneer/packages/joy-media/src/transport.mock.ts new file mode 100644 index 0000000000..a79f776ea5 --- /dev/null +++ b/pioneer/packages/joy-media/src/transport.mock.ts @@ -0,0 +1,98 @@ +import { MediaTransport, ChannelValidationConstraints } from './transport'; +import { Entity, Class } from '@joystream/types/versioned-store'; +import { MusicTrackType } from './schemas/music/MusicTrack'; +import { MusicAlbumType } from './schemas/music/MusicAlbum'; +import { VideoType } from './schemas/video/Video'; + +import * as mocks from './mocks'; +import { ContentLicenseType } from './schemas/general/ContentLicense'; +import { CurationStatusType } from './schemas/general/CurationStatus'; +import { FeaturedContentType } from './schemas/general/FeaturedContent'; +import { LanguageType } from './schemas/general/Language'; +import { MediaObjectType } from './schemas/general/MediaObject'; +import { MusicGenreType } from './schemas/music/MusicGenre'; +import { MusicMoodType } from './schemas/music/MusicMood'; +import { MusicThemeType } from './schemas/music/MusicTheme'; +import { PublicationStatusType } from './schemas/general/PublicationStatus'; +import { VideoCategoryType } from './schemas/video/VideoCategory'; +import { ChannelEntity } from './entities/ChannelEntity'; +import { AllMockChannels } from './stories/data/ChannelSamples'; + +export class MockTransport extends MediaTransport { + constructor () { + super(); + console.log('Create new MockTransport'); + } + + protected notImplementedYet (): T { + throw new Error('Mock transport: Requested function is not implemented yet'); + } + + allChannels (): Promise { + return this.promise(AllMockChannels); + } + + channelValidationConstraints (): Promise { + return this.notImplementedYet(); // TODO impl + } + + allClasses (): Promise { + return this.notImplementedYet(); // TODO impl + } + + allEntities (): Promise { + return this.notImplementedYet(); // TODO impl + } + + allVideos (): Promise { + return this.promise(mocks.AllVideos); + } + + allMusicTracks (): Promise { + return this.promise(mocks.AllMusicTracks); + } + + allMusicAlbums (): Promise { + return this.promise(mocks.AllMusicAlbums); + } + + featuredContent (): Promise { + return this.promise(mocks.FeaturedContent); + } + + allContentLicenses (): Promise { + return this.promise(mocks.AllContentLicenses); + } + + allCurationStatuses (): Promise { + return this.promise(mocks.AllCurationStatuses); + } + + allLanguages (): Promise { + return this.promise(mocks.AllLanguages); + } + + allMediaObjects (): Promise { + return this.promise(mocks.AllMediaObjects); + } + + allMusicGenres (): Promise { + return this.promise(mocks.AllMusicGenres); + } + + allMusicMoods (): Promise { + return this.promise(mocks.AllMusicMoods); + } + + allMusicThemes (): Promise { + return this.promise(mocks.AllMusicThemes); + } + + allPublicationStatuses (): Promise { + return this.promise(mocks.AllPublicationStatuses); + } + + allVideoCategories (): Promise { + return this.promise(mocks.AllVideoCategories); + } +} diff --git a/pioneer/packages/joy-media/src/transport.substrate.ts b/pioneer/packages/joy-media/src/transport.substrate.ts new file mode 100644 index 0000000000..64396c35f6 --- /dev/null +++ b/pioneer/packages/joy-media/src/transport.substrate.ts @@ -0,0 +1,390 @@ +import BN from 'bn.js'; +import { MediaTransport, ChannelValidationConstraints } from './transport'; +import { ClassId, Class, EntityId, Entity, ClassName } from '@joystream/types/versioned-store'; +import { InputValidationLengthConstraint } from '@joystream/types/common'; +import { PlainEntity, EntityCodecResolver } from '@joystream/types/versioned-store/EntityCodec'; +import { MusicTrackType } from './schemas/music/MusicTrack'; +import { MusicAlbumType } from './schemas/music/MusicAlbum'; +import { VideoType } from './schemas/video/Video'; +import { ContentLicenseType } from './schemas/general/ContentLicense'; +import { CurationStatusType } from './schemas/general/CurationStatus'; +import { LanguageType } from './schemas/general/Language'; +import { MediaObjectType } from './schemas/general/MediaObject'; +import { MusicGenreType } from './schemas/music/MusicGenre'; +import { MusicMoodType } from './schemas/music/MusicMood'; +import { MusicThemeType } from './schemas/music/MusicTheme'; +import { PublicationStatusType } from './schemas/general/PublicationStatus'; +import { VideoCategoryType } from './schemas/video/VideoCategory'; +import { ChannelEntity } from './entities/ChannelEntity'; +import { ChannelId, Channel } from '@joystream/types/content-working-group'; +import { ApiPromise } from '@polkadot/api/index'; +import { ApiProps } from '@polkadot/react-api/types'; +import { Vec } from '@polkadot/types'; +import { LinkageResult } from '@polkadot/types/codec/Linkage'; +import { ChannelCodec } from './schemas/channel/Channel'; +import { FeaturedContentType } from './schemas/general/FeaturedContent'; +import { AnyChannelId, asChannelId, AnyClassId, AnyEntityId } from './common/TypeHelpers'; +import { SimpleCache } from '@polkadot/joy-utils/SimpleCache'; +import { ValidationConstraint } from '@polkadot/joy-utils/ValidationConstraint'; + +const FIRST_CHANNEL_ID = 1; +const FIRST_CLASS_ID = 1; +const FIRST_ENTITY_ID = 1; + +/** + * There are entities that refer to other entities. + */ +const ClassNamesThatRequireLoadingInternals: ClassName[] = [ + 'Video', + 'MusicTrack', + 'MusicAlbum' +]; + +/** + * There are such group of entities that are safe to cache + * becase they serve as utility entities. + * Very unlikely that their values will be changed frequently. + * Even if changed, this is not a big issue from UI point of view. + */ +const ClassNamesThatCanBeCached: ClassName[] = [ + 'ContentLicense', + 'CurationStatus', + 'Language', + 'MusicGenre', + 'MusicMood', + 'MusicTheme', + 'PublicationStatus', + 'VideoCategory' +]; + +export class SubstrateTransport extends MediaTransport { + protected api: ApiPromise + + private entityCodecResolver: EntityCodecResolver | undefined + + private channelCache: SimpleCache + private entityCache: SimpleCache + private classCache: SimpleCache + + // Ids of such entities as Language, Video Category, Music Mood, etc + // will be pushed to this array later in this transport class. + private idsOfEntitiesToKeepInCache: Set = new Set() + + constructor (api: ApiProps) { + super(); + console.log('Create new SubstrateTransport'); + + if (!api) { + throw new Error('Cannot create SubstrateTransport: Substrate API is required'); + } else if (!api.isApiReady) { + throw new Error('Cannot create SubstrateTransport: Substrate API is not ready yet'); + } + + this.api = api.api; + + const loadChannelsByIds = this.loadChannelsByIds.bind(this); + const loadEntitiesByIds = this.loadPlainEntitiesByIds.bind(this); + const loadClassesByIds = this.loadClassesByIds.bind(this); + + this.channelCache = new SimpleCache('Channel Cache', loadChannelsByIds); + this.entityCache = new SimpleCache('Entity Cache', loadEntitiesByIds); + this.classCache = new SimpleCache('Class Cache', loadClassesByIds); + } + + protected notImplementedYet (): T { + throw new Error('Substrate transport: Requested function is not implemented yet'); + } + + /** Content Working Group query. */ + cwgQuery () { + return this.api.query.contentWorkingGroup; + } + + /** Versioned Store query. */ + vsQuery () { + return this.api.query.versionedStore; + } + + clearSessionCache () { + console.info('Clear cache of Substrate Transport'); + this.channelCache.clear(); + + this.entityCache.clearExcept( + this.idsOfEntitiesToKeepInCache + ); + + // Don't clean Class cache. It's safe to preserve it between transport sessions. + // this.classCache.clear() + } + + // Channels (Content Working Group module) + // ----------------------------------------------------------------- + + async nextChannelId (): Promise { + return await this.cwgQuery().nextChannelId(); + } + + async allChannelIds (): Promise { + let nextId = (await this.nextChannelId()).toNumber(); + if (nextId < 1) nextId = 1; + + const allIds: ChannelId[] = []; + for (let id = FIRST_CHANNEL_ID; id < nextId; id++) { + allIds.push(new ChannelId(id)); + } + + return allIds; + } + + async loadChannelsByIds (ids: AnyChannelId[]): Promise { + const channelTuples = await this.cwgQuery().channelById.multi(ids); + + return channelTuples.map((tuple, i) => { + const channel = tuple[0] as Channel; + const id = asChannelId(ids[i]); + const plain = ChannelCodec.fromSubstrate(id, channel); + + return { + ...plain, + rewardEarned: new BN(0), // TODO calc this value based on chain data + contentItemsCount: 0 // TODO calc this value based on chain data + }; + }); + } + + async allChannels (): Promise { + const ids = await this.allChannelIds(); + return await this.channelCache.getOrLoadByIds(ids); + } + + protected async getValidationConstraint (constraintName: string): Promise { + const constraint = await this.cwgQuery()[constraintName](); + return { + min: constraint.min.toNumber(), + max: constraint.max.toNumber() + }; + } + + async channelValidationConstraints (): Promise { + const [ + handle, + title, + description, + avatar, + banner + ] = await Promise.all([ + this.getValidationConstraint('channelHandleConstraint'), + this.getValidationConstraint('channelTitleConstraint'), + this.getValidationConstraint('channelDescriptionConstraint'), + this.getValidationConstraint('channelAvatarConstraint'), + this.getValidationConstraint('channelBannerConstraint') + ]); + return { + handle, + title, + description, + avatar, + banner + }; + } + + // Classes (Versioned Store module) + // ----------------------------------------------------------------- + + async nextClassId (): Promise { + return await this.vsQuery().nextClassId(); + } + + async allClassIds (): Promise { + const nextId = (await this.nextClassId()).toNumber(); + + const allIds: ClassId[] = []; + for (let id = FIRST_CLASS_ID; id < nextId; id++) { + allIds.push(new ClassId(id)); + } + + return allIds; + } + + async loadClassesByIds (ids: AnyClassId[]): Promise { + return await this.vsQuery().classById.multi>(ids) as unknown as Class[]; + } + + async allClasses (): Promise { + const ids = await this.allClassIds(); + return await this.classCache.getOrLoadByIds(ids); + } + + async getEntityCodecResolver (): Promise { + if (!this.entityCodecResolver) { + const classes = await this.allClasses(); + this.entityCodecResolver = new EntityCodecResolver(classes); + } + return this.entityCodecResolver; + } + + async classNamesToIdSet (classNames: ClassName[]): Promise> { + const classNameToIdMap = await this.classIdByNameMap(); + return new Set(classNames + .map(name => { + const classId = classNameToIdMap[name]; + return classId ? classId.toString() : undefined; + }) + .filter(classId => typeof classId !== 'undefined') as string[] + ); + } + + // Entities (Versioned Store module) + // ----------------------------------------------------------------- + + async nextEntityId (): Promise { + return await this.vsQuery().nextEntityId(); + } + + async allEntityIds (): Promise { + const nextId = (await this.nextEntityId()).toNumber(); + + const allIds: EntityId[] = []; + for (let id = FIRST_ENTITY_ID; id < nextId; id++) { + allIds.push(new EntityId(id)); + } + + return allIds; + } + + private async loadEntitiesByIds (ids: AnyEntityId[]): Promise { + if (!ids || ids.length === 0) return []; + + return await this.vsQuery().entityById.multi>(ids) as unknown as Entity[]; + } + + // TODO try to cache this func + private async loadPlainEntitiesByIds (ids: AnyEntityId[]): Promise { + const entities = await this.loadEntitiesByIds(ids); + const cacheClassIds = await this.classNamesToIdSet(ClassNamesThatCanBeCached); + entities.forEach(e => { + if (cacheClassIds.has(e.class_id.toString())) { + this.idsOfEntitiesToKeepInCache.add(e.id.toString()); + } + }); + + // Next logs are usefull for debug: + // console.log('cacheClassIds', cacheClassIds) + // console.log('idsOfEntitiesToKeepInCache', this.idsOfEntitiesToKeepInCache) + + return await this.toPlainEntitiesAndResolveInternals(entities); + } + + async allPlainEntities (): Promise { + const ids = await this.allEntityIds(); + return await this.entityCache.getOrLoadByIds(ids); + } + + async findPlainEntitiesByClassName (className: ClassName): Promise { + const res: T[] = []; + const clazz = await this.classByName(className); + if (!clazz) { + console.warn(`No class found by name '${className}'`); + return res; + } + + const allIds = await this.allEntityIds(); + const filteredEntities = (await this.entityCache.getOrLoadByIds(allIds)) + .filter(entity => clazz.id.eq(entity.classId)) as T[]; + + console.log(`Found ${filteredEntities.length} plain entities by class name '${className}'`); + + return filteredEntities; + } + + async toPlainEntitiesAndResolveInternals (entities: Entity[]): Promise { + const loadEntityById = this.entityCache.getOrLoadById.bind(this.entityCache); + const loadChannelById = this.channelCache.getOrLoadById.bind(this.channelCache); + + const entityCodecResolver = await this.getEntityCodecResolver(); + const loadableClassIds = await this.classNamesToIdSet(ClassNamesThatRequireLoadingInternals); + + const converted: PlainEntity[] = []; + for (const entity of entities) { + const classIdStr = entity.class_id.toString(); + const codec = entityCodecResolver.getCodecByClassId(entity.class_id); + + if (!codec) { + console.warn(`No entity codec found by class id: ${classIdStr}`); + continue; + } + + const loadInternals = loadableClassIds.has(classIdStr); + + try { + converted.push(await codec.toPlainObject( + entity, { + loadInternals, + loadEntityById, + loadChannelById + }) + ); + } catch (conversionError) { + console.error(conversionError); + } + } + + return converted; + } + + // Load entities by class name: + // ----------------------------------------------------------------- + + async featuredContent (): Promise { + const arr = await this.findPlainEntitiesByClassName('FeaturedContent'); + return arr && arr.length ? arr[0] : undefined; + } + + async allMediaObjects (): Promise { + return await this.findPlainEntitiesByClassName('MediaObject'); + } + + async allVideos (): Promise { + return await this.findPlainEntitiesByClassName('Video'); + } + + async allMusicTracks (): Promise { + return await this.findPlainEntitiesByClassName('MusicTrack'); + } + + async allMusicAlbums (): Promise { + return await this.findPlainEntitiesByClassName('MusicAlbum'); + } + + async allContentLicenses (): Promise { + return await this.findPlainEntitiesByClassName('ContentLicense'); + } + + async allCurationStatuses (): Promise { + return await this.findPlainEntitiesByClassName('CurationStatus'); + } + + async allLanguages (): Promise { + return await this.findPlainEntitiesByClassName('Language'); + } + + async allMusicGenres (): Promise { + return await this.findPlainEntitiesByClassName('MusicGenre'); + } + + async allMusicMoods (): Promise { + return await this.findPlainEntitiesByClassName('MusicMood'); + } + + async allMusicThemes (): Promise { + return await this.findPlainEntitiesByClassName('MusicTheme'); + } + + async allPublicationStatuses (): Promise { + return await this.findPlainEntitiesByClassName('PublicationStatus'); + } + + async allVideoCategories (): Promise { + return await this.findPlainEntitiesByClassName('VideoCategory'); + } +} diff --git a/pioneer/packages/joy-media/src/transport.ts b/pioneer/packages/joy-media/src/transport.ts new file mode 100644 index 0000000000..788a4c7562 --- /dev/null +++ b/pioneer/packages/joy-media/src/transport.ts @@ -0,0 +1,291 @@ +import { Transport } from '@polkadot/joy-utils/index'; +import { AccountId } from '@polkadot/types/interfaces'; +import { EntityId, Class, ClassName, unifyClassName, ClassIdByNameMap } from '@joystream/types/versioned-store'; +import { MusicTrackType, MusicTrackCodec } from './schemas/music/MusicTrack'; +import { MusicAlbumType, MusicAlbumCodec } from './schemas/music/MusicAlbum'; +import { VideoType, VideoCodec } from './schemas/video/Video'; +import { ContentLicenseType, ContentLicenseCodec } from './schemas/general/ContentLicense'; +import { CurationStatusType, CurationStatusCodec } from './schemas/general/CurationStatus'; +import { FeaturedContentType, FeaturedContentCodec } from './schemas/general/FeaturedContent'; +import { LanguageType, LanguageCodec } from './schemas/general/Language'; +import { MediaObjectType, MediaObjectCodec } from './schemas/general/MediaObject'; +import { MusicGenreType, MusicGenreCodec } from './schemas/music/MusicGenre'; +import { MusicMoodType, MusicMoodCodec } from './schemas/music/MusicMood'; +import { MusicThemeType, MusicThemeCodec } from './schemas/music/MusicTheme'; +import { PublicationStatusType, PublicationStatusCodec } from './schemas/general/PublicationStatus'; +import { VideoCategoryType, VideoCategoryCodec } from './schemas/video/VideoCategory'; +import { MediaDropdownOptions } from './common/MediaDropdownOptions'; +import { ChannelEntity } from './entities/ChannelEntity'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { isVideoChannel, isPublicChannel } from './channels/ChannelHelpers'; +import { ValidationConstraint } from '@polkadot/joy-utils/ValidationConstraint'; + +export interface ChannelValidationConstraints { + handle: ValidationConstraint; + title: ValidationConstraint; + description: ValidationConstraint; + avatar: ValidationConstraint; + banner: ValidationConstraint; +} + +export interface InternalEntities { + languages: LanguageType[]; + contentLicenses: ContentLicenseType[]; + curationStatuses: CurationStatusType[]; + musicGenres: MusicGenreType[]; + musicMoods: MusicMoodType[]; + musicThemes: MusicThemeType[]; + publicationStatuses: PublicationStatusType[]; + videoCategories: VideoCategoryType[]; +} + +export const EntityCodecByClassNameMap = { + ContentLicense: ContentLicenseCodec, + CurationStatus: CurationStatusCodec, + FeaturedContent: FeaturedContentCodec, + Language: LanguageCodec, + MediaObject: MediaObjectCodec, + MusicAlbum: MusicAlbumCodec, + MusicGenre: MusicGenreCodec, + MusicMood: MusicMoodCodec, + MusicTheme: MusicThemeCodec, + MusicTrack: MusicTrackCodec, + PublicationStatus: PublicationStatusCodec, + Video: VideoCodec, + VideoCategory: VideoCategoryCodec +}; + +function insensitiveEq (text1: string, text2: string): boolean { + const prepare = (txt: string) => txt.replace(/[\s]+/mg, '').toLowerCase(); + return prepare(text1) === prepare(text2); +} + +export abstract class MediaTransport extends Transport { + protected cachedClassIdByNameMap: ClassIdByNameMap | undefined + + protected sessionId = 0 + + protected abstract notImplementedYet (): T + + clearSessionCache (): void { /* not implemented */ } + + openSession (): void { + this.sessionId++; + console.info(`Open transport session no. ${this.sessionId}`); + } + + closeSession (): void { + this.clearSessionCache(); + console.info(`Close transport session no. ${this.sessionId}`); + } + + async session (operation: () => R | Promise): Promise { + if (typeof operation !== 'function') { + throw new Error('Operation is not a function'); + } + this.openSession(); + const res = await operation(); + this.closeSession(); + return res; + } + + abstract allChannels(): Promise + + async channelById (id: ChannelId): Promise { + return (await this.allChannels()) + .find(x => id && id.eq(x.id)); + } + + async channelsByAccount (accountId: AccountId): Promise { + return (await this.allChannels()) + .filter(x => accountId && accountId.eq(x.roleAccount)); + } + + abstract channelValidationConstraints(): Promise + + abstract allClasses(): Promise + + async classByName (className: ClassName): Promise { + return (await this.allClasses()) + .find((x) => className === unifyClassName(x.name)); + } + + async classIdByNameMap (): Promise { + if (!this.cachedClassIdByNameMap) { + const map: ClassIdByNameMap = {}; + const classes = await this.allClasses(); + classes.forEach((c) => { + const className = unifyClassName(c.name); + map[className] = c.id; + }); + this.cachedClassIdByNameMap = map; + } + return this.cachedClassIdByNameMap; + } + + abstract featuredContent(): Promise + + async topVideo (): Promise { + const content = await this.featuredContent(); + const topVideoId = content?.topVideo as unknown as number | undefined; + return !topVideoId ? undefined : await this.videoById(new EntityId(topVideoId)); + } + + async featuredVideos (): Promise { + const content = await this.featuredContent(); + const videoIds = (content?.featuredVideos || []) as unknown as number[]; + const videos = await Promise.all(videoIds.map((id) => + this.videoById(new EntityId(id)))); + return videos.filter(x => x !== undefined) as VideoType[]; + } + + async featuredAlbums (): Promise { + const content = await this.featuredContent(); + const albumIds = (content?.featuredAlbums || []) as unknown as EntityId[]; + const albums = await Promise.all(albumIds.map((id) => + this.musicAlbumById(new EntityId(id)))); + return albums.filter(x => x !== undefined) as MusicAlbumType[]; + } + + abstract allMediaObjects(): Promise + + abstract allVideos(): Promise + + abstract allMusicTracks(): Promise + + abstract allMusicAlbums(): Promise + + async videosByChannelId (channelId: ChannelId, limit?: number, additionalFilter?: (x: VideoType) => boolean): Promise { + let videos = (await this.allVideos()) + .filter(x => channelId && channelId.eq(x.channelId) && (additionalFilter || (() => true))(x)) + .sort(x => -1 * x.id); + + if (limit && limit > 0) { + videos = videos.slice(0, limit); + } + + return videos; + } + + async videosByAccount (accountId: AccountId): Promise { + const accountChannels = await this.channelsByAccount(accountId); + const accountChannelIds = new Set(accountChannels.map(x => x.id)); + + return (await this.allVideos()) + .filter(x => x.channelId && accountChannelIds.has(x.channelId)); + } + + async mediaObjectById (id: EntityId): Promise { + return (await this.allMediaObjects()) + .find(x => id && id.eq(x.id)); + } + + async videoById (id: EntityId): Promise { + return (await this.allVideos()) + .find(x => id && id.eq(x.id)); + } + + async musicTrackById (id: EntityId): Promise { + return (await this.allMusicTracks()) + .find(x => id && id.eq(x.id)); + } + + async musicAlbumById (id: EntityId): Promise { + return (await this.allMusicAlbums()) + .find(x => id && id.eq(x.id)); + } + + async allPublicChannels (): Promise { + return (await this.allChannels()) + .filter(isPublicChannel); + } + + async allVideoChannels (): Promise { + return (await this.allChannels()) + .filter(isVideoChannel); + } + + async allPublicVideoChannels (): Promise { + return (await this.allVideoChannels()) + .filter(isPublicChannel) + .sort(x => -1 * x.id); + } + + async latestPublicVideoChannels (limit = 6): Promise { + return (await this.allPublicVideoChannels()).slice(0, limit); + } + + async allPublicVideos (): Promise { + const idOfPublicPS = (await this.allPublicationStatuses()) + .find(x => + insensitiveEq(x.value, 'Public') + )?.id; + + const idsOfCuratedCS = (await this.allCurationStatuses()) + .filter(x => + insensitiveEq(x.value, 'Under review') || + insensitiveEq(x.value, 'Removed') + ).map(x => x.id); + + const isPublicAndNotCurated = (video: VideoType) => { + const isPublic = video.publicationStatus.id === idOfPublicPS; + const isNotCurated = !idsOfCuratedCS.includes(video.curationStatus?.id || -1); + const isPubChannel = video.channel ? isPublicChannel(video.channel) : true; + return isPublic && isNotCurated && isPubChannel; + }; + + return (await this.allVideos()) + .filter(isPublicAndNotCurated) + .sort(x => -1 * x.id); + } + + async latestPublicVideos (limit = 12): Promise { + return (await this.allPublicVideos()).slice(0, limit); + } + + async mediaObjectClass () { + return await this.classByName('MediaObject'); + } + + async videoClass () { + return await this.classByName('Video'); + } + + async musicTrackClass () { + return await this.classByName('MusicTrack'); + } + + async musicAlbumClass () { + return await this.classByName('MusicAlbum'); + } + + abstract allContentLicenses(): Promise + abstract allCurationStatuses(): Promise + abstract allLanguages(): Promise + abstract allMusicGenres(): Promise + abstract allMusicMoods(): Promise + abstract allMusicThemes(): Promise + abstract allPublicationStatuses(): Promise + abstract allVideoCategories(): Promise + + async allInternalEntities (): Promise { + return { + contentLicenses: await this.allContentLicenses(), + curationStatuses: await this.allCurationStatuses(), + languages: await this.allLanguages(), + musicGenres: await this.allMusicGenres(), + musicMoods: await this.allMusicMoods(), + musicThemes: await this.allMusicThemes(), + publicationStatuses: await this.allPublicationStatuses(), + videoCategories: await this.allVideoCategories() + }; + } + + async dropdownOptions (): Promise { + const res = new MediaDropdownOptions( + await this.allInternalEntities() + ); + // console.log('Transport.dropdownOptions', res) + return res; + } +} diff --git a/pioneer/packages/joy-media/src/upload/EditVideo.view.tsx b/pioneer/packages/joy-media/src/upload/EditVideo.view.tsx new file mode 100644 index 0000000000..4bcba0a122 --- /dev/null +++ b/pioneer/packages/joy-media/src/upload/EditVideo.view.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { RouteComponentProps } from 'react-router'; +import { MediaView } from '../MediaView'; +import { OuterProps, EditForm } from './UploadVideo'; +import EntityId from '@joystream/types/versioned-store/EntityId'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +type Props = OuterProps; + +export const EditVideoView = MediaView({ + component: EditForm, + membersOnly: true, + triggers: ['id'], + resolveProps: async (props) => { + const { transport, id, channelId } = props; + const channel = channelId && await transport.channelById(channelId); + const mediaObjectClass = await transport.mediaObjectClass(); + const entityClass = await transport.videoClass(); + const entity = id && await transport.videoById(id); + const opts = await transport.dropdownOptions(); + return { channel, mediaObjectClass, entityClass, entity, opts }; + } +}); + +type WithRouterProps = Props & RouteComponentProps + +export const UploadVideoWithRouter = (props: WithRouterProps) => { + const { match: { params: { channelId } } } = props; + + if (channelId) { + try { + return ; + } catch (err) { + console.log('UploadVideoWithRouter failed:', err); + } + } + + return {channelId}; +}; + +export const EditVideoWithRouter = (props: WithRouterProps) => { + const { match: { params: { id } } } = props; + + if (id) { + try { + return ; + } catch (err) { + console.log('EditVideoWithRouter failed:', err); + } + } + + return {id}; +}; + +export default EditVideoView; diff --git a/pioneer/packages/joy-media/src/upload/UploadAudio.tsx b/pioneer/packages/joy-media/src/upload/UploadAudio.tsx new file mode 100644 index 0000000000..331ad77f5f --- /dev/null +++ b/pioneer/packages/joy-media/src/upload/UploadAudio.tsx @@ -0,0 +1,174 @@ +import React from 'react'; +import { Button, Tab } from 'semantic-ui-react'; +import { Form, withFormik } from 'formik'; +import { History } from 'history'; + +import TxButton from '@polkadot/joy-utils/TxButton'; +import { ContentId } from '@joystream/types/media'; +import { onImageError } from '@polkadot/joy-utils/images'; +import { MusicTrackValidationSchema, MusicTrackType, MusicTrackClass as Fields, MusicTrackFormValues, MusicTrackToFormValues } from '../schemas/music/MusicTrack'; +import { withMediaForm, MediaFormProps, datePlaceholder } from '../common/MediaForms'; +import EntityId from '@joystream/types/versioned-store/EntityId'; +import { MediaDropdownOptions } from '../common/MediaDropdownOptions'; +import { FormTabs } from '../common/FormTabs'; + +export type OuterProps = { + history?: History; + contentId: ContentId; + fileName?: string; + id?: EntityId; + entity?: MusicTrackType; + opts?: MediaDropdownOptions; +}; + +type FormValues = MusicTrackFormValues; + +const InnerForm = (props: MediaFormProps) => { + const { + // React components for form fields: + MediaText, + MediaDropdown, + LabelledField, + + // Callbacks: + onSubmit, + onTxSuccess, + onTxFailed, + + // history, + // contentId, + entity, + opts = MediaDropdownOptions.Empty, + + // Formik stuff: + values, + dirty, + errors, + isValid, + isSubmitting, + resetForm + } = props; + + const { thumbnail } = values; + + const isNew = !entity; + + const buildTxParams = () => { + if (!isValid) return []; + + return []; + }; + + const basicInfoTab = () => + + + + + + + + ; + + const additionalTab = () => + + + + + + + + + ; + + const tabs = ; + + const renderMainButton = () => + ; + + return
+
+ {thumbnail && } +
+ +
+ + {tabs} + + + {renderMainButton()} +
; +}; + +export const EditForm = withFormik({ + + // Transform outer props into form values + mapPropsToValues: (props): FormValues => { + const { entity, fileName } = props; + const res = MusicTrackToFormValues(entity); + if (!res.title && fileName) { + res.title = fileName; + } + return res; + }, + + validationSchema: () => MusicTrackValidationSchema, + + handleSubmit: () => { + // do submitting things + } +})(withMediaForm(InnerForm) as any); + +export default EditForm; diff --git a/pioneer/packages/joy-media/src/upload/UploadAudio.view.tsx b/pioneer/packages/joy-media/src/upload/UploadAudio.view.tsx new file mode 100644 index 0000000000..b83a3e4a76 --- /dev/null +++ b/pioneer/packages/joy-media/src/upload/UploadAudio.view.tsx @@ -0,0 +1,14 @@ +import { MediaView } from '../MediaView'; +import { OuterProps, EditForm } from './UploadAudio'; + +export const UploadAudioView = MediaView({ + component: EditForm, + resolveProps: async (props) => { + const { transport, id } = props; + const entity = id ? await transport.musicTrackById(id) : undefined; + const opts = await transport.dropdownOptions(); + return { entity, opts }; + } +}); + +export default UploadAudioView; diff --git a/pioneer/packages/joy-media/src/upload/UploadVideo.tsx b/pioneer/packages/joy-media/src/upload/UploadVideo.tsx new file mode 100644 index 0000000000..ad0bfceb58 --- /dev/null +++ b/pioneer/packages/joy-media/src/upload/UploadVideo.tsx @@ -0,0 +1,448 @@ +import React from 'react'; +import { Button, Tab } from 'semantic-ui-react'; +import { Form, withFormik } from 'formik'; +import { History } from 'history'; +import moment from 'moment'; + +import TxButton, { OnTxButtonClick } from '@polkadot/joy-utils/TxButton'; +import { ContentId } from '@joystream/types/media'; +import { onImageError } from '@polkadot/joy-utils/images'; +import { VideoValidationSchema, VideoType, VideoClass as Fields, VideoFormValues, VideoToFormValues, VideoCodec, VideoPropId } from '../schemas/video/Video'; +import { MediaFormProps, withMediaForm, datePlaceholder } from '../common/MediaForms'; +import EntityId from '@joystream/types/versioned-store/EntityId'; +import { MediaDropdownOptions } from '../common/MediaDropdownOptions'; +import { FormTabs } from '../common/FormTabs'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { Credential } from '@joystream/types/common'; +import { Class, VecClassPropertyValue } from '@joystream/types/versioned-store'; +import { TxCallback } from '@polkadot/react-components/Status/types'; +import { SubmittableResult } from '@polkadot/api'; +import { nonEmptyStr, filterSubstrateEventsAndExtractData } from '@polkadot/joy-utils/index'; +import { u16, u32, bool, Option, Vec } from '@polkadot/types'; +import { isInternalProp } from '@joystream/types/versioned-store/EntityCodec'; +import { MediaObjectCodec } from '../schemas/general/MediaObject'; +import { Operation } from '@joystream/types/versioned-store/permissions/batching'; +import { OperationType } from '@joystream/types/versioned-store/permissions/batching/operation-types'; +import { ParametrizedEntity } from '@joystream/types/versioned-store/permissions/batching/parametrized-entity'; +import ParametrizedClassPropertyValue from '@joystream/types/versioned-store/permissions/batching/ParametrizedClassPropertyValue'; +import { ParametrizedPropertyValue } from '@joystream/types/versioned-store/permissions/batching/parametrized-property-value'; +import { ParameterizedClassPropertyValues } from '@joystream/types/versioned-store/permissions/batching/operations'; +import { useMyMembership } from '@polkadot/joy-utils/MyMembershipContext'; +import { isAccountAChannelOwner } from '../channels/ChannelHelpers'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +/** Example: "2019-01-23" -> 1548201600 */ +function humanDateToUnixTs (humanFriendlyDate: string): number | undefined { + return nonEmptyStr(humanFriendlyDate) ? moment(humanFriendlyDate).unix() : undefined; +} + +function isDateField (field: VideoPropId): boolean { + return field === Fields.firstReleased.id; +} + +export type OuterProps = { + history?: History; + contentId?: ContentId; + fileName?: string; + channelId?: ChannelId; + channel?: ChannelEntity; + mediaObjectClass?: Class; + entityClass?: Class; + id?: EntityId; + entity?: VideoType; + opts?: MediaDropdownOptions; +}; + +type FormValues = VideoFormValues; + +const InnerForm = (props: MediaFormProps) => { + const { + // React components for form fields: + MediaText, + MediaDropdown, + LabelledField, + + // Callbacks: + onSubmit, + // onTxSuccess, + onTxFailed, + + history, + contentId, + mediaObjectClass, + entityClass, + id, + entity, + opts, + isFieldChanged, + + values, + dirty, + errors, + isValid, + isSubmitting, + setSubmitting, + resetForm + } = props; + + const { myAccountId } = useMyMembership(); + + const { thumbnail } = values; + + if (!mediaObjectClass) { + return ; + } + + if (!entityClass) { + return ; + } + + if (entity && !isAccountAChannelOwner(entity.channel, myAccountId)) { + return ; + } + + // Next consts are used in tx params: + const with_credential = new Option(Credential, new Credential(2)); + const as_entity_maintainer = new bool(false); + const schema_id = new u16(0); + + const entityCodec = new VideoCodec(entityClass); + const mediaObjectCodec = new MediaObjectCodec(mediaObjectClass); + + const getFieldsValues = (): Partial => { + const res: Partial = {}; + + Object.keys(values).forEach((prop) => { + const fieldName = prop as VideoPropId; + const field = Fields[fieldName]; + let fieldValue = values[fieldName] as any; + + let shouldIncludeValue = true; + if (entity) { + // If we updating existing entity, then update only changed props: + shouldIncludeValue = isFieldChanged(fieldName); + } else if (field.required !== true) { + // If we creating a new entity, then provide all required props + // plus non empty non required props: + if (isInternalProp(field)) { + shouldIncludeValue = fieldValue > 0; + } else if (typeof fieldValue === 'string') { + shouldIncludeValue = nonEmptyStr(fieldValue); + } else if (Array.isArray(fieldValue) && fieldValue.length === 0) { + shouldIncludeValue = false; + } + } + + // For debugging: + // const propForLog: any = { fieldName, fieldValue } + // if (shouldIncludeValue) { + // propForLog.shouldIncludeValue = shouldIncludeValue + // } + // console.log(propForLog) + + if (shouldIncludeValue) { + if (typeof fieldValue === 'string') { + fieldValue = fieldValue.trim(); + } + if (isDateField(fieldName)) { + fieldValue = humanDateToUnixTs(fieldValue); + } + res[fieldName] = fieldValue; + } + }); + + return res; + }; + + const indexOfCreateMediaObjectOperation = new u32(0); + + const indexOfCreateVideoEntityOperation = new u32(2); + + const referToIdOfCreatedMediaObjectEntity = () => + ParametrizedEntity.InternalEntityJustAdded(indexOfCreateMediaObjectOperation); + + const referToIdOfCreatedVideoEntity = () => + ParametrizedEntity.InternalEntityJustAdded(indexOfCreateVideoEntityOperation); + + const newlyCreatedMediaObjectProp = () => { + const inClassIndexOfMediaObject = entityCodec.inClassIndexOfProp(Fields.object.id); + if (!inClassIndexOfMediaObject) { + throw new Error('Cannot not find an in-class index of "object" prop on Video entity.'); + } + + return new ParametrizedClassPropertyValue({ + in_class_index: new u16(inClassIndexOfMediaObject), + value: ParametrizedPropertyValue.InternalEntityJustAdded( + indexOfCreateMediaObjectOperation + ) + }); + }; + + const toParametrizedPropValues = ( + props: VecClassPropertyValue, + extra: ParametrizedClassPropertyValue[] = [] + ): ParameterizedClassPropertyValues => { + const parametrizedProps = props.map(p => { + const { in_class_index, value } = p; + return new ParametrizedClassPropertyValue({ + in_class_index, + value: new ParametrizedPropertyValue({ PropertyValue: value }) + }); + }); + + if (extra && extra.length) { + extra.forEach(x => parametrizedProps.push(x)); + } + + return new ParameterizedClassPropertyValues(parametrizedProps); + }; + + const newEntityOperation = (operation_type: OperationType) => { + return new Operation({ + with_credential, + as_entity_maintainer, + operation_type + }); + }; + + const prepareTxParamsForCreateMediaObject = () => { + return newEntityOperation( + OperationType.CreateEntity( + mediaObjectClass.id + ) + ); + }; + + const prepareTxParamsForAddSchemaToMediaObject = () => { + const propValues = toParametrizedPropValues( + mediaObjectCodec.toSubstrateUpdate({ + value: contentId!.encode() + }) + ); + // console.log('prepareTxParamsForAddSchemaToMediaObject:', propValues) + + return newEntityOperation( + OperationType.AddSchemaSupportToEntity( + referToIdOfCreatedMediaObjectEntity(), + schema_id, + propValues + ) + ); + }; + + const prepareTxParamsForCreateEntity = () => { + return newEntityOperation( + OperationType.CreateEntity( + entityClass.id + ) + ); + }; + + const prepareTxParamsForAddSchemaToEntity = () => { + const propValues = toParametrizedPropValues( + entityCodec.toSubstrateUpdate(getFieldsValues()), + [newlyCreatedMediaObjectProp()] + ); + + // console.log('prepareTxParamsForAddSchemaToEntity:', propValues) + + return newEntityOperation( + OperationType.AddSchemaSupportToEntity( + referToIdOfCreatedVideoEntity(), + schema_id, + propValues + ) + ); + }; + + const canSubmitTx = () => dirty && isValid && !isSubmitting; + + const buildTransactionTxParams = () => { + // No need to prepare tx params until the form is valid: + if (!canSubmitTx()) return []; + + const ops = [ + prepareTxParamsForCreateMediaObject(), + prepareTxParamsForAddSchemaToMediaObject(), + prepareTxParamsForCreateEntity(), + prepareTxParamsForAddSchemaToEntity() + ]; + + // Use for debug: + // console.log('Batch entity operations:', ops) + + return [new Vec(Operation, ops)]; + }; + + const buildUpdateEntityTxParams = () => { + // No need to prepare tx params until the form is valid: + if (!canSubmitTx()) return []; + + const updatedPropValues = entityCodec.toSubstrateUpdate(getFieldsValues()); + // console.log('buildUpdateEntityTxParams:', updatedPropValues) + + return [ + with_credential, + as_entity_maintainer, + id, // Video Entity Id + updatedPropValues + ]; + }; + + const redirectToPlaybackPage = (newEntityId?: EntityId) => { + const entityId = newEntityId || id; + if (history && entityId) { + history.push('/media/videos/' + entityId.toString()); + } + }; + + const onCreateEntitySuccess: TxCallback = (txResult: SubmittableResult) => { + setSubmitting(false); + + // Get id of newly created video entity from the second 'EntityCreated' event, + // because the first 'EntityCreated' event corresponds to a Media Object Entity. + const events = filterSubstrateEventsAndExtractData(txResult, 'EntityCreated'); + + // Return if there were less than two events: + if (!events || events.length < 2) return; + + // Get the second 'EntityCreated' event: + const videoEntityCreatedEvent = events[1]; + + // Extract id from from event: + const newId = videoEntityCreatedEvent[0] as EntityId; + console.log('New video entity id:', newId && newId.toString()); + + redirectToPlaybackPage(newId); + }; + + const onUpdateEntitySuccess: TxCallback = (_txResult: SubmittableResult) => { + setSubmitting(false); + redirectToPlaybackPage(); + }; + + const basicInfoTab = () => + + + + + + + + + ; + + const additionalTab = () => + + + + ; + + const tabs = ; + + const newOnSubmit: OnTxButtonClick = (sendTx: () => void) => { + // TODO Switch to the first tab with errors if any + + if (onSubmit) { + onSubmit(sendTx); + } + }; + + const renderTransactionButton = () => + ; + + const renderUpdateEntityButton = () => + ; + + return
+
+ {thumbnail && } +
+ +
+ {tabs} + + {!entity + ? renderTransactionButton() + : renderUpdateEntityButton() + } +
; +}; + +export const EditForm = withFormik({ + + // Transform outer props into form values + mapPropsToValues: (props): FormValues => { + const { entity, channelId, fileName } = props; + const res = VideoToFormValues(entity); + if (!res.title && fileName) { + res.title = fileName; + } + if (channelId) { + res.channelId = channelId.toNumber(); + } + return res; + }, + + validationSchema: () => VideoValidationSchema, + + handleSubmit: () => { + // do submitting things + } +})(withMediaForm(InnerForm) as any); + +export default EditForm; diff --git a/pioneer/packages/joy-media/src/utils.ts b/pioneer/packages/joy-media/src/utils.ts new file mode 100644 index 0000000000..91d876568f --- /dev/null +++ b/pioneer/packages/joy-media/src/utils.ts @@ -0,0 +1,4 @@ +export function fileNameWoExt (fileName: string): string { + const lastDotIdx = fileName.lastIndexOf('.'); + return fileName.substring(0, lastDotIdx); +} diff --git a/pioneer/packages/joy-media/src/video/PlayVideo.tsx b/pioneer/packages/joy-media/src/video/PlayVideo.tsx new file mode 100644 index 0000000000..757b2b6dd7 --- /dev/null +++ b/pioneer/packages/joy-media/src/video/PlayVideo.tsx @@ -0,0 +1,129 @@ +import React from 'react'; +import ReactMarkdown from 'react-markdown'; +import { Table } from 'semantic-ui-react'; +import { ApiProps } from '@polkadot/react-api/types'; +import { ApiConsumer } from '@polkadot/react-api/ApiContext'; +import EntityId from '@joystream/types/versioned-store/EntityId'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelPreview } from '../channels/ChannelPreview'; +import { VideoPreview } from './VideoPreview'; +import { VideoType, VideoClass as Fields, VideoGenericProp } from '../schemas/video/Video'; +import { printExplicit, printReleaseDate, printLanguage } from '../entities/EntityHelpers'; +import { MediaObjectType } from '../schemas/general/MediaObject'; +import { MediaPlayerWithResolver } from '../common/MediaPlayerWithResolver'; +import { ContentId } from '@joystream/types/media'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +export type PlayVideoProps = { + channel?: ChannelEntity; + mediaObject?: MediaObjectType; + id: EntityId; + video?: VideoType; + moreChannelVideos?: VideoType[]; + featuredVideos?: VideoType[]; +} + +type ListOfVideoPreviewProps = { + videos?: VideoType[]; +} + +function VertialListOfVideoPreviews (props: ListOfVideoPreviewProps) { + const { videos = [] } = props; + return <>{videos.map((video) => + + )}; +} + +export function PlayVideo (props: PlayVideoProps) { + const { channel, mediaObject, video, moreChannelVideos = [], featuredVideos = [] } = props; + + if (!mediaObject || !video) { + return ; + } + + if (!channel) { + return ; + } + + const metaField = (field: VideoGenericProp, value: React.ReactNode | string) => ( + typeof video[field.id] !== 'undefined' && + + {field.name} + {value} + + ); + + const printLinks = (links?: string[]) => { + return (links || []).map((x, i) => +
+ {x} +
+ ); + }; + + const metaTable = <> +

Video details

+ + + {metaField(Fields.explicit, printExplicit(video.explicit))} + {metaField(Fields.firstReleased, printReleaseDate(video.firstReleased))} + {metaField(Fields.language, printLanguage(video.language))} + {metaField(Fields.category, video.category?.value)} + {metaField(Fields.license, video.license?.value)} + {metaField(Fields.attribution, video.attribution)} + {metaField(Fields.links, printLinks(video.links))} + {metaField(Fields.curationStatus, video.curationStatus?.value)} + +
+ ; + + // TODO show video only to its owner, if the video is not public. + // see isPublicVideo() function. + + const contentId = ContentId.decode(mediaObject.value); + + // console.log('PlayVideo: props', props) + + return
+
+
+
+ + + {(apiProps?: ApiProps): React.ReactNode => + + } + + + + + {video.description && + + } +
+
+ {metaTable} +
+
+
+ +
+ {featuredVideos.length > 0 && +
+

Featured videos

+ +
+ } + {moreChannelVideos.length > 0 && +
+

More from this channel

+ +
+ } +
+
; +} diff --git a/pioneer/packages/joy-media/src/video/PlayVideo.view.tsx b/pioneer/packages/joy-media/src/video/PlayVideo.view.tsx new file mode 100644 index 0000000000..501abcd8ae --- /dev/null +++ b/pioneer/packages/joy-media/src/video/PlayVideo.view.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { RouteComponentProps } from 'react-router'; +import { MediaView } from '../MediaView'; +import { PlayVideoProps, PlayVideo } from './PlayVideo'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { EntityId } from '@joystream/types/versioned-store'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +type Props = PlayVideoProps; + +export const PlayVideoView = MediaView({ + component: PlayVideo, + triggers: ['id'], + resolveProps: async (props) => { + const { transport, id } = props; + + const video = await transport.videoById(id); + if (!video) return {}; + + const channelId = new ChannelId(video.channelId); + const channel = await transport.channelById(channelId); + const moreChannelVideos = (await transport.videosByChannelId(channelId, 5, x => x.id !== video.id)); + const featuredVideos = await transport.featuredVideos(); + const mediaObject = video.object; + + return { channel, mediaObject, video, moreChannelVideos, featuredVideos }; + } +}); + +export const PlayVideoWithRouter = (props: Props & RouteComponentProps) => { + const { match: { params: { id } } } = props; + + if (id) { + try { + return ; + } catch (err) { + console.log('PlayVideoWithRouter failed:', err); + } + } + + return {id}; +}; diff --git a/pioneer/packages/joy-media/src/video/VideoPreview.tsx b/pioneer/packages/joy-media/src/video/VideoPreview.tsx new file mode 100644 index 0000000000..f07a559696 --- /dev/null +++ b/pioneer/packages/joy-media/src/video/VideoPreview.tsx @@ -0,0 +1,85 @@ +import React, { CSSProperties } from 'react'; +import { Link } from 'react-router-dom'; +import { BgImg } from '../common/BgImg'; +import { VideoType } from '../schemas/video/Video'; +import { useMyMembership } from '@polkadot/joy-utils/MyMembershipContext'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { isAccountAChannelOwner } from '../channels/ChannelHelpers'; +import { ChannelAvatarAndName } from '../channels/ChannelAvatarAndName'; + +export type VideoPreviewProps = { + id: number; + title: string; + thumbnail: string; + + channel?: ChannelEntity; + withChannel?: boolean; + + // Preview-specific props: + size?: 'normal' | 'small'; + orientation?: 'vertical' | 'horizontal'; +}; + +export function VideoPreview (props: VideoPreviewProps) { + const { myAccountId } = useMyMembership(); + const { id, channel, withChannel = false, title, size = 'normal', orientation = 'vertical' } = props; + + let width = 210; + let height = 118; + + if (size === 'small') { + width = 168; + height = 94; + } + + const descStyle: CSSProperties = { + maxWidth: orientation === 'vertical' + ? width + : width * 1.5 + }; + + const playbackUrl = `/media/videos/${id}`; + const iAmOwner = isAccountAChannelOwner(channel, myAccountId); + + return ( +
+ + + + + +
+ + +

{title}

+ + + {withChannel && channel && + + } + + {iAmOwner && +
+ + + Edit + +
+ } +
+
+ ); +} + +export function toVideoPreviews (items: VideoType[]): VideoPreviewProps[] { + return items.map(x => ({ + id: x.id, + title: x.title, + thumbnail: x.thumbnail + })); +} diff --git a/pioneer/packages/joy-members/README.md b/pioneer/packages/joy-members/README.md new file mode 100644 index 0000000000..0317053ead --- /dev/null +++ b/pioneer/packages/joy-members/README.md @@ -0,0 +1 @@ +# Membership module for Joystream node diff --git a/pioneer/packages/joy-members/package.json b/pioneer/packages/joy-members/package.json new file mode 100644 index 0000000000..1cb161c08c --- /dev/null +++ b/pioneer/packages/joy-members/package.json @@ -0,0 +1,15 @@ +{ + "name": "@polkadot/joy-members", + "version": "0.1.1", + "description": "Membership module for Joystream node", + "main": "index.js", + "scripts": {}, + "author": "Joystream contributors", + "maintainers": [], + "dependencies": { + "@babel/runtime": "^7.10.5", + "@polkadot/react-components": "0.51.1", + "@polkadot/react-query": "0.51.1", + "@polkadot/joy-utils": "^0.1.1" + } +} diff --git a/pioneer/packages/joy-members/src/Dashboard.tsx b/pioneer/packages/joy-members/src/Dashboard.tsx new file mode 100644 index 0000000000..b1d965b23f --- /dev/null +++ b/pioneer/packages/joy-members/src/Dashboard.tsx @@ -0,0 +1,102 @@ +import React from 'react'; +import BN from 'bn.js'; + +import { ApiProps } from '@polkadot/react-api/types'; +import { I18nProps } from '@polkadot/react-components/types'; +import { withCalls } from '@polkadot/react-api/hoc'; +import { Label } from 'semantic-ui-react'; +import { formatNumber } from '@polkadot/util'; +import { bool as Bool } from '@polkadot/types'; + +import { Section } from '@polkadot/joy-utils/react/components'; +import translate from './translate'; +import { queryMembershipToProp } from './utils'; + +import { FIRST_MEMBER_ID } from './constants'; + +type Props = ApiProps & I18nProps & { + newMembershipsAllowed?: Bool; + nextMemberId?: BN; + minHandleLength?: BN; + maxHandleLength?: BN; + maxAvatarUriLength?: BN; + maxAboutTextLength?: BN; +}; + +class Dashboard extends React.PureComponent { + renderGeneral () { + const p = this.props; + const { newMembershipsAllowed: isAllowed } = p; + let isAllowedColor: 'grey' | 'green' | 'red' = 'grey'; + + if (isAllowed) { + isAllowedColor = isAllowed.eq(true) ? 'green' : 'red'; + } + + return ( +
+ + + + + +
+ ); + } + + renderValidation () { + const p = this.props; + + return ( +
+ + + + + + +
+ ); + } + + render () { + return ( +
+ {this.renderGeneral()} + {this.renderValidation()} +
+ ); + } +} + +export default translate( + withCalls( + queryMembershipToProp('newMembershipsAllowed'), + queryMembershipToProp('nextMemberId'), + queryMembershipToProp('minHandleLength'), + queryMembershipToProp('maxHandleLength'), + queryMembershipToProp('maxAvatarUriLength'), + queryMembershipToProp('maxAboutTextLength') + )(Dashboard) +); diff --git a/pioneer/packages/joy-members/src/Details.tsx b/pioneer/packages/joy-members/src/Details.tsx new file mode 100644 index 0000000000..fa2375c690 --- /dev/null +++ b/pioneer/packages/joy-members/src/Details.tsx @@ -0,0 +1,182 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Table, Loader } from 'semantic-ui-react'; +import ReactMarkdown from 'react-markdown'; +import { IdentityIcon } from '@polkadot/react-components'; +import { ApiProps } from '@polkadot/react-api/types'; +import { I18nProps } from '@polkadot/react-components/types'; +import { withCalls } from '@polkadot/react-api/hoc'; +import { Option } from '@polkadot/types'; +import BalanceDisplay from '@polkadot/react-components/Balance'; +import AddressMini from '@polkadot/react-components/AddressMini'; +import { formatNumber } from '@polkadot/util'; + +import translate from './translate'; +import { MemberId, Membership, EntryMethod, Paid, Screening, Genesis, SubscriptionId } from '@joystream/types/members'; +import { queryMembershipToProp } from './utils'; +import { Seat } from '@joystream/types/council'; +import { nonEmptyStr, queryToProp } from '@polkadot/joy-utils/functions/misc'; +import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/react/hocs/accounts'; + +type Props = ApiProps & I18nProps & MyAccountProps & { + preview?: boolean; + memberId: MemberId; + membership?: Membership; + activeCouncil?: Seat[]; +}; + +class Component extends React.PureComponent { + render () { + const { membership } = this.props; + + return membership && !membership.handle.isEmpty + ? this.renderProfile(membership) + : ( +
+ +
+ ); + } + + private renderProfile (membership: Membership) { + const { + preview = false, + myAddress, + activeCouncil = [] + } = this.props; + + const { + handle, + avatar_uri, + root_account, + controller_account + } = membership; + + const hasAvatar = avatar_uri && nonEmptyStr(avatar_uri.toString()); + const isMyProfile = myAddress && (myAddress === root_account.toString() || myAddress === controller_account.toString()); + const isCouncilor: boolean = ( + (activeCouncil.find((x) => root_account.eq(x.member)) !== undefined) || + (activeCouncil.find((x) => controller_account.eq(x.member)) !== undefined) + ); + + return ( + <> +
+ {hasAvatar + ? + : + } +
+
+ {handle.toString()} + {isMyProfile && Edit my profile} +
+
+ {isCouncilor && + + + Council member + } + +
MemberId: {this.props.memberId.toString()}
+
+
+
+ {!preview && this.renderDetails(membership, isCouncilor)} + + ); + } + + private renderDetails (membership: Membership, isCouncilor: boolean) { + const { + about, + registered_at_block, + registered_at_time, + entry, + suspended, + subscription, + root_account, + controller_account + + } = membership; + + const { memberId } = this.props; + + return ( + + + + Membership ID + {memberId.toNumber()} + + + Root account + + + + Controller account + + + + Registered on + {new Date(registered_at_time.toNumber()).toLocaleString()} at block #{formatNumber(registered_at_block)} + + + Suspended? + {suspended.eq(true) ? 'Yes' : 'No'} + + + Council member? + {isCouncilor ? 'Yes' : 'No'} + + + Entry method + {this.renderEntryMethod(entry)} + + + Subscription ID + {this.renderSubscription(subscription)} + + + About + + + +
+ ); + } + + private renderEntryMethod (entry: EntryMethod) { + const etype = entry.type; + + if (etype === Paid.name) { + const paid = entry.value as Paid; + + return
Paid, terms ID: {paid.toNumber()}
; + } else if (etype === Screening.name) { + const accountId = entry.value as Screening; + + return
Screened by
; + } else if (etype === Genesis.name) { + return
Created at Genesis
; + } else { + return Unknown; + } + } + + private renderSubscription (subscription: Option) { + return subscription.isNone + ? No subscription yet. + : subscription.value.toString(); + } +} + +export default translate(withMyAccount( + withCalls( + queryToProp('query.council.activeCouncil'), + queryMembershipToProp( + 'membershipById', + { paramName: 'memberId', propName: 'membership' } + ) + )(Component) +)); diff --git a/pioneer/packages/joy-members/src/DetailsByHandle.tsx b/pioneer/packages/joy-members/src/DetailsByHandle.tsx new file mode 100644 index 0000000000..ded39c31eb --- /dev/null +++ b/pioneer/packages/joy-members/src/DetailsByHandle.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +import { I18nProps } from '@polkadot/react-components/types'; +import { withCalls } from '@polkadot/react-api/hoc'; +import { stringToU8a, u8aToHex } from '@polkadot/util'; + +import translate from './translate'; +import Details from './Details'; +import { MemberId } from '@joystream/types/members'; +import { queryMembershipToProp } from './utils'; + +type DetailsByHandleProps = { + handle: string; + memberIdByHandle?: MemberId; +}; + +function DetailsByHandleInner (p: DetailsByHandleProps) { + const { memberIdByHandle: memberId } = p; + + return memberId !== undefined // here we can't make distinction value existing and loading + ?
+
+
+ : Member profile not found.; +} + +const DetailsByHandle = withCalls( + queryMembershipToProp('memberIdByHandle', 'handle') +)(DetailsByHandleInner); + +type Props = I18nProps & { + match: { + params: { + handle: string; + }; + }; +}; + +class Component extends React.PureComponent { + render () { + const { match: { params: { handle } } } = this.props; + const handleHex = u8aToHex(stringToU8a(handle)); + + return ( + + ); + } +} + +export default translate(Component); diff --git a/pioneer/packages/joy-members/src/EditForm.tsx b/pioneer/packages/joy-members/src/EditForm.tsx new file mode 100644 index 0000000000..9d0c229173 --- /dev/null +++ b/pioneer/packages/joy-members/src/EditForm.tsx @@ -0,0 +1,312 @@ +import BN from 'bn.js'; +import React, { useContext } from 'react'; +import { Link } from 'react-router-dom'; +import { Form, Field, withFormik, FormikProps } from 'formik'; +import * as Yup from 'yup'; + +import { Vec } from '@polkadot/types'; +import { Section, TxButton } from '@polkadot/joy-utils/react/components'; +import * as JoyForms from '@polkadot/joy-utils/react/components/forms'; +import { SubmittableResult } from '@polkadot/api'; +import { MemberId, Membership, PaidTermId, PaidMembershipTerms } from '@joystream/types/members'; +import { OptionText } from '@joystream/types/common'; +import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/react/hocs/accounts'; +import { queryMembershipToProp } from './utils'; +import { withCalls, ApiContext } from '@polkadot/react-api/index'; +import { Button, Message } from 'semantic-ui-react'; +import { formatBalance } from '@polkadot/util'; +import { TxFailedCallback, TxCallback } from '@polkadot/react-components/Status/types'; +import isEqual from 'lodash/isEqual'; + +// TODO get next settings from Substrate: +const HANDLE_REGEX = /^[a-z0-9_]+$/; + +const buildSchema = (p: ValidationProps) => + Yup.object().shape({ + handle: Yup.string() + .matches(HANDLE_REGEX, 'Handle can have only lowercase letters (a-z), numbers (0-9) and underscores (_).') + .min(p.minHandleLength, `Handle is too short. Minimum length is ${p.minHandleLength} chars.`) + .max(p.maxHandleLength, `Handle is too long. Maximum length is ${p.maxHandleLength} chars.`) + .required('Handle is required'), + avatar: Yup.string() + .url('Avatar must be a valid URL of an image.') + .max(p.maxAvatarUriLength, `Avatar URL is too long. Maximum length is ${p.maxAvatarUriLength} chars.`), + about: Yup.string().max(p.maxAboutTextLength, `Text is too long. Maximum length is ${p.maxAboutTextLength} chars.`) + }); + +type ValidationProps = { + minHandleLength: number; + maxHandleLength: number; + maxAvatarUriLength: number; + maxAboutTextLength: number; +}; + +type OuterProps = ValidationProps & { + profile?: Membership; + paidTerms: PaidMembershipTerms; + paidTermId: PaidTermId; + memberId?: MemberId; +}; + +type FormValues = { + handle: string; + avatar: string; + about: string; +}; + +type FieldName = keyof FormValues; + +type FormProps = OuterProps & FormikProps; + +const LabelledField = JoyForms.LabelledField(); + +const LabelledText = JoyForms.LabelledText(); + +const InnerForm = (props: FormProps) => { + const { + profile, + paidTerms, + paidTermId, + initialValues, + values, + touched, + dirty, + isValid, + isSubmitting, + setSubmitting, + resetForm, + memberId + } = props; + + const { api } = useContext(ApiContext); + + const onSubmit = (sendTx: () => void) => { + if (isValid) sendTx(); + }; + + const onTxFailed: TxFailedCallback = (txResult: SubmittableResult | null) => { + setSubmitting(false); + + if (txResult == null) { + // Tx cancelled. + + } + }; + + const onTxSuccess: TxCallback = (_txResult: SubmittableResult) => { + setSubmitting(false); + }; + + // TODO extract to forms.tsx + const isFieldChanged = (field: FieldName): boolean => { + return dirty && touched[field] === true && !isEqual(values[field], initialValues[field]); + }; + + // TODO extract to forms.tsx + const fieldToTextOption = (field: FieldName): OptionText => { + return isFieldChanged(field) + ? api.createType('Option', values[field]) + : api.createType('Option', null); + }; + + const buildTxParams = () => { + if (!isValid) return []; + + const userInfo = [ + fieldToTextOption('handle'), + fieldToTextOption('avatar'), + fieldToTextOption('about') + ]; + + if (profile) { + // update profile + return [memberId, ...userInfo]; + } else { + // register as new member + return [paidTermId, ...userInfo]; + } + }; + + // TODO show warning that you don't have enough balance to buy a membership + + return ( +
+
+ + + + + + {!profile && paidTerms && ( + +

+ Membership costs {formatBalance(paidTerms.fee)} tokens. +

+

+ {'By clicking the "Register" button you agree to our '} + Terms of Service + and + Privacy Policy. +

+
+ )} + +
+ +
+
+ +
+ ); +}; + +const EditForm = withFormik({ + // Transform outer props into form values + mapPropsToValues: (props) => { + const { profile: p } = props; + + return { + handle: p ? p.handle.toString() : '', + avatar: p ? p.avatar_uri.toString() : '', + about: p ? p.about.toString() : '' + }; + }, + + validationSchema: buildSchema, + + handleSubmit: (values) => { + // do submitting things + } +})(InnerForm); + +type WithMembershipDataProps = { + memberId?: MemberId; + membership?: Membership; + paidTermsId: PaidTermId; + paidTerms?: PaidMembershipTerms; + minHandleLength?: BN; + maxHandleLength?: BN; + maxAvatarUriLength?: BN; + maxAboutTextLength?: BN; +}; + +function WithMembershipDataInner (p: WithMembershipDataProps) { + const triedToFindProfile = !p.memberId || p.membership; + + if ( + triedToFindProfile && + p.paidTerms && + p.minHandleLength && + p.maxHandleLength && + p.maxAvatarUriLength && + p.maxAboutTextLength + ) { + const membership = (p.memberId && p.membership && !p.membership.handle.isEmpty) + ? p.membership + : undefined; + + if (!membership && p.paidTerms.isEmpty) { + console.error('Could not find active paid membership terms'); + } + + return ( + + ); + } else return Loading...; +} + +const WithMembershipData = withCalls( + queryMembershipToProp('minHandleLength'), + queryMembershipToProp('maxHandleLength'), + queryMembershipToProp('maxAvatarUriLength'), + queryMembershipToProp('maxAboutTextLength'), + queryMembershipToProp('membershipById', { paramName: 'memberId', propName: 'membership' }), + queryMembershipToProp('paidMembershipTermsById', { paramName: 'paidTermsId', propName: 'paidTerms' }) +)(WithMembershipDataInner); + +type WithMembershipDataWrapperProps = MyAccountProps & { + memberIdsByRootAccountId?: Vec; + memberIdsByControllerAccountId?: Vec; + paidTermsIds?: Vec; +}; + +function WithMembershipDataWrapperInner (p: WithMembershipDataWrapperProps) { + if (p.allAccounts && !Object.keys(p.allAccounts).length) { + return ( + + Please create a key to get started. +
+ + Create key + +
+
+ ); + } + + if (p.memberIdsByRootAccountId && p.memberIdsByControllerAccountId && p.paidTermsIds) { + if (p.paidTermsIds.length) { + const [memberId] = p.memberIdsByRootAccountId.toArray().concat(p.memberIdsByControllerAccountId.toArray()); + + return ; + } else { + console.error('Active paid membership terms is empty'); + } + } + + return Loading...; +} + +const WithMembershipDataWrapper = withMyAccount( + withCalls( + queryMembershipToProp('memberIdsByRootAccountId', 'myAddress'), + queryMembershipToProp('memberIdsByControllerAccountId', 'myAddress'), + queryMembershipToProp('activePaidMembershipTerms', { propName: 'paidTermsIds' }) + )(WithMembershipDataWrapperInner) +); + +export default WithMembershipDataWrapper; diff --git a/pioneer/packages/joy-members/src/List.tsx b/pioneer/packages/joy-members/src/List.tsx new file mode 100644 index 0000000000..e82d92ef46 --- /dev/null +++ b/pioneer/packages/joy-members/src/List.tsx @@ -0,0 +1,103 @@ +import BN from 'bn.js'; +import React from 'react'; + +import { ApiProps } from '@polkadot/react-api/types'; +import { I18nProps } from '@polkadot/react-components/types'; + +import { Section } from '@polkadot/joy-utils/react/components'; +import translate from './translate'; +import Details from './Details'; +import { MemberId } from '@joystream/types/members'; +import { RouteComponentProps, Redirect } from 'react-router-dom'; +import { Pagination, Icon, PaginationProps } from 'semantic-ui-react'; +import styled from 'styled-components'; +import { withApi } from '@polkadot/react-api'; + +const StyledPagination = styled(Pagination)` + border-bottom: 1px solid #ddd !important; +`; + +type Props = ApiProps & I18nProps & RouteComponentProps & { + firstMemberId: BN; + membersCreated: BN; + match: { params: { page?: string } }; +}; + +type State = Record; + +const MEMBERS_PER_PAGE = 20; + +class Component extends React.PureComponent { + state: State = {}; + + onPageChange = (e: React.MouseEvent, data: PaginationProps) => { + const { history } = this.props; + + history.push(`/members/list/${data.activePage || 1}`); + } + + renderPagination (currentPage: number, pagesCount: number) { + return ( + , icon: true }} + firstItem={{ content: , icon: true }} + lastItem={{ content: , icon: true }} + prevItem={{ content: , icon: true }} + nextItem={{ content: , icon: true }} + totalPages={ pagesCount } + onPageChange={ this.onPageChange } + /> + ); + } + + render () { + const { + firstMemberId, + membersCreated, + match: { params: { page } }, + api + } = this.props; + + const membersCount = membersCreated.toNumber(); + const pagesCount = Math.ceil(membersCount / MEMBERS_PER_PAGE) || 1; + const currentPage = Math.min(parseInt(page || '1'), pagesCount); + + if (currentPage.toString() !== page) { + return ; + } + + const ids: MemberId[] = []; + + if (membersCount > 0) { + const firstId = firstMemberId.toNumber() + (currentPage - 1) * MEMBERS_PER_PAGE; + const lastId = Math.min(firstId + MEMBERS_PER_PAGE, membersCount) - 1; + + for (let i = firstId; i <= lastId; i++) { + ids.push(api.createType('MemberId', i)); + } + } + + return ( +
1 && this.renderPagination(currentPage, pagesCount)) || undefined }> + { + membersCount === 0 + ? No registered members yet. + : ( +
+ {ids.map((id, i) => +
+ )} +
+ ) + } +
+ ); + } +} + +export default translate(withApi(Component)); diff --git a/pioneer/packages/joy-members/src/MemberPreview.tsx b/pioneer/packages/joy-members/src/MemberPreview.tsx new file mode 100644 index 0000000000..7af4e59633 --- /dev/null +++ b/pioneer/packages/joy-members/src/MemberPreview.tsx @@ -0,0 +1,118 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +import { ApiProps } from '@polkadot/react-api/types'; +import { I18nProps } from '@polkadot/react-components/types'; +import { withCalls, withMulti } from '@polkadot/react-api/hoc'; +import { Vec } from '@polkadot/types'; +import { AccountId } from '@polkadot/types/interfaces'; +import IdentityIcon from '@polkadot/react-components/IdentityIcon'; + +import translate from './translate'; +import { MemberId, Membership } from '@joystream/types/members'; +import { queryMembershipToProp } from './utils'; +import { Seat } from '@joystream/types/council'; +import { nonEmptyStr, queryToProp } from '@polkadot/joy-utils/functions/misc'; +import { FlexCenter, MutedSpan } from '@polkadot/joy-utils/react/components'; + +const AvatarSizePx = 36; +const InlineAvatarSizePx = 24; + +type MemberPreviewProps = ApiProps & I18nProps & { + accountId: AccountId; + memberId?: MemberId; + membership?: Membership; + activeCouncil?: Seat[]; + prefixLabel?: string; + inline?: boolean; + className?: string; + style?: React.CSSProperties; +}; + +class InnerMemberPreview extends React.PureComponent { + render () { + const { membership } = this.props; + + return membership && !membership.handle.isEmpty + ? this.renderProfile(membership) + : null; + } + + private renderProfile (membership: Membership) { + const { activeCouncil = [], accountId, prefixLabel, inline, className, style } = this.props; + const { handle, avatar_uri } = membership; + + const hasAvatar = avatar_uri && nonEmptyStr(avatar_uri.toString()); + const isCouncilor: boolean = accountId !== undefined && activeCouncil.find((x) => accountId.eq(x.member)) !== undefined; + + const avatarSize = inline ? InlineAvatarSizePx : AvatarSizePx; + + return
+ + {prefixLabel && + {prefixLabel} + } + {hasAvatar ? ( + + ) : ( + + ) + } +
+
+ {handle.toString()} +
+ {!inline && ( +
+ {isCouncilor && + + + Council member + } +
+ )} +
+
+
; + } +} + +type WithMemberIdByAccountIdProps = { + memberIdsByRootAccountId?: Vec; + memberIdsByControllerAccountId?: Vec; +}; + +const withMemberIdByAccountId = withCalls( + queryMembershipToProp('memberIdsByRootAccountId', 'accountId'), + queryMembershipToProp('memberIdsByControllerAccountId', 'accountId') +); + +// Get first matching memberid controlled by an account +function setMemberIdByAccountId (Component: React.ComponentType) { + return function (props: WithMemberIdByAccountIdProps & MemberPreviewProps) { + const { memberIdsByRootAccountId, memberIdsByControllerAccountId } = props; + + if (memberIdsByRootAccountId && memberIdsByControllerAccountId) { + memberIdsByRootAccountId.concat(memberIdsByControllerAccountId); + + if (memberIdsByRootAccountId.length) { + return ; + } else { + return Member not found; + } + } else { + return null; + } + }; +} + +export const MemberPreview = withMulti( + InnerMemberPreview, + translate, + withMemberIdByAccountId, + setMemberIdByAccountId, + withCalls( + queryToProp('query.council.activeCouncil'), // TODO Refactor: extract ActiveCouncilContext + queryMembershipToProp('membershipById', { paramName: 'memberId', propName: 'membership' }) + ) +); diff --git a/pioneer/packages/joy-members/src/constants.ts b/pioneer/packages/joy-members/src/constants.ts new file mode 100644 index 0000000000..8bc49ee03b --- /dev/null +++ b/pioneer/packages/joy-members/src/constants.ts @@ -0,0 +1,3 @@ +import BN from 'bn.js'; + +export const FIRST_MEMBER_ID = new BN(0); diff --git a/pioneer/packages/joy-members/src/index.tsx b/pioneer/packages/joy-members/src/index.tsx new file mode 100644 index 0000000000..15613c0f8e --- /dev/null +++ b/pioneer/packages/joy-members/src/index.tsx @@ -0,0 +1,89 @@ + +import BN from 'bn.js'; +import React from 'react'; +import { Route, Switch } from 'react-router'; + +import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withCalls, withMulti } from '@polkadot/react-api/hoc'; +import Tabs from '@polkadot/react-components/Tabs'; +import { TabItem } from '@polkadot/react-components/Tabs/types'; + +import { queryMembershipToProp } from './utils'; +import translate from './translate'; +import Dashboard from './Dashboard'; +import List from './List'; +import DetailsByHandle from './DetailsByHandle'; +import EditForm from './EditForm'; +import { withMyAccount, MyAccountProps } from '@polkadot/joy-utils/react/hocs/accounts'; +import { FIRST_MEMBER_ID } from './constants'; +import { RouteComponentProps } from 'react-router-dom'; + +import styled from 'styled-components'; +import style from './style'; + +const MembersMain = styled.main`${style}`; + +// define out internal types +type Props = AppProps & ApiProps & I18nProps & MyAccountProps & { + nextMemberId?: BN; +}; + +class App extends React.PureComponent { + private buildTabs (): TabItem[] { + const { t, nextMemberId: memberCount, iAmMember } = this.props; + + return [ + { + name: 'list', + text: t('All members') + ` (${memberCount?.toString() || '-'})`, + forceMatchParams: true + }, + { + name: 'edit', + text: iAmMember ? 'My profile' : t('Register') + }, + { + name: 'dashboard', + text: t('Dashboard') + } + ]; + } + + private renderList (routeProps: RouteComponentProps) { + const { nextMemberId, ...otherProps } = this.props; + + return nextMemberId + ? + : Loading...; + } + + render () { + const { basePath } = this.props; + const tabs = this.buildTabs(); + + return ( + +
+ +
+ + + + this.renderList(props) } /> + + this.renderList(props) } /> + +
+ ); + } +} + +export default withMulti( + App, + translate, + withMyAccount, + withCalls( + queryMembershipToProp('nextMemberId') + ) +); diff --git a/pioneer/packages/joy-members/src/style.ts b/pioneer/packages/joy-members/src/style.ts new file mode 100644 index 0000000000..bf36ffbc48 --- /dev/null +++ b/pioneer/packages/joy-members/src/style.ts @@ -0,0 +1,62 @@ +import { css } from 'styled-components'; + +export default css` + .ProfilePreviews, + .FullProfile { + .item { + .image { + padding: 0 !important; + } + .description { + font-size: 1rem; + } + } + } + .ProfilePreviews { + &.ui.list>.item:first-child { + padding-top: .75rem; + } + &.ui.list>.item:last-child { + padding-bottom: .75rem; + } + .MyProfile { + background-color: #FFF8E1; + } + } + .ProfileDetails { + padding-left: 1rem !important; + .handle { + margin-right: 1rem; + .button { + padding: .5rem .75rem; + } + } + } + .ProfileDetailsTable { + font-size: 1rem !important; + tr td:first-child { + width: 1%; + white-space: nowrap; + } + } + + .JoyMemberPreview { + margin-right: .5rem; + .PrefixLabel { + margin-right: .5rem; + } + .Avatar { + margin-right: .5rem; + border-radius: 100%; + } + .Content { + .Username { + font-weight: bold; + } + .Details { + font-weight: 100; + opacity: .75; + } + } + } +`; diff --git a/pioneer/packages/joy-members/src/translate.ts b/pioneer/packages/joy-members/src/translate.ts new file mode 100644 index 0000000000..b684c8511e --- /dev/null +++ b/pioneer/packages/joy-members/src/translate.ts @@ -0,0 +1,3 @@ +import { withTranslation } from 'react-i18next'; + +export default withTranslation(['members', 'ui']); diff --git a/pioneer/packages/joy-members/src/utils.ts b/pioneer/packages/joy-members/src/utils.ts new file mode 100644 index 0000000000..95d209d09d --- /dev/null +++ b/pioneer/packages/joy-members/src/utils.ts @@ -0,0 +1,7 @@ +// TODO: Move to joy-utils? +import { queryToProp } from '@polkadot/joy-utils/functions/misc'; +import { Options as QueryOptions } from '@polkadot/react-api/hoc/types'; + +export const queryMembershipToProp = (storageItem: string, paramNameOrOpts?: string | QueryOptions) => { + return queryToProp(`query.members.${storageItem}`, paramNameOrOpts); +}; diff --git a/pioneer/packages/joy-pages/README.md b/pioneer/packages/joy-pages/README.md new file mode 100644 index 0000000000..ce2cd5446a --- /dev/null +++ b/pioneer/packages/joy-pages/README.md @@ -0,0 +1,3 @@ +# Joystream Pages Module + +Static pages rendered from markdown content. diff --git a/pioneer/packages/joy-pages/package.json b/pioneer/packages/joy-pages/package.json new file mode 100644 index 0000000000..bbd6934caf --- /dev/null +++ b/pioneer/packages/joy-pages/package.json @@ -0,0 +1,15 @@ +{ + "name": "@polkadot/joy-pages", + "version": "0.1.1", + "description": "Joystream Pages module", + "main": "index.js", + "scripts": {}, + "author": "Joystream contributors", + "maintainers": [], + "dependencies": { + "@babel/runtime": "^7.10.5", + "@polkadot/react-components": "0.51.1", + "@polkadot/react-query": "0.51.1", + "@polkadot/joy-utils": "^0.1.1" + } +} diff --git a/pioneer/packages/joy-pages/src/Page.tsx b/pioneer/packages/joy-pages/src/Page.tsx new file mode 100644 index 0000000000..3140f476bc --- /dev/null +++ b/pioneer/packages/joy-pages/src/Page.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import ReactMarkdown from 'react-markdown'; +import './index.css'; + +type Props = { + md: string; +}; + +export default class Page extends React.PureComponent { + render () { + return ( +
+ +
+ ); + } +} diff --git a/pioneer/packages/joy-pages/src/index.css b/pioneer/packages/joy-pages/src/index.css new file mode 100644 index 0000000000..b0fa7e8952 --- /dev/null +++ b/pioneer/packages/joy-pages/src/index.css @@ -0,0 +1,4 @@ +.JoyPage { + margin: 2rem 0; + max-width: 900px; +} diff --git a/pioneer/packages/joy-pages/src/index.tsx b/pioneer/packages/joy-pages/src/index.tsx new file mode 100644 index 0000000000..189dc921f3 --- /dev/null +++ b/pioneer/packages/joy-pages/src/index.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import Page from './Page'; + +import ToS_md from './md/ToS.md'; + +import Privacy_md from './md/Privacy.md'; + +export function ToS () { + return ; +} + +export function Privacy () { + return ; +} diff --git a/pioneer/packages/joy-pages/src/md/Privacy.md b/pioneer/packages/joy-pages/src/md/Privacy.md new file mode 100644 index 0000000000..9b262c970a --- /dev/null +++ b/pioneer/packages/joy-pages/src/md/Privacy.md @@ -0,0 +1,79 @@ +# Privacy and Cookies +**Last updated on the 17th of April 2019** + +Jsgenesis values your privacy. + +This Privacy Policy ("Privacy Policy") and Cookie Policy ("Cookie Policy") explains how Jsgenesis AS ("Jsgenesis", "Company", "We", "Us", "Our") collect and use data and information when you ("User) use on or any of the Joystream products, developed in the GitHub organization [Joystream](https://github.com/JoyStream). These products (collectively "Software") include, but are not limited to, [all pages under the joystream.org domain](https://www.joystream.org/) ("Website"), the [Joyful node](https://github.com/Joystream/substrate-node-joystream) ("Full Node"), the [Colossus Storage Node](https://github.com/Joystream/storage-node-joystream) ("Storage node"), and the Pioneer User Interface, either [self hosted](https://github.com/Joystream/apps) or [hosted by Us](http://testnet.joystream.org/) ("App"). + +Relevant to the Privacy Policy and Cookie Policy are the following terms: +* The term "Blockchain" refers to the blockchain(s) assembled by the Full Node. +* The term "Content" refers to media files accessible through our Software. +* The term "Keys" refers to a private/public cryptographic keypair, that Users can generate in order to write (and decrypt data) on the Blockchain. +* The term "Membership" refers to tying your Keys to a public profile, allowing users to access Content and interact with the Blockchain. +* The term "Memo" refers to a markdown enabled text field, where users can input data tied to their Keys. + + +# Privacy Policy +**Last updated on the 19th of May 2020** + +## 1. Agreement to the Policy +By using any of Our Software, the User are accepting this Privacy Policy. If you are acting on behalf of another company or an employer, you must have the rights to act on their behalf. The Privacy Policy is not extended to any of our newsletters, where Users are bound by the [privacy policy](https://mailchimp.com/legal/privacy/) of [Mailchimp](https://mailchimp.com/). + +The Privacy Policy does not apply to any other third party services including, but not limited to, applications, websites, tools or software, even if accessible through links or guides in our Software. + +## 2. Changes to Policy +This Privacy Policy may be changed at the sole discretion of Company. If any material changes are made, the User will be notified in the Service that is used. Note that adding new products to be included in the term Software , e.g. a new User facing product replacing the App or a new tool for uploading Content, is not considered material as it will not affect Users unless they adopt the new product. Changing softare names, terminology used in this Privacy Policy, and changin link locations are aslo examples of non-material changes. + +## 3. Information Collected +All data written to the Blockchain, is implicitly collected not only by Company, but also anyone else in the world that is running the Full Node locally, or accessed via the App or a third party. +This includes, but is not limited to, Content hashes, Membership profile, Memo field, and any other way a User can record data on the Blockchain. + +Company uses [Google Analytics](https://marketingplatform.google.com/about/analytics/), with IP anonymization, to collect statistics on Website and the version of App hosted by us. All customizable data sharing settings are turned off to improve the privacy of Users. + +Company will not sell your data for advertising, or other purposes. + + +# Cookie Policy +**Last updated on the 17th of April 2019** + +Company uses cookies on Website and App when hosted by Us (collectively "Service"). By using the Service, you consent to the use of cookies. + +Our Cookies Policy explains what cookies are, how we use cookies, how third-parties we partner with may use cookies on the Service, your choices regarding cookies and further information about cookies. + +## 1. What are Cookies? + +Cookies are small pieces of text sent by your web browser by a website you visit. A cookie file is stored in your web browser and allows the Service or a third-party to recognize you and make your next visit easier and the Service more useful to you. + +Cookies can be *persistent* or *session* cookies. + +## 2. How we use Cookies + +We use cookies for the following purposes our Service: + +* Provide Analytics +* Store preferences +* Persistant local storage of Keys and Membership. + +## 3. Third-party Cookies + +In addition to our own cookies, we also use various third-party cookies to report usage statistics of the Service, deliver advertisements on and through the Service, and so on. They include: + +* Google Analytics +* Mailchimp (Only when signing up for any of our newsletters) +* Godaddy + +Please see Item 3. of the Privacy Policy for more information on the extent of these providers. + +## Your Regarding Cookies + +If you would like to delete cookies or instruct your web browser to delete or refuse cookies, please visit the help pages of your web browser. + +Please note, however, that if you delete cookies or refuse to accept them, you might not be able to use all of the features we offer, you may not be able to store your preferences, and some of our pages might not display properly. + + +MORE INFORMATION About Cookies + +You can learn more about cookies and the following third-party websites: + +* [AllAboutCookies](http://www.allaboutcookies.org/) +* [Network Advertising Initiative](http://www.networkadvertising.org/) diff --git a/pioneer/packages/joy-pages/src/md/ToS.md b/pioneer/packages/joy-pages/src/md/ToS.md new file mode 100644 index 0000000000..b5084448dd --- /dev/null +++ b/pioneer/packages/joy-pages/src/md/ToS.md @@ -0,0 +1,40 @@ +# Terms of Service +**Last updated on the 17th of April 2019** + +The Terms of Service ("Agreement") is a binding obligation between you ("User") and Jsgenesis AS ("Company", "We", "Us", "Our") for use of our Products. These products (collectively "Software") include [all pages under the joystream.org domain](https://www.joystream.org/) ("Website"), the [Joyful node](https://github.com/Joystream/substrate-node-joystream) ("Full node"), the [Colossus Storage Node](https://github.com/Joystream/storage-node-joystream) ("Storage node"), and the Pioneer User Interface, either [self hosted ](https://github.com/Joystream/apps) or [hosted by Us](http://testnet.joystream.org/) ("App"). + +## 1. Agreement to Terms +By using this Software, the User are agreeing to be bound by this Agreement. If you are acting on behalf of another company or an employer, you must have the rights to act on their behalf. + +## 2. Changes to Terms +This Agreement may be changed at the sole discretion of Company without notice. Your continued use of our Software is a confirmation of Users acceptance of the newest Agreement. + +## 3. Privacy Policy +Please see our [privacy policy](https://joystream.org/privacy-cookies) ("Privacy Policy") for information regarding privacy. + +## 4. Membership +By generating private/public cryptographic keys ("Keys") or applying for a membership account ("Membershp"), you accept the risk of losing access to your Keys and Membership. Reasons include, but is not limited to: + 1. Losing passwords + 2. Losing recovery seeds or mnemonics + 3. Deleting accounts and backups + 4. Security breaches + +Under no circumstance will Company take any responsiblity for loss resulting of losing access to Membership or Keys. + +## 5. User Conduct + +By using any of Our Software, you agree to not state, write, link to, download, distribute, share or encourage other users to state, write, link to, download, distribute, share or encourage anything that: +1. breach or infringe any copyright or intellectual property of any third party. +2. is abusive, malicious, threatening or unlawful in any way. + +Company has not reviewed all content of this website, and is not responsible for content submitted or provided by individuals or groups not directly tied to them. + +## 6. Responsibilites and Risks + +In no event shall Company, its contractors, employees or owners be liable for any damage or loss of any kind to User arising out of the use or inability to use any Software made by Company. + +In no event shall Company, its contractors, employees or owners be liable for any damage or loss of any kind to User resulting of clicking links, following guides, using software or doing anything else recommended by Company. + +## 7. Governing Law + +These terms and conditions are governed by and construed in accordance with the laws of Norway. \ No newline at end of file diff --git a/pioneer/packages/joy-pages/src/translate.ts b/pioneer/packages/joy-pages/src/translate.ts new file mode 100644 index 0000000000..bf3239537b --- /dev/null +++ b/pioneer/packages/joy-pages/src/translate.ts @@ -0,0 +1,3 @@ +import { withTranslation } from 'react-i18next'; + +export default withTranslation(['pages', 'ui']); diff --git a/pioneer/packages/joy-proposals/.skip-build b/pioneer/packages/joy-proposals/.skip-build new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/joy-proposals/README.md b/pioneer/packages/joy-proposals/README.md new file mode 100644 index 0000000000..c29c707284 --- /dev/null +++ b/pioneer/packages/joy-proposals/README.md @@ -0,0 +1 @@ +# Proposals module for Joystream node diff --git a/pioneer/packages/joy-proposals/package.json b/pioneer/packages/joy-proposals/package.json new file mode 100644 index 0000000000..145902f003 --- /dev/null +++ b/pioneer/packages/joy-proposals/package.json @@ -0,0 +1,16 @@ +{ + "name": "@polkadot/joy-proposals", + "version": "0.1.1", + "description": "Proposals module for Joystream node", + "main": "index.js", + "scripts": {}, + "author": "Joystream contributors", + "maintainers": [], + "dependencies": { + "@babel/runtime": "^7.7.1", + "@polkadot/joy-utils": "^0.1.1", + "@polkadot/react-components": "0.37.0-beta.63", + "@polkadot/react-query": "0.37.0-beta.63", + "react-dropzone": "^10.2.2" + } +} diff --git a/pioneer/packages/joy-proposals/src/NotDone.tsx b/pioneer/packages/joy-proposals/src/NotDone.tsx new file mode 100644 index 0000000000..07fa01c4b1 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/NotDone.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export default function NotDone (props: any) { + return ( + <> +

This is not implemented yet :(

+
however, here is your props.
+ {JSON.stringify(props)} + + ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/Body.tsx b/pioneer/packages/joy-proposals/src/Proposal/Body.tsx new file mode 100644 index 0000000000..1989d2b698 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/Body.tsx @@ -0,0 +1,388 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Card, Header, Button, Icon, Message } from 'semantic-ui-react'; +import { ProposalType } from '@polkadot/joy-utils/types/proposals'; +import { bytesToString } from '@polkadot/joy-utils/functions/misc'; +import styled from 'styled-components'; +import AddressMini from '@polkadot/react-components/AddressMiniJoy'; +import TxButton from '@polkadot/joy-utils/TxButton'; +import { ProposalId, TerminateRoleParameters } from '@joystream/types/proposals'; +import { MemberId, Membership } from '@joystream/types/members'; +import ProfilePreview from '@polkadot/joy-utils/MemberProfilePreview'; +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; +import { Option, Bytes } from '@polkadot/types/'; +import { BlockNumber } from '@polkadot/types/interfaces'; +import { formatBalance } from '@polkadot/util'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; +import ReactMarkdown from 'react-markdown'; +import { WorkingGroupOpeningPolicyCommitment, RewardPolicy } from '@joystream/types/working-group'; +import { + ActivateOpeningAt, + ActivateOpeningAtKeys, + StakingPolicy +} from '@joystream/types/hiring'; +import { WorkingGroup, WorkingGroupKey } from '@joystream/types/common'; +import { ApplicationsDetailsByOpening } from '@polkadot/joy-utils/react/components/working-groups/ApplicationDetails'; +import { LeadInfoFromId } from '@polkadot/joy-utils/react/components/working-groups/LeadInfo'; +import { formatReward } from '@polkadot/joy-utils/functions/format'; + +type BodyProps = { + title: string; + description: string; + params: any[]; + type: ProposalType; + iAmProposer: boolean; + proposalId: number | ProposalId; + proposerId: number | MemberId; + isCancellable: boolean; + cancellationFee: number; +}; + +function ProposedAddress (props: { address?: string | null }) { + if (props.address === null || props.address === undefined) { + return <>NONE; + } + + return ( + + ); +} + +function ProposedMember (props: { memberId?: MemberId | number | null }) { + if (props.memberId === null || props.memberId === undefined) { + return <>NONE; + } + const memberId: MemberId | number = props.memberId; + + const transport = useTransport(); + const [member, error, loading] = usePromise( + () => transport.members.membershipById(memberId), + null + ); + + return ( + + { (member && !member.handle.isEmpty) ? ( + + ) : 'Profile not found' } + + ); +} + +const ParsedHRT = styled.pre` + font-size: 14px; + font-weight: normal; + background: #eee; + border-radius: 0.5rem; + padding: 1rem; + margin: 0; + white-space: pre-wrap; +`; + +type ParsedParamValue = string | number | JSX.Element; + +class ParsedParam { + name: string; + value: ParsedParamValue; + fullWidth: boolean; + + constructor (name: string, value: ParsedParamValue, fullWidth = false) { + this.name = name; + this.value = value; + this.fullWidth = fullWidth; + } +} + +// The methods for parsing params by Proposal type. +const paramParsers: { [x in ProposalType]: (params: any[]) => ParsedParam[]} = { + Text: ([content]) => [ + new ParsedParam( + 'Content', + , + true + ) + ], + RuntimeUpgrade: ([hash, filesize]) => [ + new ParsedParam('Blake2b256 hash of WASM code', hash, true), + new ParsedParam('File size', filesize + ' bytes') + ], + SetElectionParameters: ([params]) => [ + new ParsedParam('Announcing period', params.announcing_period + ' blocks'), + new ParsedParam('Voting period', params.voting_period + ' blocks'), + new ParsedParam('Revealing period', params.revealing_period + ' blocks'), + new ParsedParam('Council size', params.council_size + ' members'), + new ParsedParam('Candidacy limit', params.candidacy_limit + ' members'), + new ParsedParam('New term duration', params.new_term_duration + ' blocks'), + new ParsedParam('Min. council stake', formatBalance(params.min_council_stake)), + new ParsedParam('Min. voting stake', formatBalance(params.min_voting_stake)) + ], + Spending: ([amount, account]) => [ + new ParsedParam('Amount', formatBalance(amount)), + new ParsedParam('Account', ) + ], + SetLead: ([memberId, accountId]) => [ + new ParsedParam('Member', ), + new ParsedParam('Account id', ) + ], + SetContentWorkingGroupMintCapacity: ([capacity]) => [ + new ParsedParam('Mint capacity', formatBalance(capacity)) + ], + EvictStorageProvider: ([accountId]) => [ + new ParsedParam('Storage provider account', ) + ], + SetValidatorCount: ([count]) => [ + new ParsedParam('Validator count', count) + ], + SetStorageRoleParameters: ([params]) => [ + new ParsedParam('Min. stake', formatBalance(params.min_stake)), + // "Min. actors": params.min_actors, + new ParsedParam('Max. actors', params.max_actors), + new ParsedParam('Reward', formatBalance(params.reward)), + new ParsedParam('Reward period', params.reward_period + ' blocks'), + // "Bonding period": params.bonding_period + " blocks", + new ParsedParam('Unbonding period', params.unbonding_period + ' blocks'), + // "Min. service period": params.min_service_period + " blocks", + // "Startup grace period": params.startup_grace_period + " blocks", + new ParsedParam('Entry request fee', formatBalance(params.entry_request_fee)) + ], + AddWorkingGroupLeaderOpening: ([{ activate_at, commitment, human_readable_text, working_group }]) => { + const workingGroup = new WorkingGroup(working_group); + const activateAt = new ActivateOpeningAt(activate_at); + const activateAtBlock = activateAt.type === ActivateOpeningAtKeys.ExactBlock ? activateAt.value : null; + const OPCommitment = new WorkingGroupOpeningPolicyCommitment(commitment); + const { + application_staking_policy: applicationSP, + role_staking_policy: roleSP, + application_rationing_policy: rationingPolicy + } = OPCommitment; + let HRT = bytesToString(new Bytes(human_readable_text)); + try { HRT = JSON.stringify(JSON.parse(HRT), undefined, 4); } catch (e) { /* Do nothing */ } + const formatStake = (stake: Option) => ( + stake.isSome ? stake.unwrap().amount_mode.type + `(${stake.unwrap().amount})` : 'NONE' + ); + const formatPeriod = (unstakingPeriod: Option) => ( + unstakingPeriod.unwrapOr(0) + ' blocks' + ); + return [ + new ParsedParam('Working group', workingGroup.type), + new ParsedParam('Activate at', `${activateAt.type}${activateAtBlock ? `(${activateAtBlock.toString()})` : ''}`), + new ParsedParam('Application stake', formatStake(applicationSP)), + new ParsedParam('Role stake', formatStake(roleSP)), + new ParsedParam( + 'Max. applications', + rationingPolicy.isSome ? rationingPolicy.unwrap().max_active_applicants.toNumber() : 'UNLIMITED' + ), + new ParsedParam( + 'Terminate unstaking period (role stake)', + formatPeriod(OPCommitment.terminate_role_stake_unstaking_period) + ), + new ParsedParam( + 'Exit unstaking period (role stake)', + formatPeriod(OPCommitment.exit_role_stake_unstaking_period) + ), + // + new ParsedParam( + 'Terminate unstaking period (appl. stake)', + formatPeriod(OPCommitment.terminate_application_stake_unstaking_period) + ), + new ParsedParam( + 'Exit unstaking period (appl. stake)', + formatPeriod(OPCommitment.exit_role_application_stake_unstaking_period) + ), + new ParsedParam( + 'Appl. accepted unstaking period (appl. stake)', + formatPeriod(OPCommitment.fill_opening_successful_applicant_application_stake_unstaking_period) + ), + new ParsedParam( + 'Appl. failed unstaking period (role stake)', + formatPeriod(OPCommitment.fill_opening_failed_applicant_role_stake_unstaking_period) + ), + new ParsedParam( + 'Appl. failed unstaking period (appl. stake)', + formatPeriod(OPCommitment.fill_opening_failed_applicant_application_stake_unstaking_period) + ), + new ParsedParam( + 'Crowded out unstaking period (role stake)', + roleSP.isSome ? formatPeriod(roleSP.unwrap().crowded_out_unstaking_period_length) : '0 blocks' + ), + new ParsedParam( + 'Review period expierd unstaking period (role stake)', + roleSP.isSome ? formatPeriod(roleSP.unwrap().review_period_expired_unstaking_period_length) : '0 blocks' + ), + new ParsedParam( + 'Crowded out unstaking period (appl. stake)', + applicationSP.isSome ? formatPeriod(applicationSP.unwrap().crowded_out_unstaking_period_length) : '0 blocks' + ), + new ParsedParam( + 'Review period expierd unstaking period (appl. stake)', + applicationSP.isSome ? formatPeriod(applicationSP.unwrap().review_period_expired_unstaking_period_length) : '0 blocks' + ), + // + new ParsedParam('Human readable text', { HRT }, true) + ]; + }, + SetWorkingGroupMintCapacity: ([capacity, group]) => [ + new ParsedParam('Working group', (new WorkingGroup(group)).type), + new ParsedParam('Mint capacity', formatBalance(capacity)) + ], + BeginReviewWorkingGroupLeaderApplication: ([id, group]) => [ + new ParsedParam('Working group', (new WorkingGroup(group)).type), + // TODO: Adjust the link to work with multiple groups after working-groups are normalized! + new ParsedParam('Opening id', #{id}) + ], + FillWorkingGroupLeaderOpening: ([params]) => { + const { opening_id, successful_application_id, reward_policy, working_group } = params; + const rewardPolicy = reward_policy && new RewardPolicy(reward_policy); + return [ + new ParsedParam('Working group', (new WorkingGroup(working_group)).type), + // TODO: Adjust the link to work with multiple groups after working-groups are normalized! + new ParsedParam('Opening id', #{opening_id}), + new ParsedParam('Reward policy', rewardPolicy ? formatReward(rewardPolicy, true) : 'NONE'), + new ParsedParam( + 'Result', + , + true + ) + ]; + }, + SlashWorkingGroupLeaderStake: ([leadId, amount, group]) => [ + new ParsedParam('Working group', (new WorkingGroup(group)).type), + new ParsedParam('Slash amount', formatBalance(amount)), + new ParsedParam('Lead', , true) + ], + DecreaseWorkingGroupLeaderStake: ([leadId, amount, group]) => [ + new ParsedParam('Working group', (new WorkingGroup(group)).type), + new ParsedParam('Decrease amount', formatBalance(amount)), + new ParsedParam('Lead', , true) + ], + SetWorkingGroupLeaderReward: ([leadId, amount, group]) => [ + new ParsedParam('Working group', (new WorkingGroup(group)).type), + new ParsedParam('New reward amount', formatBalance(amount)), + new ParsedParam('Lead', , true) + ], + TerminateWorkingGroupLeaderRole: ([params]) => { + const paramsObj = new TerminateRoleParameters(params); + const { working_group: workingGroup, rationale, worker_id: leadId, slash } = paramsObj; + return [ + new ParsedParam('Working group', workingGroup.type), + new ParsedParam('Rationale', bytesToString(rationale), true), + new ParsedParam('Slash stake', slash.isTrue ? 'YES' : 'NO'), + new ParsedParam('Lead', , true) + ]; + } +}; + +const StyledProposalDescription = styled(Card.Description)` + font-size: 1.15rem; +`; +const ProposalParams = styled.div` + border: 1px solid rgba(0,0,0,.2); + padding: 1.5rem 2rem 1rem 2rem; + position: relative; + margin-top: 1.7rem; + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 1rem; + grid-row-gap: 0.5rem; + @media screen and (max-width: 767px) { + grid-template-columns: 1fr; + } +`; +const ParamsHeader = styled.h4` + position: absolute; + top: 0; + transform: translateY(-50%); + background: #fff; + font-weight: normal; + padding: 0.3rem; + left: 0.5rem; +`; +type ProposalParamProps = { fullWidth?: boolean }; +const ProposalParam = ({ fullWidth, children }: React.PropsWithChildren) => ( +
+ { children } +
+); +const ProposalParamName = styled.div` + font-size: 0.9rem; + font-weight: normal; +`; +const ProposalParamValue = styled.div` + color: black; + word-wrap: break-word; + word-break: break-word; + font-size: 1.15rem; + font-weight: bold; + & .TextProposalContent { + font-weight: normal; + } +`; + +export default function Body ({ + type, + title, + description, + params = [], + iAmProposer, + proposalId, + proposerId, + isCancellable, + cancellationFee +}: BodyProps) { + const parseParams = paramParsers[type]; + const parsedParams = parseParams(params); + return ( + + + +
{title}
+
+ + + + + Parameters: + { parsedParams.map(({ name, value, fullWidth }) => ( + + {name}: + {value} + + ))} + + { iAmProposer && isCancellable && (<> + + + Proposal cancellation +

+ {'You can only cancel your proposal while it\'s still in the Voting Period.'} +

+

+ The cancellation fee for this type of proposal is:  + { cancellationFee ? formatBalance(cancellationFee) : 'NONE' } +

+ + { sendTx(); } } + className={'icon left labeled'} + > + + Withdraw proposal + + +
+
+ ) } +
+
+ ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/ChooseProposalType.css b/pioneer/packages/joy-proposals/src/Proposal/ChooseProposalType.css new file mode 100644 index 0000000000..424821bb01 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/ChooseProposalType.css @@ -0,0 +1,6 @@ +.ChooseProposalType .filters { + text-align: right; +} +.ChooseProposalType .filters .dropdown { + width: 200px; +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/ChooseProposalType.tsx b/pioneer/packages/joy-proposals/src/Proposal/ChooseProposalType.tsx new file mode 100644 index 0000000000..fbd8194ab6 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/ChooseProposalType.tsx @@ -0,0 +1,40 @@ +import React, { useState } from 'react'; +import ProposalTypePreview from './ProposalTypePreview'; +import { Item, Dropdown } from 'semantic-ui-react'; + +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; +import { Categories } from '@polkadot/joy-utils/types/proposals'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; +import './ChooseProposalType.css'; +import { RouteComponentProps } from 'react-router-dom'; + +export default function ChooseProposalType (props: RouteComponentProps) { + const transport = useTransport(); + + const [proposalTypes, error, loading] = usePromise(() => transport.proposals.proposalsTypesParameters(), []); + const [category, setCategory] = useState(''); + + return ( +
+ +
+ ({ value: category, text: category }))} + value={category} + onChange={(e, data) => setCategory((data.value || '').toString())} + clearable + selection + /> +
+ + {proposalTypes + .filter(typeInfo => (!category || typeInfo.category === category) && !typeInfo.outdated) + .map((typeInfo, idx) => ( + + ))} + +
+
+ ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/Details.tsx b/pioneer/packages/joy-proposals/src/Proposal/Details.tsx new file mode 100644 index 0000000000..3289f0037b --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/Details.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import { Item, Header, Label } from 'semantic-ui-react'; +import { ParsedProposal } from '@polkadot/joy-utils/types/proposals'; +import { metadata as proposalConsts } from '@polkadot/joy-utils/consts/proposals'; +import { ExtendedProposalStatus } from './ProposalDetails'; +import styled from 'styled-components'; + +import ProfilePreview from '@polkadot/joy-utils/MemberProfilePreview'; + +const DetailsContainer = styled(Item.Group)` + display: grid; + width: auto; + grid-template-columns: repeat(5, auto) 1fr; + grid-column-gap: 5rem; + + & .item .extra { + margin-bottom: 0.5em !important; + } + + @media screen and (max-width: 1199px) { + grid-template-columns: repeat(3, auto); + grid-template-rows: repeat(2, auto); + + & .item:first-child { + grid-row: 1/3; + } + + & .item { + margin: 0.5em 0 !important; + } + } + + @media screen and (max-width: 767px) { + grid-template-columns: repeat(2, auto); + grid-template-rows: repeat(3, auto); + + & .item:first-child { + grid-column: 1/3; + } + + & .item { + margin: 0.5em 0 !important; + } + } +`; + +const BlockInfo = styled.div` + font-size: 0.9em; +`; + +type DetailProps = { + name: string; + value?: string; +}; + +const Detail: React.FunctionComponent = ({ name, value, children }) => ( + + + { name }: + { value &&
{value}
} + { children } +
+
+); + +type DetailsProps = { + proposal: ParsedProposal; + extendedStatus: ExtendedProposalStatus; + proposerLink?: boolean; +}; + +export default function Details ({ proposal, extendedStatus, proposerLink = false }: DetailsProps) { + const { type, createdAt, createdAtBlock, proposer } = proposal; + const { displayStatus, periodStatus, expiresIn, finalizedAtBlock, executedAtBlock, executionFailReason } = extendedStatus; + return ( + + + + { `${createdAt.toLocaleString()}` } + + + { proposalConsts[type].outdated && } + + + + { createdAtBlock && Created at block #{ createdAtBlock } } + { finalizedAtBlock && Finalized at block #{ finalizedAtBlock } } + { executedAtBlock && ( + + { displayStatus === 'ExecutionFailed' ? 'Execution failed at' : 'Executed at' } block + #{ executedAtBlock } + + ) } + + + { (periodStatus !== null) && } + {expiresIn !== null && ( + + ) } + {executionFailReason && } + + ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/Proposal.css b/pioneer/packages/joy-proposals/src/Proposal/Proposal.css new file mode 100644 index 0000000000..d3dd08ab20 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/Proposal.css @@ -0,0 +1,29 @@ +.Proposal { + position: relative; + + .description { + word-wrap: break-word; + word-break: break-word; + } + + /* Ovverrides Semantic UI for the details page.*/ + .ui.items > .item:first-child { + margin: 1em 0; + } + + .center-content { + justify-content: center; + } + + .bold { + font-weight: 700; + } + + .details-param { + display: flex; + } + + .ui.tabular.list-menu { + margin-bottom: 2rem; + } +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/ProposalDetails.tsx b/pioneer/packages/joy-proposals/src/Proposal/ProposalDetails.tsx new file mode 100644 index 0000000000..075f87843a --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/ProposalDetails.tsx @@ -0,0 +1,175 @@ +import React from 'react'; + +import Details from './Details'; +import Body from './Body'; +import VotingSection from './VotingSection'; +import Votes from './Votes'; +import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/MyAccount'; +import { ParsedProposal } from '@polkadot/joy-utils/types/proposals'; +import { withCalls } from '@polkadot/react-api'; +import { withMulti } from '@polkadot/react-api/with'; + +import './Proposal.css'; +import { ProposalId, ProposalDecisionStatuses, ApprovedProposalStatuses, ExecutionFailedStatus } from '@joystream/types/proposals'; +import { BlockNumber } from '@polkadot/types/interfaces'; +import { MemberId } from '@joystream/types/members'; +import { Seat } from '@joystream/types/council'; +import ProposalDiscussion from './discussion/ProposalDiscussion'; + +import styled from 'styled-components'; + +const ProposalDetailsMain = styled.div` + display: flex; + @media screen and (max-width: 1199px) { + flex-direction: column; + } +`; + +const ProposalDetailsVoting = styled.div` + min-width: 30%; + margin-left: 3%; + @media screen and (max-width: 1399px) { + min-width: 40%; + } + @media screen and (max-width: 1199px) { + margin-left: 0; + } +`; + +const ProposalDetailsDiscussion = styled.div` + margin-top: 1rem; + max-width: 67%; + @media screen and (max-width: 1399px) { + max-width: none; + } +`; + +// TODO: That should probably be moved to joy-utils/functions/proposals (or transport) +type BasicProposalStatus = 'Active' | 'Finalized'; +type ProposalPeriodStatus = 'Voting period' | 'Grace period'; +type ProposalDisplayStatus = BasicProposalStatus | ProposalDecisionStatuses | ApprovedProposalStatuses; + +export type ExtendedProposalStatus = { + displayStatus: ProposalDisplayStatus; + periodStatus: ProposalPeriodStatus | null; + expiresIn: number | null; + finalizedAtBlock: number | null; + executedAtBlock: number | null; + executionFailReason: string | null; +} + +export function getExtendedStatus (proposal: ParsedProposal, bestNumber: BlockNumber | undefined): ExtendedProposalStatus { + const basicStatus = Object.keys(proposal.status)[0] as BasicProposalStatus; + let expiresIn: number | null = null; + + let displayStatus: ProposalDisplayStatus = basicStatus; + let periodStatus: ProposalPeriodStatus | null = null; + let finalizedAtBlock: number | null = null; + let executedAtBlock: number | null = null; + let executionFailReason: string | null = null; + + const best = bestNumber ? bestNumber.toNumber() : 0; + + const { votingPeriod, gracePeriod } = proposal.parameters; + const blockAge = best - proposal.createdAtBlock; + + if (basicStatus === 'Active') { + periodStatus = 'Voting period'; + expiresIn = Math.max(votingPeriod - blockAge, 0) || null; + } + + if (basicStatus === 'Finalized') { + const { finalizedAt, proposalStatus } = proposal.status.Finalized; + const decisionStatus: ProposalDecisionStatuses = Object.keys(proposalStatus)[0] as ProposalDecisionStatuses; + displayStatus = decisionStatus; + finalizedAtBlock = finalizedAt as number; + if (decisionStatus === 'Approved') { + const approvedStatus: ApprovedProposalStatuses = Object.keys(proposalStatus.Approved)[0] as ApprovedProposalStatuses; + if (approvedStatus === 'PendingExecution') { + const finalizedAge = best - finalizedAt; + periodStatus = 'Grace period'; + expiresIn = Math.max(gracePeriod - finalizedAge, 0) || null; + } else { + // Executed / ExecutionFailed + displayStatus = approvedStatus; + executedAtBlock = finalizedAtBlock + gracePeriod; + if (approvedStatus === 'ExecutionFailed') { + const executionFailedStatus = proposalStatus.Approved.ExecutionFailed as ExecutionFailedStatus; + executionFailReason = Buffer.from(executionFailedStatus.error.toString().replace('0x', ''), 'hex').toString(); + } + } + } + } + + return { + displayStatus, + periodStatus, + expiresIn: best ? expiresIn : null, + finalizedAtBlock, + executedAtBlock, + executionFailReason + }; +} + +type ProposalDetailsProps = MyAccountProps & { + proposal: ParsedProposal; + proposalId: ProposalId; + bestNumber?: BlockNumber; + council?: Seat[]; +}; + +function ProposalDetails ({ + proposal, + proposalId, + myAddress, + myMemberId, + iAmMember, + council, + bestNumber +}: ProposalDetailsProps) { + const iAmCouncilMember = Boolean(iAmMember && council && council.some(seat => seat.member.toString() === myAddress)); + const iAmProposer = Boolean(iAmMember && myMemberId !== undefined && proposal.proposerId === myMemberId.toNumber()); + const extendedStatus = getExtendedStatus(proposal, bestNumber); + const isVotingPeriod = extendedStatus.periodStatus === 'Voting period'; + return ( +
+
+ + + + { iAmCouncilMember && ( + + ) } + + + + + + +
+ ); +} + +export default withMulti( + ProposalDetails, + withMyAccount, + withCalls( + ['derive.chain.bestNumber', { propName: 'bestNumber' }], + ['query.council.activeCouncil', { propName: 'council' }] // TODO: Handle via transport? + ) +); diff --git a/pioneer/packages/joy-proposals/src/Proposal/ProposalFromId.tsx b/pioneer/packages/joy-proposals/src/Proposal/ProposalFromId.tsx new file mode 100644 index 0000000000..4ddfa58251 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/ProposalFromId.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { RouteComponentProps } from 'react-router-dom'; +import ProposalDetails from './ProposalDetails'; +import { useProposalSubscription } from '@polkadot/joy-utils/react/hooks'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; +import { ProposalId } from '@joystream/types/proposals'; + +export default function ProposalFromId (props: RouteComponentProps) { + const { + match: { + params: { id } + } + } = props; + + const proposalState = useProposalSubscription(new ProposalId(id)); + + return ( + + + + ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/ProposalPreview.tsx b/pioneer/packages/joy-proposals/src/Proposal/ProposalPreview.tsx new file mode 100644 index 0000000000..c562d634f6 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/ProposalPreview.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { Header, Card } from 'semantic-ui-react'; +import Details from './Details'; +import { ParsedProposal } from '@polkadot/joy-utils/types/proposals'; +import { getExtendedStatus } from './ProposalDetails'; +import { BlockNumber } from '@polkadot/types/interfaces'; +import styled from 'styled-components'; +import ReactMarkdown from 'react-markdown'; + +import './Proposal.css'; + +const ProposalIdBox = styled.div` + position: absolute; + top: 0; + right: 0; + padding: 1rem; + color: rgba(0,0,0,0.4); + font-size: 1.1em; +`; + +const ProposalDesc = styled.div` + padding: 0.5rem 1rem; + border: 1px solid #ddd; + border-radius: 0.25rem; +`; + +export type ProposalPreviewProps = { + proposal: ParsedProposal; + bestNumber?: BlockNumber; +}; +export default function ProposalPreview ({ proposal, bestNumber }: ProposalPreviewProps) { + const extendedStatus = getExtendedStatus(proposal, bestNumber); + return ( + + { `#${proposal.id.toString()}` } + + +
{proposal.title}
+
+ + + + + +
+ + + ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/ProposalPreviewList.tsx b/pioneer/packages/joy-proposals/src/Proposal/ProposalPreviewList.tsx new file mode 100644 index 0000000000..22df3b8803 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/ProposalPreviewList.tsx @@ -0,0 +1,102 @@ +import React, { useState } from 'react'; +import { Button, Card, Container, Icon, Pagination } from 'semantic-ui-react'; +import styled from 'styled-components'; +import { Link, useLocation } from 'react-router-dom'; + +import ProposalPreview from './ProposalPreview'; +import { ParsedProposal, proposalStatusFilters, ProposalStatusFilter, ProposalsBatch } from '@polkadot/joy-utils/types/proposals'; +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; +import { withCalls } from '@polkadot/react-api'; +import { BlockNumber } from '@polkadot/types/interfaces'; +import { Dropdown } from '@polkadot/react-components'; + +type ProposalPreviewListProps = { + bestNumber?: BlockNumber; +}; + +const FilterContainer = styled.div` + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 1.75rem; +`; +const StyledDropdown = styled(Dropdown)` + .dropdown { + width: 200px; + } +`; +const PaginationBox = styled.div` + margin-bottom: 1em; +`; + +function ProposalPreviewList ({ bestNumber }: ProposalPreviewListProps) { + const { pathname } = useLocation(); + const transport = useTransport(); + const [activeFilter, setActiveFilter] = useState('All'); + const [currentPage, setCurrentPage] = useState(1); + const [proposalsBatch, error, loading] = usePromise( + () => transport.proposals.proposalsBatch(activeFilter, currentPage), + undefined, + [activeFilter, currentPage] + ); + + const filterOptions = proposalStatusFilters.map(filter => ({ + text: filter, + value: filter + })); + + const _onChangePrefix = (f: ProposalStatusFilter) => { + setCurrentPage(1); + setActiveFilter(f); + }; + + return ( + + + + + + + { proposalsBatch && (<> + + { proposalsBatch.totalBatches > 1 && ( + , icon: true }} + firstItem={{ content: , icon: true }} + lastItem={{ content: , icon: true }} + prevItem={{ content: , icon: true }} + nextItem={{ content: , icon: true }} + totalPages={ proposalsBatch.totalBatches } + onPageChange={ (e, data) => setCurrentPage((data.activePage && parseInt(data.activePage.toString())) || 1) } + /> + ) } + + { proposalsBatch.proposals.length + ? ( + + {proposalsBatch.proposals.map((prop: ParsedProposal, idx: number) => ( + + ))} + + ) + : `There are currently no ${activeFilter !== 'All' ? activeFilter.toLocaleLowerCase() : 'submitted'} proposals.` + } + ) } + + + ); +} + +export default withCalls(['derive.chain.bestNumber', { propName: 'bestNumber' }])( + ProposalPreviewList +); diff --git a/pioneer/packages/joy-proposals/src/Proposal/ProposalType.css b/pioneer/packages/joy-proposals/src/Proposal/ProposalType.css new file mode 100644 index 0000000000..764535e812 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/ProposalType.css @@ -0,0 +1,48 @@ +.ProposalType { + background: #fff !important; + box-shadow: 0 1px 3px 0 #d4d4d5, 0 0 0 1px #d4d4d5 !important; + padding: 1em !important; + border-radius: 0.3em !important; +} +.ProposalType .header { + font-size: 1.5em !important; + line-height: 1; +} +.ProposalType .description-text { + flex-grow: 1; +} +.ProposalType .actions { + margin: 0 2em; + padding-top: 1em; +} +.ProposalType .proposal-details { + display: flex; + margin: 0 -2em; +} +.ProposalType .proposal-detail { + margin: 1em 2em; +} +.ProposalType .detail-value { + font-size: 1.2em; + font-weight: 700; +} + +@media only screen and (max-width: 1199px) { + .ProposalType .proposal-details { + flex-direction: column; + margin: 1em 0; + } + .ProposalType .proposal-detail { + display: flex; + justify-content: space-between; + margin: .5em 0; + } +} + +@media only screen and (max-width: 767px) { + .ProposalType .actions { + padding: 0; + margin: 0; + text-align: right; + } +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx b/pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx new file mode 100644 index 0000000000..092848a77b --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx @@ -0,0 +1,150 @@ +import React from 'react'; + +import { History } from 'history'; +import { Item, Icon, Button, Label } from 'semantic-ui-react'; + +import { ProposalType, Category } from '@polkadot/joy-utils/types/proposals'; +import _ from 'lodash'; +import styled from 'styled-components'; +import useVoteStyles from './useVoteStyles'; +import { formatBalance } from '@polkadot/util'; + +import './ProposalType.css'; + +const QuorumsAndThresholds = styled.div` + display: grid; + grid-template-columns: min-content min-content; + grid-template-rows: auto auto; + grid-row-gap: 0.5rem; + grid-column-gap: 0.5rem; + margin-bottom: 1rem; + @media screen and (max-width: 480px) { + grid-template-columns: min-content; + } +`; + +const QuorumThresholdLabel = styled(Label)` + opacity: 0.75; + white-space: nowrap; + margin: 0 !important; + display: flex !important; + align-items: center; + & b { + font-size: 1.2em; + margin-left: auto; + padding-left: 0.3rem; + } +`; + +const CreateButton = styled(Button)` + font-size: 1.1em !important; + white-space: nowrap; + margin-right: 0; +`; + +export type ProposalTypeInfo = { + type: ProposalType; + category: Category; + description: string; + stake: number; + cancellationFee?: number; + gracePeriod: number; + votingPeriod: number; + approvalQuorum: number; + approvalThreshold: number; + slashingQuorum: number; + slashingThreshold: number; +}; + +type ProposalTypePreviewProps = { + typeInfo: ProposalTypeInfo; + history: History; +}; + +const ProposalTypeDetail = (props: { title: string; value: string }) => ( +
+
{ `${props.title}:` }
+
{ props.value }
+
+); + +export default function ProposalTypePreview (props: ProposalTypePreviewProps) { + const { + typeInfo: { + type, + description, + stake, + cancellationFee, + gracePeriod, + votingPeriod, + approvalQuorum, + approvalThreshold, + slashingQuorum, + slashingThreshold + } + } = props; + + const handleClick = () => { + if (!props.history) return; + props.history.push(`/proposals/new/${_.kebabCase(type)}`); + }; + + return ( + + {/* + TODO: We can add it once we have the actual assets + + */} + + {_.startCase(type)} + {description} +
+ + + 1 ? 's' : ''}` : 'NONE' } /> + 1 ? 's' : ''}` : 'NONE' } /> +
+ + { approvalQuorum && ( + + + Approval Quorum: { approvalQuorum }% + + ) } + { approvalThreshold && ( + + + Approval Threshold: { approvalThreshold }% + + ) } + { slashingQuorum && ( + + + Slashing Quorum: { slashingQuorum }% + + ) } + { slashingThreshold && ( + + + Slashing Threshold: { slashingThreshold }% + + ) } + +
+
+ + Create + + +
+
+ ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/Votes.tsx b/pioneer/packages/joy-proposals/src/Proposal/Votes.tsx new file mode 100644 index 0000000000..6eec0b42c8 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/Votes.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { Header, Divider, Table, Icon } from 'semantic-ui-react'; +import useVoteStyles from './useVoteStyles'; +import { VoteKind } from '@joystream/types/proposals'; +import { VoteKindStr } from './VotingSection'; +import ProfilePreview from '@polkadot/joy-utils/MemberProfilePreview'; +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; +import { ParsedProposal, ProposalVotes } from '@polkadot/joy-utils/types/proposals'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; + +type VotesProps = { + proposal: ParsedProposal; +}; + +export default function Votes ({ proposal: { id, votingResults } }: VotesProps) { + const transport = useTransport(); + const [votes, error, loading] = usePromise( + () => transport.proposals.votes(id), + null, + [votingResults] + ); + + return ( + + { (votes && votes.votes.length > 0) + ? ( + <> +
+ All Votes: ({votes.votes.length}/{votes.councilMembersLength}) +
+ + + + {votes.votes.map((proposalVote, idx) => { + const { vote, member } = proposalVote; + const voteStr = (vote as VoteKind).type.toString() as VoteKindStr; + const { icon, textColor } = useVoteStyles(voteStr); + return ( + + + + {voteStr} + + + + + + ); + })} + +
+ + ) + : ( +
No votes have been submitted!
+ ) + } +
+ ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx b/pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx new file mode 100644 index 0000000000..7e10d4b1fb --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx @@ -0,0 +1,115 @@ +import React, { useState } from 'react'; + +import { Icon, Button, Message, Divider, Header } from 'semantic-ui-react'; +import useVoteStyles from './useVoteStyles'; +import TxButton from '@polkadot/joy-utils/TxButton'; +import { MemberId } from '@joystream/types/members'; +import { ProposalId, VoteKind, VoteKinds } from '@joystream/types/proposals'; +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; + +import styled from 'styled-components'; + +const VoteButtons = styled.div` + display: grid; + grid-gap: 0.5rem; + grid-template-rows: 1fr 1fr; + grid-template-columns: 1fr 1fr; + @media screen and (max-width: 1199px) { + grid-template-rows: auto; + grid-template-columns: repeat(4, 1fr); + } + @media screen and (max-width: 767px) { + grid-template-rows: 1fr 1fr; + grid-template-columns: 1fr 1fr; + } +`; + +export type VoteKindStr = typeof VoteKinds[number]; + +type VoteButtonProps = { + memberId: MemberId; + voteKind: VoteKindStr; + proposalId: ProposalId; + onSuccess: () => void; +} +function VoteButton ({ voteKind, proposalId, memberId, onSuccess }: VoteButtonProps) { + const { icon, color } = useVoteStyles(voteKind); + return ( + // Button.Group "cheat" to force TxButton color + + sendTx() } + txFailedCb={ () => null } + txSuccessCb={ onSuccess } + className={'icon left labeled'}> + + { voteKind } + + + ); +} + +type VotingSectionProps = { + memberId: MemberId; + proposalId: ProposalId; + isVotingPeriod: boolean; +}; + +export default function VotingSection ({ + memberId, + proposalId, + isVotingPeriod +}: VotingSectionProps) { + const transport = useTransport(); + const [voted, setVoted] = useState(null); + const [vote] = usePromise( + () => transport.proposals.voteByProposalAndMember(proposalId, memberId), + undefined + ); + + if (vote === undefined) { + // Loading / error + return null; + } + + const voteStr: VoteKindStr | null = voted || (vote && vote.type.toString() as VoteKindStr); + + if (voteStr) { + const { icon, color } = useVoteStyles(voteStr); + + return ( + + + + You voted {`"${voteStr}"`} + + + ); + } else if (!isVotingPeriod) { + return null; + } + + return ( + <> +
Sumbit your vote
+ + + { VoteKinds.map((voteKind) => + setVoted(voteKind) }/> + ) } + + + ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPost.tsx b/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPost.tsx new file mode 100644 index 0000000000..40d3125e22 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPost.tsx @@ -0,0 +1,111 @@ +import React, { useState } from 'react'; +import { Button, Icon } from 'semantic-ui-react'; +import { ParsedPost } from '@polkadot/joy-utils/types/proposals'; +import MemberProfilePreview from '@polkadot/joy-utils/MemberProfilePreview'; +import DiscussionPostForm from './DiscussionPostForm'; +import { MemberId } from '@joystream/types/members'; +import { useTransport } from '@polkadot/joy-utils/react/hooks'; +import styled from 'styled-components'; +import ReactMarkdown from 'react-markdown'; + +const StyledComment = styled.div` + display: flex; + margin-bottom: 1rem; + flex-direction: column; +`; +const AuthorAndDate = styled.div` + display: flex; + justify-content: space-between; + width: 100%; + @media screen and (max-width: 767px) { + flex-direction: column; + } +`; +const Author = styled.div` + margin-bottom: 0.5rem; +`; +const CreationDate = styled.div` + color: rgba(0,0,0,.4); +`; +const ContentAndActions = styled.div` + display: flex; + flex-grow: 1; +`; +const CommentContent = styled.div` + flex-grow: 1; + padding: 0.5rem; + padding-left: 1rem; +`; +const CommentActions = styled.div` +`; +const CommentAction = styled(Button)` +`; + +type ProposalDiscussionPostProps = { + post: ParsedPost; + memberId?: MemberId; + refreshDiscussion: () => void; +} + +export default function DiscussionPost ({ + post, + memberId, + refreshDiscussion +}: ProposalDiscussionPostProps) { + const { author, authorId, text, createdAt, editsCount } = post; + const [editing, setEditing] = useState(false); + const constraints = useTransport().proposals.discussionContraints(); + const canEdit = ( + memberId && + post.postId && + authorId.toNumber() === memberId.toNumber() && + editsCount < constraints.maxPostEdits + ); + const onEditSuccess = () => { + setEditing(false); + refreshDiscussion(); + }; + + return ( + (memberId && editing) ? ( + + ) : ( + + + { author && ( + + + + ) } + + { createdAt.toLocaleString() } + + + + + + + { canEdit && ( + + setEditing(true)} + primary + size="tiny" + icon> + + + + ) } + + + ) + ); +} diff --git a/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPostForm.tsx b/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPostForm.tsx new file mode 100644 index 0000000000..5b7ff204fd --- /dev/null +++ b/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPostForm.tsx @@ -0,0 +1,141 @@ +import React from 'react'; +import { Form, Field, withFormik, FormikProps } from 'formik'; +import * as Yup from 'yup'; + +import TxButton from '@polkadot/joy-utils/TxButton'; +import * as JoyForms from '@polkadot/joy-utils/forms'; +import { SubmittableResult } from '@polkadot/api'; +import { Button } from 'semantic-ui-react'; +import { TxFailedCallback, TxCallback } from '@polkadot/react-components/Status/types'; +import { ParsedPost, DiscussionContraints } from '@polkadot/joy-utils/types/proposals'; +import { ThreadId } from '@joystream/types/common'; +import { MemberId } from '@joystream/types/members'; + +type OuterProps = { + post?: ParsedPost; + threadId: ThreadId; + memberId: MemberId; + onSuccess: () => void; + constraints: DiscussionContraints; +}; + +type FormValues = { + text: string; +}; + +type InnerProps = OuterProps & FormikProps; + +const LabelledField = JoyForms.LabelledField(); + +const DiscussionPostFormInner = (props: InnerProps) => { + const { + isValid, + isSubmitting, + setSubmitting, + resetForm, + values, + post, + memberId, + threadId, + onSuccess + } = props; + + const isEditForm = post && post.postId; + + const onSubmit = (sendTx: () => void) => { + if (isValid) sendTx(); + }; + + const onTxFailed: TxFailedCallback = (txResult: SubmittableResult | null) => { + setSubmitting(false); + }; + + const onTxSuccess: TxCallback = (_txResult: SubmittableResult) => { + setSubmitting(false); + resetForm(); + onSuccess(); + }; + + const buildTxParams = () => { + if (!isValid) return []; + + if (isEditForm) { + return [ + memberId, + threadId, + post?.postId, + values.text + ]; + } + + return [ + memberId, + threadId, + values.text + ]; + }; + + return ( +
+ + + + + + { isEditForm ? ( + + )} + + +
; +} diff --git a/pioneer/packages/joy-media/src/common/FormTabs.tsx b/pioneer/packages/joy-media/src/common/FormTabs.tsx new file mode 100644 index 0000000000..1d1b32c1df --- /dev/null +++ b/pioneer/packages/joy-media/src/common/FormTabs.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { Menu, Label, Tab } from 'semantic-ui-react'; +import { FormikErrors } from 'formik'; +import { GenericMediaProp } from './MediaForms'; + +type FormTab = { + id: string; + fields?: GenericMediaProp[]; + renderTitle?: () => React.ReactNode; + render?: () => React.ReactNode; +} + +type FormTabsProps = { + errors: FormikErrors; + panes: FormTab[]; +} + +export function FormTabs (props: FormTabsProps) { + const { panes, errors } = props; + + return { + const { + id, + fields = [], + renderTitle = () => id, + render = () => null + } = tab; + + const tabErrors: any[] = []; + fields.forEach(f => { + const err = errors[f.id]; + if (err) { + tabErrors.push(err); + } + }); + + // Currently we don't show error counter because it's markup is broken: + // a red circle with a number is shifted quite far from the right border of its tab. + const showErrorCounter = false; + + const errCount = tabErrors.length; + const errTooltip = 'Number of errors on this tab'; + + const menuItem = + + {renderTitle()} + {showErrorCounter && errCount > 0 && + + } + ; + + return { menuItem, render }; + })} + />; +} diff --git a/pioneer/packages/joy-media/src/common/MediaDropdownOptions.tsx b/pioneer/packages/joy-media/src/common/MediaDropdownOptions.tsx new file mode 100644 index 0000000000..0fd9b77b0b --- /dev/null +++ b/pioneer/packages/joy-media/src/common/MediaDropdownOptions.tsx @@ -0,0 +1,44 @@ +import ISO6391 from 'iso-639-1'; +import { DropdownItemProps } from 'semantic-ui-react'; +import { LanguageType } from '../schemas/general/Language'; +import { TextValueEntity } from '@joystream/types/versioned-store/EntityCodec'; +import { InternalEntities } from '../transport'; + +const buildOptions = (entities: TextValueEntity[]): DropdownItemProps[] => + entities.map(x => ({ key: x.id, value: x.id, text: x.value })); + +const buildLanguageOptions = (entities: LanguageType[]): DropdownItemProps[] => + entities.map(x => ({ key: x.id, value: x.id, text: ISO6391.getName(x.value) })); + +export class MediaDropdownOptions { + public languageOptions: DropdownItemProps[] + public contentLicenseOptions: DropdownItemProps[] + public curationStatusOptions: DropdownItemProps[] + public musicGenreOptions: DropdownItemProps[] + public musicMoodOptions: DropdownItemProps[] + public musicThemeOptions: DropdownItemProps[] + public publicationStatusOptions: DropdownItemProps[] + public videoCategoryOptions: DropdownItemProps[] + + constructor (props: InternalEntities) { + this.languageOptions = buildLanguageOptions(props.languages); + this.contentLicenseOptions = buildOptions(props.contentLicenses); + this.curationStatusOptions = buildOptions(props.curationStatuses); + this.musicGenreOptions = buildOptions(props.musicGenres); + this.musicMoodOptions = buildOptions(props.musicMoods); + this.musicThemeOptions = buildOptions(props.musicThemes); + this.publicationStatusOptions = buildOptions(props.publicationStatuses); + this.videoCategoryOptions = buildOptions(props.videoCategories); + } + + static Empty = new MediaDropdownOptions({ + languages: [], + contentLicenses: [], + curationStatuses: [], + musicGenres: [], + musicMoods: [], + musicThemes: [], + publicationStatuses: [], + videoCategories: [] + }); +} diff --git a/pioneer/packages/joy-media/src/common/MediaForms.tsx b/pioneer/packages/joy-media/src/common/MediaForms.tsx new file mode 100644 index 0000000000..0607e27095 --- /dev/null +++ b/pioneer/packages/joy-media/src/common/MediaForms.tsx @@ -0,0 +1,192 @@ +import React from 'react'; +import { Dropdown, DropdownItemProps, DropdownProps } from 'semantic-ui-react'; +import { FormikProps, Field } from 'formik'; +import * as JoyForms from '@polkadot/joy-utils/forms'; +import { SubmittableResult } from '@polkadot/api'; +import { TxFailedCallback, TxCallback } from '@polkadot/react-components/Status/types'; +import { MediaDropdownOptions } from './MediaDropdownOptions'; +import { OnTxButtonClick } from '@polkadot/joy-utils/TxButton'; +import isEqual from 'lodash/isEqual'; +import { componentName } from '@polkadot/joy-utils/react/helpers'; + +export const datePlaceholder = 'Date in format yyyy-mm-dd'; + +export type FormCallbacks = { + onSubmit: OnTxButtonClick; + onTxSuccess: TxCallback; + onTxFailed: TxFailedCallback; +}; + +export type GenericMediaProp = { + id: keyof FormValues; + type: string; + name: string; + description?: string; + required?: boolean; + minItems?: number; + maxItems?: number; + minTextLength?: number; + maxTextLength?: number; + classId?: any; +}; + +type BaseFieldProps = OuterProps & FormikProps & { + field: GenericMediaProp; +}; + +type MediaTextProps = + BaseFieldProps & JoyForms.LabelledProps; + +type MediaFieldProps = + BaseFieldProps & + JoyForms.LabelledProps & { + fieldProps: any; + } + +type MediaDropdownProps = + BaseFieldProps & + { + options: DropdownItemProps[]; + }; + +type FormFields = { + LabelledText: React.FunctionComponent>; + LabelledField: React.FunctionComponent>; + MediaText: React.FunctionComponent>; + MediaField: React.FunctionComponent>; + MediaDropdown: React.FunctionComponent>; +}; + +export type MediaFormProps = + OuterProps & + FormikProps & + FormFields & + FormCallbacks & { + opts: MediaDropdownOptions; + isFieldChanged: (field: keyof FormValues | GenericMediaProp) => boolean; + }; + +export function withMediaForm +(Component: React.ComponentType>) { + type FieldName = keyof FormValues + + type FieldObject = GenericMediaProp + + const LabelledText = JoyForms.LabelledText(); + + const LabelledField = JoyForms.LabelledField(); + + function MediaText (props: MediaTextProps) { + const { field: f } = props; + return !f ? null : ; + } + + const MediaField = (props: MediaFieldProps) => { + const { field: f, fieldProps = {}, placeholder, className, style, ...otherProps } = props; + + const { id } = f; + + const allFieldProps = { + name: id, + id, + placeholder, + className, + style, + disabled: otherProps.isSubmitting, + ...fieldProps + }; + + return !f ? null : ( + + + + ); + }; + + const MediaDropdown = (props: MediaDropdownProps) => { + const { field: f, options = [] } = props; + const id = f.id as string; + const value = (props.values as any)[id] || ''; + + return { + props.setFieldTouched(id, true); + }, + onChange: (_event: any, data: DropdownProps) => { + props.setFieldValue(id, data.value); + } + }} />; + }; + + const ResultComponent: React.FunctionComponent> = + (props: MediaFormProps) => { + const { + initialValues, + values, + dirty, + touched, + errors, + isValid, + setSubmitting, + opts = MediaDropdownOptions.Empty + } = props; + + const isFieldChanged = (field: FieldName | FieldObject): boolean => { + const fieldName = typeof field === 'string' ? field : (field as FieldObject).id; + return ( + dirty && + touched[fieldName] === true && + !isEqual(values[fieldName], initialValues[fieldName]) + ); + }; + + const onSubmit = (sendTx: () => void) => { + if (isValid) { + sendTx(); + } else { + console.log('Form is invalid. Errors:', errors); + } + }; + + const onTxSuccess: TxCallback = (_txResult: SubmittableResult) => { + setSubmitting(false); + }; + + const onTxFailed: TxFailedCallback = (txResult: SubmittableResult | null) => { + setSubmitting(false); + if (txResult === null) { + // Tx cancelled + + } + }; + + const allProps = { + ...props, + + // Callbacks: + onSubmit, + onTxSuccess, + onTxFailed, + + // Components: + LabelledText, + LabelledField, + MediaText, + MediaField, + MediaDropdown, + + // Other + opts, + isFieldChanged + }; + + return ; + }; + ResultComponent.displayName = `withMediaForm(${componentName(Component)})`; + return ResultComponent; +} diff --git a/pioneer/packages/joy-media/src/common/MediaPlayerView.tsx b/pioneer/packages/joy-media/src/common/MediaPlayerView.tsx new file mode 100644 index 0000000000..ff0d7a2193 --- /dev/null +++ b/pioneer/packages/joy-media/src/common/MediaPlayerView.tsx @@ -0,0 +1,143 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import DPlayer from 'react-dplayer'; +import APlayer from 'react-aplayer'; + +import { ApiProps } from '@polkadot/react-api/types'; +import { I18nProps } from '@polkadot/react-components/types'; +import { withCalls, withMulti } from '@polkadot/react-api/with'; +import { Option } from '@polkadot/types/codec'; + +import translate from '../translate'; +import { DiscoveryProviderProps } from '../DiscoveryProvider'; +import { DataObject, ContentId } from '@joystream/types/media'; +import { VideoType } from '../schemas/video/Video'; +import { isAccountAChannelOwner } from '../channels/ChannelHelpers'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { useMyMembership } from '@polkadot/joy-utils/MyMembershipContext'; +import { JoyError } from '@polkadot/joy-utils/JoyStatus'; + +const PLAYER_COMMON_PARAMS = { + lang: 'en', + autoplay: true, + theme: '#2185d0' +}; + +// This is just a part of Player's methods that are used in this component. +// To see all the methods available on APlayer and DPlayer visit the next URLs: +// http://aplayer.js.org/#/home?id=api +// http://dplayer.js.org/#/home?id=api +interface PartOfPlayer { + pause: () => void; + destroy: () => void; +} + +export type RequiredMediaPlayerProps = { + channel: ChannelEntity; + video: VideoType; + contentId: ContentId; +} + +type ContentProps = { + contentType?: string; + dataObjectOpt?: Option; + resolvedAssetUrl?: string; +} + +type MediaPlayerViewProps = ApiProps & I18nProps & +DiscoveryProviderProps & RequiredMediaPlayerProps & ContentProps + +type PlayerProps = RequiredMediaPlayerProps & ContentProps + +function Player (props: PlayerProps) { + const { video, resolvedAssetUrl: url, contentType = 'video/video' } = props; + const { thumbnail: cover } = video; + const prefix = contentType.substring(0, contentType.indexOf('/')); + + const [player, setPlayer] = useState(); + + const onPlayerCreated = (newPlayer: PartOfPlayer) => { + console.log('onPlayerCreated:', newPlayer); + setPlayer(newPlayer); + }; + + const destroyPlayer = () => { + if (!player) return; + + console.log('Destroy the current player'); + player.pause(); + player.destroy(); + setPlayer(undefined); + }; + + useEffect(() => { + return () => { + destroyPlayer(); + }; + }, [url]); + + if (prefix === 'video') { + const video = { url, name, pic: cover }; + return ; + } else if (prefix === 'audio') { + const audio = { url, name, cover }; + return ; + } + + return {contentType}; +} + +function InnerComponent (props: MediaPlayerViewProps) { + const { video, resolvedAssetUrl: url } = props; + + const { dataObjectOpt, channel } = props; + if (!dataObjectOpt || dataObjectOpt.isNone) { + return null; + } + + // TODO extract and show the next info from dataObject: + // {"owner":"5GSMNn8Sy8k64mGUWPDafjMZu9bQNX26GujbBQ1LeJpNbrfg","added_at":{"block":2781,"time":1582750854000},"type_id":1,"size":3664485,"liaison":"5HN528fspu4Jg3KXWm7Pu7aUK64RSBz2ZSbwo1XKR9iz3hdY","liaison_judgement":1,"ipfs_content_id":"QmNk4QczoJyPTAKdfoQna6KhAz3FwfjpKyRBXAZHG5djYZ"} + + const { myAccountId } = useMyMembership(); + const iAmOwner = isAccountAChannelOwner(channel, myAccountId); + + return ( + + ); +} + +export const MediaPlayerView = withMulti( + InnerComponent, + translate, + withCalls( + ['query.dataDirectory.dataObjectByContentId', + { paramName: 'contentId', propName: 'dataObjectOpt' }] + ) +); diff --git a/pioneer/packages/joy-media/src/common/MediaPlayerWithResolver.tsx b/pioneer/packages/joy-media/src/common/MediaPlayerWithResolver.tsx new file mode 100644 index 0000000000..73c32612ff --- /dev/null +++ b/pioneer/packages/joy-media/src/common/MediaPlayerWithResolver.tsx @@ -0,0 +1,159 @@ +import React, { useState, useEffect } from 'react'; +import axios, { CancelTokenSource } from 'axios'; +import _ from 'lodash'; + +import { ApiProps } from '@polkadot/react-api/types'; +import { I18nProps } from '@polkadot/react-components/types'; +import { withMulti } from '@polkadot/react-api/with'; +import { Option } from '@polkadot/types/codec'; +import { StorageProviderId, Worker } from '@joystream/types/working-group'; + +import translate from '../translate'; +import { DiscoveryProviderProps, withDiscoveryProvider } from '../DiscoveryProvider'; +import { DataObjectStorageRelationshipId, DataObjectStorageRelationship } from '@joystream/types/media'; +import { Message } from 'semantic-ui-react'; +import { MediaPlayerView, RequiredMediaPlayerProps } from './MediaPlayerView'; +import { JoyInfo } from '@polkadot/joy-utils/JoyStatus'; +import { MultipleLinkedMapEntry } from '@polkadot/joy-utils/index'; + +type Props = ApiProps & I18nProps & DiscoveryProviderProps & RequiredMediaPlayerProps; + +function newCancelSource (): CancelTokenSource { + return axios.CancelToken.source(); +} + +function InnerComponent (props: Props) { + const { contentId, api, discoveryProvider } = props; + + const [error, setError] = useState(); + const [resolvedAssetUrl, setResolvedAssetUrl] = useState(); + const [contentType, setContentType] = useState(); + const [cancelSource, setCancelSource] = useState(newCancelSource()); + + const getActiveStorageProviderIds = async (): Promise => { + const nextId = await api.query.storageWorkingGroup.nextWorkerId() as StorageProviderId; + // This is chain specfic, but if next id is still 0, it means no workers have been added, + // so the workerById is empty + if (nextId.eq(0)) { + return []; + } + + const workers = new MultipleLinkedMapEntry( + StorageProviderId, + Worker, + await api.query.storageWorkingGroup.workerById() + ); + + return workers.linked_keys; + }; + + const resolveAsset = async () => { + setError(undefined); + setCancelSource(newCancelSource()); + + const rids: DataObjectStorageRelationshipId[] = await api.query.dataObjectStorageRegistry.relationshipsByContentId(contentId) as any; + + const allRelationships: Option[] = await Promise.all(rids.map((id) => api.query.dataObjectStorageRegistry.relationships(id))) as any; + + // Providers that have signalled onchain that they have the asset + let readyProviders = allRelationships.filter(r => r.isSome).map(r => r.unwrap()) + .filter(r => r.ready) + .map(r => r.storage_provider); + + // runtime doesn't currently guarantee unique set + readyProviders = _.uniqBy(readyProviders, provider => provider.toString()); + + if (!readyProviders.length) { + setError(new Error('No Storage Providers found storing this content')); + return; + } + + // filter out providers no longer active - relationships of providers that have left + // are not pruned onchain. + const activeProviders = await getActiveStorageProviderIds(); + readyProviders = _.intersectionBy(activeProviders, readyProviders, provider => provider.toString()); + + console.log(`Found ${readyProviders.length} providers ready to serve content: ${readyProviders}`); + + // shuffle to spread the load + readyProviders = _.shuffle(readyProviders); + + // TODO: prioritize already resolved providers, least reported unreachable, closest + // by geography etc.. + + // loop over providers until we find one that responds + while (readyProviders.length) { + const provider = readyProviders.shift(); + if (!provider) continue; + + let assetUrl: string | undefined; + try { + assetUrl = await discoveryProvider.resolveAssetEndpoint(provider, contentId.encode(), cancelSource.token); + } catch (err) { + if (axios.isCancel(err)) { + return; + } else { + continue; + } + } + + try { + console.log('Check URL of resolved asset:', assetUrl); + const response = await axios.head(assetUrl, { cancelToken: cancelSource.token }); + + setContentType(response.headers['content-type'] || 'video/video'); + setResolvedAssetUrl(assetUrl); + + return; + } catch (err) { + if (axios.isCancel(err)) { + return; + } else { + if (!err.response || (err.response.status >= 500 && err.response.status <= 504)) { + // network connection error + discoveryProvider.reportUnreachable(provider); + } + + // try next provider + continue; + } + } + } + + setError(new Error('Unable to reach any provider serving this content')); + }; + + useEffect(() => { + resolveAsset(); + + return () => { + cancelSource.cancel(); + }; + }, [contentId.encode()]); + + console.log('Content id:', contentId.encode()); + console.log('Resolved asset URL:', resolvedAssetUrl); + + if (error) { + return ( + + Error loading media content +

{error.toString()}

+ +
+ ); + } + + if (!resolvedAssetUrl) { + return Resolving media content.; + } + + const playerProps = { ...props, contentType, resolvedAssetUrl }; + return ; +} + +export const MediaPlayerWithResolver = withMulti( + InnerComponent, + translate, + withDiscoveryProvider +); diff --git a/pioneer/packages/joy-media/src/common/NoContentYet.tsx b/pioneer/packages/joy-media/src/common/NoContentYet.tsx new file mode 100644 index 0000000000..6af76b8e3b --- /dev/null +++ b/pioneer/packages/joy-media/src/common/NoContentYet.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const NoContentYet: React.FunctionComponent = (props) => { + return
{props.children}
; +}; + +export default NoContentYet; diff --git a/pioneer/packages/joy-media/src/common/TypeHelpers.ts b/pioneer/packages/joy-media/src/common/TypeHelpers.ts new file mode 100644 index 0000000000..4feaa33d92 --- /dev/null +++ b/pioneer/packages/joy-media/src/common/TypeHelpers.ts @@ -0,0 +1,43 @@ +import BN from 'bn.js'; +import { ChannelId } from '@joystream/types/content-working-group'; +import { EntityId, ClassId } from '@joystream/types/versioned-store'; + +export type AnyChannelId = ChannelId | BN | number | string + +export type AnyEntityId = EntityId | BN | number | string + +export type AnyClassId = ClassId | BN | number | string + +function canBeId (id: BN | number | string): boolean { + return id instanceof BN || typeof id === 'number' || typeof id === 'string'; +} + +export function asChannelId (id: AnyChannelId): ChannelId { + if (id instanceof ChannelId) { + return id; + } else if (canBeId(id)) { + return new ChannelId(id); + } else { + throw new Error(`Not supported format for Channel id: ${id}`); + } +} + +export function asEntityId (id: AnyEntityId): EntityId { + if (id instanceof EntityId) { + return id; + } else if (canBeId(id)) { + return new EntityId(id); + } else { + throw new Error(`Not supported format for Entity id: ${id}`); + } +} + +export function asClassId (id: AnyClassId): ClassId { + if (id instanceof ClassId) { + return id; + } else if (canBeId(id)) { + return new ClassId(id); + } else { + throw new Error(`Not supported format for Class id: ${id}`); + } +} diff --git a/pioneer/packages/joy-media/src/common/index.css b/pioneer/packages/joy-media/src/common/index.css new file mode 100644 index 0000000000..5daaa16903 --- /dev/null +++ b/pioneer/packages/joy-media/src/common/index.css @@ -0,0 +1,405 @@ +$blackFont: #222; +$grayFont: #999; + +$bgHover: #f2f2f2; +$bgSelected: #c7e7fa; + +$borderColor: #e4e4e4; + +.NoContentYet { + padding: 2rem 0; + color: $grayFont; +} + +.JoyBgImg { + background-color: #ccc; + background-size: cover; + background-position: center; +} + +.JoySection { + .ViewAllLink { + float: right; + font-size: 1rem; + font-weight: normal; + text-transform: uppercase; + margin-left: 1rem; + } +} +.Ellipsis { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.JoyTopActionBar { + margin: 1rem 0; + + .button { + margin-right: .5rem; + } +} + +.JoyListOfPreviews { + background-color: #ddd; + + .CheckboxCell { + padding-left: 1rem; + } + + .NoItems { + display: block; + background-color: #fafafa; + padding: 1rem 0; + } +} + +.JoyMusicAlbumPreview, +.JoyMusicTrackPreview { + display: flex; + + &.vertical { + flex-direction: column; + } + + &.horizontal { + flex-direction: row; + + .AlbumCover { + margin-right: 1rem; + } + } + + .JoyListOfPreviews & { + background-color: #fafafa; + margin-top: 1px; + padding: .5rem 0; + + &:hover { + background-color: $bgHover; + } + + &.DraggableItem, + &.SelectedItem { + background-color: $bgSelected; + } + } + + .AlbumNumber { + color: $grayFont; + width: 3.5rem; + text-align: right; + padding-right: 1rem; + } + + .AlbumCover { + text-align: right; + white-space: nowrap; + + $size: 60px; + width: $size; + + img { + max-width: $size; + max-height: $size; + } + } + + .AlbumDescription { + padding: 0 1rem; + width: 100%; + + .AlbumTitle { + font-size: 1rem; + font-weight: bold; + color: $blackFont; + margin-top: 0; + margin-bottom: 0.5rem; + } + + .AlbumArtist, + .AlbumTracksCount { + color: $grayFont; + font-size: .9rem; + } + } + + .AlbumActions { + white-space: nowrap; + opacity: 0; + + .button { + margin-right: .5rem; + } + } + &:hover .AlbumActions { + opacity: 1; + } +} + +/* Music Album */ +/* ------------------------------------------------------ */ + +.JoyMusicAlbumPreview { + display: inline-flex; + flex-direction: column; + padding: 0 1rem 1rem 0; + $size: 200px; + + .AlbumDescription { + text-align: left; + padding: 0; + } + + .AlbumCover { + width: $size; + height: $size; + min-width: $size; + min-height: $size; + } + + .AlbumTitle { + @extend .Ellipsis; + + font-size: 1rem; + font-weight: bold; + color: $blackFont; + margin-top: 0; + margin-bottom: 0.5rem; + } + &.vertical { + .AlbumTitle { + margin-top: 0.5rem; + } + } + + .AlbumArtist { + color: $grayFont; + font-size: 1rem; + } + + .AlbumActions { + opacity: 1; + } +} + +/* Channels */ +/* ------------------------------------------------------ */ + +.ChannelTitle { + color: $blackFont; + display: flex; +} + +.ChannelAvatar { + display: table; + border-radius: 50%; + margin-right: 1rem; + + &.big { + margin-right: 2rem; + } +} + +.JoyChannels { + + .ui.message.JoyInlineMsg { + display: inline-flex; + width: auto; + margin-top: 0; + + &.CreateBtn { + cursor: pointer; + + /* Disable text selection by user: */ + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Old versions of Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + supported by Chrome, Opera and Firefox */ + + &:hover, + &:focus { + background-color: #f3fce0; + } + &:active { + background-color: #e0f4b7; + } + } + } + + .ChannelPreview { + display: flex; + + .ChannelStats { + margin-left: 3rem; + text-align: right; + + .statistic { + .label, + .value { + text-align: right; + white-space: nowrap; + } + .label { + color: $grayFont; + font-weight: normal; + font-size: .9rem; + } + } + } + } +} + +.JoyViewChannel { + .ChannelHeader { + margin-bottom: 1rem; + } + .ChannelCover { + background-size: cover; + margin-bottom: 2rem; + height: 200px; + } +} + +.ChannelPreview { + display: flex; + align-items: normal; + + .ListOfChannels & { + margin-bottom: 1rem; + } + + .JoyPlayAlbum & { + margin: .5rem 0; + } + + .ChannelDetails { + width: 100%; + } + + .ChannelTitle { + font-size: 1.5rem; + font-weight: 500; + margin: 0; + margin-top: .75rem; + + .ChannelHeader & { + margin-top: 1rem; + } + } + + &.small { + align-items: center; + + .ChannelTitle { + margin-top: 0; + font-size: 1rem; + a { + color: $blackFont; + } + } + .ChannelAvatar { + margin-right: .5rem; + } + } + + &.big { + .ChannelTitle { + font-size: 2rem; + } + } + + .ChannelSubtitle { + color: $grayFont; + font-size: .9rem; + text-transform: uppercase; + margin-top: .5rem; + + .icon { + margin-right: .5rem; + } + } + + .ChannelDesc { + margin-top: .75rem; + } +} + +/* Play Album, Video */ +/* ------------------------------------------------------ */ + +.JoyPlayAlbum { + display: flex; + flex-direction: row; + + .JoyPlayAlbum_Main { + display: flex; + flex-direction: row; + margin-right: 1rem; + + .JoyPlayAlbum_CurrentTrack { + margin-right: 1rem; + + .AlbumTitle { + font-size: 1.5rem; + margin-top: 1rem; + } + .AlbumArtist { + font-size: 1.15rem; + } + } + + .JoyPlayAlbum_AlbumTracks { + .TrackRow { + cursor: pointer; + + &.Current { + background-color: $bgSelected; + } + + .JoyMusicAlbumPreview { + padding: 0; + } + .TrackNumber { + color: $grayFont; + width: 2rem; + padding-left: .5rem !important; + } + .TrackTitle { + padding-right: .5rem !important; + } + .AlbumDescription { + max-width: 300px; + } + } + } + + .JoyPlayAlbum_AlbumTracks, + .JoyPlayAlbum_MetaInfo { + td:first-child { + font-weight: bold; + color: $grayFont; + } + } + } + + .JoyPlayAlbum_RightSidePanel { + max-width: 450px; + } +} + +.ContentHeader { + border-bottom: 1px solid $borderColor; + padding-bottom: 1rem; + margin-bottom: 1rem; +} + +.PlayVideoDetails { + border-top: 1px solid $borderColor; + padding-top: 1rem; + margin-top: 1rem; + margin-bottom: 2rem; +} \ No newline at end of file diff --git a/pioneer/packages/joy-media/src/entities/ChannelEntity.ts b/pioneer/packages/joy-media/src/entities/ChannelEntity.ts new file mode 100644 index 0000000000..11845f7f3c --- /dev/null +++ b/pioneer/packages/joy-media/src/entities/ChannelEntity.ts @@ -0,0 +1,10 @@ +import BN from 'bn.js'; +import { ChannelType } from '../schemas/channel/Channel'; + +// TODO rename to EnrichedChannelType +export type ChannelEntity = ChannelType & { + + // Stats: + rewardEarned: BN; + contentItemsCount: number; +}; diff --git a/pioneer/packages/joy-media/src/entities/EntityHelpers.ts b/pioneer/packages/joy-media/src/entities/EntityHelpers.ts new file mode 100644 index 0000000000..6ddf0f451c --- /dev/null +++ b/pioneer/packages/joy-media/src/entities/EntityHelpers.ts @@ -0,0 +1,15 @@ +import moment from 'moment'; +import ISO6391 from 'iso-639-1'; +import { LanguageType } from '../schemas/general/Language'; + +export function printExplicit (explicit?: boolean): string { + return explicit === true ? 'Yes' : 'No'; +} + +export function printReleaseDate (linuxTimestamp?: number): string { + return !linuxTimestamp ? '' : moment(linuxTimestamp * 1000).format('YYYY-MM-DD'); +} + +export function printLanguage (language?: LanguageType): string { + return !language ? '' : ISO6391.getName(language.value); +} diff --git a/pioneer/packages/joy-media/src/entities/MusicAlbumEntity.ts b/pioneer/packages/joy-media/src/entities/MusicAlbumEntity.ts new file mode 100644 index 0000000000..adecd88a0d --- /dev/null +++ b/pioneer/packages/joy-media/src/entities/MusicAlbumEntity.ts @@ -0,0 +1,26 @@ +export type MusicAlbumEntity = { + title: string; + artist: string; + thumbnail: string; + description: string; + + explicit: boolean; + license: string; + + year: number; + month?: number; + date?: number; + + genre?: string; + mood?: string; + theme?: string; + + language?: string; + links?: string[]; + lyrics?: string; + composer?: string; + reviews?: string; + + // publicationStatus: ... + // curationStatus: ... +}; diff --git a/pioneer/packages/joy-media/src/entities/MusicTrackEntity.ts b/pioneer/packages/joy-media/src/entities/MusicTrackEntity.ts new file mode 100644 index 0000000000..c4fd93e8af --- /dev/null +++ b/pioneer/packages/joy-media/src/entities/MusicTrackEntity.ts @@ -0,0 +1,18 @@ +export type MusicTrackEntity = { + + // Basic: + title: string; + description?: string; + thumbnail?: string; + visibility?: string; + album?: string; + + // Additional: + artist?: string; + composer?: string; + genre?: string; + mood?: string; + theme?: string; + explicit?: boolean; + license?: string; +}; diff --git a/pioneer/packages/joy-media/src/explore/AllChannels.tsx b/pioneer/packages/joy-media/src/explore/AllChannels.tsx new file mode 100644 index 0000000000..86a3cf5046 --- /dev/null +++ b/pioneer/packages/joy-media/src/explore/AllChannels.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import Section from '@polkadot/joy-utils/Section'; +import { MediaView } from '../MediaView'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelPreview } from '../channels/ChannelPreview'; + +export type Props = { + channels?: ChannelEntity[]; +} + +export function AllChannels (props: Props) { + const { channels = [] } = props; + + return channels.length === 0 + ? No channels found + :
+ {channels.map((x) => + + )} +
; +} + +export const AllChannelsView = MediaView({ + component: AllChannels, + resolveProps: async ({ transport }) => ({ + channels: await transport.allPublicVideoChannels() + }) +}); diff --git a/pioneer/packages/joy-media/src/explore/AllVideos.tsx b/pioneer/packages/joy-media/src/explore/AllVideos.tsx new file mode 100644 index 0000000000..588981f2d5 --- /dev/null +++ b/pioneer/packages/joy-media/src/explore/AllVideos.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import Section from '@polkadot/joy-utils/Section'; +import { VideoPreviewProps, VideoPreview } from '../video/VideoPreview'; +import { MediaView } from '../MediaView'; + +export type Props = { + videos?: VideoPreviewProps[]; +} + +export function AllVideos (props: Props) { + const { videos = [] } = props; + + return videos.length === 0 + ? No videos found + :
+ {videos.map((x) => + + )} +
; +} + +export const AllVideosView = MediaView({ + component: AllVideos, + resolveProps: async ({ transport }) => ({ + videos: await transport.allPublicVideos() + }) +}); diff --git a/pioneer/packages/joy-media/src/explore/ExploreContent.tsx b/pioneer/packages/joy-media/src/explore/ExploreContent.tsx new file mode 100644 index 0000000000..b8a1aba49e --- /dev/null +++ b/pioneer/packages/joy-media/src/explore/ExploreContent.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import Section from '@polkadot/joy-utils/Section'; +import { VideoPreviewProps, VideoPreview } from '../video/VideoPreview'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelPreview } from '../channels/ChannelPreview'; + +const LatestVideosTitle = () => ( +
+ Latest videos + All videos +
+); + +const LatestChannelsTitle = () => ( +
+ Latest video channels + All channels +
+); + +export type ExploreContentProps = { + featuredVideos?: VideoPreviewProps[]; + latestVideos?: VideoPreviewProps[]; + latestVideoChannels?: ChannelEntity[]; +} + +export function ExploreContent (props: ExploreContentProps) { + const { featuredVideos = [], latestVideos = [], latestVideoChannels = [] } = props; + + return
+ {featuredVideos.length > 0 && +
+ {featuredVideos.map((x) => + + )} +
+ } + {latestVideos.length > 0 && +
}> + {latestVideos.map((x) => + + )} +
+ } + {latestVideoChannels.length > 0 && +
}> + {latestVideoChannels.map((x) => + + )} +
+ } +
; +} diff --git a/pioneer/packages/joy-media/src/explore/ExploreContent.view.tsx b/pioneer/packages/joy-media/src/explore/ExploreContent.view.tsx new file mode 100644 index 0000000000..c4cb0f5d9f --- /dev/null +++ b/pioneer/packages/joy-media/src/explore/ExploreContent.view.tsx @@ -0,0 +1,23 @@ +import { MediaView } from '../MediaView'; +import { ExploreContentProps, ExploreContent } from './ExploreContent'; + +export const ExploreContentView = MediaView({ + component: ExploreContent, + resolveProps: async (props) => { + const { transport } = props; + + const [ + latestVideoChannels, + latestVideos, + featuredVideos + ] = await Promise.all([ + transport.latestPublicVideoChannels(), + transport.latestPublicVideos(), + transport.featuredVideos() + ]); + + return { featuredVideos, latestVideos, latestVideoChannels }; + } +}); + +export default ExploreContentView; diff --git a/pioneer/packages/joy-media/src/explore/PlayContent.tsx b/pioneer/packages/joy-media/src/explore/PlayContent.tsx new file mode 100644 index 0000000000..96f28b7cf3 --- /dev/null +++ b/pioneer/packages/joy-media/src/explore/PlayContent.tsx @@ -0,0 +1,92 @@ +import React, { useState } from 'react'; +import { MusicAlbumPreviewProps, MusicAlbumPreview } from '../music/MusicAlbumPreview'; +import { MusicTrackReaderPreviewProps, MusicTrackReaderPreview } from '../music/MusicTrackReaderPreview'; +import { Pluralize } from '@polkadot/joy-utils/Pluralize'; +import { Table } from 'semantic-ui-react'; +import { ChannelEntity } from '../entities/ChannelEntity'; +import { ChannelPreview } from '../channels/ChannelPreview'; + +type Props = { + channel: ChannelEntity; + tracks: MusicTrackReaderPreviewProps[]; + currentTrackIndex?: number; + featuredAlbums?: MusicAlbumPreviewProps[]; +}; + +// TODO get meta from track item +const meta = { + artist: 'Berlin Philharmonic', + composer: 'Wolfgang Amadeus Mozart', + genre: 'Classical Music', + mood: 'Relaxing', + theme: 'Dark', + explicit: false, + license: 'Public Domain' +}; + +export function PlayContent (props: Props) { + const { channel, tracks = [], currentTrackIndex = 0, featuredAlbums = [] } = props; + + const [currentTrack, setCurrentTrack] = useState(tracks[currentTrackIndex]); + + const metaField = (label: React.ReactNode, value: React.ReactNode) => + + {label} + {value} + ; + + const metaTable = <> +

Track Info

+ + + {metaField('Artist', meta.artist)} + {metaField('Composer', meta.composer)} + {metaField('Genre', meta.genre)} + {metaField('Mood', meta.mood)} + {metaField('Theme', meta.theme)} + {metaField('Explicit', meta.explicit ? 'Yes' : 'No')} + {metaField('License', meta.license)} + +
+ ; + + const albumTracks = ( +
+

+ + + {tracks.map((x, i) => { + const isCurrent = x.id === currentTrack.id; + const className = 'TrackRow ' + (isCurrent ? 'Current' : ''); + + return ( + setCurrentTrack(x)}> + {i + 1} + {x.title} + + ); + })} + +
+
+ ); + + return
+
+
+ + +
+
+ {albumTracks} + {metaTable} +
+
+ {featuredAlbums.length > 0 && +
+

Featured albums

+ {featuredAlbums.map(x => )} +
+ } +
; +} diff --git a/pioneer/packages/joy-media/src/index.css b/pioneer/packages/joy-media/src/index.css new file mode 100644 index 0000000000..2b7395c214 --- /dev/null +++ b/pioneer/packages/joy-media/src/index.css @@ -0,0 +1,131 @@ +.JoyPaperWidth { + max-width: 900px; + margin: 0 auto; +} + +.UploadBox { + display: flex; + flex-direction: row; + justify-content: center; + + .UploadSelectForm { + max-width: 500px; + width: 100%; + } + + .UploadInputFile { + padding: 2rem 3rem; + margin-bottom: 1rem; + text-align: center; + height: auto; + + &:hover, + &.FileSelected { + border: 1px solid #2185d0 !important; + .label { + color: #2185d0 !important; + } + } + + i.cloud.icon { + font-size: 3rem; + } + } + + .UploadButtonBox { + text-align: center; + } + + .UploadProgress { + margin-left: calc(210px + 1rem) !important; + width: 100%; + max-width: 600px; + } +} + +.PlayBox { + max-width: 700px; + margin-bottom: 1rem; + h1, h2 { + text-transform: none; + margin: 0; + } + .ContentHeader { + margin-top: 1.5rem; + margin-bottom: .5rem; + } + .DownloadBtn { + float: right; + margin-left: .5rem; + } + .ContentDesc { + margin-top: 1rem; + } +} + +.MediaGrid { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 880px; + + .MediaCell { + width: 25%; + + &.MyContent { + /* background-color: #fff8e1; */ + } + &:hover { + background-color: #deeffc; + } + .CellContent { + padding: 5px; + margin-bottom: 15px; + overflow: hidden; + + h3 { + font-size: 1rem; + font-weight: bold; + margin: 1rem 0 .5rem 0; + } + } + + .ThumbBox { + display: flex; + flex-direction: row; + justify-content: center; + margin-bottom: 5px; + + .ThumbImg { + width: 210px; + max-height: 118px; + display: block; + } + } + } +} + +.EditMetaBox { + display: flex; + flex-direction: row; + flex-wrap: wrap; + /* width: 880px; */ + + .EditMetaThumb { + width: 100%; + max-width: 210px; + max-height: 118px; + margin-right: 1rem; + + img { + width: 100%; + max-width: 210px; + max-height: 118px; + } + } + + .JoySection { + width: 100%; + max-width: 600px; + } +} diff --git a/pioneer/packages/joy-media/src/index.tsx b/pioneer/packages/joy-media/src/index.tsx new file mode 100644 index 0000000000..c499fa2529 --- /dev/null +++ b/pioneer/packages/joy-media/src/index.tsx @@ -0,0 +1,81 @@ + +import React from 'react'; +import { Route, Switch } from 'react-router'; + +import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import Tabs, { TabItem } from '@polkadot/react-components/Tabs'; +import { ApiProps } from '@polkadot/react-api/types'; +import { withMulti } from '@polkadot/react-api/with'; + +import './index.css'; +import './common/index.css'; + +import translate from './translate'; +import { useMyAccount } from '@polkadot/joy-utils/MyAccountContext'; +import { UploadWithRouter } from './Upload'; +import { DiscoveryProviderProps, DiscoveryProviderProvider } from './DiscoveryProvider'; +import { SubstrateTransportProvider } from './TransportContext'; +import { ChannelsByOwnerWithRouter } from './channels/ChannelsByOwner.view'; +import { EditChannelView, EditChannelWithRouter } from './channels/EditChannel.view'; +import { ExploreContentView } from './explore/ExploreContent.view'; +import { ViewChannelWithRouter } from './channels/ViewChannel.view'; +import { EditVideoWithRouter } from './upload/EditVideo.view'; +import { PlayVideoWithRouter } from './video/PlayVideo.view'; +import { AllVideosView } from './explore/AllVideos'; +import { AllChannelsView } from './explore/AllChannels'; +// import { VideosByOwner } from './video/VideosByOwner'; + +type Props = AppProps & I18nProps & ApiProps & DiscoveryProviderProps & {}; + +function App (props: Props) { + const { t, basePath } = props; + const { state: { address: myAddress } } = useMyAccount(); + + const tabs: TabItem[] = [ + { + isRoot: true, + name: 'explore', + text: t('Explore') + }, + !myAddress ? undefined : { + name: `account/${myAddress}/channels`, + text: t('My channels') + } + // !myAddress ? undefined : { + // name: `account/${myAddress}/videos`, + // text: t('My videos') + // } + ].filter(x => x !== undefined) as TabItem[]; + + return ( + + +
+
+ +
+ + + + + + + + + {/* */} + + + + + + +
+
+
+ ); +} + +export default withMulti( + App, + translate +); diff --git a/pioneer/packages/joy-media/src/mocks/ContentLicense.mock.ts b/pioneer/packages/joy-media/src/mocks/ContentLicense.mock.ts new file mode 100644 index 0000000000..e9c5ffc99d --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/ContentLicense.mock.ts @@ -0,0 +1,14 @@ +import { newEntityId } from './EntityId.mock'; +import { ContentLicenseType } from '../schemas/general/ContentLicense'; + +const values = [ + 'Public Domain', + 'Share Alike', + 'No Derivatives', + 'No Commercial' +]; + +export const AllContentLicenses: ContentLicenseType[] = + values.map(value => ({ id: newEntityId(), value })) as unknown as ContentLicenseType[]; // A hack to fix TS compilation. + +export const ContentLicense = AllContentLicenses[0]; diff --git a/pioneer/packages/joy-media/src/mocks/CurationStatus.mock.ts b/pioneer/packages/joy-media/src/mocks/CurationStatus.mock.ts new file mode 100644 index 0000000000..275a650103 --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/CurationStatus.mock.ts @@ -0,0 +1,18 @@ +import { newEntityId } from './EntityId.mock'; +import { CurationStatusType } from '../schemas/general/CurationStatus'; + +function newEntity (value: string): CurationStatusType { + return { id: newEntityId(), value } as unknown as CurationStatusType; // A hack to fix TS compilation. +} + +export const CurationStatus = { + Edited: newEntity('Edited'), + UpdatedSchema: newEntity('Updated schema'), + UnderReview: newEntity('Under review'), + Removed: newEntity('Removed') +}; + +export const AllCurationStatuses: CurationStatusType[] = + Object.values(CurationStatus); + +export const DefaultCurationStatus = CurationStatus.Edited; diff --git a/pioneer/packages/joy-media/src/mocks/EntityId.mock.ts b/pioneer/packages/joy-media/src/mocks/EntityId.mock.ts new file mode 100644 index 0000000000..8b5e2b33c4 --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/EntityId.mock.ts @@ -0,0 +1,9 @@ +let value = 1; + +export function nextEntityId (): number { + return value; +} + +export function newEntityId (): number { + return value++; +} diff --git a/pioneer/packages/joy-media/src/mocks/FeaturedContent.mock.ts b/pioneer/packages/joy-media/src/mocks/FeaturedContent.mock.ts new file mode 100644 index 0000000000..a178d73ded --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/FeaturedContent.mock.ts @@ -0,0 +1,10 @@ +import { newEntityId } from './EntityId.mock'; +import { FeaturedContentType } from '../schemas/general/FeaturedContent'; +import { AllVideos, AllMusicAlbums, Video } from '.'; + +export const FeaturedContent: FeaturedContentType = { + id: newEntityId(), + topVideo: Video, + featuredVideos: AllVideos, + featuredAlbums: AllMusicAlbums +} as unknown as FeaturedContentType; // A hack to fix TS compilation. diff --git a/pioneer/packages/joy-media/src/mocks/Language.mock.ts b/pioneer/packages/joy-media/src/mocks/Language.mock.ts new file mode 100644 index 0000000000..70ebd051fe --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/Language.mock.ts @@ -0,0 +1,11 @@ +import { newEntityId } from './EntityId.mock'; +import { LanguageType } from '../schemas/general/Language'; + +const values = [ + 'aa', 'ab', 'ae', 'af', 'ak', 'am', 'an', 'ar', 'as', 'av', 'ay', 'az', 'ba', 'be', 'bg', 'bh', 'bi', 'bm', 'bn', 'bo', 'br', 'bs', 'ca', 'ce', 'ch', 'co', 'cr', 'cs', 'cu', 'cv', 'cy', 'da', 'de', 'dv', 'dz', 'ee', 'el', 'en', 'eo', 'es', 'et', 'eu', 'fa', 'ff', 'fi', 'fj', 'fo', 'fr', 'fy', 'ga', 'gd', 'gl', 'gn', 'gu', 'gv', 'ha', 'he', 'hi', 'ho', 'hr', 'ht', 'hu', 'hy', 'hz', 'ia', 'id', 'ie', 'ig', 'ii', 'ik', 'io', 'is', 'it', 'iu', 'ja', 'jv', 'ka', 'kg', 'ki', 'kj', 'kk', 'kl', 'km', 'kn', 'ko', 'kr', 'ks', 'ku', 'kv', 'kw', 'ky', 'la', 'lb', 'lg', 'li', 'ln', 'lo', 'lt', 'lu', 'lv', 'mg', 'mh', 'mi', 'mk', 'ml', 'mn', 'mr', 'ms', 'mt', 'my', 'na', 'nb', 'nd', 'ne', 'ng', 'nl', 'nn', 'no', 'nr', 'nv', 'ny', 'oc', 'oj', 'om', 'or', 'os', 'pa', 'pi', 'pl', 'ps', 'pt', 'qu', 'rm', 'rn', 'ro', 'ru', 'rw', 'sa', 'sc', 'sd', 'se', 'sg', 'si', 'sk', 'sl', 'sm', 'sn', 'so', 'sq', 'sr', 'ss', 'st', 'su', 'sv', 'sw', 'ta', 'te', 'tg', 'th', 'ti', 'tk', 'tl', 'tn', 'to', 'tr', 'ts', 'tt', 'tw', 'ty', 'ug', 'uk', 'ur', 'uz', 've', 'vi', 'vo', 'wa', 'wo', 'xh', 'yi', 'yo', 'za', 'zh', 'zu' +]; + +export const AllLanguages: LanguageType[] = + values.map(value => ({ id: newEntityId(), value })) as unknown as LanguageType[]; // A hack to fix TS compilation. + +export const Language = AllLanguages[0]; diff --git a/pioneer/packages/joy-media/src/mocks/MediaObject.mock.ts b/pioneer/packages/joy-media/src/mocks/MediaObject.mock.ts new file mode 100644 index 0000000000..7fce1ed0ee --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/MediaObject.mock.ts @@ -0,0 +1,17 @@ +import { newEntityId } from './EntityId.mock'; +import { MediaObjectType } from '../schemas/general/MediaObject'; + +const values = [ + '5Gm2XPvDm1RhYW2CEbVvrMbRFguL4TMmzP3tm72wvhCZdx5G', + '5GTGqmWTurJhYY5UoHzFrAqAxL5ry4Jegw9pmjKniQ3KWWww', + '5CbyRopmCNwLYyRCwHrmovoQ15MMCau9v9cmazbWuQjY9DG2', + '5GTXWLWgfCM6GpsBkeJQZvF6RFvZh3SjCsH8aGUY1WwV5YGU', + '5CSBeDZR5baBcnLYZsP839P1uqZKfz3D9Uip43uvhUd56XAq', + '5EXsnf4sS6wVsgjqQmT2jchP2LdGXLxZZSJijjTiUxcLm7Vg', + '5HRieqw8oRZfwc6paio4TrBeYvmdTstGB2KoKE9gL5qLnAQY' +]; + +export const AllMediaObjects: MediaObjectType[] = + values.map(value => ({ id: newEntityId(), value })) as unknown as MediaObjectType[]; // A hack to fix TS compilation. + +export const MediaObject = AllMediaObjects[0]; diff --git a/pioneer/packages/joy-media/src/mocks/MusicAlbum.mock.ts b/pioneer/packages/joy-media/src/mocks/MusicAlbum.mock.ts new file mode 100644 index 0000000000..29715b11f6 --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/MusicAlbum.mock.ts @@ -0,0 +1,34 @@ +import { newEntityId } from './EntityId.mock'; +import { MusicAlbumType } from '../schemas/music/MusicAlbum'; +import { MusicGenre } from './MusicGenre.mock'; +import { MusicMood } from './MusicMood.mock'; +import { MusicTheme } from './MusicTheme.mock'; +import { DefaultPublicationStatus } from './PublicationStatus.mock'; +import { DefaultCurationStatus } from './CurationStatus.mock'; +import { ContentLicense } from './ContentLicense.mock'; +import { Language } from './Language.mock'; + +export const MusicAlbum: MusicAlbumType = { + id: newEntityId(), + title: 'Riddle', + artist: 'Liquid Stone', + thumbnail: 'https://images.unsplash.com/photo-1484352491158-830ef5692bb3?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60', + description: 'Building material is any material which is used for construction purposes. Many naturally occurring substances, such as clay, rocks, sand, and wood, even twigs and leaves, have been used to construct buildings.\n\nApart from naturally occurring materials, many man-made products are in use, some more and some less synthetic.', + firstReleased: 567425123, // 1987 year. + genre: MusicGenre, + mood: MusicMood, + theme: MusicTheme, + tracks: [], + language: Language, + link: [], + lyrics: undefined, + composerOrSongwriter: 'Massive Sand', + reviews: [], + publicationStatus: DefaultPublicationStatus, + curationStatus: DefaultCurationStatus, + explicit: false, + license: ContentLicense, + attribution: undefined +} as unknown as MusicAlbumType; // A hack to fix TS compilation. + +export const AllMusicAlbums: MusicAlbumType[] = [MusicAlbum]; diff --git a/pioneer/packages/joy-media/src/mocks/MusicGenre.mock.ts b/pioneer/packages/joy-media/src/mocks/MusicGenre.mock.ts new file mode 100644 index 0000000000..9c005ed01d --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/MusicGenre.mock.ts @@ -0,0 +1,31 @@ +import { newEntityId } from './EntityId.mock'; +import { MusicGenreType } from '../schemas/music/MusicGenre'; + +const values = [ + 'Avant-Garde', + 'Blues', + 'Children\'s', + 'Classical', + 'Comedy/Spoken', + 'Country', + 'Easy Listening', + 'Electronic', + 'Folk', + 'Holiday', + 'International', + 'Jazz', + 'Latin', + 'New Age', + 'Pop/Rock', + 'R&B', + 'Rap', + 'Reggae', + 'Religious', + 'Stage & Screen', + 'Vocal' +]; + +export const AllMusicGenres: MusicGenreType[] = + values.map(value => ({ id: newEntityId(), value })) as unknown as MusicGenreType[]; // A hack to fix TS compilation. + +export const MusicGenre = AllMusicGenres[0]; diff --git a/pioneer/packages/joy-media/src/mocks/MusicMood.mock.ts b/pioneer/packages/joy-media/src/mocks/MusicMood.mock.ts new file mode 100644 index 0000000000..b2624c0176 --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/MusicMood.mock.ts @@ -0,0 +1,299 @@ +import { newEntityId } from './EntityId.mock'; +import { MusicMoodType } from '../schemas/music/MusicMood'; + +const values = [ + 'Acerbic', + 'Aggressive', + 'Agreeable', + 'Airy', + 'Ambitious', + 'Amiable/Good-Natured', + 'Angry', + 'Angst-Ridden', + 'Anguished/Distraught', + 'Angular', + 'Animated', + 'Apocalyptic', + 'Arid', + 'Athletic', + 'Atmospheric', + 'Austere', + 'Autumnal', + 'Belligerent', + 'Benevolent', + 'Bitter', + 'Bittersweet', + 'Bleak', + 'Boisterous', + 'Bombastic', + 'Brash', + 'Brassy', + 'Bravado', + 'Bright', + 'Brittle', + 'Brooding', + 'Calm/Peaceful', + 'Campy', + 'Capricious', + 'Carefree', + 'Cartoonish', + 'Cathartic', + 'Celebratory', + 'Cerebral', + 'Cheerful', + 'Child-like', + 'Circular', + 'Clinical', + 'Cold', + 'Comic', + 'Complex', + 'Concise', + 'Confident', + 'Confrontational', + 'Cosmopolitan', + 'Crunchy', + 'Cynical/Sarcastic', + 'Dark', + 'Declamatory', + 'Defiant', + 'Delicate', + 'Demonic', + 'Desperate', + 'Detached', + 'Devotional', + 'Difficult', + 'Dignified/Noble', + 'Dramatic', + 'Dreamy', + 'Driving', + 'Druggy', + 'Earnest', + 'Earthy', + 'Ebullient', + 'Eccentric', + 'Ecstatic', + 'Eerie', + 'Effervescent', + 'Elaborate', + 'Elegant', + 'Elegiac', + 'Energetic', + 'Enigmatic', + 'Epic', + 'Erotic', + 'Ethereal', + 'Euphoric', + 'Exciting', + 'Exotic', + 'Explosive', + 'Extroverted', + 'Exuberant', + 'Fantastic/Fantasy-like', + 'Feral', + 'Feverish', + 'Fierce', + 'Fiery', + 'Flashy', + 'Flowing', + 'Fractured', + 'Freewheeling', + 'Fun', + 'Funereal', + 'Gentle', + 'Giddy', + 'Gleeful', + 'Gloomy', + 'Graceful', + 'Greasy', + 'Grim', + 'Gritty', + 'Gutsy', + 'Happy', + 'Harsh', + 'Hedonistic', + 'Heroic', + 'Hostile', + 'Humorous', + 'Hungry', + 'Hymn-like', + 'Hyper', + 'Hypnotic', + 'Improvisatory', + 'Indulgent', + 'Innocent', + 'Insular', + 'Intense', + 'Intimate', + 'Introspective', + 'Ironic', + 'Irreverent', + 'Jovial', + 'Joyous', + 'Kinetic', + 'Knotty', + 'Laid-Back/Mellow', + 'Languid', + 'Lazy', + 'Light', + 'Literate', + 'Lively', + 'Lonely', + 'Lush', + 'Lyrical', + 'Macabre', + 'Magical', + 'Majestic', + 'Malevolent', + 'Manic', + 'Marching', + 'Martial', + 'Meandering', + 'Mechanical', + 'Meditative', + 'Melancholy', + 'Menacing', + 'Messy', + 'Mighty', + 'Monastic', + 'Monumental', + 'Motoric', + 'Mysterious', + 'Mystical', + 'Naive', + 'Narcotic', + 'Narrative', + 'Negative', + 'Nervous/Jittery', + 'Nihilistic', + 'Nocturnal', + 'Nostalgic', + 'Ominous', + 'Optimistic', + 'Opulent', + 'Organic', + 'Ornate', + 'Outraged', + 'Outrageous', + 'Paranoid', + 'Passionate', + 'Pastoral', + 'Patriotic', + 'Perky', + 'Philosophical', + 'Plain', + 'Plaintive', + 'Playful', + 'Poignant', + 'Positive', + 'Powerful', + 'Precious', + 'Provocative', + 'Pulsing', + 'Pure', + 'Quirky', + 'Rambunctious', + 'Ramshackle', + 'Raucous', + 'Reassuring/Consoling', + 'Rebellious', + 'Reckless', + 'Refined', + 'Reflective', + 'Regretful', + 'Relaxed', + 'Reserved', + 'Resolute', + 'Restrained', + 'Reverent', + 'Rhapsodic', + 'Rollicking', + 'Romantic', + 'Rousing', + 'Rowdy', + 'Rustic', + 'Sacred', + 'Sad', + 'Sarcastic', + 'Sardonic', + 'Satirical', + 'Savage', + 'Scary', + 'Scattered', + 'Searching', + 'Self-Conscious', + 'Sensual', + 'Sentimental', + 'Serious', + 'Severe', + 'Sexual', + 'Sexy', + 'Shimmering', + 'Silly', + 'Sleazy', + 'Slick', + 'Smooth', + 'Snide', + 'Soft/Quiet', + 'Somber', + 'Soothing', + 'Sophisticated', + 'Spacey', + 'Sparkling', + 'Sparse', + 'Spicy', + 'Spiritual', + 'Spontaneous', + 'Spooky', + 'Sprawling', + 'Sprightly', + 'Springlike', + 'Stately', + 'Street-Smart', + 'Striding', + 'Strong', + 'Stylish', + 'Suffocating', + 'Sugary', + 'Summery', + 'Suspenseful', + 'Swaggering', + 'Sweet', + 'Swinging', + 'Technical', + 'Tender', + 'Tense/Anxious', + 'Theatrical', + 'Thoughtful', + 'Threatening', + 'Thrilling', + 'Thuggish', + 'Tragic', + 'Transparent/Translucent', + 'Trashy', + 'Trippy', + 'Triumphant', + 'Tuneful', + 'Turbulent', + 'Uncompromising', + 'Understated', + 'Unsettling', + 'Uplifting', + 'Urgent', + 'Virile', + 'Visceral', + 'Volatile', + 'Vulgar', + 'Warm', + 'Weary', + 'Whimsical', + 'Wintry', + 'Wistful', + 'Witty', + 'Wry', + 'Yearning' +]; + +export const AllMusicMoods: MusicMoodType[] = + values.map(value => ({ id: newEntityId(), value })) as unknown as MusicMoodType[]; // A hack to fix TS compilation. + +export const MusicMood = AllMusicMoods[0]; diff --git a/pioneer/packages/joy-media/src/mocks/MusicTheme.mock.ts b/pioneer/packages/joy-media/src/mocks/MusicTheme.mock.ts new file mode 100644 index 0000000000..20373fb812 --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/MusicTheme.mock.ts @@ -0,0 +1,192 @@ +import { newEntityId } from './EntityId.mock'; +import { MusicThemeType } from '../schemas/music/MusicTheme'; + +const values = [ + 'Adventure', + 'Affection/Fondness', + 'Affirmation', + 'Anger/Hostility', + 'Animals', + 'Anniversary', + 'Argument', + 'At the Beach', + 'At the Office', + 'Autumn', + 'Award Winners', + 'Awareness', + 'Background Music', + 'Biographical', + 'Birth', + 'Birthday', + 'Breakup', + 'Cars', + 'Celebration', + 'Celebrities', + 'Children', + 'Christmas', + 'Christmas Party', + 'City Life', + 'Classy Gatherings', + 'Club', + 'Comfort', + 'Conflict', + 'Cool & Cocky', + 'Country Life', + 'Crime', + 'D-I-V-O-R-C-E', + 'Dance Party', + 'Day Driving', + 'Daydreaming', + 'Death', + 'Despair', + 'Destiny', + 'Dinner Ambiance', + 'Disappointment', + 'Dreaming', + 'Drinking', + 'Drugs', + 'Early Morning', + 'Easter', + 'Empowering', + 'Everyday Life', + 'Exercise/Workout', + 'Family', + 'Family Gatherings', + 'Fantasy', + 'Fear', + 'Feeling Blue', + 'Flying', + 'Food/Eating', + 'Forgiveness', + 'Fourth of July', + 'Freedom', + 'Friendship', + 'Funeral', + 'Girls Night Out', + 'Good Times', + 'Goodbyes', + 'Graduation', + 'Guys Night Out', + 'Halloween', + 'Hanging Out', + 'Happiness', + 'Healing/Comfort', + 'Heartache', + 'Heartbreak', + 'High School', + 'Historical Events', + 'Holidays', + 'Home', + 'Homecoming', + 'Hope', + 'Housework', + 'Illness', + 'In Love', + 'Introspection', + 'Jealousy', + 'Joy', + 'Late Night', + 'Lifecycle', + 'Loneliness', + 'Long Walk', + 'Loss/Grief', + 'Lying', + 'Magic', + 'Maverick', + 'Meditation', + 'Memorial', + 'Military', + 'Mischievous', + 'Monday Morning', + 'Money', + 'Moon', + 'Morning', + 'Motivation', + 'Music', + 'Myths & Legends', + 'Nature', + 'New Love', + 'Night Driving', + 'Nighttime', + 'Open Road', + 'Other Times & Places', + 'Pain', + 'Parenthood', + 'Partying', + 'Passion', + 'Patriotism', + 'Peace', + 'Picnic', + 'Playful', + 'Poetry', + 'Politics/Society', + 'Pool Party', + 'Prom', + 'Promises', + 'Protest', + 'Rainy Day', + 'Reflection', + 'Regret', + 'Relationships', + 'Relaxation', + 'Religion', + 'Reminiscing', + 'Reunion', + 'Revolutionary', + 'Road Trip', + 'Romance', + 'Romantic Evening', + 'Scary Music', + 'School', + 'Science', + 'SciFi', + 'Seduction', + 'Separation', + 'Sex', + 'Slow Dance', + 'Small Gathering', + 'Solitude', + 'Sorrow', + 'Sports', + 'Spring', + 'Starry Sky', + 'Starting Out', + 'Stay in Bed', + 'Storms', + 'Street Life', + 'Summer', + 'Sun', + 'Sunday Afternoon', + 'Sweet Dreams', + 'Teenagers', + 'Temptation', + 'TGIF', + 'Thanksgiving', + 'The Creative Side', + 'The Great Outdoors', + 'Theme', + 'Tragedy', + 'Travel', + 'Truth', + 'Vacation', + 'Victory', + 'Violence', + 'Visions', + 'War', + 'Water', + 'Weather', + 'Wedding', + 'Winter', + 'Wisdom', + 'Word Play', + 'Work', + 'World View', + 'Yearning', + 'Youth', + 'Zeitgeist' +]; + +export const AllMusicThemes: MusicThemeType[] = + values.map(value => ({ id: newEntityId(), value })) as unknown as MusicThemeType[]; // A hack to fix TS compilation. + +export const MusicTheme = AllMusicThemes[0]; diff --git a/pioneer/packages/joy-media/src/mocks/MusicTrack.mock.ts b/pioneer/packages/joy-media/src/mocks/MusicTrack.mock.ts new file mode 100644 index 0000000000..2fbceb5fd0 --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/MusicTrack.mock.ts @@ -0,0 +1,33 @@ +import { newEntityId } from './EntityId.mock'; +import { MusicTrackType } from '../schemas/music/MusicTrack'; +import { MusicGenre } from './MusicGenre.mock'; +import { MusicMood } from './MusicMood.mock'; +import { MusicTheme } from './MusicTheme.mock'; +import { DefaultPublicationStatus } from './PublicationStatus.mock'; +import { DefaultCurationStatus } from './CurationStatus.mock'; +import { ContentLicense } from './ContentLicense.mock'; +import { Language } from './Language.mock'; + +export const MusicTrack: MusicTrackType = { + id: newEntityId(), + title: 'Requiem (Mozart)', + artist: 'Berlin Philharmonic', + thumbnail: 'https://assets.classicfm.com/2017/36/mozart-1504532179-list-handheld-0.jpg', + description: 'The Requiem in D minor, K. 626, is a requiem mass by Wolfgang Amadeus Mozart (1756–1791). Mozart composed part of the Requiem in Vienna in late 1791, but it was unfinished at his death on 5 December the same year.', + language: Language, + firstReleased: 567425967, // 1987 year. + genre: MusicGenre, + mood: MusicMood, + theme: MusicTheme, + link: [], + composerOrSongwriter: 'Mozart', + lyrics: undefined, + object: undefined, + publicationStatus: DefaultPublicationStatus, + curationStatus: DefaultCurationStatus, + explicit: false, + license: ContentLicense, + attribution: undefined +} as unknown as MusicTrackType; // A hack to fix TS compilation. + +export const AllMusicTracks: MusicTrackType[] = [MusicTrack]; diff --git a/pioneer/packages/joy-media/src/mocks/PublicationStatus.mock.ts b/pioneer/packages/joy-media/src/mocks/PublicationStatus.mock.ts new file mode 100644 index 0000000000..084b3dcfbd --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/PublicationStatus.mock.ts @@ -0,0 +1,16 @@ +import { newEntityId } from './EntityId.mock'; +import { PublicationStatusType } from '../schemas/general/PublicationStatus'; + +function newEntity (value: string): PublicationStatusType { + return { id: newEntityId(), value } as unknown as PublicationStatusType; // A hack to fix TS compilation. +} + +export const PublicationStatus = { + Publiс: newEntity('Publiс'), + Unlisted: newEntity('Unlisted') +}; + +export const AllPublicationStatuses: PublicationStatusType[] = + Object.values(PublicationStatus); + +export const DefaultPublicationStatus = PublicationStatus.Publiс; diff --git a/pioneer/packages/joy-media/src/mocks/Video.mock.ts b/pioneer/packages/joy-media/src/mocks/Video.mock.ts new file mode 100644 index 0000000000..ab6967b356 --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/Video.mock.ts @@ -0,0 +1,53 @@ +import { newEntityId } from './EntityId.mock'; +import { VideoType } from '../schemas/video/Video'; +import { Language } from './Language.mock'; +import { VideoCategory } from './VideoCategory.mock'; +import { DefaultPublicationStatus } from './PublicationStatus.mock'; +import { DefaultCurationStatus } from './CurationStatus.mock'; +import { ContentLicense } from './ContentLicense.mock'; + +const titles = [ + 'Arborvitae (Thuja occidentalis)', + 'Black Ash (Fraxinus nigra)', + 'White Ash (Fraxinus americana)', + 'Bigtooth Aspen (Populus grandidentata)', + 'Quaking Aspen (Populus tremuloides)', + 'Basswood (Tilia americana)', + 'American Beech (Fagus grandifolia)', + 'Black Birch (Betula lenta)', + 'Gray Birch (Betula populifolia)', + 'Paper Birch (Betula papyrifera)', + 'Yellow Birch (Betula alleghaniensis)', + 'Butternut (Juglans cinerea)', + 'Black Cherry (Prunus serotina)', + 'Pin Cherry (Prunus pensylvanica)' +]; + +const thumbnails = [ + 'https://images.unsplash.com/photo-1477414348463-c0eb7f1359b6?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=60', + 'https://images.unsplash.com/photo-1484352491158-830ef5692bb3?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60', + 'https://images.unsplash.com/photo-1543467091-5f0406620f8b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + 'https://images.unsplash.com/photo-1526749837599-b4eba9fd855e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + 'https://images.unsplash.com/photo-1504567961542-e24d9439a724?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + 'https://images.unsplash.com/photo-1543716091-a840c05249ec?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60', + 'https://images.unsplash.com/photo-1444465693019-aa0b6392460d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=200&q=60' +]; + +export const AllVideos: VideoType[] = thumbnails.map((thumbnail, i) => ({ + id: newEntityId(), + title: titles[i], + thumbnail, + description: 'Nature, in the broadest sense, is the natural, physical, or material world or universe. "Nature" can refer to the phenomena of the physical world, and also to life in general. The study of nature is a large, if not the only, part of science. Although humans are part of nature, human activity is often understood as a separate category from other natural phenomena.', + language: Language, + firstReleased: 567425543, // 1987 year. + category: VideoCategory, + link: [], + object: undefined, + publicationStatus: DefaultPublicationStatus, + curationStatus: DefaultCurationStatus, + explicit: true, + license: ContentLicense, + attribution: undefined +})) as unknown as VideoType[]; // A hack to fix TS compilation. + +export const Video = AllVideos[0]; diff --git a/pioneer/packages/joy-media/src/mocks/VideoCategory.mock.ts b/pioneer/packages/joy-media/src/mocks/VideoCategory.mock.ts new file mode 100644 index 0000000000..d235004232 --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/VideoCategory.mock.ts @@ -0,0 +1,25 @@ +import { newEntityId } from './EntityId.mock'; +import { VideoCategoryType } from '../schemas/video/VideoCategory'; + +const values = [ + 'Film & Animation', + 'Autos & Vehicles', + 'Music', + 'Pets & Animals', + 'Sports', + 'Travel & Events', + 'Gaming', + 'People & Blogs', + 'Comedy', + 'Entertainment', + 'News & Politics', + 'Howto & Style', + 'Education', + 'Science & Technology', + 'Nonprofits & Activism' +]; + +export const AllVideoCategories: VideoCategoryType[] = + values.map(value => ({ id: newEntityId(), value })) as unknown as VideoCategoryType[]; // A hack to fix TS compilation. + +export const VideoCategory = AllVideoCategories[0]; diff --git a/pioneer/packages/joy-media/src/mocks/index.ts b/pioneer/packages/joy-media/src/mocks/index.ts new file mode 100644 index 0000000000..8427d9995d --- /dev/null +++ b/pioneer/packages/joy-media/src/mocks/index.ts @@ -0,0 +1,14 @@ +export * from './ContentLicense.mock'; +export * from './CurationStatus.mock'; +export * from './EntityId.mock'; +export * from './FeaturedContent.mock'; +export * from './Language.mock'; +export * from './MediaObject.mock'; +export * from './MusicAlbum.mock'; +export * from './MusicGenre.mock'; +export * from './MusicMood.mock'; +export * from './MusicTheme.mock'; +export * from './MusicTrack.mock'; +export * from './PublicationStatus.mock'; +export * from './Video.mock'; +export * from './VideoCategory.mock'; diff --git a/pioneer/packages/joy-media/src/music/EditAlbumModal.tsx b/pioneer/packages/joy-media/src/music/EditAlbumModal.tsx new file mode 100644 index 0000000000..8c16f9b078 --- /dev/null +++ b/pioneer/packages/joy-media/src/music/EditAlbumModal.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Button, Modal } from 'semantic-ui-react'; +import { TracksOfMyMusicAlbumProps, TracksOfMyMusicAlbum } from './MusicAlbumTracks'; + +export const EditAlbumModal = (props: TracksOfMyMusicAlbumProps) => { + return Edit album} centered={false}> + Edit My Album + + + + + + ; +}; diff --git a/pioneer/packages/joy-media/src/music/EditMusicAlbum.tsx b/pioneer/packages/joy-media/src/music/EditMusicAlbum.tsx new file mode 100644 index 0000000000..4d8cd00f27 --- /dev/null +++ b/pioneer/packages/joy-media/src/music/EditMusicAlbum.tsx @@ -0,0 +1,180 @@ +import React from 'react'; +import { Button, Tab } from 'semantic-ui-react'; +import { Form, withFormik } from 'formik'; +import { History } from 'history'; + +import TxButton from '@polkadot/joy-utils/TxButton'; +import { onImageError } from '@polkadot/joy-utils/images'; +import { ReorderableTracks } from './ReorderableTracks'; +import { MusicAlbumValidationSchema, MusicAlbumType, MusicAlbumClass as Fields, MusicAlbumFormValues, MusicAlbumToFormValues } from '../schemas/music/MusicAlbum'; +import { withMediaForm, MediaFormProps, datePlaceholder } from '../common/MediaForms'; +import EntityId from '@joystream/types/versioned-store/EntityId'; +import { MediaDropdownOptions } from '../common/MediaDropdownOptions'; +import { MusicTrackReaderPreviewProps } from './MusicTrackReaderPreview'; +import { FormTabs } from '../common/FormTabs'; + +export type OuterProps = { + history?: History; + id?: EntityId; + entity?: MusicAlbumType; + tracks?: MusicTrackReaderPreviewProps[]; + opts?: MediaDropdownOptions; +}; + +type FormValues = MusicAlbumFormValues; + +const InnerForm = (props: MediaFormProps) => { + const { + // React components for form fields: + MediaText, + MediaDropdown, + LabelledField, + + // Callbacks: + onSubmit, + onTxSuccess, + onTxFailed, + + // history, + entity, + tracks = [], + opts = MediaDropdownOptions.Empty, + + values, + dirty, + errors, + isValid, + isSubmitting, + resetForm + } = props; + + const { thumbnail } = values; + + const isNew = !entity; + + const buildTxParams = () => { + if (!isValid) return []; + + return []; + }; + + const basicInfoTab = () => + + + + + + + + + ; + + const additionalTab = () => + + + + + + + + ; + + const tracksTab = () => + This album has no tracks yet.} + /> + ; + + const tabs = ; + + const renderMainButton = () => + ; + + return
+
+ {thumbnail && } +
+ +
+ + {tabs} + + + {renderMainButton()} +
; +}; + +export const EditForm = withFormik({ + + // Transform outer props into form values + mapPropsToValues: (props): FormValues => { + const { entity } = props; + return MusicAlbumToFormValues(entity); + }, + + validationSchema: MusicAlbumValidationSchema, + + handleSubmit: () => { + // do submitting things + } +})(withMediaForm(InnerForm) as any); + +export default EditForm; diff --git a/pioneer/packages/joy-media/src/music/EditMusicAlbum.view.tsx b/pioneer/packages/joy-media/src/music/EditMusicAlbum.view.tsx new file mode 100644 index 0000000000..4e52b8657c --- /dev/null +++ b/pioneer/packages/joy-media/src/music/EditMusicAlbum.view.tsx @@ -0,0 +1,15 @@ +import { MediaView } from '../MediaView'; +import { OuterProps, EditForm } from './EditMusicAlbum'; + +export const EditMusicAlbumView = MediaView({ + component: EditForm, + triggers: ['id'], + resolveProps: async (props) => { + const { transport, id } = props; + const entity = id ? await transport.musicAlbumById(id) : undefined; + const opts = await transport.dropdownOptions(); + return { entity, opts }; + } +}); + +export default EditMusicAlbumView; diff --git a/pioneer/packages/joy-media/src/music/MusicAlbumPreview.tsx b/pioneer/packages/joy-media/src/music/MusicAlbumPreview.tsx new file mode 100644 index 0000000000..3281bdf322 --- /dev/null +++ b/pioneer/packages/joy-media/src/music/MusicAlbumPreview.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { Button } from 'semantic-ui-react'; +import { Pluralize } from '@polkadot/joy-utils/Pluralize'; +import { BgImg } from '../common/BgImg'; +import { ChannelEntity } from '../entities/ChannelEntity'; + +export type MusicAlbumPreviewProps = { + id: string; + title: string; + artist: string; + cover: string; + tracksCount: number; + + // Extra props: + channel?: ChannelEntity; + size?: number; + withActions?: boolean; +}; + +export function MusicAlbumPreview (props: MusicAlbumPreviewProps) { + const { size = 200 } = props; + + // TODO show the channel this album belongs to. + + return
+ + + +
+

{props.title}

+
{props.artist}
+
+
+ + {props.withActions &&
+
} +
; +} diff --git a/pioneer/packages/joy-media/src/music/MusicAlbumTracks.tsx b/pioneer/packages/joy-media/src/music/MusicAlbumTracks.tsx new file mode 100644 index 0000000000..ff48a86e2e --- /dev/null +++ b/pioneer/packages/joy-media/src/music/MusicAlbumTracks.tsx @@ -0,0 +1,58 @@ +import React, { useState } from 'react'; +import { Button, CheckboxProps } from 'semantic-ui-react'; +import { Pluralize } from '@polkadot/joy-utils/Pluralize'; +import { EditableMusicTrackPreviewProps, MusicTrackPreview } from './MusicTrackPreview'; +import { MusicAlbumPreviewProps, MusicAlbumPreview } from './MusicAlbumPreview'; + +export type TracksOfMyMusicAlbumProps = { + album: MusicAlbumPreviewProps; + tracks?: EditableMusicTrackPreviewProps[]; +}; + +export function TracksOfMyMusicAlbum (props: TracksOfMyMusicAlbumProps) { + const [idxsOfSelectedTracks, setIdxsOfSelectedTracks] = useState(new Set()); + + const { album, tracks = [] } = props; + const tracksCount = (tracks && tracks.length) || 0; + + const onTrackSelect = ( + trackIdx: number, + _event: React.FormEvent, + data: CheckboxProps + ) => { + const set = new Set(idxsOfSelectedTracks); + data.checked + ? set.add(trackIdx) + : set.delete(trackIdx) + ; + setIdxsOfSelectedTracks(set); + }; + + const selectedCount = idxsOfSelectedTracks.size; + + const removeButtonText = Remove from album; + + return <> + + +
+
+ +
+ {tracksCount === 0 + ? This album has no tracks yet + : tracks.map((track, i) => + onTrackSelect(i, e, d)} + withRemoveButton + /> + ) + } +
+ ; +} diff --git a/pioneer/packages/joy-media/src/music/MusicTrackPreview.tsx b/pioneer/packages/joy-media/src/music/MusicTrackPreview.tsx new file mode 100644 index 0000000000..09b980fe71 --- /dev/null +++ b/pioneer/packages/joy-media/src/music/MusicTrackPreview.tsx @@ -0,0 +1,58 @@ +import React, { useState } from 'react'; +import { Button, Checkbox, CheckboxProps } from 'semantic-ui-react'; + +type OnCheckboxChange = (event: React.FormEvent, data: CheckboxProps) => void; + +export type EditableMusicTrackPreviewProps = { + id: string; + title: string; + artist: string; + thumbnail: string; + position?: number; + selected?: boolean; + onSelect?: OnCheckboxChange; + onEdit?: () => void; + onRemove?: () => void; + withEditButton?: boolean; + withRemoveButton?: boolean; + withActionLabels?: boolean; + isDraggable?: boolean; +}; + +export function MusicTrackPreview (props: EditableMusicTrackPreviewProps) { + const { + withActionLabels = false, + selected = false, + onEdit = () => { /* do nothing */ }, + onRemove = () => { /* do nothing */ } + } = props; + + const [checked, setChecked] = useState(selected); + + const onChange: OnCheckboxChange = (e, d) => { + try { + props.onSelect && props.onSelect(e, d); + } catch (err) { + console.log('Error during checkbox change:', err); + } + setChecked(d.checked || false); + }; + + return
+ {props.onSelect &&
+ +
} + {props.position &&
{props.position}
} +
+ +
+
+

{props.title}

+
{props.artist}
+
+
+ {props.withEditButton &&
+
; +} diff --git a/pioneer/packages/joy-media/src/music/MusicTrackReaderPreview.tsx b/pioneer/packages/joy-media/src/music/MusicTrackReaderPreview.tsx new file mode 100644 index 0000000000..caccabf532 --- /dev/null +++ b/pioneer/packages/joy-media/src/music/MusicTrackReaderPreview.tsx @@ -0,0 +1,30 @@ +import React, { CSSProperties } from 'react'; +import { BgImg } from '../common/BgImg'; + +export type MusicTrackReaderPreviewProps = { + id: string; + title: string; + artist: string; + thumbnail: string; + size?: number; + orientation?: 'vertical' | 'horizontal'; +}; + +export function MusicTrackReaderPreview (props: MusicTrackReaderPreviewProps) { + const { size = 200, orientation = 'vertical' } = props; + + const descStyle: CSSProperties = {}; + if (orientation === 'vertical') { + descStyle.maxWidth = size; + } + + return
+ + + +
+

{props.title}

+
{props.artist}
+
+
; +} diff --git a/pioneer/packages/joy-media/src/music/MyMusicAlbums.tsx b/pioneer/packages/joy-media/src/music/MyMusicAlbums.tsx new file mode 100644 index 0000000000..3fb498e2dc --- /dev/null +++ b/pioneer/packages/joy-media/src/music/MyMusicAlbums.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Button } from 'semantic-ui-react'; +import { MusicAlbumPreviewProps, MusicAlbumPreview } from './MusicAlbumPreview'; + +export type MyMusicAlbumsProps = { + albums?: MusicAlbumPreviewProps[]; +}; + +export function MyMusicAlbums (props: MyMusicAlbumsProps) { + const { albums = [] } = props; + const albumCount = (albums && albums.length) || 0; + + return <> +

{`My music albums (${albumCount})`}

+
+
+
+ {albumCount === 0 + ? {'You don\'t have music albums yet'} + : albums.map((album, i) => + + ) + } +
+ ; +} diff --git a/pioneer/packages/joy-media/src/music/MyMusicTracks.tsx b/pioneer/packages/joy-media/src/music/MyMusicTracks.tsx new file mode 100644 index 0000000000..2f5a964617 --- /dev/null +++ b/pioneer/packages/joy-media/src/music/MyMusicTracks.tsx @@ -0,0 +1,160 @@ +import React, { useState } from 'react'; +import { Button, CheckboxProps, Dropdown, Message } from 'semantic-ui-react'; + +import { Pluralize } from '@polkadot/joy-utils/Pluralize'; +import Section from '@polkadot/joy-utils/Section'; +import { EditableMusicTrackPreviewProps, MusicTrackPreview } from './MusicTrackPreview'; +import { ReorderableTracks } from './ReorderableTracks'; +import { MusicAlbumPreviewProps } from './MusicAlbumPreview'; + +export type MyMusicTracksProps = { + albums?: MusicAlbumPreviewProps[]; + tracks?: EditableMusicTrackPreviewProps[]; +}; + +export function MyMusicTracks (props: MyMusicTracksProps) { + const [idsOfSelectedTracks, setIdsOfSelectedTracks] = useState(new Set()); + + const onTrackSelect = ( + track: EditableMusicTrackPreviewProps, + _event: React.FormEvent, + data: CheckboxProps + ) => { + const { id } = track; + const set = new Set(idsOfSelectedTracks); + + data.checked + ? set.add(id) + : set.delete(id) + ; + setIdsOfSelectedTracks(set); + }; + + const { albums = [], tracks = [] } = props; + const albumsCount = albums.length; + const tracksCount = tracks.length; + const selectedCount = idsOfSelectedTracks.size; + + let longestAlbumName = ''; + albums.forEach(x => { + if (longestAlbumName.length < x.title.length) { + longestAlbumName = x.title; + } + }); + + const albumsDropdownOptions = albums.map(x => { + const { id } = x; + return { + key: id, + value: id, + text: x.title, + image: x.cover + }; + }); + + const [showSecondScreen, setShowSecondScreen] = useState(false); + const [albumName, setAlbumName] = useState(); + + const AlbumDropdown = () => { + const style = { + display: 'inline-block', + opacity: selectedCount ? 1 : 0, + + // This is a required hack to fit every dropdown items on a single line: + minWidth: `${longestAlbumName.length / 1.5}rem` + }; + + return
+ { + const selectedAlbum = albums.find(x => x.id === id); + if (selectedAlbum) { + setAlbumName(selectedAlbum.title); + setShowSecondScreen(true); + } + }} + options={albumsDropdownOptions} + placeholder='Select an album' + search + selection + value={albumName} + /> +
; + }; + + const AddTracksText = () => albumsCount + ? + Add to + + : + You have no albums. + + +
+ +
+ ); +}; + +// Helper that provides additional wrappers for proposal forms + +export function withProposalFormData ( + FormContainerComponent: React.ComponentType +): React.ComponentType { + return withMulti(FormContainerComponent, withOnlyMembers, withCalls('query.balances.totalIssuance')); +} diff --git a/pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx b/pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx new file mode 100644 index 0000000000..4c228e15dd --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx @@ -0,0 +1,117 @@ +import React from 'react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import { + GenericProposalForm, + GenericFormValues, + genericFormDefaultValues, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps +} from './GenericProposalForm'; +import { FormField } from './FormFields'; +import { ProposalType } from '@polkadot/joy-utils/types/proposals'; +import { WorkingGroupKey, WorkingGroupDef } from '@joystream/types/common'; +import './forms.css'; +import { Dropdown, Message } from 'semantic-ui-react'; +import { usePromise, useTransport } from '@polkadot/joy-utils/react/hooks'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; +import { WorkerData } from '@polkadot/joy-utils/types/workingGroups'; +import { LeadInfo } from '@polkadot/joy-utils/react/components/working-groups/LeadInfo'; + +export type FormValues = GenericFormValues & { + workingGroup: WorkingGroupKey; +}; + +export const defaultValues: FormValues = { + ...genericFormDefaultValues, + workingGroup: 'Storage' +}; + +// Aditional props coming all the way from export comonent into the inner form. +type FormAdditionalProps = { + txMethod: string; + submitParams: any[]; + proposalType: ProposalType; + showLead?: boolean; + leadRequired?: boolean; + leadStakeRequired?: boolean; + leadRewardRequired?: boolean; + onLeadChange?: (lead: WorkerData | null) => void; + disabled?: boolean; +}; + +// We don't exactly use "container" and "export" components here, but those types are useful for +// generiting the right "FormInnerProps" +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +export type FormInnerProps = ProposalFormInnerProps; + +export const GenericWorkingGroupProposalForm: React.FunctionComponent = props => { + const { + handleChange, + errors, + touched, + values, + showLead = true, + leadRequired = false, + leadStakeRequired = false, + leadRewardRequired = false, + onLeadChange, + disabled = false + } = props; + const transport = useTransport(); + const [lead, error, loading] = usePromise( + () => transport.workingGroups.currentLead(values.workingGroup), + null, + [values.workingGroup], + onLeadChange + ); + const leadRes = { lead, error, loading }; + const leadMissing = leadRequired && (!leadRes.loading && !leadRes.error) && !leadRes.lead; + const stakeMissing = leadStakeRequired && (!leadRes.loading && !leadRes.error) && (leadRes.lead && !leadRes.lead.stake); + const rewardMissing = leadRewardRequired && (!leadRes.loading && !leadRes.error) && (leadRes.lead && !leadRes.lead.reward); + const isDisabled = disabled || leadMissing || stakeMissing || rewardMissing || leadRes.error; + + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + return ( + + + ({ text: wgKey + ' Working Group', value: wgKey }))} + value={values.workingGroup} + onChange={ handleChange } + /> + + { showLead && ( + + + + ) } + { leadMissing && ( + + Leader required + Selected working group has no active leader. An active leader is required in order to create this proposal. + + ) } + { stakeMissing && ( + + No role stake + Selected working group leader has no associated role stake, which is required in order to create this proposal. + + ) } + { rewardMissing && ( + + No reward relationship + Selected working group leader has no reward relationship, which is required in order to create this proposal. + + ) } + { props.children } + + ); +}; diff --git a/pioneer/packages/joy-proposals/src/forms/LabelWithHelp.tsx b/pioneer/packages/joy-proposals/src/forms/LabelWithHelp.tsx new file mode 100644 index 0000000000..d8880ba5bc --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/LabelWithHelp.tsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react'; +import { Icon, Label, Transition } from 'semantic-ui-react'; + +type LabelWithHelpProps = { text: string; help: string }; + +export default function LabelWithHelp (props: LabelWithHelpProps) { + const [open, setOpen] = useState(false); + return ( + + ); +} diff --git a/pioneer/packages/joy-proposals/src/forms/MintCapacityForm.tsx b/pioneer/packages/joy-proposals/src/forms/MintCapacityForm.tsx new file mode 100644 index 0000000000..8c6c0296cc --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/MintCapacityForm.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import * as Yup from 'yup'; +import { getFormErrorLabelsProps } from './errorHandling'; +import { + GenericProposalForm, + GenericFormValues, + genericFormDefaultOptions, + genericFormDefaultValues, + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps +} from './GenericProposalForm'; +import Validation from '../validationSchema'; +import { InputFormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import { ProposalType } from '@polkadot/joy-utils/types/proposals'; +import { formatBalance } from '@polkadot/util'; +import './forms.css'; + +export type FormValues = GenericFormValues & { + capacity: string; +}; + +const defaultValues: FormValues = { + ...genericFormDefaultValues, + capacity: '' +}; + +type MintCapacityGroup = 'Council' | 'Content Working Group'; + +// Aditional props coming all the way from export comonent into the inner form. +type FormAdditionalProps = { + mintCapacityGroup: MintCapacityGroup; + txMethod: string; + proposalType: ProposalType; +}; +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +const MintCapacityForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, mintCapacityGroup, values, txMethod, initialData, proposalType } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + return ( + + + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.SetContentWorkingGroupMintCapacity() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'MintCapacityForm' +})(MintCapacityForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/RuntimeUpgradeForm.tsx b/pioneer/packages/joy-proposals/src/forms/RuntimeUpgradeForm.tsx new file mode 100644 index 0000000000..ea16a093b7 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/RuntimeUpgradeForm.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { Form } from 'semantic-ui-react'; +import * as Yup from 'yup'; +import { + GenericProposalForm, + GenericFormValues, + genericFormDefaultOptions, + genericFormDefaultValues, + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps +} from './GenericProposalForm'; +import Validation from '../validationSchema'; +import { withFormContainer } from './FormContainer'; +import './forms.css'; +import FileDropdown from './FileDropdown'; + +export type FormValues = GenericFormValues & { + // wasm blob as ArrayBuffer, or an Error string + WASM: ArrayBuffer | string; +}; + +const defaultValues: FormValues = { + ...genericFormDefaultValues, + WASM: new ArrayBuffer(0) +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +const RuntimeUpgradeForm: React.FunctionComponent = props => { + const { errors, setFieldValue, setFieldTouched, values, touched } = props; + return ( + + + + setFieldValue={setFieldValue} + setFieldTouched={setFieldTouched} + defaultText="Drag-n-drop WASM bytecode of a runtime upgrade (*.wasm)" + acceptedFormats=".wasm" + name="WASM" + error={touched.WASM ? errors.WASM : undefined} + interpretAs='binary' + /> + + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.RuntimeUpgrade() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'RuntimeUpgradeForm' +})(RuntimeUpgradeForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupLeadForm.tsx b/pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupLeadForm.tsx new file mode 100644 index 0000000000..17bb481874 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupLeadForm.tsx @@ -0,0 +1,188 @@ +import React, { useEffect, useState } from 'react'; +import { Dropdown, Label, Loader, Message, Icon, DropdownItemProps, DropdownOnSearchChangeData, DropdownProps } from 'semantic-ui-react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import * as Yup from 'yup'; +import { + GenericProposalForm, + GenericFormValues, + genericFormDefaultOptions, + genericFormDefaultValues, + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps +} from './GenericProposalForm'; +import Validation from '../validationSchema'; +import { FormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; +import { Membership } from '@joystream/types/members'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; +import _ from 'lodash'; +import './forms.css'; + +export type FormValues = GenericFormValues & { + workingGroupLead: any; +}; + +const defaultValues: FormValues = { + ...genericFormDefaultValues, + workingGroupLead: '' +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +function memberOptionKey (id: number, profile: Membership) { + return `${id}:${profile.root_account.toString()}`; +} + +const MEMBERS_QUERY_MIN_LENGTH = 4; +const MEMBERS_NONE_OPTION: DropdownItemProps = { + key: '- NONE -', + text: '- NONE -', + value: 'none' +}; + +function membersToOptions (members: { id: number; profile: Membership }[]) { + return [MEMBERS_NONE_OPTION].concat( + members + .map(({ id, profile }) => ({ + key: profile.handle, + text: `${profile.handle} (id:${id})`, + value: memberOptionKey(id, profile), + image: profile.avatar_uri.toString() ? { avatar: true, src: profile.avatar_uri } : null + })) + ); +} + +function filterMembers (options: DropdownItemProps[], query: string) { + if (query.length < MEMBERS_QUERY_MIN_LENGTH) { + return [MEMBERS_NONE_OPTION]; + } + const regexp = new RegExp(_.escapeRegExp(query)); + return options.filter((opt) => regexp.test((opt.text || '').toString())); +} + +type MemberWithId = { id: number; profile: Membership }; + +const SetContentWorkingGroupsLeadForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, values } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + // State + const [membersOptions, setMembersOptions] = useState([] as DropdownItemProps[]); + const [filteredOptions, setFilteredOptions] = useState([] as DropdownItemProps[]); + const [membersSearchQuery, setMembersSearchQuery] = useState(''); + // Transport + const transport = useTransport(); + const [members, /* error */, loading] = usePromise( + () => transport.council.membersExceptCouncil(), + [] + ); + const [currentLead, clError, clLoading] = usePromise( + () => transport.contentWorkingGroup.currentLead(), + null + ); + // Generate members options array on load + useEffect(() => { + if (members.length) { + setMembersOptions(membersToOptions(members)); + } + }, [members]); + // Filter options on search query change (we "pulled-out" this logic here to avoid lags) + useEffect(() => { + setFilteredOptions(filterMembers(membersOptions, membersSearchQuery)); + }, [membersSearchQuery]); + + return ( + + + {loading ? ( + <> + Fetching members... + + ) : (<> + + { + (!values.workingGroupLead || membersSearchQuery.length > 0) && + (MEMBERS_QUERY_MIN_LENGTH - membersSearchQuery.length) > 0 && ( + + ) + } + options } + // On search change we update it in our state + onSearchChange={ (e: React.SyntheticEvent, data: DropdownOnSearchChangeData) => { + setMembersSearchQuery(data.searchQuery); + } } + name="workingGroupLead" + placeholder={ 'Start typing member handle or "id:[ID]" query...' } + fluid + selection + options={filteredOptions} + onChange={ + (e: React.ChangeEvent, data: DropdownProps) => { + // Fix TypeScript issue + const originalHandler = handleChange as (e: React.ChangeEvent, data: DropdownProps) => void; + originalHandler(e, data); + if (!data.value) { + setMembersSearchQuery(''); + } + } + } + value={values.workingGroupLead} + /> + {errorLabelsProps.workingGroupLead && + + + + Current Content Working Group lead: { (currentLead && currentLead.profile.handle) || 'NONE' } + + + )} + + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.SetLead() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'SetContentWorkingGroupLeadForm' +})(SetContentWorkingGroupsLeadForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupMintCapForm.tsx b/pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupMintCapForm.tsx new file mode 100644 index 0000000000..be7f149cb1 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupMintCapForm.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import MintCapacityForm from './MintCapacityForm'; +import { RouteComponentProps } from 'react-router'; +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; + +const ContentWorkingGroupMintCapForm = (props: RouteComponentProps) => { + const transport = useTransport(); + const [mintCapacity, error, loading] = usePromise(() => transport.contentWorkingGroup.currentMintCap(), 0); + + return ( + + + + ); +}; + +export default ContentWorkingGroupMintCapForm; diff --git a/pioneer/packages/joy-proposals/src/forms/SetCouncilMintCapForm.tsx b/pioneer/packages/joy-proposals/src/forms/SetCouncilMintCapForm.tsx new file mode 100644 index 0000000000..6e646fc10b --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SetCouncilMintCapForm.tsx @@ -0,0 +1,14 @@ +// import React from 'react'; +// import { default as MintCapacityForm } from './MintCapacityForm'; +import { RouteComponentProps } from 'react-router'; + +const CouncilMintCapForm = (props: RouteComponentProps) => ( + null + // +); + +export default CouncilMintCapForm; diff --git a/pioneer/packages/joy-proposals/src/forms/SetCouncilParamsForm.tsx b/pioneer/packages/joy-proposals/src/forms/SetCouncilParamsForm.tsx new file mode 100644 index 0000000000..1cafa3f9c8 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SetCouncilParamsForm.tsx @@ -0,0 +1,212 @@ +import React, { useEffect, useState } from 'react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import { Divider, Form } from 'semantic-ui-react'; +import * as Yup from 'yup'; +import { + GenericProposalForm, + GenericFormValues, + genericFormDefaultOptions, + genericFormDefaultValues, + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps +} from './GenericProposalForm'; +import Validation from '../validationSchema'; +import { InputFormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import { createType } from '@polkadot/types'; +import './forms.css'; +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; +import _ from 'lodash'; +import { ElectionParameters } from '@joystream/types/council'; +import { PromiseComponent } from '@polkadot/joy-utils/react/components'; + +export type FormValues = GenericFormValues & { + announcingPeriod: string; + votingPeriod: string; + minVotingStake: string; + revealingPeriod: string; + minCouncilStake: string; + newTermDuration: string; + candidacyLimit: string; + councilSize: string; +}; + +const defaultValues: FormValues = { + ...genericFormDefaultValues, + announcingPeriod: '', + votingPeriod: '', + minVotingStake: '', + revealingPeriod: '', + minCouncilStake: '', + newTermDuration: '', + candidacyLimit: '', + councilSize: '' +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +function createElectionParameters (values: FormValues): ElectionParameters { + return new ElectionParameters({ + announcing_period: createType('BlockNumber', parseInt(values.announcingPeriod)), + voting_period: createType('BlockNumber', parseInt(values.votingPeriod)), + revealing_period: createType('BlockNumber', parseInt(values.revealingPeriod)), + council_size: createType('u32', values.councilSize), + candidacy_limit: createType('u32', values.candidacyLimit), + new_term_duration: createType('BlockNumber', parseInt(values.newTermDuration)), + min_council_stake: createType('Balance', values.minCouncilStake), + min_voting_stake: createType('Balance', values.minVotingStake) + }); +} + +const SetCouncilParamsForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, values, setFieldValue, setFieldError } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + const [placeholders, setPlaceholders] = useState<{ [k in keyof FormValues]: string }>(defaultValues); + + const transport = useTransport(); + const [councilParams, error, loading] = usePromise(() => transport.council.electionParameters(), null); + useEffect(() => { + if (councilParams) { + const fetchedPlaceholders = { ...placeholders }; + const fieldsToPopulate = [ + 'announcing_period', + 'voting_period', + 'min_voting_stake', + 'revealing_period', + 'min_council_stake', + 'new_term_duration', + 'candidacy_limit', + 'council_size' + ] as const; + fieldsToPopulate.forEach(field => { + const camelCaseField = _.camelCase(field) as keyof FormValues; + setFieldValue(camelCaseField, councilParams[field].toString()); + fetchedPlaceholders[camelCaseField] = councilParams[field].toString(); + }); + setPlaceholders(fetchedPlaceholders); + } + }, [councilParams]); + + // This logic may be moved somewhere else in the future, but it's quite easy to enforce it here: + if (!errors.candidacyLimit && !errors.councilSize && parseInt(values.candidacyLimit) < parseInt(values.councilSize)) { + setFieldError('candidacyLimit', `Candidacy limit must be >= council size (${values.councilSize})`); + } + + return ( + + + Voting + + + + + + + Council + + + + + + + + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.SetElectionParameters() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'SetCouncilParamsForm' +})(SetCouncilParamsForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/SetMaxValidatorCountForm.tsx b/pioneer/packages/joy-proposals/src/forms/SetMaxValidatorCountForm.tsx new file mode 100644 index 0000000000..e274d2dff6 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SetMaxValidatorCountForm.tsx @@ -0,0 +1,78 @@ +import React, { useEffect } from 'react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import * as Yup from 'yup'; +import { + GenericProposalForm, + GenericFormValues, + genericFormDefaultOptions, + genericFormDefaultValues, + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps +} from './GenericProposalForm'; +import Validation from '../validationSchema'; +import { InputFormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; +import './forms.css'; + +export type FormValues = GenericFormValues & { + maxValidatorCount: string; +}; + +const defaultValues: FormValues = { + ...genericFormDefaultValues, + maxValidatorCount: '' +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +const SetMaxValidatorCountForm: React.FunctionComponent = props => { + const transport = useTransport(); + const [validatorCount] = usePromise(() => transport.validators.maxCount(), 20); + const { handleChange, errors, touched, values, setFieldValue } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + + useEffect(() => { + if (validatorCount) { + setFieldValue('maxValidatorCount', validatorCount); + } + }, [validatorCount]); + return ( + + + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.SetValidatorCount() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'SetMaxValidatorCountForm' +})(SetMaxValidatorCountForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/SetWorkingGroupLeadRewardForm.tsx b/pioneer/packages/joy-proposals/src/forms/SetWorkingGroupLeadRewardForm.tsx new file mode 100644 index 0000000000..39f15b2a13 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SetWorkingGroupLeadRewardForm.tsx @@ -0,0 +1,93 @@ +import React, { useState } from 'react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import * as Yup from 'yup'; +import { + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps, + genericFormDefaultOptions +} from './GenericProposalForm'; +import { + GenericWorkingGroupProposalForm, + FormValues as WGFormValues, + defaultValues as wgFromDefaultValues +} from './GenericWorkingGroupProposalForm'; +import { InputFormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import './forms.css'; +import { Grid } from 'semantic-ui-react'; +import { formatBalance } from '@polkadot/util'; +import _ from 'lodash'; +import Validation from '../validationSchema'; +import { WorkerData } from '@polkadot/joy-utils/types/workingGroups'; + +export type FormValues = WGFormValues & { + amount: string; +}; + +const defaultValues: FormValues = { + ...wgFromDefaultValues, + amount: '' +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +const SetWorkingGroupLeadRewardForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, values, myMemberId } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + const [lead, setLead] = useState(null); + + return ( + setLead(lead)} + submitParams={[ + myMemberId, + values.title, + values.rationale, + '{STAKE}', + lead?.workerId, + values.amount, + values.workingGroup + ]} + > + { (lead && lead.reward) && ( + + + + + + ) } + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.SetWorkingGroupLeaderReward() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'SetWorkingGroupLeadRewardForm' +})(SetWorkingGroupLeadRewardForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/SetWorkingGroupMintCapacityForm.tsx b/pioneer/packages/joy-proposals/src/forms/SetWorkingGroupMintCapacityForm.tsx new file mode 100644 index 0000000000..a53006273f --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SetWorkingGroupMintCapacityForm.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import * as Yup from 'yup'; +import { + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps, + genericFormDefaultOptions +} from './GenericProposalForm'; +import { + GenericWorkingGroupProposalForm, + FormValues as WGFormValues, + defaultValues as wgFromDefaultValues +} from './GenericWorkingGroupProposalForm'; +import { InputFormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import './forms.css'; +import { Grid } from 'semantic-ui-react'; +import { formatBalance } from '@polkadot/util'; +import _ from 'lodash'; +import Validation from '../validationSchema'; + +export type FormValues = WGFormValues & { + capacity: string; +}; + +const defaultValues: FormValues = { + ...wgFromDefaultValues, + capacity: '' +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +const SetWorkingGroupMintCapacityForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, values, myMemberId } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + + return ( + + + + + + + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.SetWorkingGroupMintCapacity() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'SetWorkingGroupMintCapacityForm' +})(SetWorkingGroupMintCapacityForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/SignalForm.tsx b/pioneer/packages/joy-proposals/src/forms/SignalForm.tsx new file mode 100644 index 0000000000..1163346db8 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SignalForm.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import * as Yup from 'yup'; +import { + GenericProposalForm, + GenericFormValues, + genericFormDefaultOptions, + genericFormDefaultValues, + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps +} from './GenericProposalForm'; +import Validation from '../validationSchema'; +import { TextareaFormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import './forms.css'; + +export type FormValues = GenericFormValues & { + description: string; +}; + +const defaultValues: FormValues = { + ...genericFormDefaultValues, + description: '' +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +const SignalForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, values } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + + return ( + + + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.Text() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'SignalForm' +})(SignalForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/SlashWorkingGroupLeadStakeForm.tsx b/pioneer/packages/joy-proposals/src/forms/SlashWorkingGroupLeadStakeForm.tsx new file mode 100644 index 0000000000..c9e4692a62 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SlashWorkingGroupLeadStakeForm.tsx @@ -0,0 +1,101 @@ +import React, { useState, useEffect } from 'react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import * as Yup from 'yup'; +import { + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps, + genericFormDefaultOptions +} from './GenericProposalForm'; +import { + GenericWorkingGroupProposalForm, + FormValues as WGFormValues, + defaultValues as wgFromDefaultValues +} from './GenericWorkingGroupProposalForm'; +import { InputFormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import './forms.css'; +import { Grid } from 'semantic-ui-react'; +import { formatBalance } from '@polkadot/util'; +import _ from 'lodash'; +import Validation from '../validationSchema'; +import { WorkerData } from '@polkadot/joy-utils/types/workingGroups'; + +export type FormValues = WGFormValues & { + amount: string; +}; + +const defaultValues: FormValues = { + ...wgFromDefaultValues, + amount: '' +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +const SlashWorkingGroupLeadStakeForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, values, myMemberId, setFieldError } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + const [lead, setLead] = useState(null); + // Here we validate if stake <= current lead stake. + // Because it depends on selected working group, + // there's no easy way to do it using validationSchema + useEffect(() => { + if (lead && parseInt(values.amount) > (lead.stake || 0) && !errors.amount) { + setFieldError('amount', `The stake cannot exceed current leader's stake (${formatBalance(lead.stake)})`); + } + }); + + return ( + setLead(lead)} + submitParams={[ + myMemberId, + values.title, + values.rationale, + '{STAKE}', + lead?.workerId, + values.amount, + values.workingGroup + ]} + > + { (lead && lead.stake) && ( + + + + + + ) } + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.SlashWorkingGroupLeaderStake() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'SlashWorkingGroupLeadStakeForm' +})(SlashWorkingGroupLeadStakeForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/SpendingProposalForm.tsx b/pioneer/packages/joy-proposals/src/forms/SpendingProposalForm.tsx new file mode 100644 index 0000000000..125e2535d9 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/SpendingProposalForm.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { getFormErrorLabelsProps } from './errorHandling'; +import * as Yup from 'yup'; +import { Label } from 'semantic-ui-react'; +import { + GenericProposalForm, + GenericFormValues, + genericFormDefaultOptions, + genericFormDefaultValues, + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps +} from './GenericProposalForm'; +import Validation from '../validationSchema'; +import { InputFormField, FormField } from './FormFields'; +import { withFormContainer } from './FormContainer'; +import { InputAddress } from '@polkadot/react-components/index'; +import { formatBalance } from '@polkadot/util'; +import './forms.css'; + +export type FormValues = GenericFormValues & { + destinationAccount: any; + tokens: string; +}; + +const defaultValues: FormValues = { + ...genericFormDefaultValues, + destinationAccount: '', + tokens: '' +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export comonent into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps; +type FormInnerProps = ProposalFormInnerProps; + +const SpendingProposalForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, values, setFieldValue } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + return ( + + + + setFieldValue('destinationAccount', address)} + type="all" + placeholder="Select Destination Account" + value={values.destinationAccount} + /> + {errorLabelsProps.destinationAccount && + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.Spending() + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'SpendingProposalsForm' +})(SpendingProposalForm); + +export default withProposalFormData(FormContainer); diff --git a/pioneer/packages/joy-proposals/src/forms/TerminateWorkingGroupLeaderForm.tsx b/pioneer/packages/joy-proposals/src/forms/TerminateWorkingGroupLeaderForm.tsx new file mode 100644 index 0000000000..c44b225042 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/TerminateWorkingGroupLeaderForm.tsx @@ -0,0 +1,125 @@ +import React, { useState } from 'react'; +import * as Yup from 'yup'; +import { + withProposalFormData, + ProposalFormExportProps, + ProposalFormContainerProps, + ProposalFormInnerProps, + genericFormDefaultOptions +} from './GenericProposalForm'; +import { + GenericWorkingGroupProposalForm, + FormValues as WGFormValues, + defaultValues as wgFromDefaultValues +} from './GenericWorkingGroupProposalForm'; +import { withFormContainer } from './FormContainer'; +import './forms.css'; +import _ from 'lodash'; +import Validation from '../validationSchema'; +import { WorkerData } from '@polkadot/joy-utils/types/workingGroups'; +import { getFormErrorLabelsProps } from './errorHandling'; +import FormField, { TextareaFormField } from './FormFields'; +import { Checkbox } from 'semantic-ui-react'; +import { TerminateRoleParameters } from '@joystream/types/proposals'; +import { WorkerId } from '@joystream/types/working-group'; +import { Bytes } from '@polkadot/types'; +import { WorkingGroup, InputValidationLengthConstraint } from '@joystream/types/common'; +import { bool as Bool } from '@polkadot/types/primitive'; +import { withCalls } from '@polkadot/react-api'; +import { formatBalance } from '@polkadot/util'; + +export type FormValues = WGFormValues & { + terminationRationale: string; + slashStake: boolean; +}; + +const defaultValues: FormValues = { + ...wgFromDefaultValues, + terminationRationale: '', + slashStake: false +}; + +type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form. +type ExportComponentProps = ProposalFormExportProps; +type FormContainerProps = ProposalFormContainerProps & { + terminationRationaleConstraint?: InputValidationLengthConstraint; +}; +type FormInnerProps = ProposalFormInnerProps; + +const valuesToTerminateRoleParams = (values: FormValues, lead: WorkerData): TerminateRoleParameters => { + return new TerminateRoleParameters({ + worker_id: new WorkerId(lead.workerId), + rationale: new Bytes(values.terminationRationale), + slash: lead.stake ? new Bool(values.slashStake) : new Bool(false), + working_group: new WorkingGroup(values.workingGroup) + }); +}; + +const TerminateWorkingGroupLeaderForm: React.FunctionComponent = props => { + const { handleChange, errors, touched, values, myMemberId, setFieldValue } = props; + const errorLabelsProps = getFormErrorLabelsProps(errors, touched); + const [lead, setLead] = useState(null); + + return ( + setLead(lead)} + submitParams={[ + myMemberId, + values.title, + values.rationale, + '{STAKE}', + lead && valuesToTerminateRoleParams(values, lead) + ]} + > + { lead && (<> + + { lead.stake && ( + + { setFieldValue('slashStake', data.checked); }} + label={ `Slash leader stake (${formatBalance(lead.stake)})` } + checked={values.slashStake}/> + + ) } + ) } + + ); +}; + +const FormContainer = withFormContainer({ + mapPropsToValues: (props: FormContainerProps) => ({ + ...defaultValues, + ...(props.initialData || {}) + }), + validationSchema: (props: FormContainerProps) => Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.TerminateWorkingGroupLeaderRole( + props.terminationRationaleConstraint || InputValidationLengthConstraint.createWithMaxAllowed() + ) + }), + handleSubmit: genericFormDefaultOptions.handleSubmit, + displayName: 'TerminateWorkingGroupLeaderForm' +})(TerminateWorkingGroupLeaderForm); + +export default withCalls( + ['query.storageWorkingGroup.workerExitRationaleText', { propName: 'terminationRationaleConstraint' }] +)( + withProposalFormData(FormContainer) +); diff --git a/pioneer/packages/joy-proposals/src/forms/errorHandling.ts b/pioneer/packages/joy-proposals/src/forms/errorHandling.ts new file mode 100644 index 0000000000..8e5c1ea272 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/errorHandling.ts @@ -0,0 +1,35 @@ +import { FormikErrors, FormikTouched } from 'formik'; +import { LabelProps } from 'semantic-ui-react'; + +type FieldErrorLabelProps = LabelProps | undefined; // This is used for displaying semantic-ui errors +export type FormErrorLabelsProps = { [T in keyof ValuesT]: FieldErrorLabelProps }; + +// Single form field error state. +// Takes formik "errors" and "touched" objects and the field name as arguments. +// Returns value to use ie. in the semantic-ui Form.Input error prop. +export function getErrorLabelProps ( + errors: FormikErrors, + touched: FormikTouched, + fieldName: keyof ValuesT, + pointing: LabelProps['pointing'] = undefined + +): FieldErrorLabelProps | undefined { + return (errors[fieldName] && touched[fieldName]) + ? { content: errors[fieldName], pointing, size: 'large' } + : undefined; +} + +// All form fields error states (uses default value for "pointing"). +// Takes formik "errors" and "touched" objects as arguments. +// Returns object with field names as properties and values that can be used ie. for semantic-ui Form.Input error prop +export function getFormErrorLabelsProps ( + errors: FormikErrors, + touched: FormikTouched +): FormErrorLabelsProps { + const errorStates: Partial> = {}; + for (const fieldName in errors) { + errorStates[fieldName] = getErrorLabelProps(errors, touched, fieldName); + } + + return errorStates as FormErrorLabelsProps; +} diff --git a/pioneer/packages/joy-proposals/src/forms/forms.css b/pioneer/packages/joy-proposals/src/forms/forms.css new file mode 100644 index 0000000000..d800c9c497 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/forms.css @@ -0,0 +1,23 @@ +.Forms { + .proposal-form { + margin: 0 auto; + } + + .ui.form.proposal-form { + & label { + font-size: 1rem; + } + + & input[name="tokens"] { + max-width: 16rem; + } + } + + .form-buttons { + display: flex; + } + + .ui.dropdown .ui.avatar.image { + width: 2em !important; + } +} diff --git a/pioneer/packages/joy-proposals/src/forms/index.ts b/pioneer/packages/joy-proposals/src/forms/index.ts new file mode 100644 index 0000000000..5faaa3ca94 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/forms/index.ts @@ -0,0 +1,17 @@ +export { default as SignalForm } from './SignalForm'; +export { default as SpendingProposalForm } from './SpendingProposalForm'; +export { default as MintCapacityForm } from './MintCapacityForm'; +export { default as SetCouncilParamsForm } from './SetCouncilParamsForm'; +export { default as SetContentWorkingGroupLeadForm } from './SetContentWorkingGroupLeadForm'; +export { default as RuntimeUpgradeForm } from './RuntimeUpgradeForm'; +export { default as SetContentWorkingGroupMintCapForm } from './SetContentWorkingGroupMintCapForm'; +export { default as SetCouncilMintCapForm } from './SetCouncilMintCapForm'; +export { default as SetMaxValidatorCountForm } from './SetMaxValidatorCountForm'; +export { default as AddWorkingGroupOpeningForm } from './AddWorkingGroupOpeningForm'; +export { default as SetWorkingGroupMintCapacityForm } from './SetWorkingGroupMintCapacityForm'; +export { default as BeginReviewLeaderApplicationsForm } from './BeginReviewLeaderApplicationsForm'; +export { default as FillWorkingGroupLeaderOpeningForm } from './FillWorkingGroupLeaderOpeningForm'; +export { default as DecreaseWorkingGroupLeadStakeFrom } from './DecreaseWorkingGroupLeadStakeForm'; +export { default as SlashWorkingGroupLeadStakeForm } from './SlashWorkingGroupLeadStakeForm'; +export { default as SetWorkingGroupLeadRewardForm } from './SetWorkingGroupLeadRewardForm'; +export { default as TerminateWorkingGroupLeaderForm } from './TerminateWorkingGroupLeaderForm'; diff --git a/pioneer/packages/joy-proposals/src/index.css b/pioneer/packages/joy-proposals/src/index.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/joy-proposals/src/index.tsx b/pioneer/packages/joy-proposals/src/index.tsx new file mode 100644 index 0000000000..ffa06eae0a --- /dev/null +++ b/pioneer/packages/joy-proposals/src/index.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import { Route, Switch } from 'react-router'; +import { Link } from 'react-router-dom'; +import styled from 'styled-components'; +import { Breadcrumb } from 'semantic-ui-react'; + +import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import { TransportProvider } from '@polkadot/joy-utils/react/context'; +import { ProposalPreviewList, ProposalFromId, ChooseProposalType } from './Proposal'; +import _ from 'lodash'; + +import './index.css'; + +import translate from './translate'; +import NotDone from './NotDone'; +import { + SignalForm, + SpendingProposalForm, + SetContentWorkingGroupLeadForm, + SetContentWorkingGroupMintCapForm, + SetCouncilParamsForm, + SetMaxValidatorCountForm, + RuntimeUpgradeForm, + AddWorkingGroupOpeningForm, + SetWorkingGroupMintCapacityForm, + BeginReviewLeaderApplicationsForm, + FillWorkingGroupLeaderOpeningForm, + DecreaseWorkingGroupLeadStakeFrom, + SlashWorkingGroupLeadStakeForm, + SetWorkingGroupLeadRewardForm, + TerminateWorkingGroupLeaderForm +} from './forms'; + +interface Props extends AppProps, I18nProps {} + +const StyledHeader = styled.header` + text-align: left; + + .ui.breadcrumb { + padding: 1.4rem 0 0 .4rem; + font-size: 1.4rem; + } +`; + +function App (props: Props): React.ReactElement { + const { basePath } = props; + + return ( + +
+ + + + ( + <> + Proposals + + New proposal + + {_.startCase(props.match.params.type)} + + )} /> + + Proposals + + New proposal + + + Proposals + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ ); +} + +export default translate(App); diff --git a/pioneer/packages/joy-proposals/src/stories/ProposalDetails.stories.tsx b/pioneer/packages/joy-proposals/src/stories/ProposalDetails.stories.tsx new file mode 100644 index 0000000000..4a7f3246cf --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/ProposalDetails.stories.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import '../index.css'; + +import MockProposalDetails from './data/ProposalDetails.mock'; +import { ProposalDetails } from '../Proposal'; + +export default { + title: 'Proposals | Details' +}; + +export const HasToVote = () => ; + +export const VotedApproved = () => ( + +); + +export const VotedAbstain = () => ( + +); + +export const VotedReject = () => ( + +); + +export const VotedSlash = () => ; diff --git a/pioneer/packages/joy-proposals/src/stories/ProposalForms.stories.tsx b/pioneer/packages/joy-proposals/src/stories/ProposalForms.stories.tsx new file mode 100644 index 0000000000..8741642416 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/ProposalForms.stories.tsx @@ -0,0 +1,32 @@ +import '../index.css'; +import { + SignalForm, + SpendingProposalForm, + SetCouncilParamsForm, + SetContentWorkingGroupLeadForm, + RuntimeUpgradeForm, + SetContentWorkingGroupMintCapForm, + SetCouncilMintCapForm, + SetMaxValidatorCountForm +} from '../forms'; +import withMock from './withMock'; + +export default { + title: 'Proposals | Forms' +}; + +export const Signal = () => withMock(SignalForm); + +export const SpendingProposal = () => withMock(SpendingProposalForm); + +export const SetCouncilParams = () => withMock(SetCouncilParamsForm); + +export const SetContentWorkingGroupLead = () => withMock(SetContentWorkingGroupLeadForm); + +export const RuntimeUpgrade = () => withMock(RuntimeUpgradeForm); + +export const ContentWorkingGroupMintCap = () => withMock(SetContentWorkingGroupMintCapForm); + +export const CouncilMintCap = () => withMock(SetCouncilMintCapForm); + +export const SetMaxValidatorCount = () => withMock(SetMaxValidatorCountForm); diff --git a/pioneer/packages/joy-proposals/src/stories/ProposalPreview.stories.tsx b/pioneer/packages/joy-proposals/src/stories/ProposalPreview.stories.tsx new file mode 100644 index 0000000000..bd24681372 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/ProposalPreview.stories.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import '../index.css'; + +import MockProposalPreview from './data/ProposalPreview.mock'; +import { ProposalPreview } from '../Proposal'; + +export default { + title: 'Proposals | Preview' +}; + +export const Default = () => ; diff --git a/pioneer/packages/joy-proposals/src/stories/ProposalPreviewList.stories.tsx b/pioneer/packages/joy-proposals/src/stories/ProposalPreviewList.stories.tsx new file mode 100644 index 0000000000..dcd08b3f8a --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/ProposalPreviewList.stories.tsx @@ -0,0 +1,9 @@ +import '../index.css'; +import { ProposalPreviewList } from '../Proposal'; +import withMock from './withMock'; + +export default { + title: 'Proposals | Preview List' +}; + +export const Default = () => withMock(ProposalPreviewList); diff --git a/pioneer/packages/joy-proposals/src/stories/ProposalTypes.stories.tsx b/pioneer/packages/joy-proposals/src/stories/ProposalTypes.stories.tsx new file mode 100644 index 0000000000..fd64981595 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/ProposalTypes.stories.tsx @@ -0,0 +1,9 @@ +import '../index.css'; +import { ChooseProposalType } from '../Proposal'; +import withMock from './withMock'; + +export default { + title: 'Proposals | Proposal Types' +}; + +export const Default = () => withMock(ChooseProposalType); diff --git a/pioneer/packages/joy-proposals/src/stories/data/ProposalDetails.mock.ts b/pioneer/packages/joy-proposals/src/stories/data/ProposalDetails.mock.ts new file mode 100644 index 0000000000..474db8c862 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/data/ProposalDetails.mock.ts @@ -0,0 +1,51 @@ +import { ParsedProposal } from '@polkadot/joy-utils/types/proposals'; +import { ProposalId } from '@joystream/types/proposals'; + +const mockedProposal: ParsedProposal = { + id: new ProposalId(100), + title: 'Awesome Proposal', + description: 'Please send me some tokens for coffee', + createdAtBlock: 36, + type: 'Text', + details: ['Ciao'], + parameters: { + approvalQuorumPercentage: 66, + approvalThresholdPercentage: 80, + gracePeriod: 0, + requiredStake: 101520, + slashingQuorumPercentage: 60, + slashingThresholdPercentage: 80, + votingPeriod: 7200 + }, + proposerId: 303, + status: { + Active: { + stakeId: 0, + sourceAccountId: '5C4hrfkRjSLwQSFVtCvtbV6wctV1WFnkiexUZWLAh4Bc7jib' + } + }, + proposer: { + about: 'Bob', + avatar_uri: 'https://react.semantic-ui.com/images/avatar/large/steve.jpg', + controller_account: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', + handle: 'bob55', + registered_at_block: 18, + registered_at_time: 1588087314000, + entry: { + Paid: 0 + }, + root_account: '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', + subscription: null, + suspended: false + }, + votingResults: { + abstensions: 3, + approvals: 0, + rejections: 1, + slashes: 0 + }, + createdAt: new Date('Mar 25, 2020 at 14:20'), + cancellationFee: 5 +}; + +export default mockedProposal; diff --git a/pioneer/packages/joy-proposals/src/stories/data/ProposalPreview.mock.ts b/pioneer/packages/joy-proposals/src/stories/data/ProposalPreview.mock.ts new file mode 100644 index 0000000000..ff6cfecb7f --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/data/ProposalPreview.mock.ts @@ -0,0 +1,8 @@ +import { ProposalPreviewProps } from '../../Proposal/ProposalPreview'; +import mockedProposal from '../data/ProposalDetails.mock'; + +const mockedProposalPreview: ProposalPreviewProps = { + proposal: mockedProposal +}; + +export default mockedProposalPreview; diff --git a/pioneer/packages/joy-proposals/src/stories/data/ProposalPreviewList.mock.ts b/pioneer/packages/joy-proposals/src/stories/data/ProposalPreviewList.mock.ts new file mode 100644 index 0000000000..4cf5485ca6 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/data/ProposalPreviewList.mock.ts @@ -0,0 +1,95 @@ +import mockedProposal from './ProposalDetails.mock'; + +// const MockProposalPreviewList: ParsedProposal[] = [ +// { +// title: "Send me some tokens for coffee", +// description: +// "Change the total reward across all validators in a given block. This is not the direct reward, but base reward for Pallet staking module. The minimum value must be greater than 450 tJOY based on current runtime. Also, coffee is getting expensive.", +// finalized: "approved", +// details: { +// createdBy: { +// name: "Satoshi", +// avatar: "https://react.semantic-ui.com/images/avatar/large/steve.jpg" +// }, +// stage: "Finalized", +// substage: "Grace Period", +// createdAt: "Mar 25, 2020 at 14:20", +// type: "Spending Proposal", +// expiresIn: 5678 +// } +// }, +// { +// title: "Send me some tokens for coffee", +// description: +// "Change the total reward across all validators in a given block. This is not the direct reward, but base reward for Pallet staking module. The minimum value must be greater than 450 tJOY based on current runtime. Also, coffee is getting expensive.", + +// finalized: "slashed", +// details: { +// createdBy: { +// name: "David Douglas", +// avatar: "https://react.semantic-ui.com/images/avatar/large/elliot.jpg" +// }, +// stage: "Active", +// substage: "Grace Period", +// createdAt: "Mar 25, 2020 at 14:20", +// type: "Spending Proposal", +// expiresIn: 5678 +// } +// }, +// { +// title: "Send me some tokens for coffee", +// description: +// "Change the total reward across all validators in a given block. This is not the direct reward, but base reward for Pallet staking module. The minimum value must be greater than 450 tJOY based on current runtime. Also, coffee is getting expensive.", + +// finalized: "approved", +// details: { +// createdBy: { +// name: "David Douglas", +// avatar: "https://react.semantic-ui.com/images/avatar/large/elliot.jpg" +// }, +// stage: "Active", +// substage: "Grace Period", +// createdAt: "Mar 25, 2020 at 14:20", +// type: "Spending Proposal", +// expiresIn: 5678 +// } +// }, +// { +// title: "Send me some tokens for coffee", +// description: +// "Change the total reward across all validators in a given block. This is not the direct reward, but base reward for Pallet staking module. The minimum value must be greater than 450 tJOY based on current runtime. Also, coffee is getting expensive.", + +// finalized: "approved", +// details: { +// createdBy: { +// name: "David Douglas", +// avatar: "https://react.semantic-ui.com/images/avatar/large/elliot.jpg" +// }, +// stage: "Active", +// substage: "Grace Period", +// createdAt: "Mar 25, 2020 at 14:20", +// type: "Spending Proposal", +// expiresIn: 5678 +// } +// }, +// { +// title: "Send me some tokens for coffee", +// description: +// "Change the total reward across all validators in a given block. This is not the direct reward, but base reward for Pallet staking module. The minimum value must be greater than 450 tJOY based on current runtime. Also, coffee is getting expensive.", + +// finalized: "withdrawn", +// details: { +// createdBy: { +// name: "David Douglas", +// avatar: "https://react.semantic-ui.com/images/avatar/large/elliot.jpg" +// }, +// stage: "Active", +// substage: "Grace Period", +// createdAt: "Mar 25, 2020 at 14:20", +// type: "Spending Proposal", +// expiresIn: 5678 +// } +// } +// ]; +const MockProposalPreviewList = Array.from({ length: 5 }, (_, i) => mockedProposal); +export default MockProposalPreviewList; diff --git a/pioneer/packages/joy-proposals/src/stories/data/ProposalTypesInfo.mock.ts b/pioneer/packages/joy-proposals/src/stories/data/ProposalTypesInfo.mock.ts new file mode 100644 index 0000000000..c545757586 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/data/ProposalTypesInfo.mock.ts @@ -0,0 +1,151 @@ +import { ProposalTypeInfo } from '../../Proposal/ProposalTypePreview'; +import { Categories } from '@polkadot/joy-utils/types/proposals'; + +const MockProposalTypesInfo: ProposalTypeInfo[] = [ + { + type: 'Text', + category: Categories.other, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 5, + cancellationFee: 0, + gracePeriod: 0, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + }, + { + type: 'Spending', + category: Categories.other, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 10, + cancellationFee: 5, + gracePeriod: 3, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + }, + { + type: 'RuntimeUpgrade', + category: Categories.other, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 100, + cancellationFee: 10, + gracePeriod: 14, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + }, + { + type: 'EvictStorageProvider', + category: Categories.other, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 100, + cancellationFee: 10, + gracePeriod: 1, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + }, + { + type: 'SetStorageRoleParameters', + category: Categories.other, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 500, + cancellationFee: 60, + gracePeriod: 14, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + }, + { + type: 'SetValidatorCount', + category: Categories.validators, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 45, + cancellationFee: 10, + gracePeriod: 5, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + }, + { + type: 'SetContentWorkingGroupMintCapacity', + category: Categories.cwg, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 90, + cancellationFee: 8, + gracePeriod: 5, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + }, + { + type: 'SetLead', + category: Categories.cwg, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 500, + cancellationFee: 50, + gracePeriod: 7, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + }, + { + type: 'SetElectionParameters', + category: Categories.council, + description: + 'Change the total reward across all validators in a given block.' + + 'This is not the direct reward, but base reward for Pallet staking module.' + + 'The minimum value must be greater than 450 tJOY based on current runtime.', + stake: 1000, + cancellationFee: 100, + gracePeriod: 30, + votingPeriod: 10000, + approvalQuorum: 80, + approvalThreshold: 80, + slashingQuorum: 80, + slashingThreshold: 80 + } +]; + +export default MockProposalTypesInfo; diff --git a/pioneer/packages/joy-proposals/src/stories/withMock.tsx b/pioneer/packages/joy-proposals/src/stories/withMock.tsx new file mode 100644 index 0000000000..23d921e795 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/stories/withMock.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { createMemoryHistory, createLocation } from 'history'; +import { match, RouteComponentProps } from 'react-router'; + +const history = createMemoryHistory(); +const path = '/'; +const matchObj: match<{}> = { + isExact: false, + path, + url: path, + params: {} +}; +const location = createLocation(path); + +const MockRouteProps: RouteComponentProps = { + history, + match: matchObj, + location +}; + +export default function withMock (Component: React.ComponentType) { + // TODO: Use mock transport + return ; +} diff --git a/pioneer/packages/joy-proposals/src/translate.ts b/pioneer/packages/joy-proposals/src/translate.ts new file mode 100644 index 0000000000..ebd6954450 --- /dev/null +++ b/pioneer/packages/joy-proposals/src/translate.ts @@ -0,0 +1,3 @@ +import { withTranslation } from 'react-i18next'; + +export default withTranslation(['proposals', 'ui']); diff --git a/pioneer/packages/joy-proposals/src/validationSchema.ts b/pioneer/packages/joy-proposals/src/validationSchema.ts new file mode 100644 index 0000000000..7b3c68c28c --- /dev/null +++ b/pioneer/packages/joy-proposals/src/validationSchema.ts @@ -0,0 +1,434 @@ +import * as Yup from 'yup'; +import { schemaValidator, ActivateOpeningAtKeys } from '@joystream/types/hiring'; +import { ProposalTypes } from '@polkadot/joy-utils/types/proposals'; +import { GenericFormValues } from './forms/GenericProposalForm'; +import { InputValidationLengthConstraint } from '@joystream/types/common'; +import { FormValues as SignalFormValues } from './forms/SignalForm'; +import { FormValues as RuntimeUpgradeFormValues } from './forms/RuntimeUpgradeForm'; +import { FormValues as SetCouncilParamsFormValues } from './forms/SetCouncilParamsForm'; +import { FormValues as SpendingProposalFormValues } from './forms/SpendingProposalForm'; +import { FormValues as SetContentWorkingGroupLeadFormValues } from './forms/SetContentWorkingGroupLeadForm'; +import { FormValues as SetContentWorkingGroupMintCapacityFormValues } from './forms/MintCapacityForm'; +import { FormValues as SetMaxValidatorCountFormValues } from './forms/SetMaxValidatorCountForm'; +import { FormValues as AddWorkingGroupLeaderOpeningFormValues } from './forms/AddWorkingGroupOpeningForm'; +import { FormValues as SetWorkingGroupMintCapacityFormValues } from './forms/SetWorkingGroupMintCapacityForm'; +import { FormValues as BeginReviewLeaderApplicationsFormValues } from './forms/BeginReviewLeaderApplicationsForm'; +import { FormValues as FillWorkingGroupLeaderOpeningFormValues } from './forms/FillWorkingGroupLeaderOpeningForm'; +import { FormValues as DecreaseWorkingGroupLeadStakeFormValues } from './forms/DecreaseWorkingGroupLeadStakeForm'; +import { FormValues as SlashWorkingGroupLeadStakeFormValues } from './forms/SlashWorkingGroupLeadStakeForm'; +import { FormValues as SetWorkingGroupLeadRewardFormValues } from './forms/SetWorkingGroupLeadRewardForm'; +import { FormValues as TerminateWorkingGroupLeaderFormValues } from './forms/TerminateWorkingGroupLeaderForm'; + +// TODO: If we really need this (currency unit) we can we make "Validation" a functiction that returns an object. +// We could then "instantialize" it in "withFormContainer" where instead of passing +// "validationSchema" (in each form component file) we would just pass "validationSchemaKey" or just "proposalType" (ie. SetLead). +// Then we could let the "withFormContainer" handle the actual "validationSchema" for "withFormik". In that case it could easily +// pass stuff like totalIssuance or currencyUnit here (ie.: const validationSchema = Validation(currencyUnit, totalIssuance)[proposalType];) +const CURRENCY_UNIT = undefined; + +// All +const TITLE_MAX_LENGTH = 40; +const RATIONALE_MAX_LENGTH = 3000; + +// Text +const DESCRIPTION_MAX_LENGTH = 5000; + +// Runtime Upgrade +const FILE_SIZE_BYTES_MIN = 1; +const FILE_SIZE_BYTES_MAX = 2000000; + +// Set Election Parameters +const ANNOUNCING_PERIOD_MAX = 43200; +const ANNOUNCING_PERIOD_MIN = 14400; +const VOTING_PERIOD_MIN = 14400; +const VOTING_PERIOD_MAX = 28800; +const REVEALING_PERIOD_MIN = 14400; +const REVEALING_PERIOD_MAX = 28800; +const MIN_COUNCIL_STAKE_MIN = 1; +const MIN_COUNCIL_STAKE_MAX = 100000; +const NEW_TERM_DURATION_MIN = 14400; +const NEW_TERM_DURATION_MAX = 432000; +const CANDIDACY_LIMIT_MIN = 25; +const CANDIDACY_LIMIT_MAX = 100; +const COUNCIL_SIZE_MAX = 20; +const COUNCIL_SIZE_MIN = 4; +const MIN_VOTING_STAKE_MIN = 1; +const MIN_VOTING_STAKE_MAX = 100000; + +// Spending +const TOKENS_MIN = 0; +const TOKENS_MAX = 2000000; + +// Set Validator Count +const MAX_VALIDATOR_COUNT_MIN = 4; +const MAX_VALIDATOR_COUNT_MAX = 100; + +// Content Working Group Mint Capacity +const MINT_CAPACITY_MIN = 0; +const MINT_CAPACITY_MAX = 1000000; + +// Add Working Group Leader Opening Parameters +// TODO: Discuss the actual values +const MIN_EXACT_BLOCK_MINUS_CURRENT = 14400 * 5; // ~5 days +const MAX_EXACT_BLOCK_MINUS_CURRENT = 14400 * 60; // 2 months +const MAX_REVIEW_PERIOD_LENGTH_MIN = 14400 * 5; // ~5 days +const MAX_REVIEW_PERIOD_LENGTH_MAX = 14400 * 60; // 2 months +const MAX_APPLICATIONS_MIN = 1; +const MAX_APPLICATIONS_MAX = 1000; +const APPLICATION_STAKE_VALUE_MIN = 1; +const APPLICATION_STAKE_VALUE_MAX = 1000000; +const ROLE_STAKE_VALUE_MIN = 1; +const ROLE_STAKE_VALUE_MAX = 1000000; +const TERMINATE_ROLE_UNSTAKING_MIN = 0; +const TERMINATE_ROLE_UNSTAKING_MAX = 14 * 14400; // 14 days +const LEAVE_ROLE_UNSTAKING_MIN = 0; +const LEAVE_ROLE_UNSTAKING_MAX = 14 * 14400; // 14 days + +// Set Working Group Mint Capacity +// TODO: Discuss the actual values +const WG_MINT_CAP_MIN = 0; +const WG_MINT_CAP_MAX = 1000000; + +// Fill Working Group Leader Opening / Set Working Group Lead Reward +// TODO: Discuss the actual values +const MIN_REWARD_AMOUNT = 1; +const MAX_REWARD_AMOUNT = 100000; +const MIN_REWARD_INTERVAL = 1; +const MAX_REWARD_INTERVAL = 30 * 14400; // 30 days +// 3 days margin (voting_period) to prevent FillOpeningInvalidNextPaymentBlock +// Should we worry that much about it though? +const MIN_NEXT_PAYMENT_BLOCK_MINUS_CURRENT = 3 * 14400; +const MAX_NEXT_PAYMENT_BLOCK_MINUS_CURRENT = 30 * 14400; // 30 days + +// Decrease/Slash Working Group Leader Stake +const DECREASE_LEAD_STAKE_MIN = 1; +const SLASH_LEAD_STAKE_MIN = 1; +// Max is validated in form component, because it depends on selected working group's leader stake + +function errorMessage (name: string, min?: number | string, max?: number | string, unit?: string): string { + return `${name} should be at least ${min} and no more than ${max}${unit ? ` ${unit}.` : '.'}`; +} + +/* +Validation is used to validate a proposal form. +Each proposal type should validate the fields of his form, anything is valid as long as it fits in a Yup Schema. +In a form, validation should be injected in the Yup Schema just by accessing it in this object. +Ex: +// Text Form + +import Validation from 'path/to/validationSchema' +... + validationSchema: Yup.object().shape({ + ...genericFormDefaultOptions.validationSchema, + ...Validation.Text() + }), + +*/ + +type ProposalTypeKeys = typeof ProposalTypes[number]; +type OutdatedProposals = 'EvictStorageProvider' | 'SetStorageRoleParameters'; +type ValidationTypeKeys = Exclude | 'All'; + +/* eslint-disable @typescript-eslint/indent */ +// /\ This prevents eslint from trying to make "stairs" out of those multiple conditions. +// They are more readable when one is directly under the other (switch-case style) +type FormValuesByType = + T extends 'All' ? GenericFormValues : + T extends 'Text' ? Omit : + T extends 'RuntimeUpgrade' ? Omit : + T extends 'SetElectionParameters' ? Omit : + T extends 'Spending' ? Omit : + T extends 'SetLead' ? Omit : + T extends 'SetContentWorkingGroupMintCapacity' ? Omit : + T extends 'SetValidatorCount' ? Omit : + T extends 'AddWorkingGroupLeaderOpening' ? Omit : + T extends 'SetWorkingGroupMintCapacity' ? Omit : + T extends 'BeginReviewWorkingGroupLeaderApplication' ? Omit : + T extends 'FillWorkingGroupLeaderOpening' ? Omit : + T extends 'DecreaseWorkingGroupLeaderStake' ? Omit : + T extends 'SlashWorkingGroupLeaderStake' ? Omit : + T extends 'SetWorkingGroupLeaderReward' ? Omit : + T extends 'TerminateWorkingGroupLeaderRole' ? Omit : + never; + +type ValidationSchemaFuncParamsByType = + T extends 'AddWorkingGroupLeaderOpening' ? [number, InputValidationLengthConstraint] : + T extends 'FillWorkingGroupLeaderOpening' ? [number] : + T extends 'TerminateWorkingGroupLeaderRole' ? [InputValidationLengthConstraint] : + []; + +/* eslint-enable @typescript-eslint/indent */ + +type ValidationSchemaFunc = (...params: ParamsT) => +({ [fieldK in keyof FieldValuesT]: Yup.Schema }); + +type ValidationType = { + [validationTypeK in ValidationTypeKeys]: ValidationSchemaFunc< + FormValuesByType, + ValidationSchemaFuncParamsByType + > +}; + +// Helpers for common validation +function minMaxInt (min: number, max: number, fieldName: string) { + return Yup.number() + .required(`${fieldName} is required!`) + .integer(`${fieldName} must be an integer!`) + .min(min, errorMessage(fieldName, min, max)) + .max(max, errorMessage(fieldName, min, max)); +} + +const Validation: ValidationType = { + All: () => ({ + title: Yup.string() + .required('Title is required!') + .max(TITLE_MAX_LENGTH, `Title should be under ${TITLE_MAX_LENGTH} characters.`), + rationale: Yup.string() + .required('Rationale is required!') + .max(RATIONALE_MAX_LENGTH, `Rationale should be under ${RATIONALE_MAX_LENGTH} characters.`) + }), + Text: () => ({ + description: Yup.string() + .required('Description is required!') + .max(DESCRIPTION_MAX_LENGTH, `Description should be under ${DESCRIPTION_MAX_LENGTH}`) + }), + RuntimeUpgrade: () => ({ + WASM: Yup.mixed() + .test('fileArrayBuffer', 'Unexpected data format, file cannot be processed.', value => typeof value.byteLength !== 'undefined') + .test('fileSizeMin', `Minimum file size is ${FILE_SIZE_BYTES_MIN} bytes.`, value => value.byteLength >= FILE_SIZE_BYTES_MIN) + .test('fileSizeMax', `Maximum file size is ${FILE_SIZE_BYTES_MAX} bytes.`, value => value.byteLength <= FILE_SIZE_BYTES_MAX) + }), + SetElectionParameters: () => ({ + announcingPeriod: Yup.number() + .required('All fields must be filled!') + .integer('This field must be an integer.') + .min( + ANNOUNCING_PERIOD_MIN, + errorMessage('The announcing period', ANNOUNCING_PERIOD_MIN, ANNOUNCING_PERIOD_MAX, 'blocks') + ) + .max( + ANNOUNCING_PERIOD_MAX, + errorMessage('The announcing period', ANNOUNCING_PERIOD_MIN, ANNOUNCING_PERIOD_MAX, 'blocks') + ), + votingPeriod: Yup.number() + .required('All fields must be filled!') + .integer('This field must be an integer.') + .min(VOTING_PERIOD_MIN, errorMessage('The voting period', VOTING_PERIOD_MIN, VOTING_PERIOD_MAX, 'blocks')) + .max(VOTING_PERIOD_MAX, errorMessage('The voting period', VOTING_PERIOD_MIN, VOTING_PERIOD_MAX, 'blocks')), + minVotingStake: Yup.number() + .required('All fields must be filled!') + .integer('This field must be an integer.') + .min( + MIN_VOTING_STAKE_MIN, + errorMessage('The minimum voting stake', MIN_VOTING_STAKE_MIN, MIN_VOTING_STAKE_MAX, CURRENCY_UNIT) + ) + .max( + MIN_VOTING_STAKE_MAX, + errorMessage('The minimum voting stake', MIN_VOTING_STAKE_MIN, MIN_VOTING_STAKE_MAX, CURRENCY_UNIT) + ), + revealingPeriod: Yup.number() + .required('All fields must be filled!') + .integer('This field must be an integer.') + .min( + REVEALING_PERIOD_MIN, + errorMessage('The revealing period', REVEALING_PERIOD_MIN, REVEALING_PERIOD_MAX, 'blocks') + ) + .max( + REVEALING_PERIOD_MAX, + errorMessage('The revealing period', REVEALING_PERIOD_MIN, REVEALING_PERIOD_MAX, 'blocks') + ), + minCouncilStake: Yup.number() + .required('All fields must be filled!') + .integer('This field must be an integer.') + .min( + MIN_COUNCIL_STAKE_MIN, + errorMessage('The minimum council stake', MIN_COUNCIL_STAKE_MIN, MIN_COUNCIL_STAKE_MAX, CURRENCY_UNIT) + ) + .max( + MIN_COUNCIL_STAKE_MAX, + errorMessage('The minimum council stake', MIN_COUNCIL_STAKE_MIN, MIN_COUNCIL_STAKE_MAX, CURRENCY_UNIT) + ), + newTermDuration: Yup.number() + .required('All fields must be filled!') + .integer('This field must be an integer.') + .min( + NEW_TERM_DURATION_MIN, + errorMessage('The new term duration', NEW_TERM_DURATION_MIN, NEW_TERM_DURATION_MAX, 'blocks') + ) + .max( + NEW_TERM_DURATION_MAX, + errorMessage('The new term duration', NEW_TERM_DURATION_MIN, NEW_TERM_DURATION_MAX, 'blocks') + ), + candidacyLimit: Yup.number() + .required('All fields must be filled!') + .integer('This field must be an integer.') + .min(CANDIDACY_LIMIT_MIN, errorMessage('The candidacy limit', CANDIDACY_LIMIT_MIN, CANDIDACY_LIMIT_MAX)) + .max(CANDIDACY_LIMIT_MAX, errorMessage('The candidacy limit', CANDIDACY_LIMIT_MIN, CANDIDACY_LIMIT_MAX)), + councilSize: Yup.number() + .required('All fields must be filled!') + .integer('This field must be an integer.') + .min(COUNCIL_SIZE_MIN, errorMessage('The council size', COUNCIL_SIZE_MIN, COUNCIL_SIZE_MAX)) + .max(COUNCIL_SIZE_MAX, errorMessage('The council size', COUNCIL_SIZE_MIN, COUNCIL_SIZE_MAX)) + }), + Spending: () => ({ + tokens: Yup.number() + .positive('The token amount should be positive.') + .integer('This field must be an integer.') + .max(TOKENS_MAX, errorMessage('The amount of tokens', TOKENS_MIN, TOKENS_MAX)) + .required('You need to specify an amount of tokens.'), + destinationAccount: Yup.string() + .required('Select a destination account!') + }), + SetLead: () => ({ + workingGroupLead: Yup.string().required('Select a proposed lead!') + }), + SetContentWorkingGroupMintCapacity: () => ({ + capacity: Yup.number() + .positive('Mint capacity should be positive.') + .integer('This field must be an integer.') + .min(MINT_CAPACITY_MIN, errorMessage('Mint capacity', MINT_CAPACITY_MIN, MINT_CAPACITY_MAX, CURRENCY_UNIT)) + .max(MINT_CAPACITY_MAX, errorMessage('Mint capacity', MINT_CAPACITY_MIN, MINT_CAPACITY_MAX, CURRENCY_UNIT)) + .required('You need to specify a mint capacity.') + }), + SetValidatorCount: () => ({ + maxValidatorCount: Yup.number() + .required('Enter the max validator count') + .integer('This field must be an integer.') + .min( + MAX_VALIDATOR_COUNT_MIN, + errorMessage('The max validator count', MAX_VALIDATOR_COUNT_MIN, MAX_VALIDATOR_COUNT_MAX) + ) + .max( + MAX_VALIDATOR_COUNT_MAX, + errorMessage('The max validator count', MAX_VALIDATOR_COUNT_MIN, MAX_VALIDATOR_COUNT_MAX) + ) + }), + AddWorkingGroupLeaderOpening: (currentBlock: number, { min: HRTMin, max: HRTMax }: InputValidationLengthConstraint) => ({ + workingGroup: Yup.string(), + activateAt: Yup.string().required(), + activateAtBlock: Yup.number() + .when('activateAt', { + is: ActivateOpeningAtKeys.ExactBlock, + then: minMaxInt( + MIN_EXACT_BLOCK_MINUS_CURRENT + currentBlock, + MAX_EXACT_BLOCK_MINUS_CURRENT + currentBlock, + 'Exact block' + ) + }), + maxReviewPeriodLength: minMaxInt(MAX_REVIEW_PERIOD_LENGTH_MIN, MAX_REVIEW_PERIOD_LENGTH_MAX, 'Max. review period length'), + applicationsLimited: Yup.boolean(), + maxApplications: Yup.number() + .when('applicationsLimited', { + is: true, + then: minMaxInt(MAX_APPLICATIONS_MIN, MAX_APPLICATIONS_MAX, 'Max. number of applications') + }), + applicationStakeRequired: Yup.boolean(), + applicationStakeMode: Yup.string(), + applicationStakeValue: Yup.number() + .when('applicationStakeRequired', { + is: true, + then: minMaxInt(APPLICATION_STAKE_VALUE_MIN, APPLICATION_STAKE_VALUE_MAX, 'Application stake value') + }), + roleStakeRequired: Yup.boolean(), + roleStakeMode: Yup.string(), + roleStakeValue: Yup.number() + .when('roleStakeRequired', { + is: true, + then: minMaxInt(ROLE_STAKE_VALUE_MIN, ROLE_STAKE_VALUE_MAX, 'Role stake value') + }), + terminateRoleUnstakingPeriod: minMaxInt( + TERMINATE_ROLE_UNSTAKING_MIN, + TERMINATE_ROLE_UNSTAKING_MAX, + 'Terminate role unstaking period' + ), + leaveRoleUnstakingPeriod: minMaxInt( + LEAVE_ROLE_UNSTAKING_MIN, + LEAVE_ROLE_UNSTAKING_MAX, + 'Leave role unstaking period' + ), + humanReadableText: Yup.string() + .required() + .test( + 'schemaIsValid', + 'Schema validation failed!', + function (val) { + let schemaObj: any; + try { + schemaObj = JSON.parse(val); + } catch (e) { + return this.createError({ message: 'Schema validation failed: Invalid JSON' }); + } + const isValid = schemaValidator(schemaObj); + const errors = schemaValidator.errors || []; + if (!isValid) { + return this.createError({ + message: 'Schema validation failed: ' + errors.map(e => `${e.message}${e.dataPath && ` (${e.dataPath})`}`).join(', ') + }); + } + return true; + } + ) + .min(HRTMin.toNumber(), `human_readable_text must be at least ${HRTMin.toNumber()} character(s) long`) + .max(HRTMax.toNumber(), `human_readable_text cannot be more than ${HRTMax.toNumber()} character(s) long`) + }), + SetWorkingGroupMintCapacity: () => ({ + workingGroup: Yup.string(), + capacity: minMaxInt(WG_MINT_CAP_MIN, WG_MINT_CAP_MAX, 'Mint capacity') + }), + BeginReviewWorkingGroupLeaderApplication: () => ({ + workingGroup: Yup.string(), + openingId: Yup.number().required('Select an opening!') + }), + FillWorkingGroupLeaderOpening: (currentBlock: number) => ({ + workingGroup: Yup.string(), + openingId: Yup.number().required('Select an opening!'), + successfulApplicant: Yup.number().required('Select a succesful applicant!'), + includeReward: Yup.boolean(), + rewardAmount: Yup.number() + .when('includeReward', { + is: true, + then: minMaxInt(MIN_REWARD_AMOUNT, MAX_REWARD_AMOUNT, 'Reward amount') + }), + rewardNextBlock: Yup.number() + .when('includeReward', { + is: true, + then: minMaxInt( + MIN_NEXT_PAYMENT_BLOCK_MINUS_CURRENT + currentBlock, + MAX_NEXT_PAYMENT_BLOCK_MINUS_CURRENT + currentBlock, + 'Next payment block' + ) + }), + rewardRecurring: Yup.boolean(), + rewardInterval: Yup.number() + .when(['includeReward', 'rewardRecurring'], { + is: true, + then: minMaxInt(MIN_REWARD_INTERVAL, MAX_REWARD_INTERVAL, 'Reward interval') + }) + }), + DecreaseWorkingGroupLeaderStake: () => ({ + workingGroup: Yup.string(), + amount: Yup.number() + .required('Amount is required!') + .min(DECREASE_LEAD_STAKE_MIN, `Amount must be greater than ${DECREASE_LEAD_STAKE_MIN}`) + }), + SlashWorkingGroupLeaderStake: () => ({ + workingGroup: Yup.string(), + amount: Yup.number() + .required('Amount is required!') + .min(SLASH_LEAD_STAKE_MIN, `Amount must be greater than ${SLASH_LEAD_STAKE_MIN}`) + }), + SetWorkingGroupLeaderReward: () => ({ + workingGroup: Yup.string(), + amount: minMaxInt(MIN_REWARD_AMOUNT, MAX_REWARD_AMOUNT, 'Reward amount') + }), + TerminateWorkingGroupLeaderRole: ({ min, max }: InputValidationLengthConstraint) => ({ + workingGroup: Yup.string(), + terminationRationale: Yup.string() + .required('Termination rationale is required') + .min(min.toNumber(), `Termination rationale must be at least ${min.toNumber()} character(s) long`) + .max(max.toNumber(), `Termination rationale cannot be more than ${max.toNumber()} character(s) long`), + slashStake: Yup.boolean() + }) +}; + +export default Validation; diff --git a/pioneer/packages/joy-roles/.skip-build b/pioneer/packages/joy-roles/.skip-build new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pioneer/packages/joy-roles/README.md b/pioneer/packages/joy-roles/README.md new file mode 100644 index 0000000000..ec3c03a581 --- /dev/null +++ b/pioneer/packages/joy-roles/README.md @@ -0,0 +1 @@ +# @polkadot/joy-roles \ No newline at end of file diff --git a/pioneer/packages/joy-roles/jest.config.js b/pioneer/packages/joy-roles/jest.config.js new file mode 100644 index 0000000000..d8a2483eba --- /dev/null +++ b/pioneer/packages/joy-roles/jest.config.js @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { pathsToModuleNameMapper } = require('ts-jest/utils'); +// In the following statement, replace `./tsconfig` with the path to your `tsconfig` file +// which contains the path mapping (ie the `compilerOptions.paths` option): +const { compilerOptions } = require('../../tsconfig.json'); + +module.exports = { + roots: [ + '/src' + ], + preset: 'ts-jest', + testEnvironment: 'node', + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '/../../' }) +}; diff --git a/pioneer/packages/joy-roles/package.json b/pioneer/packages/joy-roles/package.json new file mode 100644 index 0000000000..07bbeba30d --- /dev/null +++ b/pioneer/packages/joy-roles/package.json @@ -0,0 +1,28 @@ +{ + "name": "@polkadot/joy-roles", + "version": "0.0.1", + "description": "Staked roles module for Joystream node", + "main": "index.js", + "scripts": { + "test": "jest", + "test-watch": "jest --watch", + "lint": "eslint -c ../../tsconfig.eslint.json --ext .js,.jsx,.ts,.tsx . && tsc --noEmit --pretty" + }, + "author": "Joystream contributors", + "maintainers": [], + "dependencies": { + "@babel/runtime": "^7.7.1", + "@polkadot/joy-utils": "^0.1.1", + "@polkadot/react-components": "0.37.0-beta.63", + "@polkadot/react-query": "0.37.0-beta.63", + "@types/faker": "^4.1.8", + "faker": "^4.1.0", + "marked": "^0.7.0", + "moment": "^2.24.0", + "react-compound-slider": "^2.4.0", + "react-moment": "^0.9.6", + "react-number-format": "^4.3.1", + "react-semantic-ui-range": "^0.7.0", + "typescript-formatter": "^7.2.2" + } +} diff --git a/pioneer/packages/joy-roles/src/OpeningMetadata.ts b/pioneer/packages/joy-roles/src/OpeningMetadata.ts new file mode 100644 index 0000000000..412364ef59 --- /dev/null +++ b/pioneer/packages/joy-roles/src/OpeningMetadata.ts @@ -0,0 +1,12 @@ +import { WorkingGroups } from './working_groups'; +import { OpeningType } from '@joystream/types/working-group'; + +export type OpeningMetadata = { + id: string; + group: WorkingGroups; + type?: OpeningType; +} + +export type OpeningMetadataProps = { + meta: OpeningMetadata; +} diff --git a/pioneer/packages/joy-roles/src/StakeRequirement.tsx b/pioneer/packages/joy-roles/src/StakeRequirement.tsx new file mode 100644 index 0000000000..31a025295c --- /dev/null +++ b/pioneer/packages/joy-roles/src/StakeRequirement.tsx @@ -0,0 +1,81 @@ +import React from 'react'; + +import { Balance } from '@polkadot/types/interfaces'; +import { formatBalance } from '@polkadot/util'; + +export enum StakeType { + Fixed = 0, + AtLeast, +} + +export interface IStakeRequirement { + anyRequirement(): boolean; + qualifier(): string | null; + value: Balance; + fixed(): boolean; + atLeast(): boolean; + describe(): any; +} + +export abstract class StakeRequirement { + hard: Balance + type: StakeType + + constructor (hard: Balance, stakeType: StakeType = StakeType.Fixed) { + this.hard = hard; + this.type = stakeType; + } + + anyRequirement (): boolean { + return !this.hard.isZero(); + } + + qualifier (): string | null { + if (this.type === StakeType.AtLeast) { + return 'at least'; + } + return null; + } + + get value (): Balance { + return this.hard; + } + + fixed (): boolean { + return this.type === StakeType.Fixed; + } + + atLeast (): boolean { + return this.type === StakeType.AtLeast; + } +} + +export class ApplicationStakeRequirement extends StakeRequirement implements IStakeRequirement { + describe (): any { + if (!this.anyRequirement()) { + return null; + } + + return ( +

+ You must stake {this.qualifier()} {formatBalance(this.hard)} to apply for this role. This stake will be returned to you when the hiring process is complete, whether or not you are hired, and will also be used to rank applications. +

+ ); + } +} + +export class RoleStakeRequirement extends StakeRequirement implements IStakeRequirement { + describe (): any { + if (!this.anyRequirement()) { + return null; + } + + return ( +

+ You must stake {this.qualifier()} {formatBalance(this.hard)} to be eligible for this role. + { 'You may lose this stake if you\'re hired and then dismised from this role.' } + This stake will be returned if your application is unsuccessful, and will also be used to rank applications. +

+ ); + } +} diff --git a/pioneer/packages/joy-roles/src/balances.spec.ts b/pioneer/packages/joy-roles/src/balances.spec.ts new file mode 100644 index 0000000000..efd406b784 --- /dev/null +++ b/pioneer/packages/joy-roles/src/balances.spec.ts @@ -0,0 +1,50 @@ +import { Balance } from '@polkadot/types/interfaces'; +import { u128 } from '@polkadot/types'; +import { Avg, AvgDelta, Min, Step, Sum } from './balances'; + +describe('Balance arithmetic', (): void => { + it('Can calculate a sum', (): void => { + const input: Balance[] = []; + for (let i = 0; i < 10; i++) { + input.push(new u128(i)); + } + + expect(Sum(input).toNumber()).toEqual(45); + }); + + it('Can calculate an average', (): void => { + const input: Balance[] = []; + for (let i = 0; i < 10; i++) { + input.push(new u128(i)); + } + + expect(Avg(input).toNumber()).toEqual(4); + }); + + it('Can calculate an average delta', (): void => { + const input: Balance[] = []; + for (let i = 0; i < 10; i++) { + input.push(new u128(i)); + } + + expect(AvgDelta(input).toNumber()).toEqual(1); + }); + + it('Can calculate a step value with large numbers', (): void => { + const input: Balance[] = []; + for (let i = 0; i < 10; i++) { + input.push(new u128(i * 10)); + } + + expect(Step(input).toNumber()).toEqual(4); + }); + + it('Can calculate a step value with small numbers', (): void => { + const input: Balance[] = []; + for (let i = 0; i < 10; i++) { + input.push(new u128(i)); + } + + expect(Min(Step(input)).toNumber()).toEqual(1); + }); +}); diff --git a/pioneer/packages/joy-roles/src/balances.ts b/pioneer/packages/joy-roles/src/balances.ts new file mode 100644 index 0000000000..d0c85731ce --- /dev/null +++ b/pioneer/packages/joy-roles/src/balances.ts @@ -0,0 +1,43 @@ +import { Balance } from '@polkadot/types/interfaces'; +import { u128 } from '@polkadot/types'; + +export const Zero = new u128(0); +export const One = new u128(1); + +export const Add = (x: Balance, y: Balance): Balance => new u128(x.add(y)); +export const Sub = (x: Balance, y: Balance): Balance => new u128(x.sub(y)); +export const Sum = (balances: Balance[]): Balance => balances.reduce(Add, Zero); + +export const Avg = (xs: Balance[]): Balance => + xs[0] === undefined ? Zero : new u128(Sum(xs).divn(xs.length)); + +export const AvgDelta = (xs: Balance[]): Balance => { + if (xs.length < 2) { + return One; + } + + const pairs: Balance[] = []; + + xs.forEach((x, i) => { + if (i > 0) { + pairs.push(Sub(x, xs[i - 1])); + } + }); + + return Avg(pairs); +}; + +// An average value to 'step' up balances, like on the nudge controls for a slider +export const Step = (xs: Balance[], ticks = 10): Balance => new u128(Avg(xs).divn(ticks)); +export const Min = (x: Balance, min: Balance = One): Balance => x.gte(min) ? x : min; +export const Sort = (xs: Balance[]): Balance[] => { + xs.sort((a, b): number => { + if (a.eq(b)) { + return 0; + } else if (a.gt(b)) { + return 1; + } + return -1; + }); + return xs; +}; diff --git a/pioneer/packages/joy-roles/src/classifiers.spec.ts b/pioneer/packages/joy-roles/src/classifiers.spec.ts new file mode 100644 index 0000000000..8d0e18c730 --- /dev/null +++ b/pioneer/packages/joy-roles/src/classifiers.spec.ts @@ -0,0 +1,195 @@ +import { Option, Text, u32 } from '@polkadot/types'; +import { + AcceptingApplications, + ActiveOpeningStage, + ApplicationRationingPolicy, + StakingPolicy, + Opening, OpeningStage, + ReviewPeriod +} from '@joystream/types/hiring'; + +import { + OpeningState, + IBlockQueryer, + classifyOpeningStage, OpeningStageClassification +} from './classifiers'; + +class MockBlockQueryer { + hash: string + timestamp: Date + + constructor ( + hash = 'somehash', + timestamp: Date = new Date() + ) { + this.hash = hash; + this.timestamp = timestamp; + } + + // eslint-disable-next-line @typescript-eslint/require-await + async blockHash (height: number): Promise { + return this.hash; + } + + // eslint-disable-next-line @typescript-eslint/require-await + async blockTimestamp (height: number): Promise { + return this.timestamp; + } + + // eslint-disable-next-line @typescript-eslint/require-await + async expectedBlockTime (): Promise { + return 6; + } +} + +type Test = { + description: string; + input: { + queryer: IBlockQueryer; + opening: Opening; + }; + output: OpeningStageClassification; +} + +describe('hiring.Opening-> OpeningStageClassification', (): void => { + const now = new Date('2020-01-23T11:47:04.433Z'); + + const cases: Test[] = [ + { + description: 'WaitingToBegin', + input: { + opening: new Opening({ + created: new u32(100), + stage: new OpeningStage({ + WaitingToBegin: { + begins_at_block: new u32(100) + } + }), + max_review_period_length: new u32(100), + application_rationing_policy: new Option(ApplicationRationingPolicy), + application_staking_policy: new Option(StakingPolicy), + role_staking_policy: new Option(StakingPolicy), + human_readable_text: new Text() + }), + queryer: new MockBlockQueryer('somehash', now) + }, + output: { + state: OpeningState.WaitingToBegin, + starting_block: 100, + starting_block_hash: 'somehash', + starting_time: now + } + }, + { + description: 'Accepting applications', + input: { + opening: new Opening({ + created: new u32(100), + stage: new OpeningStage({ + Active: { + stage: new ActiveOpeningStage({ + acceptingApplications: new AcceptingApplications({ + started_accepting_applicants_at_block: new u32(100) + }) + }) + } + }), + max_review_period_length: new u32(100), + application_rationing_policy: new Option(ApplicationRationingPolicy), + application_staking_policy: new Option(StakingPolicy), + role_staking_policy: new Option(StakingPolicy), + human_readable_text: new Text() + }), + queryer: new MockBlockQueryer('somehash', now) + }, + output: { + state: OpeningState.AcceptingApplications, + starting_block: 100, + starting_block_hash: 'somehash', + starting_time: now + } + }, + { + description: 'In review period', + input: { + opening: new Opening({ + created: new u32(100), + stage: new OpeningStage({ + Active: { + stage: new ActiveOpeningStage({ + reviewPeriod: new ReviewPeriod({ + started_accepting_applicants_at_block: new u32(100), + started_review_period_at_block: new u32(100) + }) + }) + } + }), + max_review_period_length: new u32(14400), + application_rationing_policy: new Option(ApplicationRationingPolicy), + application_staking_policy: new Option(StakingPolicy), + role_staking_policy: new Option(StakingPolicy), + human_readable_text: new Text() + }), + queryer: new MockBlockQueryer('somehash', now) + }, + output: { + state: OpeningState.InReview, + starting_block: 100, + starting_block_hash: 'somehash', + starting_time: now, + review_end_block: 14500, + review_end_time: new Date('2020-01-24T11:47:04.433Z') + } + } + /* + * jest is having trouble with the enum type + { + description: "Deactivated: cancelled", + input: { + opening: new Opening({ + created: new u32(100), + stage: new OpeningStage({ + 'Active': { + stage: new ActiveOpeningStage({ + reviewPeriod: new Deactivated({ + cause: new OpeningDeactivationCause( + OpeningDeactivationCauseKeys.CancelledBeforeActivation, + ), + deactivated_at_block: new u32(123), + started_accepting_applicants_at_block: new u32(100), + started_review_period_at_block: new Option(u32, 100), + }) + }) + } + }), + max_review_period_length: new u32(100), + application_rationing_policy: new Option(ApplicationRationingPolicy), + application_staking_policy: new Option(StakingPolicy), + role_staking_policy: new Option(StakingPolicy), + human_readable_text: new Text(), + }), + queryer: new MockBlockQueryer("somehash", now), + }, + output: { + state: OpeningState.Cancelled, + starting_block: 123, + starting_block_hash: "somehash", + starting_time: now, + }, + }, + */ + ]; + + cases.forEach((test: Test) => { + it(test.description, async () => { + expect( + await classifyOpeningStage( + test.input.queryer, + test.input.opening + ) + ).toEqual( + test.output + ); + }); + }); +}); diff --git a/pioneer/packages/joy-roles/src/classifiers.ts b/pioneer/packages/joy-roles/src/classifiers.ts new file mode 100644 index 0000000000..e8c4695c5e --- /dev/null +++ b/pioneer/packages/joy-roles/src/classifiers.ts @@ -0,0 +1,286 @@ +import moment from 'moment'; + +import { Option, u128 } from '@polkadot/types'; +import { Balance } from '@polkadot/types/interfaces'; + +import { + Application, + AcceptingApplications, ReviewPeriod, + WaitingToBeingOpeningStageVariant, + ActiveOpeningStageVariant, + Opening, + OpeningStageKeys, + Deactivated, OpeningDeactivationCauseKeys, + StakingPolicy, + StakingAmountLimitMode, StakingAmountLimitModeKeys, + ApplicationStageKeys, + ApplicationDeactivationCause, ApplicationDeactivationCauseKeys, + UnstakingApplicationStage, + InactiveApplicationStage +} from '@joystream/types/hiring'; + +import { + StakeRequirement, + ApplicationStakeRequirement, + RoleStakeRequirement, + StakeType +} from './StakeRequirement'; + +export enum CancelledReason { + ApplicantCancelled = 0, + HirerCancelledApplication, + HirerCancelledOpening, + NoOneHired, +} + +export enum OpeningState { + WaitingToBegin = 0, + AcceptingApplications, + InReview, + Complete, + Cancelled, +} + +export interface OpeningStageClassification { + state: OpeningState; + starting_block: number; + starting_block_hash: string; + starting_time: Date; + review_end_time?: Date; + review_end_block?: number; +} + +export interface IBlockQueryer { + blockHash(height: number): Promise; + blockTimestamp(height: number): Promise; + expectedBlockTime: () => Promise; +} + +async function classifyActiveOpeningStageAcceptingApplications ( + queryer: IBlockQueryer, + stage: AcceptingApplications +): Promise { + const blockNumber = stage.started_accepting_applicants_at_block.toNumber(); + return { + state: OpeningState.AcceptingApplications, + starting_block: blockNumber, + starting_block_hash: await queryer.blockHash(blockNumber), + starting_time: await queryer.blockTimestamp(blockNumber) + }; +} + +async function classifyActiveOpeningStageReviewPeriod ( + opening: Opening, + queryer: IBlockQueryer, + stage: ReviewPeriod +): Promise { + const blockNumber = stage.started_review_period_at_block.toNumber(); + const maxReviewLengthInBlocks = opening.max_review_period_length.toNumber(); + const [startDate, blockTime] = await Promise.all([ + queryer.blockTimestamp(blockNumber), + queryer.expectedBlockTime() + ]); + const endDate = moment(startDate).add(maxReviewLengthInBlocks * blockTime, 's'); + + return { + state: OpeningState.InReview, + starting_block: blockNumber, + starting_block_hash: await queryer.blockHash(blockNumber), + starting_time: startDate, + review_end_time: endDate.toDate(), + review_end_block: blockNumber + maxReviewLengthInBlocks + }; +} + +async function classifyActiveOpeningStageDeactivated ( + queryer: IBlockQueryer, + stage: Deactivated +): Promise { + const blockNumber = stage.deactivated_at_block.toNumber(); + const [startDate] = await Promise.all([ + queryer.blockTimestamp(blockNumber) + ]); + + let state: OpeningState; + + switch (stage.cause.type) { + case OpeningDeactivationCauseKeys.CancelledBeforeActivation: + case OpeningDeactivationCauseKeys.CancelledAcceptingApplications: + case OpeningDeactivationCauseKeys.CancelledInReviewPeriod: + case OpeningDeactivationCauseKeys.ReviewPeriodExpired: + state = OpeningState.Cancelled; + break; + + case OpeningDeactivationCauseKeys.Filled: + state = OpeningState.Complete; + break; + + default: + state = OpeningState.Complete; + break; + } + + return { + state: state, + starting_block: blockNumber, + starting_block_hash: await queryer.blockHash(blockNumber), + starting_time: startDate + }; +} + +async function classifyActiveOpeningStage ( + opening: Opening, + queryer: IBlockQueryer, + stage: ActiveOpeningStageVariant +): Promise { + if (stage.stage.isOfType('AcceptingApplications')) { + return classifyActiveOpeningStageAcceptingApplications( + queryer, + stage.stage.asType('AcceptingApplications') + ); + } + if (stage.stage.isOfType('ReviewPeriod')) { + return classifyActiveOpeningStageReviewPeriod( + opening, + queryer, + stage.stage.asType('ReviewPeriod') + ); + } + if (stage.stage.isOfType('Deactivated')) { + return classifyActiveOpeningStageDeactivated( + queryer, + stage.stage.value as Deactivated + ); + } + + throw new Error('Unknown active opening stage: ' + stage.stage.type); +} + +async function classifyWaitingToBeginStage ( + opening: Opening, + queryer: IBlockQueryer, + stage: WaitingToBeingOpeningStageVariant +): Promise { + const blockNumber = opening.created.toNumber(); + return { + state: OpeningState.WaitingToBegin, + starting_block: blockNumber, + starting_block_hash: await queryer.blockHash(blockNumber), + starting_time: await queryer.blockTimestamp(blockNumber) + }; +} + +export async function classifyOpeningStage (queryer: IBlockQueryer, opening: Opening): Promise { + switch (opening.stage.type) { + case OpeningStageKeys.WaitingToBegin: + return classifyWaitingToBeginStage( + opening, + queryer, + opening.stage.value as WaitingToBeingOpeningStageVariant + ); + + case OpeningStageKeys.Active: + return classifyActiveOpeningStage( + opening, + queryer, + opening.stage.value as ActiveOpeningStageVariant + ); + } + + throw new Error('Unknown stage type: ' + opening.stage.type); +} + +export type StakeRequirementSetClassification = { + application: ApplicationStakeRequirement; + role: RoleStakeRequirement; +} + +interface StakeRequirementConstructor { + new(hard: Balance, stakeType?: StakeType): T; +} + +function classifyStakeType (mode: StakingAmountLimitMode): StakeType { + switch (mode.type) { + case StakingAmountLimitModeKeys.AtLeast: + return StakeType.AtLeast; + + case StakingAmountLimitModeKeys.Exact: + return StakeType.Fixed; + } + + throw new Error('Unknown stake type: ' + mode.type); +} + +function classifyStakeRequirement ( + constructor: StakeRequirementConstructor, + option: Option +): T { + if (option.isNone) { + return new constructor(new u128(0)); + } + + const policy = option.unwrap(); + + return new constructor( + policy.amount, + classifyStakeType(policy.amount_mode) + ); +} + +export function classifyOpeningStakes (opening: Opening): StakeRequirementSetClassification { + return { + application: classifyStakeRequirement( + ApplicationStakeRequirement, + opening.application_staking_policy + ), + role: classifyStakeRequirement( + RoleStakeRequirement, + opening.role_staking_policy + ) + }; +} + +function classifyApplicationCancellationFromCause (cause: ApplicationDeactivationCause): CancelledReason | undefined { + console.log(cause.type); + switch (cause.type) { + case ApplicationDeactivationCauseKeys.External: + return CancelledReason.ApplicantCancelled; + + case ApplicationDeactivationCauseKeys.OpeningCancelled: + case ApplicationDeactivationCauseKeys.OpeningFilled: + return CancelledReason.HirerCancelledOpening; + + case ApplicationDeactivationCauseKeys.ReviewPeriodExpired: + return CancelledReason.NoOneHired; + } + + return undefined; +} + +export function classifyApplicationCancellation (a: Application): CancelledReason | undefined { + switch (a.stage.type) { + case ApplicationStageKeys.Unstaking: + return classifyApplicationCancellationFromCause( + (a.stage.value as UnstakingApplicationStage).cause + ); + + case ApplicationStageKeys.Inactive: + return classifyApplicationCancellationFromCause( + (a.stage.value as InactiveApplicationStage).cause + ); + } + + return undefined; +} + +export function isApplicationHired (a: Application): boolean { + switch (a.stage.type) { + case ApplicationStageKeys.Unstaking: + return (a.stage.value as UnstakingApplicationStage).cause.type === ApplicationDeactivationCauseKeys.Hired; + + case ApplicationStageKeys.Inactive: + return (a.stage.value as InactiveApplicationStage).cause.type === ApplicationDeactivationCauseKeys.Hired; + } + + return false; +} diff --git a/pioneer/packages/joy-roles/src/elements.tsx b/pioneer/packages/joy-roles/src/elements.tsx new file mode 100644 index 0000000000..5af9089c51 --- /dev/null +++ b/pioneer/packages/joy-roles/src/elements.tsx @@ -0,0 +1,251 @@ +import React, { useEffect, useState } from 'react'; +import moment from 'moment'; +import { Card, Icon, Image, Label, Statistic, Button } from 'semantic-ui-react'; +import { Link } from 'react-router-dom'; + +import { Balance } from '@polkadot/types/interfaces'; +import { formatBalance } from '@polkadot/util'; +import Identicon from '@polkadot/react-identicon'; +import { IMembership, MemberId } from '@joystream/types/members'; +import { GenericAccountId } from '@polkadot/types'; +import { LeadRoleState } from '@joystream/types/content-working-group'; +import { WorkerId } from '@joystream/types/working-group'; +import { WorkingGroups } from './working_groups'; +import { RewardRelationship } from '@joystream/types/recurring-rewards'; +import { formatReward } from '@polkadot/joy-utils/functions/format'; +import styled from 'styled-components'; + +type BalanceProps = { + balance?: Balance; +} + +export function BalanceView (props: BalanceProps) { + return ( +
+ Balance: {formatBalance(props.balance)} +
+ ); +} + +type ProfileProps = { + profile: IMembership; +} + +export function HandleView (props: ProfileProps) { + if (typeof props.profile === 'undefined') { + return null; + } + + return ( + {props.profile.handle.toString()} + ); +} + +export type GroupMember = { + memberId: MemberId; + group: WorkingGroups; + workerId: number; + roleAccount: GenericAccountId; + profile: IMembership; + title: string; + stake?: Balance; + rewardRelationship?: RewardRelationship; +} + +export type GroupLead = { + memberId: MemberId; + workerId?: WorkerId; // In case of "working-group" module + roleAccount: GenericAccountId; + profile: IMembership; + title: string; + stage?: LeadRoleState; + stake?: Balance; + rewardRelationship?: RewardRelationship; +} + +export function GroupLeadView (props: GroupLead) { + let avatar = ; + if (typeof props.profile.avatar_uri !== 'undefined' && props.profile.avatar_uri.toString() !== '') { + avatar = ; + } + + const { stake, rewardRelationship } = props; + + return ( + + + + {avatar} + + + {props.title} + + { props.workerId && ( + + ) } + + + + + + + + ); +} + +const StakeAndReward = styled.div` + display: grid; + grid-template-columns: 1fr; + grid-row-gap: 0.25em; + margin-bottom: 1em; +`; + +type GroupMemberDetailsProps = { + rewardRelationship?: RewardRelationship; + stake?: Balance; +} + +export function GroupMemberDetails (props: GroupMemberDetailsProps) { + const [showDetails, setShowDetails] = useState(false); + const details: JSX.Element[] = []; + + if (props.stake && props.stake.toNumber() > 0) { + details.push( + + ); + } else { + details.push( + + ); + } + + if (props.rewardRelationship) { + const reward = props.rewardRelationship; + details.push( + + ); + details.push( + + ); + details.push( + + ); + details.push( + + ); + } else { + details.push( + + ); + } + + return ( + + { showDetails && ( + + + {details.map((detail, index) =>
{detail}
)} +
+
+ ) } + +
+ ); +} + +export function GroupMemberView (props: GroupMember) { + let avatar = ; + if (typeof props.profile.avatar_uri !== 'undefined' && props.profile.avatar_uri.toString() !== '') { + avatar = ; + } + + const { stake, rewardRelationship } = props; + + return ( + + + + {avatar} + + + {props.title} + + + + + + + ); +} + +type CountdownProps = { + end: Date; +} + +export function Countdown (props: CountdownProps) { + let interval = -1; + + const [days, setDays] = useState(undefined); + const [hours, setHours] = useState(undefined); + const [minutes, setMinutes] = useState(undefined); + const [seconds, setSeconds] = useState(undefined); + + const update = () => { + const then = moment(props.end); + const now = moment(); + const d = moment.duration(then.diff(now)); + setDays(d.days()); + setHours(d.hours()); + setMinutes(d.minutes()); + setSeconds(d.seconds()); + }; + + interval = window.setInterval(update, 1000); + + useEffect(() => { + update(); + return () => { + clearInterval(interval); + }; + }, []); + + if (!seconds) { + return null; + } + + return ( +
+ + {days} + Days + + + {hours} + hours + + + {minutes} + minutes + + + {seconds} + seconds + +
+ ); +} diff --git a/pioneer/packages/joy-roles/src/flows/apply.controller.tsx b/pioneer/packages/joy-roles/src/flows/apply.controller.tsx new file mode 100644 index 0000000000..7455ce0617 --- /dev/null +++ b/pioneer/packages/joy-roles/src/flows/apply.controller.tsx @@ -0,0 +1,261 @@ +import React from 'react'; + +import { formatBalance } from '@polkadot/util'; +import { u128 } from '@polkadot/types'; +import { Balance } from '@polkadot/types/interfaces'; +import AccountId from '@polkadot/types/primitive/Generic/AccountId'; + +import { Controller, View } from '@polkadot/joy-utils/index'; + +import { GenericJoyStreamRoleSchema } from '@joystream/types/hiring/schemas/role.schema.typings'; + +import { Container } from 'semantic-ui-react'; + +import { ITransport } from '../transport'; + +import { keyPairDetails, FlowModal, ProgressSteps } from './apply'; + +import { OpeningStakeAndApplicationStatus } from '../tabs/Opportunities'; +import { Min, Step, Sum } from '../balances'; +import { WorkingGroups, AvailableGroups } from '../working_groups'; + +type State = { + // Input data from state + role?: GenericJoyStreamRoleSchema; + applications?: OpeningStakeAndApplicationStatus; + keypairs?: keyPairDetails[]; // <- Where does this come from? + hasConfirmStep?: boolean; + step?: Balance; + slots?: Balance[]; + + // Data captured from form + applicationStake: Balance; + roleStake: Balance; + appDetails: any; + txKeyAddress: AccountId; + activeStep: ProgressSteps; + txInProgress: boolean; + complete: boolean; + + // Data generated for transaction + transactionDetails: Map; + roleKeyNameBase: string; + roleKeyName: string; + + // Error capture and display + hasError: boolean; +} + +const newEmptyState = (): State => { + return { + applicationStake: new u128(0), + roleStake: new u128(0), + appDetails: {}, + hasError: false, + transactionDetails: new Map(), + roleKeyNameBase: '', + roleKeyName: '', + txKeyAddress: new AccountId(), + activeStep: 0, + txInProgress: false, + complete: false + }; +}; + +export class ApplyController extends Controller { + protected currentOpeningId = -1; + protected currentGroup: WorkingGroups | null = null; + + constructor ( + transport: ITransport, + initialState: State = newEmptyState() + ) { + super(transport, initialState); + + this.transport.accounts().subscribe((keys) => this.updateAccounts(keys)); + } + + protected parseGroup (group: string | undefined): WorkingGroups | undefined { + return AvailableGroups.find(availableGroup => availableGroup === group); + } + + protected updateAccounts (keys: keyPairDetails[]) { + this.state.keypairs = keys; + this.dispatch(); + } + + findOpening (rawId: string | undefined, rawGroup: string | undefined) { + if (!rawId) { + return this.onError('ApplyController: no ID provided in params'); + } + const id = parseInt(rawId); + const group = this.parseGroup(rawGroup); + + if (!group) { + return this.onError('ApplyController: invalid group'); + } + + if (this.currentOpeningId === id && this.currentGroup === group) { + return; + } + + Promise.all( + [ + this.transport.groupOpening(group, id), + this.transport.openingApplicationRanks(group, id) + ] + ) + .then( + ([opening, ranks]) => { + const hrt = opening.opening.parse_human_readable_text_with_fallback(); + + this.state.role = hrt; + this.state.applications = opening.applications; + this.state.slots = ranks; + this.state.step = Min(Step(ranks, ranks.length)); + this.state.hasConfirmStep = + opening.applications.requiredApplicationStake.anyRequirement() || + opening.applications.requiredRoleStake.anyRequirement(); + + this.state.applicationStake = opening.applications.requiredApplicationStake.value; + this.state.roleStake = opening.applications.requiredRoleStake.value; + + this.state.activeStep = this.state.hasConfirmStep + ? ProgressSteps.ConfirmStakes + : ProgressSteps.ApplicationDetails; + + this.state.roleKeyNameBase = hrt.job.title + ' role key'; + + // When everything is collected, update the view + this.dispatch(); + } + ) + .catch( + (err: any) => { + this.currentOpeningId = -1; + this.currentGroup = null; + this.onError(err); + } + ); + + this.currentOpeningId = id; + this.currentGroup = group; + } + + setApplicationStake (b: Balance): void { + this.state.applicationStake = b; + this.dispatch(); + } + + setRoleStake (b: Balance): void { + this.state.roleStake = b; + this.dispatch(); + } + + setAppDetails (v: any): void { + this.state.appDetails = v; + this.dispatch(); + } + + setTxKeyAddress (v: any): void { + this.state.txKeyAddress = v; + this.dispatch(); + } + + setActiveStep (v: ProgressSteps): void { + this.state.activeStep = v; + this.dispatch(); + } + + setTxInProgress (v: boolean): void { + this.state.txInProgress = v; + this.dispatch(); + } + + setComplete (v: boolean): void { + this.state.complete = v; + this.dispatch(); + } + + // eslint-disable-next-line @typescript-eslint/require-await + async prepareApplicationTransaction ( + applicationStake: Balance, + roleStake: Balance, + questionResponses: any, + txKeyAddress: AccountId + ): Promise { + const totalCommitment = Sum([ + applicationStake, + roleStake + ]); + + this.state.transactionDetails.set('Application stake', formatBalance(this.state.applicationStake)); + this.state.transactionDetails.set('Role stake', formatBalance(roleStake)); + this.state.transactionDetails.set('Total commitment', formatBalance(totalCommitment)); + + this.dispatch(); + return true; + } + + private updateRoleKeyName () { + let roleKeyNamePrefix = 0; + do { + this.state.roleKeyName = `${this.state.roleKeyNameBase}${(++roleKeyNamePrefix > 1 ? ` ${roleKeyNamePrefix}` : '')}`; + } while (this.state.keypairs?.some(k => ( + k.shortName.toLowerCase() === this.state.roleKeyName.toLowerCase() + ))); + } + + async makeApplicationTransaction (): Promise { + if (!this.currentGroup || this.currentOpeningId < 0) { + throw new Error('Trying to apply to unfetched opening'); + } + this.updateRoleKeyName(); + return this.transport.applyToOpening( + this.currentGroup, + this.currentOpeningId, + this.state.roleKeyName, + this.state.txKeyAddress.toString(), + this.state.applicationStake, + this.state.roleStake, + JSON.stringify(this.state.appDetails) + ); + } +} + +export const ApplyView = View( + (state, controller, params) => { + controller.findOpening(params.get('id'), params.get('group')); + return ( + +
+ controller.prepareApplicationTransaction(...args)} + makeApplicationTransaction={() => controller.makeApplicationTransaction()} + applicationStake={state.applicationStake} + setApplicationStake={(v) => controller.setApplicationStake(v)} + roleStake={state.roleStake} + setRoleStake={(v) => controller.setRoleStake(v)} + appDetails={state.appDetails} + setAppDetails={(v) => controller.setAppDetails(v)} + txKeyAddress={state.txKeyAddress} + setTxKeyAddress={(v) => controller.setTxKeyAddress(v)} + activeStep={state.activeStep} + setActiveStep={(v) => controller.setActiveStep(v)} + txInProgress={state.txInProgress} + setTxInProgress={(v) => controller.setTxInProgress(v)} + complete={state.complete} + setComplete={(v) => controller.setComplete(v)} + /> +
+ ); + } +); diff --git a/pioneer/packages/joy-roles/src/flows/apply.elements.stories.tsx b/pioneer/packages/joy-roles/src/flows/apply.elements.stories.tsx new file mode 100644 index 0000000000..916876029d --- /dev/null +++ b/pioneer/packages/joy-roles/src/flows/apply.elements.stories.tsx @@ -0,0 +1,552 @@ +// TODO: FIXME: Remove the ts-nocheck and fix errors! +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +import React, { useState } from 'react'; +import { number, object, withKnobs } from '@storybook/addon-knobs'; +import { Card, Container, Message } from 'semantic-ui-react'; + +import { u128, GenericAccountId } from '@polkadot/types'; +import { Balance } from '@polkadot/types/interfaces'; + +import { + ApplicationDetails +} from '@joystream/types/schemas/role.schema'; +import { + ConfirmStakesStage, ConfirmStakesStageProps, + ProgressStepsView, ProgressStepsProps, ProgressSteps, + ApplicationDetailsStage, ApplicationDetailsStageProps, + SubmitApplicationStage, SubmitApplicationStageProps, + DoneStage, DoneStageProps, + FundSourceSelector, + StakeRankSelector, StakeRankSelectorProps, + ConfirmStakes2Up, ConfirmStakes2UpProps +} from './apply'; +import { + OpeningStakeAndApplicationStatus +} from '../tabs/Opportunities'; +import { + ApplicationStakeRequirement, + RoleStakeRequirement, + StakeType +} from '../StakeRequirement'; + +import 'semantic-ui-css/semantic.min.css'; +import '@polkadot/joy-roles/index.sass'; + +export default { + title: 'Roles / Components / Apply flow / Elements', + decorators: [withKnobs] +}; + +const applicationSliderOptions = { + range: true, + min: 0, + max: 20, + step: 1 +}; + +const moneySliderOptions = { + range: true, + min: 0, + max: 1000000, + step: 500 +}; + +const applications: OpeningStakeAndApplicationStatus = { + numberOfApplications: number('Applications count', 0, applicationSliderOptions, 'Role rationing policy'), + maxNumberOfApplications: number('Application max', 0, applicationSliderOptions, 'Role rationing policy'), + requiredApplicationStake: new ApplicationStakeRequirement( + new u128(number('Application stake', 500, moneySliderOptions, 'Role stakes')) + ), + requiredRoleStake: new RoleStakeRequirement( + new u128(number('Role stake', 0, moneySliderOptions, 'Role stakes')) + ) +}; + +type TestProps = { + _description: string; +} + +export function ProgressIndicator () { + const permutations: (ProgressStepsProps & TestProps)[] = [ + { + _description: 'Three steps, second active', + activeStep: ProgressSteps.SubmitApplication, + hasConfirmStep: false + }, + { + _description: 'Four steps, first active', + activeStep: ProgressSteps.ConfirmStakes, + hasConfirmStep: true + }, + { + _description: 'Four steps, second active', + activeStep: ProgressSteps.SubmitApplication, + hasConfirmStep: true + } + ]; + + return ( + + {permutations.map((permutation, key) => ( + +

{permutation._description}

+ + + +
+ ))} +
+ ); +} + +export function FundSourceSelectorFragment () { + const [address, setAddress] = useState(); + const [passphrase, setPassphrase] = useState(''); + + const props = { + transactionFee: new u128(number('Transaction fee', 500, moneySliderOptions, 'Application Tx')), + keypairs: [ + { + shortName: 'KP1', + accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'), + balance: new u128(23342) + }, + { + shortName: 'KP2', + accountId: new GenericAccountId('5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'), + balance: new u128(993342) + }, + { + shortName: 'KP3', + accountId: new GenericAccountId('5DBaczGTDhcHgwsZzNE5qW15GrQxxdyros4pYkcKrSUovFQ9'), + balance: new u128(242) + } + ] + }; + + return ( + + + + + + + +

Address: {address ? address.toString() : 'not set'}

+

Passphrase: {passphrase}

+
+
+ ); +} + +export function StakeRankSelectorFragment () { + const [stake, setStake] = useState(new u128(0)); + + // List of the minimum stake required to beat each rank + const slots: Balance[] = []; + for (let i = 0; i < 10; i++) { + slots.push(new u128((i * 100) + 10 + i + 1)); + } + + const props: StakeRankSelectorProps = { + stake: stake, + setStake: setStake, + slots: slots, + step: new u128(10) + }; + + return ( + + + + + + + + Slots: {JSON.stringify(slots)}
+ Stake: {stake.toString()} +
+
+ ); +} + +export function SelectTwoMinimumStakes () { + const [applicationStake, setApplicationStake] = useState(new u128(1)); + const [roleStake, setRoleStake] = useState(new u128(2)); + + // List of the minimum stake required to beat each rank + const slots: Balance[] = []; + for (let i = 0; i < 20; i++) { + slots.push(new u128((i * 100) + 10 + i + 1)); + } + + const props: ConfirmStakes2UpProps & TestProps = { + _description: 'One fixed stake (application), no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(1)), + requiredRoleStake: new RoleStakeRequirement(new u128(2)), + maxNumberOfApplications: 0, + numberOfApplications: 0 + }, + defactoMinimumStake: new u128(0), + step: new u128(5), + slots: slots, + selectedApplicationStake: applicationStake, + setSelectedApplicationStake: setApplicationStake, + selectedRoleStake: roleStake, + setSelectedRoleStake: setRoleStake + }; + + return ( + + + + + + + + ); +} + +export function StageAConfirmStakes () { + const permutations: (any & TestProps)[] = [ + { + _description: 'One fixed stake (application), no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)), + requiredRoleStake: new RoleStakeRequirement(new u128(0)), + maxNumberOfApplications: 0, + numberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'One fixed stake (role), no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)), + requiredRoleStake: new RoleStakeRequirement(new u128(1213)), + maxNumberOfApplications: 0, + numberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'Two fixed stakes, no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)), + requiredRoleStake: new RoleStakeRequirement(new u128(10)), + maxNumberOfApplications: 0, + numberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'One fixed stake (application), 20 applicant limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)), + requiredRoleStake: new RoleStakeRequirement(new u128(0)), + maxNumberOfApplications: 20, + numberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'One fixed stake (role), 20 applicant limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(456)), + requiredRoleStake: new RoleStakeRequirement(new u128(0)), + maxNumberOfApplications: 20, + numberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'Two fixed stakes, 20 applicant limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)), + requiredRoleStake: new RoleStakeRequirement(new u128(10)), + maxNumberOfApplications: 20, + numberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'One minimum stake (application), no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast), + requiredRoleStake: new RoleStakeRequirement(new u128(0)), + maxNumberOfApplications: 0, + numberOfApplications: 20 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'One minimum stake (role), no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)), + requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast), + maxNumberOfApplications: 0, + numberOfApplications: 20 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'Two minimum stakes, no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast), + requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast), + maxNumberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'Minimum application stake, fixed role stake, no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast), + requiredRoleStake: new RoleStakeRequirement(new u128(10)), + maxNumberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'Minimum role stake, fixed application stake, no limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)), + requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast), + maxNumberOfApplications: 0 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'One minimum stake (application), 20 applicant limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast), + requiredRoleStake: new RoleStakeRequirement(new u128(0)), + maxNumberOfApplications: 0, + numberOfApplications: 20 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'One minimum stake (role), 20 applicant limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)), + requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast), + maxNumberOfApplications: 0, + numberOfApplications: 20 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'Two minimum stakes, 20 applicant limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast), + requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast), + maxNumberOfApplications: 20 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'Minimum application stake, fixed role stake, 20 applicant limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast), + requiredRoleStake: new RoleStakeRequirement(new u128(10)), + maxNumberOfApplications: 0, + numberOfApplications: 20 + }, + defactoMinimumStake: new u128(0) + }, + { + _description: 'Minimum role stake, fixed application stake, 20 applicant limit', + applications: { + requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)), + requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast), + maxNumberOfApplications: 0, + numberOfApplications: 20 + }, + defactoMinimumStake: new u128(0) + } + ]; + + const keypairs = [ + { + shortName: 'KP1', + accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'), + balance: new u128(23342) + }, + { + shortName: 'KP2', + accountId: new GenericAccountId('5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'), + balance: new u128(993342) + }, + { + shortName: 'KP3', + accountId: new GenericAccountId('5DBaczGTDhcHgwsZzNE5qW15GrQxxdyros4pYkcKrSUovFQ9'), + balance: new u128(242) + } + ]; + + // List of the minimum stake required to beat each rank + const slots: Balance[] = []; + for (let i = 0; i < 20; i++) { + slots.push(new u128((i * 100) + 10 + i + 1)); + } + + const renders = []; + permutations.map((permutation, key) => { + const [applicationStake, setApplicationStake] = useState(new u128(0)); + const [roleStake, setRoleStake] = useState(new u128(0)); + const [stakeKeyAddress, setStakeKeyAddress] = useState(null); + const [stakeKeyPassphrase, setStakeKeyPassphrase] = useState(''); + + const [stake, setStake] = useState(new u128(0)); + const stakeRankSelectorProps: StakeRankSelectorProps = { + slots: slots, + step: new u128(10) + }; + + renders.push( + ( + +

{key}. {permutation._description}

+ + + + + A: {applicationStake.toString()}, R: {roleStake.toString()} + +
+ ) + ); + }); + + return ( + + {renders.map((render, key) => ( +
{render}
+ ))} +
+ ); +} + +export function StageBApplicationDetails () { + const [data, setData] = useState({ + 'About you': { + 'Your e-mail address': 'pre-filled' + } + }); + + const props: ApplicationDetailsStageProps = { + applicationDetails: object('JSON', { + sections: [ + { + title: 'About you', + questions: [ + { + title: 'Your name', + type: 'text' + }, + { + title: 'Your e-mail address', + type: 'text' + } + ] + }, + { + title: 'Your experience', + questions: [ + { + title: 'Why would you be good for this role?', + type: 'text area' + } + ] + } + ] + }, 'Application questions'), + data: data, + setData: setData, + nextTransition: () => { /* do nothing */ } + }; + + return ( + + + + + + + {JSON.stringify(data)} + + + + ); +} + +export function StageCSubmitApplication () { + const props: SubmitApplicationStageProps = { + nextTransition: () => { /* do nothing */ }, + applications: applications, + transactionFee: new u128(number('Transaction fee', 500, moneySliderOptions, 'Application Tx')), + transactionDetails: new Map([ + ['Extrinsic hash', '0xae6d24d4d55020c645ddfe2e8d0faf93b1c0c9879f9bf2c439fb6514c6d1292e'], + ['SOmething else', 'abc123'] + ]), + keypairs: [ + { + shortName: 'KP1', + accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'), + balance: new u128(23342) + }, + { + shortName: 'KP2', + accountId: new GenericAccountId('5F5SwL7zwfdDN4UifacVrYKQVVYzoNcoDoGzmhVkaPN2ef8F'), + balance: new u128(993342) + }, + { + shortName: 'KP3', + accountId: new GenericAccountId('5HmMiZSGnidr3AhUk7hhZa7wJrvYyKEiT8cneyavA1ALkfJc'), + balance: new u128(242) + } + ] + }; + + return ( + + + + + + + + ); +} + +export function StageDDone () { + const props: DoneStageProps = { + applications: applications, + roleKeyName: 'NEW_ROLE_KEY' + }; + + return ( + + + + + + ); +} diff --git a/pioneer/packages/joy-roles/src/flows/apply.sass b/pioneer/packages/joy-roles/src/flows/apply.sass new file mode 100644 index 0000000000..40e7c3c953 --- /dev/null +++ b/pioneer/packages/joy-roles/src/flows/apply.sass @@ -0,0 +1,162 @@ +.apply-flow + position: absolute + width: 100% !important + height: 100% !important + top: 0 + left: 0 + z-index: 999 + padding: 1em + + .dimmer + position: fixed + top: 0 + left: 0 + width: 100% + height: 100% + background: white + + .loading + position: fixed + top: 0 + left: 0 + width: 100% + height: 100% + background: rgba(255,255,255,0.85) + z-index: 99999 + display: flex + + .spinner + margin: auto + + .inner + min-height: 100% + + h5 + font-weight: bold + color: #0e566c + + h4 + color: inherit + font-weight: bold + margin-bottom: 0.5em + + .statistic + .label, .value + text-align: left + + .label + &.fluid + width: calc(100% + 1rem + 1.2em); + + &.standout + margin-bottom: 1.2em + + .column + &.cancel + text-align: right + a + color: colour(grey, light) + transition: 0.3s; + &:hover + color: colour(blue) + + .container + &.content + margin-top: 1em + &.cta + margin-top: 1.5em + + .accordion + background-color: colour(grey, trans) + .content + padding: 0 1em !important + + .stake + .header + margin-bottom: 0.75em + text-transform: capitalize + + .ranks-and-stake + margin-top: 1em + margin-bottom: 2em + +.keypair + display: flex + flex-wrap: nowrap + justify-content: space-between + position: relative + white-space: nowrap + + .address, .balance + display: inline-block + flex: 1 + font-family: monospace + margin-left: 1rem + opacity: 0.5 + overflow: hidden + text-overflow: ellipsis + + .name + display: inline-block + flex: 1 0 + overflow: hidden + text-overflow: ellipsis + margin-left: 0.5rem + + &.uppercase + text-transform: uppercase + +.stake-rank-selector, .confirm-stakes-2up + margin-top: 1.5em + + .controls + margin-bottom: 1em + + p + padding-top: 1em + + .input + width: 30% + + .input, .button + margin-right: 0.5em + + .ticks + margin-left: 10px + margin-right: 10px + + .label + position: relative + transform: translateX(-50%) + + .tick + display: inline-block + text-align: center + border-right: 1px dotted #ccc + + &:first-of-type + border-left: 1px dotted #ccc + +.confirm-stakes-2up + .header + margin-bottom: 1em !important + .two-up + margin-top: 1em + .ticks + margin-top: 1.5em + .center + text-align: center + .controls + margin-left: 0 !important + .input + width: 100% + .fluid + input + width: calc(100% - 4em) !important + +.application-questions + .section + margin-top: 2em + + h4 + padding-bottom: 0.5em diff --git a/pioneer/packages/joy-roles/src/flows/apply.stories.tsx b/pioneer/packages/joy-roles/src/flows/apply.stories.tsx new file mode 100644 index 0000000000..16c8d5d303 --- /dev/null +++ b/pioneer/packages/joy-roles/src/flows/apply.stories.tsx @@ -0,0 +1,169 @@ +// TODO: FIXME: Remove the ts-nocheck and fix errors! +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +import React from 'react'; +import { number, object, select, text, withKnobs } from '@storybook/addon-knobs'; +import * as faker from 'faker'; + +import { u128, GenericAccountId } from '@polkadot/types'; +import { Balance } from '@polkadot/types/interfaces'; + +import { FlowModal } from './apply'; +import { + ApplicationStakeRequirement, RoleStakeRequirement, + StakeType +} from '../StakeRequirement'; + +import 'semantic-ui-css/semantic.min.css'; +import '@polkadot/joy-roles/index.sass'; + +export default { + title: 'Roles / Components / Apply flow', + decorators: [withKnobs] +}; + +const applicationSliderOptions = { + range: true, + min: 0, + max: 20, + step: 1 +}; + +const moneySliderOptions = { + range: true, + min: 0, + max: 1000000, + step: 500 +}; + +const stakeTypeOptions = { + Fixed: StakeType.Fixed, + 'At least': StakeType.AtLeast +}; + +function mockPromise (): () => Promise { + return () => new Promise((resolve, reject) => { + resolve(); + }); +} + +export const ApplicationSandbox = () => { + // List of the minimum stake required to beat each rank + const slots: Balance[] = []; + for (let i = 0; i < 20; i++) { + slots.push(new u128((i * 100) + 10 + i + 1)); + } + const props = { + role: { + version: 1, + headline: text('Headline', 'Help us curate awesome content', 'Role'), + job: { + title: text('Job title', 'Content curator', 'Role'), + description: text('Job description', faker.lorem.paragraphs(4), 'Role') + }, + application: { + sections: [ + { + title: 'About you', + questions: [ + { + title: 'Your name', + type: 'text' + }, + { + title: 'Your e-mail address', + type: 'text' + } + ] + }, + { + title: 'Your experience', + questions: [ + { + title: 'Why would you be good for this role?', + type: 'text area' + } + ] + } + ] + }, + reward: text('Reward', '10 JOY per block', 'Role'), + creator: { + membership: { + handle: text('Creator handle', 'ben', 'Role') + } + }, + process: { + details: [ + 'Some custom detail' + ] + } + }, + applications: { + numberOfApplications: number('Applications count', 0, applicationSliderOptions, 'Role rationing policy'), + maxNumberOfApplications: number('Application max', 0, applicationSliderOptions, 'Role rationing policy'), + requiredApplicationStake: new ApplicationStakeRequirement( + new u128(number('Application stake', 500, moneySliderOptions, 'Role stakes')), + select('Application stake type', stakeTypeOptions, StakeType.AtLeast, 'Role stakes') + ), + requiredRoleStake: new RoleStakeRequirement( + new u128(number('Role stake', 500, moneySliderOptions, 'Role stakes')), + select('Role stake type', stakeTypeOptions, StakeType.Fixed, 'Role stakes') + ), + defactoMinimumStake: new u128(0) + }, + creator: creator, + transactionFee: new u128(number('Transaction fee', 499, moneySliderOptions, 'Application Tx')), + keypairs: [ + { + shortName: 'KP1', + accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'), + balance: new u128(23342) + }, + { + shortName: 'KP2', + accountId: new GenericAccountId('5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'), + balance: new u128(993342) + }, + { + shortName: 'KP3', + accountId: new GenericAccountId('5DBaczGTDhcHgwsZzNE5qW15GrQxxdyros4pYkcKrSUovFQ9'), + balance: new u128(242) + } + ], + prepareApplicationTransaction: mockPromise(), + makeApplicationTransaction: mockPromise(), + transactionDetails: new Map([['Detail title', 'Detail value']]), + hasConfirmStep: true, + step: new u128(5), + slots: slots, + applicationDetails: object('JSON', { + sections: [ + { + title: 'About you', + questions: [ + { + title: 'Your name', + type: 'text' + }, + { + title: 'Your e-mail address', + type: 'text' + } + ] + }, + { + title: 'Your experience', + questions: [ + { + title: 'Why would you be good for this role?', + type: 'text area' + } + ] + } + ] + }, 'Application questions') + }; + + return ; +}; diff --git a/pioneer/packages/joy-roles/src/flows/apply.tsx b/pioneer/packages/joy-roles/src/flows/apply.tsx new file mode 100644 index 0000000000..4d59aa96ed --- /dev/null +++ b/pioneer/packages/joy-roles/src/flows/apply.tsx @@ -0,0 +1,1169 @@ +import React, { useEffect, useReducer, useState } from 'react'; +import { useHistory, Link } from 'react-router-dom'; + +import { formatBalance } from '@polkadot/util'; +import { Balance } from '@polkadot/types/interfaces'; +import { + GenericAccountId, + u128 +} from '@polkadot/types'; + +import { useMyAccount } from '@polkadot/joy-utils/MyAccountContext'; + +import { + Accordion, + Button, + Container, + Form, + Grid, + Header, + Icon, + Input, + Label, + Message, + Segment, + SemanticICONS, + Step, + Table +} from 'semantic-ui-react'; + +import Identicon from '@polkadot/react-identicon'; +import AccountId from '@polkadot/types/primitive/Generic/AccountId'; + +import { + GenericJoyStreamRoleSchema, + ApplicationDetails, + QuestionField, + QuestionSection +} from '@joystream/types/hiring/schemas/role.schema.typings'; + +import { + OpeningBodyApplicationsStatus, OpeningStakeAndApplicationStatus, + ApplicationCount, + StakeRequirementProps +} from '../tabs/Opportunities'; +import { IStakeRequirement } from '../StakeRequirement'; + +import { Loadable } from '@polkadot/joy-utils/index'; +import { Add } from '../balances'; + +type accordionProps = { + title: string; +} + +function ModalAccordion (props: React.PropsWithChildren) { + const [open, setOpen] = useState(false); + return ( + + { setOpen(!open); }} > + + {props.title} + + {props.children} + + ); +} + +function KeyPair ({ address, className, style, isUppercase, name, balance }: any): any { + return ( +
+ +
+ {name} +
+
+ {formatBalance(balance)} +
+
+ {address} +
+
+ ); +} + +export type keyPairDetails = { + shortName: string; + accountId: AccountId; + balance: Balance; +} + +export type FundSourceSelectorProps = { + keypairs: keyPairDetails[]; + totalStake?: Balance; +} + +type FundSourceCallbackProps = { + addressCallback?: (address: AccountId) => void; + passphraseCallback?: (passphrase: string) => void; +} + +export function FundSourceSelector (props: FundSourceSelectorProps & FundSourceCallbackProps) { + const pairs: any[] = []; + + const onChangeDropdown = (e: any, { value }: any) => { + if (typeof props.addressCallback !== 'undefined') { + props.addressCallback(new GenericAccountId(value)); + } + }; + + const onChangeInput = (e: any, { value }: any) => { + if (props.passphraseCallback) { + props.passphraseCallback(value); + } + }; + + props.keypairs.map((v) => { + if (props.totalStake && v.balance.lt(props.totalStake)) { + return; + } + + pairs.push({ + key: v.shortName, + text: ( + + ), + value: v.accountId.toString() + }); + }); + + useEffect(() => { + if (pairs.length > 0 && typeof props.addressCallback !== 'undefined') { + props.addressCallback(new GenericAccountId(pairs[0].accountId)); + } + }, []); + + const accCtx = useMyAccount(); + let passphraseCallback = null; + if (props.passphraseCallback) { + passphraseCallback = ( + + + + + ); + } + + return ( +
+ + + + + {passphraseCallback} +
+ ); +} + +function rankIcon (estimatedSlot: number, slots: number): SemanticICONS { + if (estimatedSlot === 1) { // 1st place + return 'thermometer'; + } else if (estimatedSlot <= (slots / 3)) { // Places 2-33 if slotsCount == 100 + return 'thermometer three quarters'; + } else if (estimatedSlot <= (slots / 1.5)) { // Places 34-66 if slotsCount == 100 + return 'thermometer half'; + } else if (estimatedSlot <= slots) { // Places 67-100 if slotsCount == 100 + return 'thermometer quarter'; + } + return 'thermometer empty'; // Places >100 for slotsCount == 100 +} + +export type StakeRankSelectorProps = { + slots: Balance[]; // List of stakes to beat + stake: Balance; + setStake: (b: Balance) => void; + step: Balance; + otherStake: Balance; + requirement: IStakeRequirement; + maxNumberOfApplications: number; +} + +export function StakeRankSelector (props: StakeRankSelectorProps) { + const slotCount = props.slots.length; + const minStake = props.maxNumberOfApplications && props.slots.length === props.maxNumberOfApplications + ? props.slots[0].sub(props.otherStake).addn(1) // Slots are ordered by stake ASC + : props.requirement.value; + const stakeSufficient = props.stake.gte(minStake); + + const ticks = []; + for (let i = 0; i < slotCount; i++) { + ticks.push(
{slotCount - i}
); + } + + let estimatedSlot = slotCount + 1; + props.slots.forEach(slotStake => props.stake.gt(slotStake.sub(props.otherStake)) && --estimatedSlot); + + const changeValue = (e: any, { value }: any) => { + const newStake = new u128(value); + props.setStake(newStake); + }; + + const slider = null; + return ( + +

Choose a stake

+ + 1 ? props.step.toNumber() : 1} + value={props.stake.toNumber() > 0 ? props.stake.toNumber() : 0} + min={minStake} + error={!stakeSufficient} + /> + { props.maxNumberOfApplications > 0 && ( + + ) } + + + {slider} + { !stakeSufficient && ( + + ) } +
+ ); +} + +export enum ProgressSteps { + ConfirmStakes = 0, + ApplicationDetails, + SubmitApplication, + Done, +} + +export type ProgressStepsProps = { + activeStep: ProgressSteps; + hasConfirmStep: boolean; +} + +interface ProgressStepDefinition { + name: string; + display: boolean; +} +interface ProgressStep extends ProgressStepDefinition { + active: boolean; + disabled: boolean; +} + +function ProgressStepView (props: ProgressStep) { + if (!props.display) { + return null; + } + + return ( + + + {props.name} + + + ); +} + +export function ProgressStepsView (props: ProgressStepsProps) { + const steps: ProgressStepDefinition[] = [ + { + name: 'Confirm stakes', + display: props.hasConfirmStep + }, + { + name: 'Application details', + display: true + }, + { + name: 'Submit application', + display: true + }, + { + name: 'Done', + display: true + } + ]; + return ( + + {steps.map((step, key) => ( + props.activeStep} + /> + ))} + + ); +} + +type CTACallback = () => void + +type CTAProps = { + negativeLabel: string; + negativeIcon: SemanticICONS; + negativeCallback: CTACallback; + positiveLabel: string; + positiveIcon: SemanticICONS; + positiveCallback?: CTACallback; +} + +function CTA (props: CTAProps) { + return ( + +