Browse Source

Merge branch 'iznik' into feature/devops-update

Gleb Urvanov 4 years ago
parent
commit
8c6761fbb6
37 changed files with 835 additions and 725 deletions
  1. 1 1
      .travis.yml
  2. 136 1
      Cargo.lock
  3. 31 31
      cli/README.md
  4. 1 1
      cli/package.json
  5. 81 108
      cli/src/Api.ts
  6. 101 113
      cli/src/Types.ts
  7. 5 3
      cli/src/base/AccountsCommandBase.ts
  8. 42 27
      cli/src/base/ApiCommandBase.ts
  9. 4 4
      cli/src/base/WorkingGroupsCommandBase.ts
  10. 2 3
      cli/src/commands/account/current.ts
  11. 4 4
      cli/src/commands/account/transferTokens.ts
  12. 11 14
      cli/src/commands/api/inspect.ts
  13. 1 0
      cli/src/commands/working-groups/application.ts
  14. 2 2
      cli/src/commands/working-groups/createOpening.ts
  15. 3 5
      cli/src/commands/working-groups/decreaseWorkerStake.ts
  16. 3 4
      cli/src/commands/working-groups/evictWorker.ts
  17. 4 8
      cli/src/commands/working-groups/fillOpening.ts
  18. 1 0
      cli/src/commands/working-groups/opening.ts
  19. 2 5
      cli/src/commands/working-groups/slashWorker.ts
  20. 2 4
      cli/src/commands/working-groups/startAcceptingApplications.ts
  21. 2 4
      cli/src/commands/working-groups/startReviewPeriod.ts
  22. 3 4
      cli/src/commands/working-groups/terminateApplication.ts
  23. 2 2
      cli/src/commands/working-groups/updateRewardAccount.ts
  24. 2 2
      cli/src/commands/working-groups/updateRoleAccount.ts
  25. 2 2
      cli/src/commands/working-groups/updateWorkerReward.ts
  26. 2 2
      cli/src/helpers/validation.ts
  27. 11 9
      cli/src/promptOptions/addWorkerOpening.ts
  28. 5 1
      cli/tsconfig.json
  29. 1 1
      node/Cargo.toml
  30. 1 1
      node/src/chain_spec/content_config.rs
  31. 79 68
      node/src/chain_spec/mod.rs
  32. 244 247
      node/src/service.rs
  33. 25 40
      runtime-modules/governance/src/council.rs
  34. 8 1
      runtime-modules/governance/src/mock.rs
  35. 1 1
      runtime/Cargo.toml
  36. 1 1
      runtime/src/lib.rs
  37. 9 1
      runtime/src/tests/mod.rs

+ 1 - 1
.travis.yml

@@ -36,7 +36,7 @@ before_script:
 script:
   - export WASM_BUILD_TOOLCHAIN=nightly-2020-05-23
   - BUILD_DUMMY_WASM_BINARY=1 cargo clippy --release --all -- -D warnings
-  - travis_wait 50 cargo test --release --verbose --all
+  - travis_wait 75 cargo test --release --verbose --all -- --ignored
   - cargo build --release
   - ls -l ./target/release/wbuild/joystream-node-runtime/
   - ./target/release/joystream-node --version

+ 136 - 1
Cargo.lock

@@ -2024,6 +2024,7 @@ dependencies = [
  "sc-network",
  "sc-rpc-api",
  "sc-service",
+ "sc-service-test",
  "sc-transaction-pool",
  "serde",
  "serde_json",
@@ -2052,7 +2053,7 @@ dependencies = [
 
 [[package]]
 name = "joystream-node-runtime"
-version = "7.0.0"
+version = "7.1.0"
 dependencies = [
  "frame-benchmarking",
  "frame-executive",
@@ -5629,6 +5630,43 @@ dependencies = [
  "wasm-timer",
 ]
 
+[[package]]
+name = "sc-service-test"
+version = "2.0.0-rc4"
+source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4"
+dependencies = [
+ "env_logger",
+ "fdlimit",
+ "futures 0.1.29",
+ "futures 0.3.4",
+ "hex-literal",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.10.2",
+ "sc-block-builder",
+ "sc-client-api",
+ "sc-client-db",
+ "sc-executor",
+ "sc-light",
+ "sc-network",
+ "sc-service",
+ "sp-api",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-core",
+ "sp-externalities",
+ "sp-panic-handler",
+ "sp-runtime",
+ "sp-state-machine",
+ "sp-storage",
+ "sp-transaction-pool",
+ "sp-trie",
+ "substrate-test-runtime",
+ "substrate-test-runtime-client",
+ "tempfile",
+ "tokio 0.1.22",
+]
+
 [[package]]
 name = "sc-state-db"
 version = "0.8.0-rc4"
@@ -6205,6 +6243,20 @@ dependencies = [
  "wasm-timer",
 ]
 
+[[package]]
+name = "sp-consensus-aura"
+version = "0.8.0-rc4"
+source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4"
+dependencies = [
+ "parity-scale-codec",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-inherents",
+ "sp-runtime",
+ "sp-std",
+ "sp-timestamp",
+]
+
 [[package]]
 name = "sp-consensus-babe"
 version = "0.8.0-rc4"
@@ -6816,6 +6868,89 @@ dependencies = [
  "tokio 0.2.22",
 ]
 
+[[package]]
+name = "substrate-test-client"
+version = "2.0.0-rc4"
+source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4"
+dependencies = [
+ "futures 0.3.4",
+ "hash-db",
+ "parity-scale-codec",
+ "sc-client-api",
+ "sc-client-db",
+ "sc-consensus",
+ "sc-executor",
+ "sc-light",
+ "sc-service",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-core",
+ "sp-keyring",
+ "sp-runtime",
+ "sp-state-machine",
+]
+
+[[package]]
+name = "substrate-test-runtime"
+version = "2.0.0-rc4"
+source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4"
+dependencies = [
+ "cfg-if",
+ "frame-executive",
+ "frame-support",
+ "frame-system",
+ "frame-system-rpc-runtime-api",
+ "log",
+ "memory-db",
+ "pallet-babe",
+ "pallet-timestamp",
+ "parity-scale-codec",
+ "parity-util-mem",
+ "sc-service",
+ "serde",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-block-builder",
+ "sp-consensus-aura",
+ "sp-consensus-babe",
+ "sp-core",
+ "sp-finality-grandpa",
+ "sp-inherents",
+ "sp-io",
+ "sp-keyring",
+ "sp-offchain",
+ "sp-runtime",
+ "sp-runtime-interface",
+ "sp-session",
+ "sp-std",
+ "sp-transaction-pool",
+ "sp-trie",
+ "sp-version",
+ "substrate-wasm-builder-runner",
+ "trie-db",
+]
+
+[[package]]
+name = "substrate-test-runtime-client"
+version = "2.0.0-rc4"
+source = "git+https://github.com/paritytech/substrate.git?rev=00768a1f21a579c478fe5d4f51e1fa71f7db9fd4#00768a1f21a579c478fe5d4f51e1fa71f7db9fd4"
+dependencies = [
+ "futures 0.3.4",
+ "parity-scale-codec",
+ "sc-block-builder",
+ "sc-client-api",
+ "sc-consensus",
+ "sc-light",
+ "sc-service",
+ "sp-api",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-core",
+ "sp-runtime",
+ "substrate-test-client",
+ "substrate-test-runtime",
+]
+
 [[package]]
 name = "substrate-wasm-builder-runner"
 version = "1.0.6"

+ 31 - 31
cli/README.md

@@ -108,7 +108,7 @@ 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)_
+_See code: [src/commands/account/choose.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/account/choose.ts)_
 
 ## `joystream-cli account:create NAME`
 
@@ -122,7 +122,7 @@ ARGUMENTS
   NAME  Account name
 ```
 
-_See code: [src/commands/account/create.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/account/create.ts)_
+_See code: [src/commands/account/create.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/account/create.ts)_
 
 ## `joystream-cli account:current`
 
@@ -137,7 +137,7 @@ ALIASES
   $ joystream-cli account:default
 ```
 
-_See code: [src/commands/account/current.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/account/current.ts)_
+_See code: [src/commands/account/current.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/account/current.ts)_
 
 ## `joystream-cli account:export PATH`
 
@@ -154,7 +154,7 @@ OPTIONS
   -a, --all  If provided, exports all existing accounts into "exported_accounts" folder inside given path
 ```
 
-_See code: [src/commands/account/export.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/account/export.ts)_
+_See code: [src/commands/account/export.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/account/export.ts)_
 
 ## `joystream-cli account:forget`
 
@@ -165,7 +165,7 @@ USAGE
   $ joystream-cli account:forget
 ```
 
-_See code: [src/commands/account/forget.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/account/forget.ts)_
+_See code: [src/commands/account/forget.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/account/forget.ts)_
 
 ## `joystream-cli account:import BACKUPFILEPATH`
 
@@ -179,7 +179,7 @@ ARGUMENTS
   BACKUPFILEPATH  Path to account backup JSON file
 ```
 
-_See code: [src/commands/account/import.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/account/import.ts)_
+_See code: [src/commands/account/import.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/account/import.ts)_
 
 ## `joystream-cli account:transferTokens RECIPIENT AMOUNT`
 
@@ -194,7 +194,7 @@ ARGUMENTS
   AMOUNT     Amount of tokens to transfer
 ```
 
-_See code: [src/commands/account/transferTokens.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/account/transferTokens.ts)_
+_See code: [src/commands/account/transferTokens.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/account/transferTokens.ts)_
 
 ## `joystream-cli api:getUri`
 
@@ -205,7 +205,7 @@ USAGE
   $ joystream-cli api:getUri
 ```
 
-_See code: [src/commands/api/getUri.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/api/getUri.ts)_
+_See code: [src/commands/api/getUri.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/api/getUri.ts)_
 
 ## `joystream-cli api:inspect`
 
@@ -221,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
@@ -249,7 +249,7 @@ EXAMPLES
   $ 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)_
+_See code: [src/commands/api/inspect.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/api/inspect.ts)_
 
 ## `joystream-cli api:setUri [URI]`
 
@@ -263,7 +263,7 @@ ARGUMENTS
   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)_
+_See code: [src/commands/api/setUri.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/api/setUri.ts)_
 
 ## `joystream-cli autocomplete [SHELL]`
 
@@ -297,7 +297,7 @@ USAGE
   $ joystream-cli council:info
 ```
 
-_See code: [src/commands/council/info.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/council/info.ts)_
+_See code: [src/commands/council/info.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/council/info.ts)_
 
 ## `joystream-cli help [COMMAND]`
 
@@ -333,7 +333,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/application.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/application.ts)_
 
 ## `joystream-cli working-groups:createOpening`
 
@@ -359,7 +359,7 @@ OPTIONS
   -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)_
+_See code: [src/commands/working-groups/createOpening.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/createOpening.ts)_
 
 ## `joystream-cli working-groups:decreaseWorkerStake WORKERID`
 
@@ -378,7 +378,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/decreaseWorkerStake.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/decreaseWorkerStake.ts)_
 
 ## `joystream-cli working-groups:evictWorker WORKERID`
 
@@ -397,7 +397,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/evictWorker.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/evictWorker.ts)_
 
 ## `joystream-cli working-groups:fillOpening WGOPENINGID`
 
@@ -416,7 +416,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/fillOpening.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/fillOpening.ts)_
 
 ## `joystream-cli working-groups:increaseStake`
 
@@ -432,7 +432,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/increaseStake.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/increaseStake.ts)_
 
 ## `joystream-cli working-groups:leaveRole`
 
@@ -448,7 +448,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/leaveRole.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/leaveRole.ts)_
 
 ## `joystream-cli working-groups:opening WGOPENINGID`
 
@@ -467,7 +467,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/opening.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/opening.ts)_
 
 ## `joystream-cli working-groups:openings`
 
@@ -483,7 +483,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/openings.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/openings.ts)_
 
 ## `joystream-cli working-groups:overview`
 
@@ -499,7 +499,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/overview.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/overview.ts)_
 
 ## `joystream-cli working-groups:slashWorker WORKERID`
 
@@ -518,7 +518,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/slashWorker.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/slashWorker.ts)_
 
 ## `joystream-cli working-groups:startAcceptingApplications WGOPENINGID`
 
@@ -537,7 +537,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/startAcceptingApplications.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/startAcceptingApplications.ts)_
 
 ## `joystream-cli working-groups:startReviewPeriod WGOPENINGID`
 
@@ -556,7 +556,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/startReviewPeriod.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/startReviewPeriod.ts)_
 
 ## `joystream-cli working-groups:terminateApplication WGAPPLICATIONID`
 
@@ -575,7 +575,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/terminateApplication.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/terminateApplication.ts)_
 
 ## `joystream-cli working-groups:updateRewardAccount [ACCOUNTADDRESS]`
 
@@ -594,7 +594,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/updateRewardAccount.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/updateRewardAccount.ts)_
 
 ## `joystream-cli working-groups:updateRoleAccount [ACCOUNTADDRESS]`
 
@@ -613,7 +613,7 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/updateRoleAccount.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/updateRoleAccount.ts)_
 
 ## `joystream-cli working-groups:updateWorkerReward WORKERID`
 
@@ -632,5 +632,5 @@ OPTIONS
                      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)_
+_See code: [src/commands/working-groups/updateWorkerReward.ts](https://github.com/Joystream/joystream/blob/master/cli/src/commands/working-groups/updateWorkerReward.ts)_
 <!-- commandsstop -->

+ 1 - 1
cli/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@joystream/cli",
   "description": "Command Line Interface for Joystream community and governance activities",
-  "version": "0.1.0",
+  "version": "0.2.0",
   "author": "Leszek Wiesner",
   "bin": {
     "joystream-cli": "./bin/run"

+ 81 - 108
cli/src/Api.ts

@@ -1,13 +1,12 @@
 import BN from 'bn.js'
-import { registerJoystreamTypes } from '@joystream/types/'
+import { types } from '@joystream/types/'
 import { ApiPromise, WsProvider } from '@polkadot/api'
-import { QueryableStorageMultiArg } from '@polkadot/api/types'
+import { QueryableStorageMultiArg, SubmittableExtrinsic, QueryableStorageEntry } from '@polkadot/api/types'
 import { formatBalance } from '@polkadot/util'
-import { Hash, Balance, Moment, BlockNumber } from '@polkadot/types/interfaces'
+import { 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 { Codec, CodecArg } from '@polkadot/types/types'
+import { Option, Vec, UInt } from '@polkadot/types'
 import {
   AccountSummary,
   CouncilInfoObj,
@@ -24,7 +23,7 @@ import {
   UnstakingPeriods,
   StakingPolicyUnstakingPeriodKey,
 } from './Types'
-import { DerivedFees, DerivedBalances } from '@polkadot/api-derive/types'
+import { DeriveBalancesAll } from '@polkadot/api-derive/types'
 import { CLIError } from '@oclif/errors'
 import ExitCodes from './ExitCodes'
 import {
@@ -46,12 +45,11 @@ import {
 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'
 
 import { InputValidationLengthConstraint } from '@joystream/types/common'
 
 export const DEFAULT_API_URI = 'ws://localhost:9944/'
-const DEFAULT_DECIMALS = new u32(12)
+const DEFAULT_DECIMALS = new BN(12)
 
 // Mapping of working group to api module
 export const apiModuleByGroup: { [key in WorkingGroups]: string } = {
@@ -72,8 +70,7 @@ export default class Api {
 
   private static async initApi(apiUri: string = DEFAULT_API_URI): Promise<ApiPromise> {
     const wsProvider: WsProvider = new WsProvider(apiUri)
-    registerJoystreamTypes()
-    const api = await ApiPromise.create({ provider: wsProvider })
+    const api = await ApiPromise.create({ provider: wsProvider, types })
 
     // Initializing some api params based on pioneer/packages/react-api/Api.tsx
     const [properties] = await Promise.all([api.rpc.system.properties()])
@@ -95,23 +92,27 @@ export default class Api {
     return new Api(originalApi)
   }
 
-  private async queryMultiOnce(queries: Parameters<typeof ApiPromise.prototype.queryMulti>[0]): Promise<Codec[]> {
-    let results: Codec[] = []
-
-    const unsub = await this._api.queryMulti(queries, (res) => {
-      results = res
+  private queryMultiOnce(queries: Parameters<typeof ApiPromise.prototype.queryMulti>[0]): Promise<Codec[]> {
+    return new Promise((resolve, reject) => {
+      let unsub: () => void
+      this._api
+        .queryMulti(queries, (res) => {
+          // unsub should already be set at this point
+          if (!unsub) {
+            reject(new CLIError('API queryMulti issue - unsub method not set!', { exit: ExitCodes.ApiError }))
+          }
+          unsub()
+          resolve(res)
+        })
+        .then((unsubscribe) => (unsub = unsubscribe))
+        .catch((e) => reject(e))
     })
-    unsub()
-
-    if (!results.length || results.length !== queries.length) {
-      throw new CLIError('API querying issue', { exit: ExitCodes.ApiError })
-    }
-
-    return results
   }
 
-  async getAccountsBalancesInfo(accountAddresses: string[]): Promise<DerivedBalances[]> {
-    const accountsBalances: DerivedBalances[] = await this._api.derive.balances.votingBalances(accountAddresses)
+  async getAccountsBalancesInfo(accountAddresses: string[]): Promise<DeriveBalancesAll[]> {
+    const accountsBalances: DeriveBalancesAll[] = await Promise.all(
+      accountAddresses.map((addr) => this._api.derive.balances.all(addr))
+    )
 
     return accountsBalances
   }
@@ -119,7 +120,7 @@ export default class Api {
   // Get on-chain data related to given account.
   // For now it's just account balances
   async getAccountSummary(accountAddresses: string): Promise<AccountSummary> {
-    const balances: DerivedBalances = (await this.getAccountsBalancesInfo([accountAddresses]))[0]
+    const balances: DeriveBalancesAll = (await this.getAccountsBalancesInfo([accountAddresses]))[0]
     // TODO: Some more information can be fetched here in the future
 
     return { balances }
@@ -146,34 +147,29 @@ export default class Api {
     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<BN> {
-    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 estimateFee(account: KeyringPair, tx: SubmittableExtrinsic<'promise'>): Promise<Balance> {
+    const paymentInfo = await tx.paymentInfo(account)
+    return paymentInfo.partialFee
   }
 
-  async transfer(account: KeyringPair, recipientAddr: string, amount: BN): Promise<Hash> {
-    const txHash = await this._api.tx.balances.transfer(recipientAddr, amount).signAndSend(account)
-    return txHash
+  createTransferTx(recipient: string, amount: BN) {
+    return this._api.tx.balances.transfer(recipient, amount)
   }
 
   // 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<T extends Codec>(result: LinkageResult) {
-    return result[0] as T
-  }
+  // TODO: This is a lot of repeated logic from "/pioneer/joy-utils/transport"
+  // It will be refactored to "joystream-js" soon
+  async entriesByIds<IDType extends UInt, ValueType extends Codec>(
+    apiMethod: QueryableStorageEntry<'promise'>,
+    firstKey?: CodecArg // First key in case of double maps
+  ): Promise<[IDType, ValueType][]> {
+    const entries: [IDType, ValueType][] = (await apiMethod.entries<ValueType>(firstKey)).map(([storageKey, value]) => [
+      // If double-map (first key is provided), we map entries by second key
+      storageKey.args[firstKey !== undefined ? 1 : 0] as IDType,
+      value,
+    ])
 
-  protected multiLinkageResult<K extends Codec, V extends Codec>(result: LinkageResult): [Vec<K>, Vec<V>] {
-    return [result[0] as Vec<K>, result[1] as Vec<V>]
+    return entries.sort((a, b) => a[0].toNumber() - b[0].toNumber())
   }
 
   protected async blockHash(height: number): Promise<string> {
@@ -214,7 +210,7 @@ export default class Api {
   }
 
   protected async stakeValue(stakeId: StakeId): Promise<Balance> {
-    const stake = this.singleLinkageResult<Stake>((await this._api.query.stake.stakes(stakeId)) as LinkageResult)
+    const stake = await this._api.query.stake.stakes<Stake>(stakeId)
     return stake.value
   }
 
@@ -223,8 +219,8 @@ export default class Api {
   }
 
   protected async workerReward(relationshipId: RewardRelationshipId): Promise<Reward> {
-    const rewardRelationship = this.singleLinkageResult<RewardRelationship>(
-      (await this._api.query.recurringRewards.rewardRelationships(relationshipId)) as LinkageResult
+    const rewardRelationship = await this._api.query.recurringRewards.rewardRelationships<RewardRelationship>(
+      relationshipId
     )
 
     return {
@@ -266,18 +262,16 @@ export default class Api {
   }
 
   async workerByWorkerId(group: WorkingGroups, workerId: number): Promise<Worker> {
-    const nextId = (await this.workingGroupApiQuery(group).nextWorkerId()) as WorkerId
+    const nextId = await this.workingGroupApiQuery(group).nextWorkerId<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<Worker>(
-      (await this.workingGroupApiQuery(group).workerById(workerId)) as LinkageResult
-    )
+    const worker = await this.workingGroupApiQuery(group).workerById<Worker>(workerId)
 
-    if (!worker.is_active) {
+    if (worker.isEmpty) {
       throw new CLIError('This worker is not active anymore')
     }
 
@@ -286,67 +280,51 @@ export default class Api {
 
   async groupMember(group: WorkingGroups, workerId: number) {
     const worker = await this.workerByWorkerId(group, workerId)
-    return await this.parseGroupMember(new WorkerId(workerId), worker)
+    return await this.parseGroupMember(this._api.createType('WorkerId', workerId), worker)
   }
 
   async groupMembers(group: WorkingGroups): Promise<GroupMember[]> {
-    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 []
-    }
+    const workerEntries = await this.entriesByIds<WorkerId, Worker>(this.workingGroupApiQuery(group).workerById)
 
-    const [workerIds, workers] = this.multiLinkageResult<WorkerId, Worker>(
-      (await this.workingGroupApiQuery(group).workerById()) as LinkageResult
+    const groupMembers: GroupMember[] = await Promise.all(
+      workerEntries.map(([id, worker]) => this.parseGroupMember(id, worker))
     )
 
-    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))
-      }
-    }
-
-    return groupMembers.reverse()
+    return groupMembers.reverse() // Sort by newest
   }
 
   async openingsByGroup(group: WorkingGroups): Promise<GroupOpening[]> {
-    const openings: GroupOpening[] = []
-    const nextId = (await this.workingGroupApiQuery(group).nextOpeningId()) as OpeningId
+    let openings: GroupOpening[] = []
+    const nextId = await this.workingGroupApiQuery(group).nextOpeningId<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))
-      }
+      const ids = Array.from(Array(nextId.toNumber()).keys()).reverse() // Sort by newest
+      openings = await Promise.all(ids.map((id) => this.groupOpening(group, id)))
     }
 
     return openings
   }
 
   protected async hiringOpeningById(id: number | OpeningId): Promise<Opening> {
-    const result = (await this._api.query.hiring.openingById(id)) as LinkageResult
-    return this.singleLinkageResult<Opening>(result)
+    const result = await this._api.query.hiring.openingById<Opening>(id)
+    return result
   }
 
   protected async hiringApplicationById(id: number | ApplicationId): Promise<Application> {
-    const result = (await this._api.query.hiring.applicationById(id)) as LinkageResult
-    return this.singleLinkageResult<Application>(result)
+    const result = await this._api.query.hiring.applicationById<Application>(id)
+    return result
   }
 
   async wgApplicationById(group: WorkingGroups, wgApplicationId: number): Promise<WGApplication> {
-    const nextAppId = (await this.workingGroupApiQuery(group).nextApplicationId()) as ApplicationId
+    const nextAppId = await this.workingGroupApiQuery(group).nextApplicationId<ApplicationId>()
 
     if (wgApplicationId < 0 || wgApplicationId >= nextAppId.toNumber()) {
       throw new CLIError('Invalid working group application ID!')
     }
 
-    return this.singleLinkageResult<WGApplication>(
-      (await this.workingGroupApiQuery(group).applicationById(wgApplicationId)) as LinkageResult
-    )
+    const result = await this.workingGroupApiQuery(group).applicationById<WGApplication>(wgApplicationId)
+    return result
   }
 
   protected async parseApplication(wgApplicationId: number, wgApplication: WGApplication): Promise<GroupApplication> {
@@ -376,18 +354,15 @@ export default class Api {
   }
 
   protected async groupOpeningApplications(group: WorkingGroups, wgOpeningId: number): Promise<GroupApplication[]> {
-    const applications: GroupApplication[] = []
-
-    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))
-    }
+    const wgApplicationEntries = await this.entriesByIds<ApplicationId, WGApplication>(
+      this.workingGroupApiQuery(group).applicationById
+    )
 
-    return applications
+    return Promise.all(
+      wgApplicationEntries
+        .filter(([, /* id */ wgApplication]) => wgApplication.opening_id.eqn(wgOpeningId))
+        .map(([id, wgApplication]) => this.parseApplication(id.toNumber(), wgApplication))
+    )
   }
 
   async groupOpening(group: WorkingGroups, wgOpeningId: number): Promise<GroupOpening> {
@@ -397,9 +372,7 @@ export default class Api {
       throw new CLIError('Invalid working group opening ID!')
     }
 
-    const groupOpening = this.singleLinkageResult<WGOpening>(
-      (await this.workingGroupApiQuery(group).openingById(wgOpeningId)) as LinkageResult
-    )
+    const groupOpening = await this.workingGroupApiQuery(group).openingById<WGOpening>(wgOpeningId)
 
     const openingId = groupOpening.hiring_opening_id.toNumber()
     const opening = await this.hiringOpeningById(openingId)
@@ -417,19 +390,19 @@ export default class Api {
       sp.isSome ? unstakingPeriod(sp.unwrap()[key]) : 0
 
     const unstakingPeriods: Partial<UnstakingPeriods> = {
-      ['review_period_expired_application_stake_unstaking_period_length']: spUnstakingPeriod(
+      'review_period_expired_application_stake_unstaking_period_length': spUnstakingPeriod(
         applSP,
         'review_period_expired_unstaking_period_length'
       ),
-      ['crowded_out_application_stake_unstaking_period_length']: spUnstakingPeriod(
+      'crowded_out_application_stake_unstaking_period_length': spUnstakingPeriod(
         applSP,
         'crowded_out_unstaking_period_length'
       ),
-      ['review_period_expired_role_stake_unstaking_period_length']: spUnstakingPeriod(
+      'review_period_expired_role_stake_unstaking_period_length': spUnstakingPeriod(
         roleSP,
         'review_period_expired_unstaking_period_length'
       ),
-      ['crowded_out_role_stake_unstaking_period_length']: spUnstakingPeriod(
+      'crowded_out_role_stake_unstaking_period_length': spUnstakingPeriod(
         roleSP,
         'crowded_out_unstaking_period_length'
       ),
@@ -493,11 +466,11 @@ export default class Api {
   }
 
   async getMemberIdsByControllerAccount(address: string): Promise<MemberId[]> {
-    const ids = (await this._api.query.members.memberIdsByControllerAccountId(address)) as Vec<MemberId>
+    const ids = await this._api.query.members.memberIdsByControllerAccountId<Vec<MemberId>>(address)
     return ids.toArray()
   }
 
   async workerExitRationaleConstraint(group: WorkingGroups): Promise<InputValidationLengthConstraint> {
-    return (await this.workingGroupApiQuery(group).workerExitRationaleText()) as InputValidationLengthConstraint
+    return await this.workingGroupApiQuery(group).workerExitRationaleText<InputValidationLengthConstraint>()
   }
 }

+ 101 - 113
cli/src/Types.ts

@@ -5,7 +5,7 @@ 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 { DeriveBalancesAll } 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'
@@ -25,6 +25,7 @@ import {
 import ajv from 'ajv'
 import { Opening, StakingPolicy, ApplicationStageKeys } from '@joystream/types/hiring'
 import { Validator } from 'inquirer'
+import { JoyStructCustom } from '@joystream/types/common'
 
 // KeyringPair type extended with mandatory "meta.name"
 // It's used for accounts/keys management within CLI.
@@ -37,7 +38,7 @@ export type NamedKeyringPair = KeyringPair & {
 
 // Summary of the account information fetched from the api for "account:current" purposes (currently just balances)
 export type AccountSummary = {
-  balances: DerivedBalances
+  balances: DeriveBalancesAll
 }
 
 // This function allows us to easily transform the tuple into the object
@@ -186,187 +187,174 @@ export type GroupOpening = {
 // 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<T> {
-  toJSON: () => T
+  toJSONObj: () => T
 }
-export class HRTJobSpecificsStruct extends Struct implements WithJSONable<JobSpecifics> {
-  constructor(value?: JobSpecifics) {
-    super(
-      {
-        title: 'Text',
-        description: 'Text',
-      },
-      value
-    )
-  }
+export class HRTJobSpecificsStruct
+  extends JoyStructCustom({
+    title: Text,
+    description: Text,
+  })
+  implements WithJSONable<JobSpecifics> {
   get title(): string {
-    return (this.get('title') as Text).toString()
+    return this.getField('title').toString()
   }
+
   get description(): string {
-    return (this.get('description') as Text).toString()
+    return this.getField('description').toString()
   }
-  toJSON(): JobSpecifics {
+
+  toJSONObj(): JobSpecifics {
     const { title, description } = this
     return { title, description }
   }
 }
-export class HRTEntryInMembershipModukeStruct extends Struct implements WithJSONable<EntryInMembershipModuke> {
-  constructor(value?: EntryInMembershipModuke) {
-    super(
-      {
-        handle: 'Text',
-      },
-      value
-    )
-  }
+export class HRTEntryInMembershipModukeStruct
+  extends JoyStructCustom({
+    handle: Text,
+  })
+  implements WithJSONable<EntryInMembershipModuke> {
   get handle(): string {
-    return (this.get('handle') as Text).toString()
+    return this.getField('handle').toString()
   }
-  toJSON(): EntryInMembershipModuke {
+
+  toJSONObj(): EntryInMembershipModuke {
     const { handle } = this
     return { handle }
   }
 }
-export class HRTCreatorDetailsStruct extends Struct implements WithJSONable<CreatorDetails> {
-  constructor(value?: CreatorDetails) {
-    super(
-      {
-        membership: HRTEntryInMembershipModukeStruct,
-      },
-      value
-    )
-  }
+export class HRTCreatorDetailsStruct
+  extends JoyStructCustom({
+    membership: HRTEntryInMembershipModukeStruct,
+  })
+  implements WithJSONable<CreatorDetails> {
   get membership(): EntryInMembershipModuke {
-    return (this.get('membership') as HRTEntryInMembershipModukeStruct).toJSON()
+    return this.getField('membership').toJSONObj()
   }
-  toJSON(): CreatorDetails {
+
+  toJSONObj(): CreatorDetails {
     const { membership } = this
     return { membership }
   }
 }
-export class HRTHiringProcessStruct extends Struct implements WithJSONable<HiringProcess> {
-  constructor(value?: HiringProcess) {
-    super(
-      {
-        details: 'Vec<Text>',
-      },
-      value
-    )
-  }
+export class HRTHiringProcessStruct
+  extends JoyStructCustom({
+    details: Vec.with(Text),
+  })
+  implements WithJSONable<HiringProcess> {
   get details(): AdditionalRolehiringProcessDetails {
-    return (this.get('details') as Vec<Text>).toArray().map((v) => v.toString())
+    return this.getField('details')
+      .toArray()
+      .map((v) => v.toString())
   }
-  toJSON(): HiringProcess {
+
+  toJSONObj(): HiringProcess {
     const { details } = this
     return { details }
   }
 }
-export class HRTQuestionFieldStruct extends Struct implements WithJSONable<QuestionField> {
-  constructor(value?: QuestionField) {
-    super(
-      {
-        title: 'Text',
-        type: 'Text',
-      },
-      value
-    )
-  }
+export class HRTQuestionFieldStruct
+  extends JoyStructCustom({
+    title: Text,
+    type: Text,
+  })
+  implements WithJSONable<QuestionField> {
   get title(): string {
-    return (this.get('title') as Text).toString()
+    return this.getField('title').toString()
   }
+
   get type(): string {
-    return (this.get('type') as Text).toString()
+    return this.getField('type').toString()
   }
-  toJSON(): QuestionField {
+
+  toJSONObj(): QuestionField {
     const { title, type } = this
     return { title, type }
   }
 }
 class HRTQuestionsFieldsVec extends Vec.with(HRTQuestionFieldStruct) implements WithJSONable<QuestionsFields> {
-  toJSON(): QuestionsFields {
-    return this.toArray().map((v) => v.toJSON())
+  toJSONObj(): QuestionsFields {
+    return this.toArray().map((v) => v.toJSONObj())
   }
 }
-export class HRTQuestionSectionStruct extends Struct implements WithJSONable<QuestionSection> {
-  constructor(value?: QuestionSection) {
-    super(
-      {
-        title: 'Text',
-        questions: HRTQuestionsFieldsVec,
-      },
-      value
-    )
-  }
+export class HRTQuestionSectionStruct
+  extends JoyStructCustom({
+    title: Text,
+    questions: HRTQuestionsFieldsVec,
+  })
+  implements WithJSONable<QuestionSection> {
   get title(): string {
-    return (this.get('title') as Text).toString()
+    return this.getField('title').toString()
   }
+
   get questions(): QuestionsFields {
-    return (this.get('questions') as HRTQuestionsFieldsVec).toJSON()
+    return this.getField('questions').toJSONObj()
   }
-  toJSON(): QuestionSection {
+
+  toJSONObj(): QuestionSection {
     const { title, questions } = this
     return { title, questions }
   }
 }
 export class HRTQuestionSectionsVec extends Vec.with(HRTQuestionSectionStruct)
   implements WithJSONable<QuestionSections> {
-  toJSON(): QuestionSections {
-    return this.toArray().map((v) => v.toJSON())
+  toJSONObj(): QuestionSections {
+    return this.toArray().map((v) => v.toJSONObj())
   }
 }
-export class HRTApplicationDetailsStruct extends Struct implements WithJSONable<ApplicationDetails> {
-  constructor(value?: ApplicationDetails) {
-    super(
-      {
-        sections: HRTQuestionSectionsVec,
-      },
-      value
-    )
-  }
+export class HRTApplicationDetailsStruct
+  extends JoyStructCustom({
+    sections: HRTQuestionSectionsVec,
+  })
+  implements WithJSONable<ApplicationDetails> {
   get sections(): QuestionSections {
-    return (this.get('sections') as HRTQuestionSectionsVec).toJSON()
+    return this.getField('sections').toJSONObj()
   }
-  toJSON(): ApplicationDetails {
+
+  toJSONObj(): ApplicationDetails {
     const { sections } = this
     return { sections }
   }
 }
-export class HRTStruct extends Struct implements WithJSONable<GenericJoyStreamRoleSchema> {
-  constructor(value?: GenericJoyStreamRoleSchema) {
-    super(
-      {
-        version: 'u32',
-        headline: 'Text',
-        job: HRTJobSpecificsStruct,
-        application: HRTApplicationDetailsStruct,
-        reward: 'Text',
-        creator: HRTCreatorDetailsStruct,
-        process: HRTHiringProcessStruct,
-      },
-      value
-    )
-  }
+export class HRTStruct
+  extends JoyStructCustom({
+    version: u32,
+    headline: Text,
+    job: HRTJobSpecificsStruct,
+    application: HRTApplicationDetailsStruct,
+    reward: Text,
+    creator: HRTCreatorDetailsStruct,
+    process: HRTHiringProcessStruct,
+  })
+  implements WithJSONable<GenericJoyStreamRoleSchema> {
   get version(): number {
-    return (this.get('version') as u32).toNumber()
+    return this.getField('version').toNumber()
   }
+
   get headline(): string {
-    return (this.get('headline') as Text).toString()
+    return this.getField('headline').toString()
   }
+
   get job(): JobSpecifics {
-    return (this.get('job') as HRTJobSpecificsStruct).toJSON()
+    return this.getField('job').toJSONObj()
   }
+
   get application(): ApplicationDetails {
-    return (this.get('application') as HRTApplicationDetailsStruct).toJSON()
+    return this.getField('application').toJSONObj()
   }
+
   get reward(): string {
-    return (this.get('reward') as Text).toString()
+    return this.getField('reward').toString()
   }
+
   get creator(): CreatorDetails {
-    return (this.get('creator') as HRTCreatorDetailsStruct).toJSON()
+    return this.getField('creator').toJSONObj()
   }
+
   get process(): HiringProcess {
-    return (this.get('process') as HRTHiringProcessStruct).toJSON()
+    return this.getField('process').toJSONObj()
   }
-  toJSON(): GenericJoyStreamRoleSchema {
+
+  toJSONObj(): GenericJoyStreamRoleSchema {
     const { version, headline, job, application, reward, creator, process } = this
     return { version, headline, job, application, reward, creator, process }
   }

+ 5 - 3
cli/src/base/AccountsCommandBase.ts

@@ -8,7 +8,7 @@ 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 { DeriveBalancesAll } from '@polkadot/api-derive/types'
 import { toFixedLength } from '../helpers/display'
 
 const ACCOUNTS_DIRNAME = 'accounts'
@@ -54,7 +54,9 @@ export default abstract class AccountsCommandBase extends ApiCommandBase {
     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))
+    keyring
+      .getPairs()
+      .forEach((pair) => this.saveAccount({ ...pair, meta: { name: pair.meta.name as string } }, '', true))
   }
 
   fetchAccountFromJsonFile(jsonBackupFilePath: string): NamedKeyringPair {
@@ -186,7 +188,7 @@ export default abstract class AccountsCommandBase extends ApiCommandBase {
     message = 'Select an account',
     showBalances = true
   ): Promise<NamedKeyringPair> {
-    let balances: DerivedBalances[]
+    let balances: DeriveBalancesAll[]
     if (showBalances) {
       balances = await this.getApi().getAccountsBalancesInfo(accounts.map((acc) => acc.address))
     }

+ 42 - 27
cli/src/base/ApiCommandBase.ts

@@ -2,13 +2,14 @@ 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 { getTypeDef, Option, Tuple, Bytes } from '@polkadot/types'
+import { Registry, Codec, CodecArg, 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 { InterfaceTypes } from '@polkadot/types/types/registry'
 import ajv from 'ajv'
 import { ApiMethodArg, ApiMethodNamedArgs, ApiParamsOptions, ApiParamOptions } from '../Types'
 import { createParamOptions } from '../helpers/promptOptions'
@@ -32,6 +33,14 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     return this.getApi().getOriginalApi()
   }
 
+  getTypesRegistry(): Registry {
+    return this.getOriginalApi().registry
+  }
+
+  createType<K extends keyof InterfaceTypes>(typeName: K, value?: unknown): InterfaceTypes[K] {
+    return this.getOriginalApi().createType(typeName, value)
+  }
+
   async init() {
     await super.init()
     let apiUri: string = this.getPreservedState().apiUri
@@ -81,6 +90,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
 
   isApiUriValid(uri: string) {
     try {
+      // eslint-disable-next-line no-new
       new WsProvider(uri)
     } catch (e) {
       return false
@@ -90,8 +100,8 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
 
   // 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)
+  protected getRawTypeDef(type: keyof InterfaceTypes) {
+    const instance = this.createType(type)
     return getTypeDef(instance.toRawType())
   }
 
@@ -120,7 +130,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
   async promptForSimple(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise<Codec> {
     // If no default provided - get default value resulting from providing empty string
     const defaultValueString =
-      paramOptions?.value?.default?.toString() || createType(typeDef.type as any, '').toString()
+      paramOptions?.value?.default?.toString() || this.createType(typeDef.type as any, '').toString()
     const providedValue = await this.simplePrompt({
       message: `Provide value for ${this.paramName(typeDef)}`,
       type: 'input',
@@ -129,7 +139,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
       default: (defaultValueString === '0x' ? '' : defaultValueString) || undefined,
       validate: paramOptions?.validator,
     })
-    return createType(typeDef.type as any, providedValue)
+    return this.createType(typeDef.type as any, providedValue)
   }
 
   // Prompt for Option<Codec> value
@@ -149,10 +159,10 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
         createParamOptions(subtype.name, defaultValue?.unwrapOr(undefined))
       )
       this.closeIndentGroup()
-      return new Option(subtype.type as any, value)
+      return this.createType(`Option<${subtype.type}>` as any, value)
     }
 
-    return new Option(subtype.type as any, null)
+    return this.createType(`Option<${subtype.type}>` as any, null)
   }
 
   // Prompt for Tuple
@@ -173,7 +183,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     }
     this.closeIndentGroup()
 
-    return new Tuple(subtypes.map((subtype) => subtype.type) as any, result)
+    return new Tuple(this.getTypesRegistry(), subtypes.map((subtype) => subtype.type) as any, result)
   }
 
   // Prompt for Struct
@@ -182,7 +192,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
 
     this.openIndentGroup()
     const structType = typeDef.type
-    const rawTypeDef = this.getRawTypeDef(structType)
+    const rawTypeDef = this.getRawTypeDef(structType as keyof InterfaceTypes)
     // 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
@@ -200,7 +210,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     }
     this.closeIndentGroup()
 
-    return createType(structType as any, structValues)
+    return this.createType(structType as any, structValues)
   }
 
   // Prompt for Vec
@@ -228,12 +238,12 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     } while (addAnother)
     this.closeIndentGroup()
 
-    return new Vec(subtype.type as any, entries)
+    return this.createType(`Vec<${subtype.type}>` as any, entries)
   }
 
   // Prompt for Enum
   async promptForEnum(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise<Enum> {
-    const enumType = typeDef.type
+    const enumType = typeDef.type as keyof InterfaceTypes
     const rawTypeDef = this.getRawTypeDef(enumType)
     // We assume enum always has array on TypeDefs inside ".sub"
     const enumSubtypes = rawTypeDef.sub as TypeDef[]
@@ -253,12 +263,12 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
 
     if (enumSubtype.type !== 'Null') {
       const subtypeOptions = createParamOptions(enumSubtype.name, defaultValue?.value)
-      return createType(enumType as any, {
+      return this.createType(enumType as any, {
         [enumSubtype.name!]: await this.promptForParam(enumSubtype.type, subtypeOptions),
       })
     }
 
-    return createType(enumType as any, enumSubtype.name)
+    return this.createType(enumType as any, enumSubtype.name)
   }
 
   // Prompt for param based on "paramType" string (ie. Option<MemeberId>)
@@ -268,7 +278,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     paramOptions?: ApiParamOptions // TODO: This is not fully implemented for all types yet
   ): Promise<ApiMethodArg> {
     const typeDef = getTypeDef(paramType)
-    const rawTypeDef = this.getRawTypeDef(paramType)
+    const rawTypeDef = this.getRawTypeDef(paramType as keyof InterfaceTypes)
 
     if (paramOptions?.forcedName) {
       typeDef.name = paramOptions.forcedName
@@ -309,18 +319,23 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     defaultValue?: Bytes,
     schemaValidator?: ajv.ValidateFunction
   ) {
-    const rawType = new jsonStruct().toRawType()
+    const JsonStructObject = jsonStruct
+    const rawType = new JsonStructObject(this.getTypesRegistry()).toRawType()
     const typeDef = getTypeDef(rawType)
 
     const defaultStruct =
-      defaultValue && new jsonStruct(JSON.parse(Buffer.from(defaultValue.toHex().replace('0x', ''), 'hex').toString()))
+      defaultValue &&
+      new JsonStructObject(
+        this.getTypesRegistry(),
+        JSON.parse(Buffer.from(defaultValue.toHex().replace('0x', ''), 'hex').toString())
+      )
 
     if (argName) {
       typeDef.name = argName
     }
 
-    let isValid = true,
-      jsonText: string
+    let isValid = true
+    let jsonText: string
     do {
       const structVal = await this.promptForStruct(typeDef, createParamOptions(typeDef.name, defaultStruct))
       jsonText = JSON.stringify(structVal.toJSON())
@@ -338,7 +353,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
       }
     } while (!isValid)
 
-    return new Bytes('0x' + Buffer.from(jsonText, 'ascii').toString('hex'))
+    return this.createType('Bytes', '0x' + Buffer.from(jsonText, 'ascii').toString('hex'))
   }
 
   async promptForExtrinsicParams(
@@ -364,18 +379,18 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     return values
   }
 
-  sendExtrinsic(account: KeyringPair, module: string, method: string, params: Codec[]) {
+  sendExtrinsic(account: KeyringPair, module: string, method: string, params: CodecArg[]) {
     return new Promise((resolve, reject) => {
       const extrinsicMethod = this.getOriginalApi().tx[module][method]
       let unsubscribe: () => void
       extrinsicMethod(...params)
-        .signAndSend(account, {}, (result: SubmittableResultImpl) => {
+        .signAndSend(account, {}, (result) => {
           // Implementation loosely based on /pioneer/packages/react-signer/src/Modal.tsx
           if (!result || !result.status) {
             return
           }
 
-          if (result.status.isFinalized) {
+          if (result.status.isInBlock) {
             unsubscribe()
             result.events
               .filter(({ event: { section } }): boolean => section === 'system')
@@ -401,7 +416,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     account: KeyringPair,
     module: string,
     method: string,
-    params: Codec[],
+    params: CodecArg[],
     warnOnly = false // If specified - only warning will be displayed (instead of error beeing thrown)
   ) {
     try {
@@ -449,7 +464,7 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
       const argName = arg.name.toString()
       const argType = arg.type.toString()
       try {
-        parsedArgs.push({ name: argName, value: createType(argType as any, draftJSONObj[parseInt(index)]) })
+        parsedArgs.push({ name: argName, value: this.createType(argType as any, draftJSONObj[parseInt(index)]) })
       } catch (e) {
         throw new CLIError(`Couldn't parse ${argName} value from draft at ${draftFilePath}!`, {
           exit: ExitCodes.InvalidFile,

+ 4 - 4
cli/src/base/WorkingGroupsCommandBase.ts

@@ -110,13 +110,13 @@ export default abstract class WorkingGroupsCommandBase extends AccountsCommandBa
       })),
     })
 
-    return acceptedApplications
+    return acceptedApplications.sort() // Sort just in case, since runtime expects them to be sorted
   }
 
   async promptForNewOpeningDraftName() {
-    let draftName = '',
-      fileExists = false,
-      overrideConfirmed = false
+    let draftName = ''
+    let fileExists = false
+    let overrideConfirmed = false
 
     do {
       draftName = await this.simplePrompt({

+ 2 - 3
cli/src/commands/account/current.ts

@@ -1,6 +1,5 @@
 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'
@@ -15,7 +14,7 @@ export default class AccountCurrent extends AccountsCommandBase {
 
     displayHeader('Account information')
     const creationDate: string = currentAccount.meta.whenCreated
-      ? moment(currentAccount.meta.whenCreated).format('YYYY-MM-DD HH:mm:ss')
+      ? moment(currentAccount.meta.whenCreated as string | number).format('YYYY-MM-DD HH:mm:ss')
       : '?'
     const accountRows: NameValueObj[] = [
       { name: 'Account name:', value: currentAccount.meta.name },
@@ -25,7 +24,7 @@ export default class AccountCurrent extends AccountsCommandBase {
     displayNameValueTable(accountRows)
 
     displayHeader('Balances')
-    const balances: DerivedBalances = summary.balances
+    const balances = summary.balances
     const balancesRows: NameValueObj[] = [
       { name: 'Total balance:', value: formatBalance(balances.votingBalance) },
       { name: 'Transferable balance:', value: formatBalance(balances.availableBalance) },

+ 4 - 4
cli/src/commands/account/transferTokens.ts

@@ -6,7 +6,6 @@ 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
@@ -36,15 +35,16 @@ export default class AccountTransferTokens extends AccountsCommandBase {
 
     // Initial validation
     validateAddress(args.recipient, 'Invalid recipient address')
-    const accBalances: DerivedBalances = (await this.getApi().getAccountsBalancesInfo([selectedAccount.address]))[0]
+    const accBalances = (await this.getApi().getAccountsBalancesInfo([selectedAccount.address]))[0]
     checkBalance(accBalances, amountBN)
 
     await this.requestAccountDecoding(selectedAccount)
 
     this.log(chalk.white('Estimating fee...'))
+    const tx = await this.getApi().createTransferTx(args.recipient, amountBN)
     let estimatedFee: BN
     try {
-      estimatedFee = await this.getApi().estimateFee(selectedAccount, args.recipient, amountBN)
+      estimatedFee = await this.getApi().estimateFee(selectedAccount, tx)
     } catch (e) {
       this.error('Could not estimate the fee.', { exit: ExitCodes.UnexpectedException })
     }
@@ -57,7 +57,7 @@ export default class AccountTransferTokens extends AccountsCommandBase {
     await this.requireConfirmation('Do you confirm the transfer?')
 
     try {
-      const txHash: Hash = await this.getApi().transfer(selectedAccount, args.recipient, amountBN)
+      const txHash: Hash = await tx.signAndSend(selectedAccount)
       this.log(chalk.greenBright('Transaction succesfully sent!'))
       this.log(chalk.white('Hash:', txHash.toString()))
     } catch (e) {

+ 11 - 14
cli/src/commands/api/inspect.ts

@@ -2,9 +2,8 @@ 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 { ConstantCodec } from '@polkadot/metadata/Decorated/consts/types'
 import ExitCodes from '../../ExitCodes'
 import chalk from 'chalk'
 import { NameValueObj, ApiMethodArg } from '../../Types'
@@ -99,7 +98,7 @@ export default class ApiInspect extends ApiCommandBase {
       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 [type.asMap.key.toString()]
     }
     return []
   }
@@ -110,14 +109,17 @@ export default class ApiInspect extends ApiCommandBase {
       const {
         meta: { type, modifier },
       } = method.creator
+      let typeName = type.toString()
       if (type.isDoubleMap) {
-        return type.asDoubleMap.value.toString()
+        typeName = type.asDoubleMap.value.toString()
       }
-      if (modifier.isOptional) {
-        return `Option<${type.toString()}>`
+      if (type.isMap) {
+        typeName = type.asMap.value.toString()
       }
+
+      return modifier.isOptional ? `Option<${typeName}>` : typeName
     }
-    // Fallback for "query" and default for "consts"
+    // Fallback for "consts"
     return this.getMethodMeta(apiType, apiModule, apiMethod).type.toString()
   }
 
@@ -127,7 +129,7 @@ export default class ApiInspect extends ApiCommandBase {
     api: ApiPromise,
     flags: ApiInspectFlags
   ): { apiType: ApiType | undefined; apiModule: string | undefined; apiMethod: string | undefined } {
-    let apiType: ApiType | undefined = undefined
+    let apiType: ApiType | undefined
     const { module: apiModule, method: apiMethod } = flags
 
     if (flags.type !== undefined) {
@@ -155,12 +157,7 @@ export default class ApiInspect extends ApiCommandBase {
     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)
+      result.push(paramValue)
     }
 
     return result

+ 1 - 0
cli/src/commands/working-groups/application.ts

@@ -11,6 +11,7 @@ export default class WorkingGroupsApplication extends WorkingGroupsCommandBase {
       description: 'Working Group Application ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }

+ 2 - 2
cli/src/commands/working-groups/createOpening.ts

@@ -53,8 +53,8 @@ export default class WorkingGroupsCreateOpening extends WorkingGroupsCommandBase
       const module = apiModuleByGroup[this.group]
       const method = 'addOpening'
 
-      let saveDraft = false,
-        params: ApiMethodArg[]
+      let saveDraft = false
+      let params: ApiMethodArg[]
       if (flags.createDraftOnly) {
         params = await this.promptForExtrinsicParams(module, method, promptOptions)
         saveDraft = true

+ 3 - 5
cli/src/commands/working-groups/decreaseWorkerStake.ts

@@ -1,6 +1,5 @@
 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'
@@ -11,6 +10,7 @@ export default class WorkingGroupsDecreaseWorkerStake extends WorkingGroupsComma
   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',
@@ -18,6 +18,7 @@ export default class WorkingGroupsDecreaseWorkerStake extends WorkingGroupsComma
       description: 'Worker ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -41,10 +42,7 @@ export default class WorkingGroupsDecreaseWorkerStake extends WorkingGroupsComma
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'decreaseStake', [
-      new WorkerId(workerId),
-      balance,
-    ])
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'decreaseStake', [workerId, balance])
 
     this.log(
       chalk.green(

+ 3 - 4
cli/src/commands/working-groups/evictWorker.ts

@@ -1,7 +1,5 @@
 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'
@@ -15,6 +13,7 @@ export default class WorkingGroupsEvictWorker extends WorkingGroupsCommandBase {
       description: 'Worker ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -43,9 +42,9 @@ export default class WorkingGroupsEvictWorker extends WorkingGroupsCommandBase {
     await this.requestAccountDecoding(account)
 
     await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateRole', [
-      new WorkerId(workerId),
+      workerId,
       rationale,
-      new bool(shouldSlash),
+      shouldSlash,
     ])
 
     this.log(chalk.green(`Worker ${chalk.white(workerId)} has been evicted!`))

+ 4 - 8
cli/src/commands/working-groups/fillOpening.ts

@@ -1,8 +1,6 @@
 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'
 
@@ -15,6 +13,7 @@ export default class WorkingGroupsFillOpening extends WorkingGroupsCommandBase {
       description: 'Working Group Opening ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -30,16 +29,13 @@ export default class WorkingGroupsFillOpening extends WorkingGroupsCommandBase {
     const opening = await this.getOpeningForLeadAction(openingId, OpeningStatus.InReview)
 
     const applicationIds = await this.promptForApplicationsToAccept(opening)
-    const rewardPolicyOpt = await this.promptForParam(
-      `Option<${RewardPolicy.name}>`,
-      createParamOptions('RewardPolicy')
-    )
+    const rewardPolicyOpt = await this.promptForParam(`Option<RewardPolicy>`, createParamOptions('RewardPolicy'))
 
     await this.requestAccountDecoding(account)
 
     await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'fillOpening', [
-      new OpeningId(openingId),
-      new ApplicationIdSet(applicationIds),
+      openingId,
+      applicationIds,
       rewardPolicyOpt,
     ])
 

+ 1 - 0
cli/src/commands/working-groups/opening.ts

@@ -15,6 +15,7 @@ export default class WorkingGroupsOpening extends WorkingGroupsCommandBase {
       description: 'Working Group Opening ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }

+ 2 - 5
cli/src/commands/working-groups/slashWorker.ts

@@ -1,6 +1,5 @@
 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'
@@ -16,6 +15,7 @@ export default class WorkingGroupsSlashWorker extends WorkingGroupsCommandBase {
       description: 'Worker ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -39,10 +39,7 @@ export default class WorkingGroupsSlashWorker extends WorkingGroupsCommandBase {
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'slashStake', [
-      new WorkerId(workerId),
-      balance,
-    ])
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'slashStake', [workerId, balance])
 
     this.log(
       chalk.green(

+ 2 - 4
cli/src/commands/working-groups/startAcceptingApplications.ts

@@ -1,7 +1,6 @@
 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 {
@@ -13,6 +12,7 @@ export default class WorkingGroupsStartAcceptingApplications extends WorkingGrou
       description: 'Working Group Opening ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -29,9 +29,7 @@ export default class WorkingGroupsStartAcceptingApplications extends WorkingGrou
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'acceptApplications', [
-      new OpeningId(openingId),
-    ])
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'acceptApplications', [openingId])
 
     this.log(
       chalk.green(`Opening ${chalk.white(openingId)} status changed to: ${chalk.white('Accepting Applications')}`)

+ 2 - 4
cli/src/commands/working-groups/startReviewPeriod.ts

@@ -1,7 +1,6 @@
 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 {
@@ -13,6 +12,7 @@ export default class WorkingGroupsStartReviewPeriod extends WorkingGroupsCommand
       description: 'Working Group Opening ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -29,9 +29,7 @@ export default class WorkingGroupsStartReviewPeriod extends WorkingGroupsCommand
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'beginApplicantReview', [
-      new OpeningId(openingId),
-    ])
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'beginApplicantReview', [openingId])
 
     this.log(chalk.green(`Opening ${chalk.white(openingId)} status changed to: ${chalk.white('In Review')}`))
   }

+ 3 - 4
cli/src/commands/working-groups/terminateApplication.ts

@@ -1,6 +1,6 @@
 import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
 import { apiModuleByGroup } from '../../Api'
-import { ApplicationStageKeys, ApplicationId } from '@joystream/types/hiring'
+import { ApplicationStageKeys } from '@joystream/types/hiring'
 import chalk from 'chalk'
 
 export default class WorkingGroupsTerminateApplication extends WorkingGroupsCommandBase {
@@ -12,6 +12,7 @@ export default class WorkingGroupsTerminateApplication extends WorkingGroupsComm
       description: 'Working Group Application ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -29,9 +30,7 @@ export default class WorkingGroupsTerminateApplication extends WorkingGroupsComm
 
     await this.requestAccountDecoding(account)
 
-    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateApplication', [
-      new ApplicationId(applicationId),
-    ])
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateApplication', [applicationId])
 
     this.log(chalk.green(`Application ${chalk.white(applicationId)} has been succesfully terminated!`))
   }

+ 2 - 2
cli/src/commands/working-groups/updateRewardAccount.ts

@@ -1,7 +1,6 @@
 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'
 
@@ -14,6 +13,7 @@ export default class WorkingGroupsUpdateRewardAccount extends WorkingGroupsComma
       description: 'New reward account address (if omitted, one of the existing CLI accounts can be selected)',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -40,7 +40,7 @@ export default class WorkingGroupsUpdateRewardAccount extends WorkingGroupsComma
 
     await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRewardAccount', [
       worker.workerId,
-      new GenericAccountId(newRewardAccount),
+      newRewardAccount,
     ])
 
     this.log(chalk.green(`Succesfully updated the reward account to: ${chalk.white(newRewardAccount)})`))

+ 2 - 2
cli/src/commands/working-groups/updateRoleAccount.ts

@@ -1,7 +1,6 @@
 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 {
@@ -13,6 +12,7 @@ export default class WorkingGroupsUpdateRoleAccount extends WorkingGroupsCommand
       description: 'New role account address (if omitted, one of the existing CLI accounts can be selected)',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -34,7 +34,7 @@ export default class WorkingGroupsUpdateRoleAccount extends WorkingGroupsCommand
 
     await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRoleAccount', [
       worker.workerId,
-      new GenericAccountId(newRoleAccount),
+      newRoleAccount,
     ])
 
     this.log(chalk.green(`Succesfully updated the role account to: ${chalk.white(newRoleAccount)})`))

+ 2 - 2
cli/src/commands/working-groups/updateWorkerReward.ts

@@ -1,6 +1,5 @@
 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'
@@ -17,6 +16,7 @@ export default class WorkingGroupsUpdateWorkerReward extends WorkingGroupsComman
       description: 'Worker ID',
     },
   ]
+
   static flags = {
     ...WorkingGroupsCommandBase.flags,
   }
@@ -56,7 +56,7 @@ export default class WorkingGroupsUpdateWorkerReward extends WorkingGroupsComman
     await this.requestAccountDecoding(account)
 
     await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRewardAmount', [
-      new WorkerId(workerId),
+      workerId,
       newRewardValue,
     ])
 

+ 2 - 2
cli/src/helpers/validation.ts

@@ -1,7 +1,7 @@
 import BN from 'bn.js'
 import ExitCodes from '../ExitCodes'
 import { decodeAddress } from '@polkadot/util-crypto'
-import { DerivedBalances } from '@polkadot/api-derive/types'
+import { DeriveBalancesAll } from '@polkadot/api-derive/types'
 import { CLIError } from '@oclif/errors'
 
 export function validateAddress(address: string, errorMessage = 'Invalid address'): void {
@@ -12,7 +12,7 @@ export function validateAddress(address: string, errorMessage = 'Invalid address
   }
 }
 
-export function checkBalance(accBalances: DerivedBalances, requiredBalance: BN): void {
+export function checkBalance(accBalances: DeriveBalancesAll, requiredBalance: BN): void {
   if (requiredBalance.gt(accBalances.availableBalance)) {
     throw new CLIError('Not enough balance available', { exit: ExitCodes.InvalidInput })
   }

+ 11 - 9
cli/src/promptOptions/addWorkerOpening.ts

@@ -1,32 +1,32 @@
 import { ApiParamsOptions, ApiParamOptions, HRTStruct } from '../Types'
-import {
-  OpeningType,
-  SlashingTerms,
-  UnslashableTerms,
-  OpeningType_Worker as OpeningTypeWorker,
-  WorkingGroupOpeningPolicyCommitment,
-} from '@joystream/types/working-group'
+import { OpeningType, WorkingGroupOpeningPolicyCommitment } from '@joystream/types/working-group'
+import { SlashingTerms } from '@joystream/types/common'
 import { Bytes } from '@polkadot/types'
 import { schemaValidator } from '@joystream/types/hiring'
+import { createType } from '@joystream/types'
 
 class OpeningPolicyCommitmentOptions implements ApiParamsOptions {
   [paramName: string]: ApiParamOptions
   public role_slashing_terms: ApiParamOptions<SlashingTerms> = {
     value: {
-      default: SlashingTerms.create('Unslashable', new UnslashableTerms()),
+      default: createType('SlashingTerms', { Unslashable: null }),
       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',
   }
@@ -37,10 +37,11 @@ class AddWrokerOpeningOptions implements ApiParamsOptions {
   // Lock value for opening_type
   public opening_type: ApiParamOptions<OpeningType> = {
     value: {
-      default: OpeningType.create('Worker', new OpeningTypeWorker()),
+      default: createType('OpeningType', { Worker: null }),
       locked: true,
     },
   }
+
   // Json schema for human_readable_text
   public human_readable_text: ApiParamOptions<Bytes> = {
     jsonSchema: {
@@ -48,6 +49,7 @@ class AddWrokerOpeningOptions implements ApiParamsOptions {
       struct: HRTStruct,
     },
   }
+
   // Lock value for role_slashing_terms
   public commitment: ApiParamOptions<WorkingGroupOpeningPolicyCommitment> = {
     nestedOptions: new OpeningPolicyCommitmentOptions(),

+ 5 - 1
cli/tsconfig.json

@@ -9,7 +9,11 @@
     "target": "es2017",
     "esModuleInterop": true,
     "types" : [ "node" ],
-    "noUnusedLocals": true
+    "noUnusedLocals": true,
+    "baseUrl": ".",
+    "paths": {
+      "@polkadot/types/augment": ["../types/augment-codec/augment-types.ts"],
+    }
   },
   "include": [
     "src/**/*"

+ 1 - 1
node/Cargo.toml

@@ -79,7 +79,7 @@ tempfile = "3.1.0"
 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' }
+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' }

+ 1 - 1
node/src/chain_spec/content_config.rs

@@ -309,7 +309,7 @@ pub fn data_directory_config_from_json(data_file: &Path) -> DataDirectoryConfig
 /// curator lead or channels.
 pub fn empty_content_working_group_config() -> ContentWorkingGroupConfig {
     ContentWorkingGroupConfig {
-        mint_capacity: 100_000,
+        mint_capacity: 0,
         curator_opening_by_id: vec![],
         next_curator_opening_id: 0,
         curator_application_by_id: vec![],

+ 79 - 68
node/src/chain_spec/mod.rs

@@ -379,76 +379,87 @@ pub fn testnet_genesis(
     }
 }
 
-// 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;
 
-// #[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::<sr25519::Public>("Alice"),
-//             vec![get_authority_keys_from_seed("Alice").0],
-//             crate::proposals_config::development(),
-//         )
-//     }
+    fn local_testnet_genesis_instant_single() -> GenesisConfig {
+        testnet_genesis(
+            vec![get_authority_keys_from_seed("Alice")],
+            get_account_id_from_seed::<sr25519::Public>("Alice"),
+            vec![get_authority_keys_from_seed("Alice").0],
+            proposals_config::development(),
+            initial_members::none(),
+            forum_config::empty(get_account_id_from_seed::<sr25519::Public>("Alice")),
+            content_config::empty_versioned_store_config(),
+            content_config::empty_versioned_store_permissions_config(),
+            content_config::empty_data_directory_config(),
+            content_config::empty_content_working_group_config(),
+            vec![],
+        )
+    }
 
-//     /// 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(),
-//         )
-//     }
+    /// 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::<sr25519::Public>("Alice"),
-//             vec![
-//                 get_authority_keys_from_seed("Alice").0,
-//                 get_authority_keys_from_seed("Bob").0,
-//             ],
-//             crate::proposals_config::development(),
-//         )
-//     }
+    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::<sr25519::Public>("Alice"),
+            vec![
+                get_authority_keys_from_seed("Alice").0,
+                get_authority_keys_from_seed("Bob").0,
+            ],
+            proposals_config::development(),
+            initial_members::none(),
+            forum_config::empty(get_account_id_from_seed::<sr25519::Public>("Alice")),
+            content_config::empty_versioned_store_config(),
+            content_config::empty_versioned_store_permissions_config(),
+            content_config::empty_data_directory_config(),
+            content_config::empty_content_working_group_config(),
+            vec![],
+        )
+    }
 
-//     /// 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(),
-//         )
-//     }
+    /// 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),
-//         );
-//     }
-// }
+    #[test]
+    #[ignore]
+    fn test_connectivity() {
+        sc_service_test::connectivity(
+            integration_test_config_with_two_authorities(),
+            |config| new_full(config),
+            |config| new_light(config),
+        );
+    }
+}

+ 244 - 247
node/src/service.rs

@@ -415,250 +415,247 @@ pub fn new_light(config: Configuration) -> Result<impl AbstractService, ServiceE
     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 = <Signature as Verify>::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::<sc_consensus_babe::AuthorityPair>("//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<Block, _, _>,
-//                      babe_link: &sc_consensus_babe::BabeLink<Block>| {
-//                         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::<H256>::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(<DigestItem as CompatibleDigestItem>::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 = <DigestItem as CompatibleDigestItem>::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::<Block> { epoch_descriptor }) as Box<dyn Any>,
-//                 );
-//                 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<u8> = 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()],
-//         )
-//     }
-// }
+#[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 = <Signature as Verify>::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::<sc_consensus_babe::AuthorityPair>("//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<Block, _, _>,
+                     babe_link: &sc_consensus_babe::BabeLink<Block>| {
+                        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::<H256>::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(<DigestItem as CompatibleDigestItem>::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 = <DigestItem as CompatibleDigestItem>::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::<Block> { epoch_descriptor }) as Box<dyn Any>,
+                );
+                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<u8> = 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()],
+        )
+    }
+}

+ 25 - 40
runtime-modules/governance/src/council.rs

@@ -34,10 +34,8 @@ decl_storage! {
 
         pub TermEndsAt get(fn term_ends_at) config() : T::BlockNumber = T::BlockNumber::from(1);
 
-        /// The mint that funds council member rewards and spending proposals budget. It is an Option
-        /// because it was introduced in a runtime upgrade. It will be automatically created when
-        /// a successful call to set_council_mint_capacity() is made.
-        pub CouncilMint get(fn council_mint) : Option<<T as minting::Trait>::MintId>;
+        /// The mint that funds council member rewards and spending proposals budget
+        pub CouncilMint get(fn council_mint) : <T as minting::Trait>::MintId;
 
         /// The reward relationships currently in place. There may not necessarily be a 1-1 correspondance with
         /// the active council, since there are multiple ways of setting/adding/removing council members, some of which
@@ -54,6 +52,21 @@ decl_storage! {
         /// How many blocks after the reward is created, the first payout will be made
         pub FirstPayoutAfterRewardCreated get(fn first_payout_after_reward_created): T::BlockNumber;
     }
+    add_extra_genesis {
+        build(|_config: &GenesisConfig<T>| {
+            // Create the council mint.
+            let mint_id_result = <minting::Module<T>>::add_mint(
+                minting::BalanceOf::<T>::from(0),
+                None
+            );
+
+            if let Ok(mint_id) = mint_id_result {
+                <CouncilMint<T>>::put(mint_id);
+            } else {
+                panic!("Failed to create a mint for the council");
+            }
+        });
+    }
 }
 
 // Event for this module.
@@ -72,15 +85,8 @@ impl<T: Trait> CouncilElected<Seats<T::AccountId, BalanceOf<T>>, T::BlockNumber>
 
         <TermEndsAt<T>>::put(next_term_ends_at);
 
-        if let Some(reward_source) = Self::council_mint() {
-            for seat in seats.iter() {
-                Self::add_reward_relationship(&seat.member, reward_source);
-            }
-        } else {
-            // Skip trying to create rewards since no mint has been created yet
-            debug::warn!(
-                "Not creating reward relationship for council seats because no mint exists"
-            );
+        for seat in seats.iter() {
+            Self::add_reward_relationship(&seat.member, Self::council_mint());
         }
 
         Self::deposit_event(RawEvent::NewCouncilTermStarted(next_term_ends_at));
@@ -96,15 +102,6 @@ impl<T: Trait> Module<T> {
         Self::active_council().iter().any(|c| c.member == *sender)
     }
 
-    /// Initializes a new mint, discarding previous mint if it existed.
-    pub fn create_new_council_mint(
-        capacity: minting::BalanceOf<T>,
-    ) -> Result<T::MintId, &'static str> {
-        let mint_id = <minting::Module<T>>::add_mint(capacity, None)?;
-        CouncilMint::<T>::put(mint_id);
-        Ok(mint_id)
-    }
-
     fn add_reward_relationship(destination: &T::AccountId, reward_source: T::MintId) {
         let recipient = <recurringrewards::Module<T>>::add_recipient();
 
@@ -174,10 +171,8 @@ decl_module! {
             // Council is being replaced so remove existing reward relationships if they exist
             Self::remove_reward_relationships();
 
-            if let Some(reward_source) = Self::council_mint() {
-                for account in accounts.clone() {
-                    Self::add_reward_relationship(&account, reward_source);
-                }
+            for account in accounts.clone() {
+                Self::add_reward_relationship(&account, Self::council_mint());
             }
 
             let new_council: Seats<T::AccountId, BalanceOf<T>> = accounts.into_iter().map(|account| {
@@ -198,9 +193,7 @@ decl_module! {
 
             ensure!(!Self::is_councilor(&account), "cannot add same account multiple times");
 
-            if let Some(reward_source) = Self::council_mint() {
-                Self::add_reward_relationship(&account, reward_source);
-            }
+            Self::add_reward_relationship(&account, Self::council_mint());
 
             let seat = Seat {
                 member: account,
@@ -246,11 +239,7 @@ decl_module! {
         pub fn set_council_mint_capacity(origin, capacity: minting::BalanceOf<T>) {
             ensure_root(origin)?;
 
-            if let Some(mint_id) = Self::council_mint() {
-                minting::Module::<T>::set_mint_capacity(mint_id, capacity).map_err(<&str>::from)?;
-            } else {
-                Self::create_new_council_mint(capacity)?;
-            }
+            minting::Module::<T>::set_mint_capacity(Self::council_mint(), capacity).map_err(<&str>::from)?;
         }
 
         /// Attempts to mint and transfer amount to destination account
@@ -258,12 +247,8 @@ decl_module! {
         fn spend_from_council_mint(origin, amount: minting::BalanceOf<T>, destination: T::AccountId) {
             ensure_root(origin)?;
 
-            if let Some(mint_id) = Self::council_mint() {
-                minting::Module::<T>::transfer_tokens(mint_id, amount, &destination)
-                    .map_err(<&str>::from)?;
-            } else {
-                return Err("CouncilHasNoMint".into());
-            }
+            minting::Module::<T>::transfer_tokens(Self::council_mint(), amount, &destination)
+                .map_err(<&str>::from)?;
         }
 
         /// Sets the council rewards which is only applied on new council being elected.

+ 8 - 1
runtime-modules/governance/src/mock.rs

@@ -8,7 +8,7 @@ use sp_core::H256;
 use sp_runtime::{
     testing::Header,
     traits::{BlakeTwo256, IdentityLookup},
-    Perbill,
+    BuildStorage, Perbill,
 };
 pub use system;
 
@@ -141,6 +141,13 @@ pub fn initial_test_ext() -> sp_io::TestExternalities {
         .assimilate_storage(&mut t)
         .unwrap();
 
+    // build the council config to initialize the mint
+    let council_config = council::GenesisConfig::<Test>::default()
+        .build_storage()
+        .unwrap();
+
+    council_config.assimilate_storage(&mut t).unwrap();
+
     t.into()
 }
 

+ 1 - 1
runtime/Cargo.toml

@@ -4,7 +4,7 @@ edition = '2018'
 name = 'joystream-node-runtime'
 # Follow convention: https://github.com/Joystream/substrate-runtime-joystream/issues/1
 # {Authoring}.{Spec}.{Impl} of the RuntimeVersion
-version = '7.0.0'
+version = '7.1.0'
 
 [dependencies]
 # Third-party dependencies

+ 1 - 1
runtime/src/lib.rs

@@ -70,7 +70,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
     spec_name: create_runtime_str!("joystream-node"),
     impl_name: create_runtime_str!("joystream-node"),
     authoring_version: 7,
-    spec_version: 0,
+    spec_version: 1,
     impl_version: 0,
     apis: crate::runtime_api::EXPORTED_RUNTIME_API_VERSIONS,
     transaction_version: 1,

+ 9 - 1
runtime/src/tests/mod.rs

@@ -4,11 +4,19 @@
 
 mod proposals_integration;
 mod storage_integration;
+use sp_runtime::BuildStorage;
 
 pub(crate) fn initial_test_ext() -> sp_io::TestExternalities {
-    let t = system::GenesisConfig::default()
+    let mut t = system::GenesisConfig::default()
         .build_storage::<crate::Runtime>()
         .unwrap();
 
+    // build the council config to initialize the mint
+    let council_config = governance::council::GenesisConfig::<crate::Runtime>::default()
+        .build_storage()
+        .unwrap();
+
+    council_config.assimilate_storage(&mut t).unwrap();
+
     t.into()
 }