Kaynağa Gözat

Merge branch 'iznik' into feature/lowdb-persistence

Gleb Urvanov 4 yıl önce
ebeveyn
işleme
c3ad6937c1
100 değiştirilmiş dosya ile 1896 ekleme ve 3640 silme
  1. 8 10
      .github/workflows/joystream-cli.yml
  2. 3 3
      cli/README.md
  3. 1 1
      cli/package.json
  4. 7 6
      cli/src/Api.ts
  5. 3 3
      cli/src/Types.ts
  6. 3 3
      cli/src/commands/api/inspect.ts
  7. 9 8
      package.json
  8. 1 1
      pioneer/packages/apps-routing/src/joy-members.ts
  9. 3 3
      pioneer/packages/joy-members/src/Dashboard.tsx
  10. 12 14
      pioneer/packages/joy-members/src/Details.tsx
  11. 3 3
      pioneer/packages/joy-members/src/DetailsByHandle.tsx
  12. 30 30
      pioneer/packages/joy-members/src/EditForm.tsx
  13. 9 9
      pioneer/packages/joy-members/src/MemberPreview.tsx
  14. 6 6
      pioneer/packages/joy-members/src/index.tsx
  15. 7 9
      pioneer/packages/joy-proposals/src/Proposal/Body.tsx
  16. 4 4
      pioneer/packages/joy-proposals/src/forms/AddWorkingGroupOpeningForm.tsx
  17. 4 4
      pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupLeadForm.tsx
  18. 0 1
      pioneer/packages/joy-proposals/src/stories/data/ProposalDetails.mock.ts
  19. 4 4
      pioneer/packages/joy-roles/src/elements.tsx
  20. 3 4
      pioneer/packages/joy-roles/src/mocks.ts
  21. 9 5
      pioneer/packages/joy-roles/src/tabs/Admin.controller.tsx
  22. 1 9
      pioneer/packages/joy-roles/src/transport.mock.ts
  23. 17 39
      pioneer/packages/joy-roles/src/transport.substrate.ts
  24. 0 3
      pioneer/packages/joy-roles/src/transport.ts
  25. 2 2
      pioneer/packages/joy-utils/src/MemberProfilePreview.tsx
  26. 2 2
      pioneer/packages/joy-utils/src/MembersDropdown.tsx
  27. 58 122
      pioneer/packages/joy-utils/src/MyAccount.tsx
  28. 5 5
      pioneer/packages/joy-utils/src/accounts.ts
  29. 12 10
      pioneer/packages/joy-utils/src/transport/contentWorkingGroup.ts
  30. 6 6
      pioneer/packages/joy-utils/src/transport/council.ts
  31. 0 3
      pioneer/packages/joy-utils/src/transport/index.ts
  32. 14 8
      pioneer/packages/joy-utils/src/transport/members.ts
  33. 4 4
      pioneer/packages/joy-utils/src/transport/proposals.ts
  34. 0 19
      pioneer/packages/joy-utils/src/transport/storageProviders.ts
  35. 2 2
      pioneer/packages/joy-utils/src/transport/workingGroups.ts
  36. 0 1
      pioneer/packages/joy-utils/src/types/members.ts
  37. 2 2
      pioneer/packages/joy-utils/src/types/proposals.ts
  38. 3 3
      pioneer/packages/joy-utils/src/types/workingGroups.ts
  39. 2 1
      tests/network-tests/package.json
  40. 1 1
      tests/network-tests/src/iznik/tests/council/electingCouncilTest.ts
  41. 1 1
      tests/network-tests/src/iznik/tests/fixtures/councilElectionModule.ts
  42. 4 4
      tests/network-tests/src/iznik/tests/fixtures/proposalsModule.ts
  43. 3 3
      tests/network-tests/src/iznik/tests/fixtures/workingGroupModule.ts
  44. 1 1
      tests/network-tests/src/iznik/tests/membership/membershipCreationTest.ts
  45. 1 1
      tests/network-tests/src/iznik/tests/proposals/contentWorkingGroupMintCapacityProposalTest.ts
  46. 1 1
      tests/network-tests/src/iznik/tests/proposals/electionParametersProposalTest.ts
  47. 1 1
      tests/network-tests/src/iznik/tests/proposals/manageLeaderRoleTest.ts
  48. 1 1
      tests/network-tests/src/iznik/tests/proposals/setLeadProposalTest.ts
  49. 1 1
      tests/network-tests/src/iznik/tests/proposals/spendingProposalTest.ts
  50. 1 1
      tests/network-tests/src/iznik/tests/proposals/textProposalTest.ts
  51. 1 1
      tests/network-tests/src/iznik/tests/proposals/updateRuntime.ts
  52. 1 1
      tests/network-tests/src/iznik/tests/proposals/validatorCountProposalTest.ts
  53. 1 1
      tests/network-tests/src/iznik/tests/proposals/workingGroupMintCapacityProposalTest.ts
  54. 1 1
      tests/network-tests/src/iznik/tests/workingGroup/atLeastValueBugTest.ts
  55. 1 1
      tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsLeadTest.ts
  56. 1 1
      tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsWorkerTest.ts
  57. 1 1
      tests/network-tests/src/iznik/tests/workingGroup/workerApplicationHappyCaseTest.ts
  58. 1 1
      tests/network-tests/src/iznik/tests/workingGroup/workerApplicationRejectionCaseTest.ts
  59. 1 1
      tests/network-tests/src/iznik/tests/workingGroup/workerPayoutTest.ts
  60. 25 14
      tests/network-tests/src/iznik/utils/apiWrapper.ts
  61. 1 1
      tests/network-tests/src/iznik/utils/utils.ts
  62. 1 0
      types/.prettierignore
  63. 11 5
      types/package.json
  64. 22 14
      types/src/JoyEnum.ts
  65. 63 34
      types/src/JoyStruct.ts
  66. 19 79
      types/src/common.ts
  67. 204 452
      types/src/content-working-group/index.ts
  68. 72 166
      types/src/council/index.ts
  69. 26 0
      types/src/definitions/augment-types.ts
  70. 2 0
      types/src/definitions/definitions.ts
  71. 12 32
      types/src/discovery.ts
  72. 90 148
      types/src/forum.ts
  73. 132 316
      types/src/hiring/index.ts
  74. 35 41
      types/src/index.ts
  75. 47 114
      types/src/media.ts
  76. 43 205
      types/src/members.ts
  77. 32 63
      types/src/mint/index.ts
  78. 172 485
      types/src/proposals.ts
  79. 30 75
      types/src/recurring-rewards/index.ts
  80. 17 62
      types/src/roles.ts
  81. 41 0
      types/src/scripts/defsFromTypes.ts
  82. 108 0
      types/src/scripts/updateAugmentTypes.ts
  83. 47 100
      types/src/stake/index.ts
  84. 36 34
      types/src/versioned-store/EntityCodec.ts
  85. 27 76
      types/src/versioned-store/PropertyType.ts
  86. 0 22
      types/src/versioned-store/PropertyTypeName.ts
  87. 25 56
      types/src/versioned-store/PropertyValue.ts
  88. 51 105
      types/src/versioned-store/index.ts
  89. 12 45
      types/src/versioned-store/permissions/ClassPermissions.ts
  90. 7 20
      types/src/versioned-store/permissions/EntityPermissions.ts
  91. 7 20
      types/src/versioned-store/permissions/PropertyOfClass.ts
  92. 7 20
      types/src/versioned-store/permissions/batching/ParametrizedClassPropertyValue.ts
  93. 8 25
      types/src/versioned-store/permissions/batching/index.ts
  94. 19 26
      types/src/versioned-store/permissions/batching/operation-types.ts
  95. 23 57
      types/src/versioned-store/permissions/batching/operations.ts
  96. 13 22
      types/src/versioned-store/permissions/batching/parametrized-entity.ts
  97. 21 26
      types/src/versioned-store/permissions/batching/parametrized-property-value.ts
  98. 8 13
      types/src/versioned-store/permissions/index.ts
  99. 9 32
      types/src/versioned-store/permissions/reference-constraint.ts
  100. 87 238
      types/src/working-group/index.ts

+ 8 - 10
.github/workflows/joystream-cli.yml

@@ -18,12 +18,11 @@ jobs:
       run: |
         yarn install --frozen-lockfile
         yarn workspace @joystream/cli checks
-    - name: npm pack test
+    - name: yarn pack test
       run: |
-        cd cli
-        npm pack | tail -1 | xargs tar xzf
-        cd package && npm link
-        joystream-cli help
+        yarn workspace @joystream/cli pack --filename cli-pack-test.tgz
+        tar zxvf ./cli/cli-pack-test.tgz -C cli
+        cd ./cli/package && yarn link
 
   cli_build_osx:
     name: MacOS Checks
@@ -41,9 +40,8 @@ jobs:
       run: |
         yarn install --frozen-lockfile --network-timeout 120000
         yarn workspace @joystream/cli checks
-    - name: npm pack test
+    - name: yarn pack test
       run: |
-        cd cli
-        npm pack | tail -1 | xargs tar xzf
-        cd package && npm link
-        joystream-cli help
+        yarn workspace @joystream/cli pack --filename cli-pack-test.tgz
+        tar zxvf ./cli/cli-pack-test.tgz -C cli
+        cd ./cli/package && yarn link

+ 3 - 3
cli/README.md

@@ -244,9 +244,9 @@ EXAMPLES
   $ api:inspect
   $ api:inspect -t=query
   $ api:inspect -t=query -M=members
-  $ api:inspect -t=query -M=members -m=memberProfile
-  $ api:inspect -t=query -M=members -m=memberProfile -e
-  $ api:inspect -t=query -M=members -m=memberProfile -e -a=1
+  $ api:inspect -t=query -M=members -m=membershipById
+  $ api:inspect -t=query -M=members -m=membershipById -e
+  $ api:inspect -t=query -M=members -m=membershipById -e -a=1
 ```
 
 _See code: [src/commands/api/inspect.ts](https://github.com/Joystream/substrate-runtime-joystream/blob/master/cli/src/commands/api/inspect.ts)_

+ 1 - 1
cli/package.json

@@ -8,7 +8,7 @@
   },
   "bugs": "https://github.com/Joystream/joystream/issues",
   "dependencies": {
-    "@joystream/types": "^0.12.0",
+    "@joystream/types": "^0.13.0",
     "@oclif/command": "^1.5.19",
     "@oclif/config": "^1.14.0",
     "@oclif/plugin-autocomplete": "^0.2.0",

+ 7 - 6
cli/src/Api.ts

@@ -43,7 +43,7 @@ import {
   OpeningId,
   StakingPolicy,
 } from '@joystream/types/hiring'
-import { MemberId, Profile } from '@joystream/types/members'
+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'
@@ -193,10 +193,11 @@ export default class Api {
     return this._api.query[module]
   }
 
-  protected async memberProfileById(memberId: MemberId): Promise<Profile | null> {
-    const profile = (await this._api.query.members.memberProfile(memberId)) as Option<Profile>
+  protected async membershipById(memberId: MemberId): Promise<Membership | null> {
+    const profile = (await this._api.query.members.membershipById(memberId)) as Membership
 
-    return profile.unwrapOr(null)
+    // Can't just use profile.isEmpty because profile.suspended is Bool (which isEmpty method always returns false)
+    return profile.handle.isEmpty ? null : profile
   }
 
   async groupLead(group: WorkingGroups): Promise<GroupMember | null> {
@@ -238,7 +239,7 @@ export default class Api {
     const roleAccount = worker.role_account_id
     const memberId = worker.member_id
 
-    const profile = await this.memberProfileById(memberId)
+    const profile = await this.membershipById(memberId)
 
     if (!profile) {
       throw new Error(`Group member profile not found! (member id: ${memberId.toNumber()})`)
@@ -358,7 +359,7 @@ export default class Api {
       wgApplicationId,
       applicationId: appId.toNumber(),
       wgOpeningId: wgApplication.opening_id.toNumber(),
-      member: await this.memberProfileById(wgApplication.member_id),
+      member: await this.membershipById(wgApplication.member_id),
       roleAccout: wgApplication.role_account_id,
       stakes: {
         application: appStakingId.isSome ? (await this.stakeValue(appStakingId.unwrap())).toNumber() : 0,

+ 3 - 3
cli/src/Types.ts

@@ -8,7 +8,7 @@ import { BlockNumber, Balance, AccountId } from '@polkadot/types/interfaces'
 import { DerivedBalances } from '@polkadot/api-derive/types'
 import { KeyringPair } from '@polkadot/keyring/types'
 import { WorkerId, OpeningType } from '@joystream/types/working-group'
-import { Profile, MemberId } from '@joystream/types/members'
+import { Membership, MemberId } from '@joystream/types/members'
 import {
   GenericJoyStreamRoleSchema,
   JobSpecifics,
@@ -103,7 +103,7 @@ export type GroupMember = {
   workerId: WorkerId
   memberId: MemberId
   roleAccount: AccountId
-  profile: Profile
+  profile: Membership
   stake?: Balance
   reward?: Reward
 }
@@ -112,7 +112,7 @@ export type GroupApplication = {
   wgApplicationId: number
   applicationId: number
   wgOpeningId: number
-  member: Profile | null
+  member: Membership | null
   roleAccout: AccountId
   stakes: {
     application: number

+ 3 - 3
cli/src/commands/api/inspect.ts

@@ -35,9 +35,9 @@ export default class ApiInspect extends ApiCommandBase {
     '$ api:inspect',
     '$ api:inspect -t=query',
     '$ api:inspect -t=query -M=members',
-    '$ api:inspect -t=query -M=members -m=memberProfile',
-    '$ api:inspect -t=query -M=members -m=memberProfile -e',
-    '$ api:inspect -t=query -M=members -m=memberProfile -e -a=1',
+    '$ api:inspect -t=query -M=members -m=membershipById',
+    '$ api:inspect -t=query -M=members -m=membershipById -e',
+    '$ api:inspect -t=query -M=members -m=membershipById -e -a=1',
   ]
 
   static flags = {

+ 9 - 8
package.json

@@ -6,7 +6,7 @@
   "scripts": {
     "test": "yarn && yarn workspaces run test",
     "test-migration": "yarn && yarn workspaces run test-migration",
-    "postinstall": "yarn workspace @joystream/types build && yarn workspace storage-node run build",
+    "postinstall": "yarn workspace @joystream/types build",
     "cargo-checks": "devops/git-hooks/pre-commit && devops/git-hooks/pre-push",
     "cargo-build": "scripts/cargo-build.sh",
     "lint": "yarn workspaces run lint"
@@ -23,14 +23,15 @@
     "devops/prettier-config"
   ],
   "resolutions": {
-    "@polkadot/api": "^0.96.1",
-    "@polkadot/api-contract": "^0.96.1",
-    "@polkadot/keyring": "^1.7.0-beta.5",
-    "@polkadot/types": "^0.96.1",
-    "@polkadot/util": "^1.7.0-beta.5",
-    "@polkadot/util-crypto": "^1.7.0-beta.5",
+    "@polkadot/api": "^1.26.1",
+    "@polkadot/api-contract": "^1.26.1",
+    "@polkadot/keyring": "^3.0.1",
+    "@polkadot/types": "^1.26.1",
+    "@polkadot/util": "^3.0.1",
+    "@polkadot/util-crypto": "^3.0.1",
+    "@polkadot/wasm-crypto": "^1.2.1",
     "babel-core": "^7.0.0-bridge.0",
-    "typescript": "^3.7.2"
+    "typescript": "^3.9.7"
   },
   "devDependencies": {
     "husky": "^4.2.5",

+ 1 - 1
pioneer/packages/apps-routing/src/joy-members.ts

@@ -6,7 +6,7 @@ export default [
   {
     Component: Members,
     display: {
-      needsApi: ['query.members.membersCreated']
+      needsApi: ['query.members.nextMemberId']
     },
     i18n: {
       defaultValue: 'Membership'

+ 3 - 3
pioneer/packages/joy-members/src/Dashboard.tsx

@@ -16,7 +16,7 @@ import { FIRST_MEMBER_ID } from './constants';
 
 type Props = ApiProps & I18nProps & {
   newMembershipsAllowed?: Bool;
-  membersCreated?: BN;
+  nextMemberId?: BN;
   minHandleLength?: BN;
   maxHandleLength?: BN;
   maxAvatarUriLength?: BN;
@@ -36,7 +36,7 @@ class Dashboard extends React.PureComponent<Props> {
         {isAllowed && (isAllowed.eq(true) ? 'Yes' : 'No')}
       </Bubble>
       <Bubble label='Next member ID'>
-        {formatNumber(p.membersCreated)}
+        {formatNumber(p.nextMemberId)}
       </Bubble>
       <Bubble label='First member ID'>
         {formatNumber(FIRST_MEMBER_ID)}
@@ -75,7 +75,7 @@ class Dashboard extends React.PureComponent<Props> {
 export default translate(
   withCalls<Props>(
     queryMembershipToProp('newMembershipsAllowed'),
-    queryMembershipToProp('membersCreated'),
+    queryMembershipToProp('nextMemberId'),
     queryMembershipToProp('minHandleLength'),
     queryMembershipToProp('maxHandleLength'),
     queryMembershipToProp('maxAvatarUriLength'),

+ 12 - 14
pioneer/packages/joy-members/src/Details.tsx

@@ -12,7 +12,7 @@ import AddressMini from '@polkadot/react-components/AddressMiniJoy';
 import { formatNumber } from '@polkadot/util';
 
 import translate from './translate';
-import { MemberId, Profile, EntryMethod, Paid, Screening, Genesis, SubscriptionId } from '@joystream/types/members';
+import { MemberId, Membership, EntryMethod, Paid, Screening, Genesis, SubscriptionId } from '@joystream/types/members';
 import { queryMembershipToProp } from './utils';
 import { Seat } from '@joystream/types/council';
 import { nonEmptyStr, queryToProp } from '@polkadot/joy-utils/index';
@@ -21,17 +21,15 @@ import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/MyAccount';
 type Props = ApiProps & I18nProps & MyAccountProps & {
   preview?: boolean;
   memberId: MemberId;
-  // This cannot be named just "memberProfile", since it will conflict with "withAccount's" memberProfile
-  // (which holds  member profile associated with currently selected account)
-  detailsMemberProfile?: Option<any>; // TODO refactor to Option<Profile>
+  membership?: Membership;
   activeCouncil?: Seat[];
 };
 
 class Component extends React.PureComponent<Props> {
   render () {
-    const { detailsMemberProfile } = this.props;
-    return detailsMemberProfile
-      ? this.renderProfile(detailsMemberProfile.unwrap() as Profile)
+    const { membership } = this.props;
+    return membership && !membership.handle.isEmpty
+      ? this.renderProfile(membership)
       : (
         <div className={'item ProfileDetails'}>
           <Loader active inline/>
@@ -39,7 +37,7 @@ class Component extends React.PureComponent<Props> {
       );
   }
 
-  private renderProfile (memberProfile: Profile) {
+  private renderProfile (membership: Membership) {
     const {
       preview = false,
       myAddress,
@@ -51,7 +49,7 @@ class Component extends React.PureComponent<Props> {
       avatar_uri,
       root_account,
       controller_account
-    } = memberProfile;
+    } = membership;
 
     const hasAvatar = avatar_uri && nonEmptyStr(avatar_uri.toString());
     const isMyProfile = myAddress && (myAddress === root_account.toString() || myAddress === controller_account.toString());
@@ -83,12 +81,12 @@ class Component extends React.PureComponent<Props> {
           </div>
         </div>
       </div>
-      {!preview && this.renderDetails(memberProfile, isCouncilor)}
+      {!preview && this.renderDetails(membership, isCouncilor)}
       </>
     );
   }
 
-  private renderDetails (memberProfile: Profile, isCouncilor: boolean) {
+  private renderDetails (membership: Membership, isCouncilor: boolean) {
     const {
       about,
       registered_at_block,
@@ -99,7 +97,7 @@ class Component extends React.PureComponent<Props> {
       root_account,
       controller_account
 
-    } = memberProfile;
+    } = membership;
 
     const { memberId } = this.props;
 
@@ -173,8 +171,8 @@ export default translate(withMyAccount(
   withCalls<Props>(
     queryToProp('query.council.activeCouncil'),
     queryMembershipToProp(
-      'memberProfile',
-      { paramName: 'memberId', propName: 'detailsMemberProfile' }
+      'membershipById',
+      { paramName: 'memberId', propName: 'membership' }
     )
   )(Component)
 ));

+ 3 - 3
pioneer/packages/joy-members/src/DetailsByHandle.tsx

@@ -11,11 +11,11 @@ import { queryMembershipToProp } from './utils';
 
 type DetailsByHandleProps = {
   handle: string;
-  handles?: MemberId;
+  memberIdByHandle?: MemberId;
 };
 
 function DetailsByHandleInner (p: DetailsByHandleProps) {
-  const { handles: memberId } = p;
+  const { memberIdByHandle: memberId } = p;
   return memberId !== undefined // here we can't make distinction value existing and loading
     ? <div className='ui massive relaxed middle aligned list FullProfile'>
       <Details memberId={memberId} />
@@ -24,7 +24,7 @@ function DetailsByHandleInner (p: DetailsByHandleProps) {
 }
 
 const DetailsByHandle = withCalls<DetailsByHandleProps>(
-  queryMembershipToProp('handles', 'handle')
+  queryMembershipToProp('memberIdByHandle', 'handle')
 )(DetailsByHandleInner);
 
 type Props = I18nProps & {

+ 30 - 30
pioneer/packages/joy-members/src/EditForm.tsx

@@ -4,12 +4,12 @@ import { Link } from 'react-router-dom';
 import { Form, Field, withFormik, FormikProps } from 'formik';
 import * as Yup from 'yup';
 
-import { Option, Vec } from '@polkadot/types';
+import { Vec } from '@polkadot/types';
 import Section from '@polkadot/joy-utils/Section';
 import TxButton from '@polkadot/joy-utils/TxButton';
 import * as JoyForms from '@polkadot/joy-utils/forms';
 import { SubmittableResult } from '@polkadot/api';
-import { MemberId, UserInfo, Profile, PaidTermId, PaidMembershipTerms } from '@joystream/types/members';
+import { MemberId, Membership, PaidTermId, PaidMembershipTerms } from '@joystream/types/members';
 import { OptionText } from '@joystream/types/common';
 import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/MyAccount';
 import { queryMembershipToProp } from './utils';
@@ -43,7 +43,7 @@ type ValidationProps = {
 };
 
 type OuterProps = ValidationProps & {
-  profile?: Profile;
+  profile?: Membership;
   paidTerms: PaidMembershipTerms;
   paidTermId: PaidTermId;
   memberId?: MemberId;
@@ -108,18 +108,18 @@ const InnerForm = (props: FormProps) => {
   const buildTxParams = () => {
     if (!isValid) return [];
 
-    const userInfo = new UserInfo({
-      handle: fieldToTextOption('handle'),
-      avatar_uri: fieldToTextOption('avatar'),
-      about: fieldToTextOption('about')
-    });
+    const userInfo = [
+      fieldToTextOption('handle'),
+      fieldToTextOption('avatar'),
+      fieldToTextOption('about')
+    ];
 
     if (profile) {
       // update profile
-      return [memberId, userInfo];
+      return [memberId, ...userInfo];
     } else {
       // register as new member
-      return [paidTermId, userInfo];
+      return [paidTermId, ...userInfo];
     }
   };
 
@@ -171,7 +171,7 @@ const InnerForm = (props: FormProps) => {
             label={profile ? 'Update my profile' : 'Register'}
             isDisabled={!dirty || isSubmitting}
             params={buildTxParams()}
-            tx={profile ? 'members.updateProfile' : 'members.buyMembership'}
+            tx={profile ? 'members.updateMembership' : 'members.buyMembership'}
             onClick={onSubmit}
             txFailedCb={onTxFailed}
             txSuccessCb={onTxSuccess}
@@ -207,19 +207,19 @@ const EditForm = withFormik<OuterProps, FormValues>({
   }
 })(InnerForm);
 
-type WithMyProfileProps = {
+type WithMembershipDataProps = {
   memberId?: MemberId;
-  memberProfile?: Option<any>; // TODO refactor to Option<Profile>
+  membership?: Membership;
   paidTermsId: PaidTermId;
-  paidTerms?: Option<PaidMembershipTerms>;
+  paidTerms?: PaidMembershipTerms;
   minHandleLength?: BN;
   maxHandleLength?: BN;
   maxAvatarUriLength?: BN;
   maxAboutTextLength?: BN;
 };
 
-function WithMyProfileInner (p: WithMyProfileProps) {
-  const triedToFindProfile = !p.memberId || p.memberProfile;
+function WithMembershipDataInner (p: WithMembershipDataProps) {
+  const triedToFindProfile = !p.memberId || p.membership;
   if (
     triedToFindProfile &&
     p.paidTerms &&
@@ -228,9 +228,9 @@ function WithMyProfileInner (p: WithMyProfileProps) {
     p.maxAvatarUriLength &&
     p.maxAboutTextLength
   ) {
-    const profile = p.memberProfile ? p.memberProfile.unwrapOr(undefined) : undefined;
+    const membership = p.membership && !p.membership.handle.isEmpty ? p.membership : undefined;
 
-    if (!profile && p.paidTerms.isNone) {
+    if (!membership && p.paidTerms.isEmpty) {
       console.error('Could not find active paid membership terms');
     }
 
@@ -240,8 +240,8 @@ function WithMyProfileInner (p: WithMyProfileProps) {
         maxHandleLength={p.maxHandleLength.toNumber()}
         maxAvatarUriLength={p.maxAvatarUriLength.toNumber()}
         maxAboutTextLength={p.maxAboutTextLength.toNumber()}
-        profile={profile as Profile}
-        paidTerms={p.paidTerms.unwrap()}
+        profile={membership}
+        paidTerms={p.paidTerms}
         paidTermId={p.paidTermsId}
         memberId={p.memberId}
       />
@@ -249,22 +249,22 @@ function WithMyProfileInner (p: WithMyProfileProps) {
   } else return <em>Loading...</em>;
 }
 
-const WithMyProfile = withCalls<WithMyProfileProps>(
+const WithMembershipData = withCalls<WithMembershipDataProps>(
   queryMembershipToProp('minHandleLength'),
   queryMembershipToProp('maxHandleLength'),
   queryMembershipToProp('maxAvatarUriLength'),
   queryMembershipToProp('maxAboutTextLength'),
-  queryMembershipToProp('memberProfile', 'memberId'),
+  queryMembershipToProp('membershipById', { paramName: 'memberId', propName: 'membership' }),
   queryMembershipToProp('paidMembershipTermsById', { paramName: 'paidTermsId', propName: 'paidTerms' })
-)(WithMyProfileInner);
+)(WithMembershipDataInner);
 
-type WithMyMemberIdProps = MyAccountProps & {
+type WithMembershipDataWrapperProps = MyAccountProps & {
   memberIdsByRootAccountId?: Vec<MemberId>;
   memberIdsByControllerAccountId?: Vec<MemberId>;
   paidTermsIds?: Vec<PaidTermId>;
 };
 
-function WithMyMemberIdInner (p: WithMyMemberIdProps) {
+function WithMembershipDataWrapperInner (p: WithMembershipDataWrapperProps) {
   if (p.allAccounts && !Object.keys(p.allAccounts).length) {
     return (
       <Message warning className="JoyMainStatus">
@@ -284,7 +284,7 @@ function WithMyMemberIdInner (p: WithMyMemberIdProps) {
       p.memberIdsByRootAccountId.concat(p.memberIdsByControllerAccountId);
       const memberId = p.memberIdsByRootAccountId.length ? p.memberIdsByRootAccountId[0] : undefined;
 
-      return <WithMyProfile memberId={memberId} paidTermsId={p.paidTermsIds[0]} />;
+      return <WithMembershipData memberId={memberId} paidTermsId={p.paidTermsIds[0]} />;
     } else {
       console.error('Active paid membership terms is empty');
     }
@@ -293,12 +293,12 @@ function WithMyMemberIdInner (p: WithMyMemberIdProps) {
   return <em>Loading...</em>;
 }
 
-const WithMyMemberId = withMyAccount(
-  withCalls<WithMyMemberIdProps>(
+const WithMembershipDataWrapper = withMyAccount(
+  withCalls<WithMembershipDataWrapperProps>(
     queryMembershipToProp('memberIdsByRootAccountId', 'myAddress'),
     queryMembershipToProp('memberIdsByControllerAccountId', 'myAddress'),
     queryMembershipToProp('activePaidMembershipTerms', { propName: 'paidTermsIds' })
-  )(WithMyMemberIdInner)
+  )(WithMembershipDataWrapperInner)
 );
 
-export default WithMyMemberId;
+export default WithMembershipDataWrapper;

+ 9 - 9
pioneer/packages/joy-members/src/MemberPreview.tsx

@@ -4,12 +4,12 @@ import { Link } from 'react-router-dom';
 import { ApiProps } from '@polkadot/react-api/types';
 import { I18nProps } from '@polkadot/react-components/types';
 import { withCalls, withMulti } from '@polkadot/react-api/with';
-import { Option, Vec } from '@polkadot/types';
+import { Vec } from '@polkadot/types';
 import { AccountId } from '@polkadot/types/interfaces';
 import IdentityIcon from '@polkadot/react-components/IdentityIcon';
 
 import translate from './translate';
-import { MemberId, Profile } from '@joystream/types/members';
+import { MemberId, Membership } from '@joystream/types/members';
 import { queryMembershipToProp } from './utils';
 import { Seat } from '@joystream/types/council';
 import { nonEmptyStr, queryToProp } from '@polkadot/joy-utils/index';
@@ -22,7 +22,7 @@ const InlineAvatarSizePx = 24;
 type MemberPreviewProps = ApiProps & I18nProps & {
   accountId: AccountId;
   memberId?: MemberId;
-  memberProfile?: Option<any>; // TODO refactor to Option<Profile>
+  membership?: Membership;
   activeCouncil?: Seat[];
   prefixLabel?: string;
   inline?: boolean;
@@ -32,15 +32,15 @@ type MemberPreviewProps = ApiProps & I18nProps & {
 
 class InnerMemberPreview extends React.PureComponent<MemberPreviewProps> {
   render () {
-    const { memberProfile } = this.props;
-    return memberProfile
-      ? this.renderProfile(memberProfile.unwrap() as Profile)
+    const { membership } = this.props;
+    return membership && !membership.handle.isEmpty
+      ? this.renderProfile(membership)
       : null;
   }
 
-  private renderProfile (memberProfile: Profile) {
+  private renderProfile (membership: Membership) {
     const { activeCouncil = [], accountId, prefixLabel, inline, className, style } = this.props;
-    const { handle, avatar_uri } = memberProfile;
+    const { handle, avatar_uri } = membership;
 
     const hasAvatar = avatar_uri && nonEmptyStr(avatar_uri.toString());
     const isCouncilor: boolean = accountId !== undefined && activeCouncil.find(x => accountId.eq(x.member)) !== undefined;
@@ -113,6 +113,6 @@ export const MemberPreview = withMulti(
   setMemberIdByAccountId,
   withCalls<MemberPreviewProps>(
     queryToProp('query.council.activeCouncil'), // TODO Refactor: extract ActiveCouncilContext
-    queryMembershipToProp('memberProfile', 'memberId')
+    queryMembershipToProp('membershipById', { paramName: 'memberId', propName: 'membership' })
   )
 );

+ 6 - 6
pioneer/packages/joy-members/src/index.tsx

@@ -22,12 +22,12 @@ import { RouteComponentProps } from 'react-router-dom';
 
 // define out internal types
 type Props = AppProps & ApiProps & I18nProps & MyAccountProps & {
-  membersCreated?: BN;
+  nextMemberId?: BN;
 };
 
 class App extends React.PureComponent<Props> {
   private buildTabs (): TabItem[] {
-    const { t, membersCreated: memberCount, iAmMember } = this.props;
+    const { t, nextMemberId: memberCount, iAmMember } = this.props;
 
     return [
       {
@@ -47,9 +47,9 @@ class App extends React.PureComponent<Props> {
   }
 
   private renderList (routeProps: RouteComponentProps) {
-    const { membersCreated, ...otherProps } = this.props;
-    return membersCreated
-      ? <List firstMemberId={FIRST_MEMBER_ID} membersCreated={membersCreated} {...otherProps} {...routeProps}/>
+    const { nextMemberId, ...otherProps } = this.props;
+    return nextMemberId
+      ? <List firstMemberId={FIRST_MEMBER_ID} membersCreated={nextMemberId} {...otherProps} {...routeProps}/>
       : <em>Loading...</em>;
   }
 
@@ -79,6 +79,6 @@ export default withMulti(
   translate,
   withMyAccount,
   withCalls<Props>(
-    queryMembershipToProp('membersCreated')
+    queryMembershipToProp('nextMemberId')
   )
 );

+ 7 - 9
pioneer/packages/joy-proposals/src/Proposal/Body.tsx

@@ -7,7 +7,7 @@ import styled from 'styled-components';
 import AddressMini from '@polkadot/react-components/AddressMiniJoy';
 import TxButton from '@polkadot/joy-utils/TxButton';
 import { ProposalId, TerminateRoleParameters } from '@joystream/types/proposals';
-import { MemberId, Profile } from '@joystream/types/members';
+import { MemberId, Membership } from '@joystream/types/members';
 import ProfilePreview from '@polkadot/joy-utils/MemberProfilePreview';
 import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks';
 import { Option, Bytes } from '@polkadot/types/';
@@ -55,20 +55,18 @@ function ProposedMember (props: { memberId?: MemberId | number | null }) {
   const memberId: MemberId | number = props.memberId;
 
   const transport = useTransport();
-  const [member, error, loading] = usePromise<Option<Profile> | null>(
-    () => transport.members.memberProfile(memberId),
+  const [member, error, loading] = usePromise<Membership | null>(
+    () => transport.members.membershipById(memberId),
     null
   );
 
-  const profile = member && member.unwrapOr(null);
-
   return (
     <PromiseComponent error={error} loading={loading} message="Fetching profile...">
-      { profile ? (
+      { (member && !member.handle.isEmpty) ? (
         <ProfilePreview
-          avatar_uri={ profile.avatar_uri.toString() }
-          root_account={ profile.root_account.toString() }
-          handle={ profile.handle.toString() }
+          avatar_uri={ member.avatar_uri.toString() }
+          root_account={ member.root_account.toString() }
+          handle={ member.handle.toString() }
           link={ true }
         />
       ) : 'Profile not found' }

+ 4 - 4
pioneer/packages/joy-proposals/src/forms/AddWorkingGroupOpeningForm.tsx

@@ -188,15 +188,15 @@ const valuesToAddOpeningParams = (values: FormValues): SimplifiedTypeInterface<I
 };
 
 const AddWorkingGroupOpeningForm: React.FunctionComponent<FormInnerProps> = props => {
-  const { handleChange, errors, touched, values, setFieldValue, myMemberId, memberProfile } = props;
+  const { handleChange, errors, touched, values, setFieldValue, myMemberId, myMembership } = props;
   useEffect(() => {
-    if (memberProfile?.isSome && !touched.humanReadableText) {
+    if (myMembership && !touched.humanReadableText) {
       setFieldValue(
         'humanReadableText',
-        JSON.stringify(HRTDefault(memberProfile.unwrap().handle.toString(), values.workingGroup), undefined, 4)
+        JSON.stringify(HRTDefault(myMembership.handle.toString(), values.workingGroup), undefined, 4)
       );
     }
-  }, [values.workingGroup, memberProfile]);
+  }, [values.workingGroup, myMembership]);
   const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
 
   return (

+ 4 - 4
pioneer/packages/joy-proposals/src/forms/SetContentWorkingGroupLeadForm.tsx

@@ -16,7 +16,7 @@ import Validation from '../validationSchema';
 import { FormField } from './FormFields';
 import { withFormContainer } from './FormContainer';
 import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks';
-import { Profile } from '@joystream/types/members';
+import { Membership } from '@joystream/types/members';
 import { PromiseComponent } from '@polkadot/joy-utils/react/components';
 import _ from 'lodash';
 import './forms.css';
@@ -35,7 +35,7 @@ type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormVal
 type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
 type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
 
-function memberOptionKey (id: number, profile: Profile) {
+function memberOptionKey (id: number, profile: Membership) {
   return `${id}:${profile.root_account.toString()}`;
 }
 
@@ -46,7 +46,7 @@ const MEMBERS_NONE_OPTION: DropdownItemProps = {
   value: 'none'
 };
 
-function membersToOptions (members: { id: number; profile: Profile }[]) {
+function membersToOptions (members: { id: number; profile: Membership }[]) {
   return [MEMBERS_NONE_OPTION].concat(
     members
       .map(({ id, profile }) => ({
@@ -66,7 +66,7 @@ function filterMembers (options: DropdownItemProps[], query: string) {
   return options.filter((opt) => regexp.test((opt.text || '').toString()));
 }
 
-type MemberWithId = { id: number; profile: Profile };
+type MemberWithId = { id: number; profile: Membership };
 
 const SetContentWorkingGroupsLeadForm: React.FunctionComponent<FormInnerProps> = props => {
   const { handleChange, errors, touched, values } = props;

+ 0 - 1
pioneer/packages/joy-proposals/src/stories/data/ProposalDetails.mock.ts

@@ -31,7 +31,6 @@ const mockedProposal: ParsedProposal = {
     handle: 'bob55',
     registered_at_block: 18,
     registered_at_time: 1588087314000,
-    roles: [],
     entry: {
       Paid: 0
     },

+ 4 - 4
pioneer/packages/joy-roles/src/elements.tsx

@@ -6,7 +6,7 @@ import { Link } from 'react-router-dom';
 import { Balance } from '@polkadot/types/interfaces';
 import { formatBalance } from '@polkadot/util';
 import Identicon from '@polkadot/react-identicon';
-import { IProfile, MemberId } from '@joystream/types/members';
+import { IMembership, MemberId } from '@joystream/types/members';
 import { GenericAccountId } from '@polkadot/types';
 import { LeadRoleState } from '@joystream/types/content-working-group';
 import { WorkerId } from '@joystream/types/working-group';
@@ -28,7 +28,7 @@ export function BalanceView (props: BalanceProps) {
 }
 
 type ProfileProps = {
-  profile: IProfile;
+  profile: IMembership;
 }
 
 export function HandleView (props: ProfileProps) {
@@ -46,7 +46,7 @@ export type GroupMember = {
   group: WorkingGroups;
   workerId: number;
   roleAccount: GenericAccountId;
-  profile: IProfile;
+  profile: IMembership;
   title: string;
   stake?: Balance;
   rewardRelationship?: RewardRelationship;
@@ -56,7 +56,7 @@ export type GroupLead = {
   memberId: MemberId;
   workerId?: WorkerId; // In case of "working-group" module
   roleAccount: GenericAccountId;
-  profile: IProfile;
+  profile: IMembership;
   title: string;
   stage?: LeadRoleState;
   stake?: Balance;

+ 3 - 4
pioneer/packages/joy-roles/src/mocks.ts

@@ -1,7 +1,7 @@
 import { bool, Option, Text, u32, u64, Vec } from '@polkadot/types';
 import AccountId from '@polkadot/types/primitive/Generic/AccountId';
 
-import { ActorInRole, IProfile, EntryMethod } from '@joystream/types/members';
+import { IMembership, EntryMethod } from '@joystream/types/members';
 
 import {
   AcceptingApplications,
@@ -11,7 +11,7 @@ import {
   ApplicationId
 } from '@joystream/types/hiring';
 
-export function mockProfile (name: string, avatar_uri = ''): IProfile {
+export function mockProfile (name: string, avatar_uri = ''): IMembership {
   return {
     handle: new Text(name),
     avatar_uri: new Text(avatar_uri),
@@ -22,8 +22,7 @@ export function mockProfile (name: string, avatar_uri = ''): IProfile {
     suspended: new bool(false),
     subscription: new Option(u64),
     root_account: new AccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'),
-    controller_account: new AccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'),
-    roles: new Vec<ActorInRole>(ActorInRole)
+    controller_account: new AccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp')
   };
 }
 

+ 9 - 5
pioneer/packages/joy-roles/src/tabs/Admin.controller.tsx

@@ -45,7 +45,7 @@ import {
 } from '@joystream/types/hiring';
 
 import {
-  Profile,
+  Membership,
   MemberId
 } from '@joystream/types/members';
 
@@ -77,7 +77,7 @@ type ids = {
 type application = ids & {
   account: string;
   memberId: number;
-  profile: Profile;
+  profile: Membership;
   stage: ApplicationStage;
   applicationStake: Balance;
   roleStake: Balance;
@@ -430,8 +430,12 @@ export class AdminController extends Controller<State, ITransport> {
     this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
-  protected async profile (id: MemberId): Promise<Option<Profile>> {
-    return (await this.api.query.members.memberProfile(id)) as Option<Profile>;
+  protected async profile (id: MemberId): Promise<Membership> {
+    const member = (await this.api.query.members.membershipById(id)) as Membership;
+    if (member.handle.isEmpty) {
+      throw new Error(`Expected member profile not found! (id: ${id.toString()}`);
+    }
+    return member;
   }
 
   protected async stakeValue (stakeId: StakeId): Promise<Balance> {
@@ -517,7 +521,7 @@ export class AdminController extends Controller<State, ITransport> {
         stage: baseApplications.value.stage,
         account: cApplication.value.role_account_id.toString(),
         memberId: cApplication.value.member_id.toNumber(),
-        profile: (await this.profile(cApplication.value.member_id)).unwrap(),
+        profile: (await this.profile(cApplication.value.member_id)),
         applicationStake: await this.applicationStake(baseApplications.value),
         roleStake: await this.roleStake(baseApplications.value),
         application: baseApplications.value

+ 1 - 9
pioneer/packages/joy-roles/src/transport.mock.ts

@@ -6,7 +6,7 @@ import { Subscribable, Transport as TransportBase } from '@polkadot/joy-utils/in
 
 import { ITransport } from './transport';
 
-import { Role, MemberId } from '@joystream/types/members';
+import { MemberId } from '@joystream/types/members';
 import {
   Opening,
   ApplicationRationingPolicy,
@@ -36,14 +36,6 @@ export class Transport extends TransportBase implements ITransport {
     return Math.random() * (max - min) + min;
   }
 
-  roles (): Promise<Array<Role>> {
-    return this.promise<Array<Role>>(
-      [
-        new Role('StorageProvider')
-      ]
-    );
-  }
-
   groupLeadStatus (group: WorkingGroups = WorkingGroups.ContentCurators): Promise<GroupLeadStatus> {
     return this.simulateApiResponse<GroupLeadStatus>({
       loaded: true

+ 17 - 39
pioneer/packages/joy-roles/src/transport.substrate.ts

@@ -32,7 +32,7 @@ import {
 import { Application, Opening, OpeningId, ApplicationId, ActiveApplicationStage } from '@joystream/types/hiring';
 import { Stake, StakeId } from '@joystream/types/stake';
 import { RewardRelationship, RewardRelationshipId } from '@joystream/types/recurring-rewards';
-import { ActorInRole, Profile, MemberId, Role, RoleKeys, ActorId } from '@joystream/types/members';
+import { Membership, MemberId, ActorId } from '@joystream/types/members';
 import { createAccount, generateSeed } from '@polkadot/joy-utils/accounts';
 
 import { WorkingGroupMembership, GroupLeadStatus } from './tabs/WorkingGroup';
@@ -176,11 +176,6 @@ export class Transport extends TransportBase implements ITransport {
     this.cachedApi.unsubscribe();
   }
 
-  async roles (): Promise<Array<Role>> {
-    const roles: any = await this.cachedApi.query.actors.availableRoles();
-    return this.promise<Array<Role>>(roles.map((role: Role) => role));
-  }
-
   protected async stakeValue (stakeId: StakeId): Promise<Balance> {
     const stake = new SingleLinkedMapEntry<Stake>(
       Stake,
@@ -209,31 +204,14 @@ export class Transport extends TransportBase implements ITransport {
     return relationship?.total_reward_received || new u128(0);
   }
 
-  protected async memberIdFromRoleAndActorId (role: Role, id: ActorId): Promise<MemberId> {
-    const memberId = (
-      await this.cachedApi.query.members.membershipIdByActorInRole(
-        new ActorInRole({
-          role: role,
-          actor_id: id
-        })
-      )
-    ) as MemberId;
-
-    return memberId;
-  }
-
-  protected memberIdFromCuratorId (curatorId: CuratorId): Promise<MemberId> {
-    return this.memberIdFromRoleAndActorId(
-      new Role(RoleKeys.Curator),
-      curatorId
-    );
-  }
+  protected async curatorMemberId (curator: Curator): Promise<MemberId> {
+    const curatorApplicationId = curator.induction.curator_application_id;
+    const curatorApplication = new SingleLinkedMapEntry<CuratorApplication>(
+      CuratorApplication,
+      await this.cachedApi.query.contentWorkingGroup.curatorApplicationById(curatorApplicationId)
+    ).value;
 
-  protected memberIdFromLeadId (leadId: LeadId): Promise<MemberId> {
-    return this.memberIdFromRoleAndActorId(
-      new Role(RoleKeys.CuratorLead),
-      leadId
-    );
+    return curatorApplication.member_id;
   }
 
   protected async workerRewardRelationship (worker: GroupWorker): Promise<RewardRelationship | undefined> {
@@ -250,12 +228,12 @@ export class Transport extends TransportBase implements ITransport {
   ): Promise<GroupMember> {
     const roleAccount = worker.role_account_id;
     const memberId = group === WorkingGroups.ContentCurators
-      ? await this.memberIdFromCuratorId(id)
+      ? await this.curatorMemberId(worker as Curator)
       : (worker as Worker).member_id;
 
-    const profile = await this.cachedApi.query.members.memberProfile(memberId) as Option<Profile>;
-    if (profile.isNone) {
-      throw new Error('no profile found');
+    const profile = await this.cachedApi.query.members.membershipById(memberId) as Membership;
+    if (profile.handle.isEmpty) {
+      throw new Error('No group member profile found!');
     }
 
     let stakeValue: Balance = new u128(0);
@@ -270,7 +248,7 @@ export class Transport extends TransportBase implements ITransport {
       group,
       memberId,
       workerId: id.toNumber(),
-      profile: profile.unwrap(),
+      profile,
       title: workerRoleNameByGroup[group],
       stake: stakeValue,
       rewardRelationship
@@ -321,7 +299,7 @@ export class Transport extends TransportBase implements ITransport {
       await this.cachedApi.query.contentWorkingGroup.leadById(leadId)
     );
 
-    const memberId = await this.memberIdFromLeadId(leadId);
+    const memberId = lead.value.member_id;
 
     return {
       lead: lead.value,
@@ -360,9 +338,9 @@ export class Transport extends TransportBase implements ITransport {
       : await this.currentStorageLead();
 
     if (currentLead !== null) {
-      const profile = await this.cachedApi.query.members.memberProfile(currentLead.memberId) as Option<Profile>;
+      const profile = await this.cachedApi.query.members.membershipById(currentLead.memberId) as Membership;
 
-      if (profile.isNone) {
+      if (profile.handle.isEmpty) {
         throw new Error(`${group} lead profile not found!`);
       }
 
@@ -379,7 +357,7 @@ export class Transport extends TransportBase implements ITransport {
           memberId: currentLead.memberId,
           workerId: currentLead.workerId,
           roleAccount: currentLead.lead.role_account_id,
-          profile: profile.unwrap(),
+          profile,
           title: _.startCase(group) + ' Lead',
           stage: group === WorkingGroups.ContentCurators ? (currentLead.lead as Lead).stage : undefined,
           stake,

+ 0 - 3
pioneer/packages/joy-roles/src/transport.ts

@@ -1,8 +1,6 @@
 import { Subscribable } from '@polkadot/joy-utils/index';
 import { Balance } from '@polkadot/types/interfaces';
 
-import { Role } from '@joystream/types/members';
-
 import { WorkingGroupMembership, GroupLeadStatus } from './tabs/WorkingGroup';
 import { WorkingGroupOpening } from './tabs/Opportunities';
 import { keyPairDetails } from './flows/apply';
@@ -10,7 +8,6 @@ import { ActiveRole, OpeningApplication } from './tabs/MyRoles';
 import { WorkingGroups } from './working_groups';
 
 export interface ITransport {
-  roles: () => Promise<Array<Role>>;
   groupLeadStatus: (group: WorkingGroups) => Promise<GroupLeadStatus>;
   curationGroup: () => Promise<WorkingGroupMembership>;
   storageGroup: () => Promise<WorkingGroupMembership>;

+ 2 - 2
pioneer/packages/joy-utils/src/MemberProfilePreview.tsx

@@ -4,7 +4,7 @@ import { IdentityIcon } from '@polkadot/react-components';
 import { Link } from 'react-router-dom';
 import { Text } from '@polkadot/types';
 import { AccountId } from '@polkadot/types/interfaces';
-import { MemberId, Profile } from '@joystream/types/members';
+import { MemberId, Membership } from '@joystream/types/members';
 import styled from 'styled-components';
 
 type ProfileItemProps = {
@@ -68,7 +68,7 @@ export default function ProfilePreview (
 }
 
 type ProfilePreviewFromStructProps = {
-  profile: Profile;
+  profile: Membership;
   link?: boolean;
   id?: number | MemberId;
 };

+ 2 - 2
pioneer/packages/joy-utils/src/MembersDropdown.tsx

@@ -1,6 +1,6 @@
 import React, { useEffect, useState, useContext } from 'react';
 import { Dropdown, DropdownItemProps, DropdownProps } from 'semantic-ui-react';
-import { Profile } from '@joystream/types/members';
+import { Membership } from '@joystream/types/members';
 import { memberFromAccount, MemberFromAccount } from './accounts';
 import { AccountId } from '@polkadot/types/interfaces';
 import { ApiContext } from '@polkadot/react-api';
@@ -14,7 +14,7 @@ const StyledMembersDropdown = styled(Dropdown)`
 `;
 
 function membersToOptions (members: MemberFromAccount[]) {
-  const validMembers = members.filter(m => m.profile !== undefined) as (MemberFromAccount & { profile: Profile })[];
+  const validMembers = members.filter(m => m.profile !== undefined) as (MemberFromAccount & { profile: Membership })[];
   return validMembers
     .map(({ id, profile, account }) => ({
       key: profile.handle,

+ 58 - 122
pioneer/packages/joy-utils/src/MyAccount.tsx

@@ -8,7 +8,7 @@ import accountObservable from '@polkadot/ui-keyring/observable/accounts';
 import { withCalls, withMulti, withObservable } from '@polkadot/react-api/index';
 import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
 
-import { MemberId, Profile } from '@joystream/types/members';
+import { MemberId, Membership } from '@joystream/types/members';
 import { CuratorId, LeadId, Lead, CurationActor, Curator } from '@joystream/types/content-working-group';
 
 import { queryMembershipToProp } from '@polkadot/joy-members/utils';
@@ -29,7 +29,7 @@ export type MyAccountProps = MyAddressProps & {
   memberIdsByControllerAccountId?: Vec<MemberId>;
   myMemberIdChecked?: boolean;
   iAmMember?: boolean;
-  memberProfile?: Option<Profile>;
+  myMembership?: Membership | null;
 
   // Content Working Group
   curatorEntries?: any; // entire linked_map: CuratorId => Curator
@@ -37,12 +37,6 @@ export type MyAccountProps = MyAddressProps & {
   contentLeadId?: LeadId;
   contentLeadEntry?: any; // linked_map value
 
-  // From member's roles
-  myContentLeadId?: LeadId;
-  myCuratorIds?: CuratorId[];
-  memberIsCurator?: boolean;
-  memberIsContentLead?: boolean;
-
   curationActor?: any;
   allAccounts?: SubjectInfo;
 };
@@ -92,7 +86,23 @@ function withMyMembership<P extends MyAccountProps> (Component: React.ComponentT
   return ResultComponent;
 }
 
-const withMyProfile = withCalls<MyAccountProps>(queryMembershipToProp('memberProfile', 'myMemberId'));
+function resolveMyProfile<P extends { myMembership?: Membership | null }> (Component: React.ComponentType<P>) {
+  const ResultComponent: React.FunctionComponent<P> = (props: P) => {
+    let { myMembership } = props;
+    myMembership = (!myMembership || myMembership.handle.isEmpty) ? null : myMembership;
+    return <Component {...props} myMembership={ myMembership } />;
+  };
+  ResultComponent.displayName = `resolveMyProfile(${componentName(Component)})`;
+  return ResultComponent;
+}
+
+const withMyProfileCall = withCalls<MyAccountProps>(queryMembershipToProp('membershipById', {
+  paramName: 'myMemberId',
+  propName: 'myMembership'
+}));
+
+const withMyProfile = <P extends MyAccountProps>(Component: React.ComponentType<P>) =>
+  withMulti(Component, withMyProfileCall, resolveMyProfile);
 
 const withContentWorkingGroupDetails = withCalls<MyAccountProps>(
   queryToProp('query.contentWorkingGroup.currentLeadId', { propName: 'isLeadSet' }),
@@ -126,146 +136,73 @@ const resolveLeadEntry = withCalls<MyAccountProps>(
 const withContentWorkingGroup = <P extends MyAccountProps>(Component: React.ComponentType<P>) =>
   withMulti(Component, withContentWorkingGroupDetails, resolveLead, resolveLeadEntry);
 
-function withMyRoles<P extends MyAccountProps> (Component: React.ComponentType<P>) {
-  const ResultComponent: React.FunctionComponent<P> = (props: P) => {
-    const { iAmMember, memberProfile } = props;
-
-    let myContentLeadId;
-    const myCuratorIds: Array<CuratorId> = [];
-
-    if (iAmMember && memberProfile && memberProfile.isSome) {
-      const profile = memberProfile.unwrap();
-      profile.roles.forEach(role => {
-        if (role.isContentLead) {
-          myContentLeadId = role.actor_id;
-        } else if (role.isCurator) {
-          myCuratorIds.push(role.actor_id);
-        }
-      });
-    }
-
-    const memberIsContentLead = myContentLeadId !== undefined;
-    const memberIsCurator = myCuratorIds.length > 0;
-
-    const newProps = {
-      memberIsContentLead,
-      memberIsCurator,
-      myContentLeadId,
-      myCuratorIds
-    };
-
-    return <Component {...props} {...newProps} />;
-  };
-  ResultComponent.displayName = `withMyRoles(${componentName(Component)})`;
-  return ResultComponent;
-}
-
-const canUseAccount = (account: AccountId, allAccounts: SubjectInfo | undefined) => {
-  if (!allAccounts || !Object.keys(allAccounts).length) {
-    return false;
-  }
-
-  const ix = Object.keys(allAccounts).findIndex(key => {
-    return account.eq(allAccounts[key].json.address);
-  });
-
-  return ix !== -1;
-};
-
 function withCurationActor<P extends MyAccountProps> (Component: React.ComponentType<P>) {
   const ResultComponent: React.FunctionComponent<P> = (props: P) => {
     const {
       myAccountId,
       isLeadSet,
       contentLeadEntry,
-      myCuratorIds,
       curatorEntries,
-      allAccounts,
-      memberIsContentLead,
-      memberIsCurator
+      allAccounts
     } = props;
 
     if (!myAccountId || !isLeadSet || !contentLeadEntry || !curatorEntries || !allAccounts) {
       return <Component {...props} />;
     }
 
-    const leadRoleAccount = isLeadSet.isSome
-      ? new SingleLinkedMapEntry<Lead>(Lead, contentLeadEntry).value.role_account
+    let lead = isLeadSet.isSome
+      ? new SingleLinkedMapEntry<Lead>(Lead, contentLeadEntry).value
       : null;
 
-    // Is current key the content lead key?
-    if (leadRoleAccount && leadRoleAccount.eq(myAccountId)) {
-      return <Component {...props} curationActor={[new CurationActor('Lead'), myAccountId]} />;
+    // Ignore lead if he's not active
+    // TODO: Does if ever happen if we query currentLeadById?
+    if (!(lead?.stage.isOfType('Active'))) {
+      lead = null;
     }
 
-    const curators = new MultipleLinkedMapEntry<CuratorId, Curator>(CuratorId, Curator, curatorEntries);
+    const curators = new MultipleLinkedMapEntry(CuratorId, Curator, curatorEntries);
+
+    const curationActorByAccount = (accountId: AccountId | string) => {
+      if (lead && lead.role_account.toString() === accountId.toString()) {
+        return new CurationActor({ Lead: null });
+      }
 
-    const correspondingCurationActor = (accountId: AccountId, curators: MultipleLinkedMapEntry<CuratorId, Curator>) => {
-      const ix = curators.linked_values.findIndex(curator => myAccountId.eq(curator.role_account) && curator.is_active);
+      const matchingCuratorIndex = curators.linked_values.findIndex(curator =>
+        curator.is_active && accountId.toString() === curator.role_account.toString()
+      );
 
-      return ix >= 0
+      return matchingCuratorIndex >= 0
         ? new CurationActor({
-          Curator: curators.linked_keys[ix]
+          Curator: curators.linked_keys[matchingCuratorIndex]
         })
         : null;
     };
 
-    const firstMatchingCurationActor = correspondingCurationActor(myAccountId, curators);
-
-    // Is the current key corresponding to an active curator role key?
-    if (firstMatchingCurationActor) {
-      return <Component {...props} curationActor={[firstMatchingCurationActor, myAccountId]} />;
-    }
-
-    // See if we have the member's lead role account
-    if (leadRoleAccount && memberIsContentLead && canUseAccount(leadRoleAccount, allAccounts)) {
-      return <Component {...props} curationActor={[new CurationActor('Lead'), leadRoleAccount]} />;
-    }
-
-    // See if we have one of the member's curator role accounts
-    if (memberIsCurator && myCuratorIds && curators.linked_keys.length) {
-      for (let i = 0; i < myCuratorIds.length; i++) {
-        const curator_id = myCuratorIds[i];
-        const ix = curators.linked_keys.findIndex(id => id.eq(curator_id));
-
-        if (ix >= 0) {
-          const curator = curators.linked_values[ix];
-          if (curator.is_active && canUseAccount(curator.role_account, allAccounts)) {
-            return (
-              <Component
-                {...props}
-                curationActor={[new CurationActor({ Curator: curator_id }), curator.role_account]}
-              />
-            );
-          }
-        }
+    // First priority - currently selected account
+    let actor = curationActorByAccount(myAccountId);
+    let actorKey: AccountId | null = myAccountId;
+    // Second priority - check other keys and find best role
+    // TODO: Prioritize current member?
+    // TODO: Perhaps just don't do that at all and force the user to select the correct key to avoid confision?
+    if (!actor) {
+      const allActorsWithKeys = Object.keys(allAccounts).map(accKey => ({
+        actor: curationActorByAccount(allAccounts[accKey].json.address),
+        key: new GenericAccountId(allAccounts[accKey].json.address)
+      }));
+      let actorWithKey = allActorsWithKeys.find(({ actor }) => actor?.isOfType('Lead'));
+      if (!actorWithKey) {
+        actorWithKey = allActorsWithKeys.find(({ actor }) => actor?.isOfType('Curator'));
       }
+      actor = actorWithKey?.actor || null;
+      actorKey = actorWithKey?.key || null;
     }
 
-    // selected key doesn't have any special role, check other available keys..
-
-    // Use lead role key if available
-    if (leadRoleAccount && canUseAccount(leadRoleAccount, allAccounts)) {
-      return <Component {...props} curationActor={[new CurationActor('Lead'), leadRoleAccount]} />;
-    }
-
-    // Use first available active curator role key if available
-    if (curators.linked_keys.length) {
-      for (let i = 0; i < curators.linked_keys.length; i++) {
-        const curator = curators.linked_values[i];
-        if (curator.is_active && canUseAccount(curator.role_account, allAccounts)) {
-          return (
-            <Component
-              {...props}
-              curationActor={[new CurationActor({ Curator: curators.linked_keys[i] }), curator.role_account]}
-            />
-          );
-        }
-      }
+    if (actor && actorKey) {
+      return <Component {...props} curationActor={[actor, actorKey]} />;
+    } else {
+      // we don't have any key that can fulfill a curation action
+      return <Component {...props} />;
     }
-
-    // we don't have any key that can fulfill a curation action
-    return <Component {...props} />;
   };
   ResultComponent.displayName = `withCurationActor(${componentName(Component)})`;
   return ResultComponent;
@@ -280,7 +217,6 @@ export const withMyAccount = <P extends MyAccountProps>(Component: React.Compone
     withMyMembership,
     withMyProfile,
     withContentWorkingGroup,
-    withMyRoles,
     withCurationActor
   );
 

+ 5 - 5
pioneer/packages/joy-utils/src/accounts.ts

@@ -10,8 +10,7 @@ import { isHex, u8aToHex } from '@polkadot/util';
 import { keyExtractSuri, mnemonicGenerate, mnemonicValidate, randomAsU8a } from '@polkadot/util-crypto';
 
 import { ApiPromise } from '@polkadot/api';
-import { MemberId, Profile } from '@joystream/types/members';
-import { Option } from '@polkadot/types';
+import { MemberId, Membership } from '@joystream/types/members';
 import { AccountId } from '@polkadot/types/interfaces';
 import { Vec } from '@polkadot/types/codec';
 
@@ -142,17 +141,18 @@ export function isPasswordValid (password: string): boolean {
   return password.length === 0 || keyring.isPassValid(password);
 }
 
-export type MemberFromAccount = { account: string; id: number; profile?: Profile };
+export type MemberFromAccount = { account: string; id: number; profile?: Membership };
 
+// TODO: Use transport instead (now that it's available in joy-utils)
 export async function memberFromAccount (api: ApiPromise, accountId: AccountId | string): Promise<MemberFromAccount> {
   const [memberId] =
     ((await api.query.members.memberIdsByRootAccountId(accountId)) as Vec<MemberId>)
       .concat((await api.query.members.memberIdsByControllerAccountId(accountId)) as Vec<MemberId>);
-  const member = (await api.query.members.memberProfile(memberId)) as Option<Profile>;
+  const member = (await api.query.members.membershipById(memberId)) as Membership;
 
   return {
     account: accountId.toString(),
     id: memberId.toNumber(),
-    profile: member.unwrapOr(undefined)
+    profile: member.handle.isEmpty ? undefined : member
   };
 }

+ 12 - 10
pioneer/packages/joy-utils/src/transport/contentWorkingGroup.ts

@@ -1,11 +1,12 @@
-import { MemberId, Profile, ActorInRole, RoleKeys, Role } from '@joystream/types/members';
+import { Membership } from '@joystream/types/members';
 import { u128, Vec, Option } from '@polkadot/types/';
 import BaseTransport from './base';
 import { MintId, Mint } from '@joystream/types/mint';
-import { LeadId } from '@joystream/types/content-working-group';
+import { LeadId, Lead } from '@joystream/types/content-working-group';
 import { ApiPromise } from '@polkadot/api';
 import MembersTransport from './members';
 import { APIQueryCache } from '../APIQueryCache';
+import { SingleLinkedMapEntry } from '..';
 
 export default class ContentWorkingGroupTransport extends BaseTransport {
   private membersT: MembersTransport;
@@ -21,19 +22,20 @@ export default class ContentWorkingGroupTransport extends BaseTransport {
     return (WGMint[0].get('capacity') as u128).toNumber();
   }
 
-  async currentLead (): Promise<{ id: number; profile: Profile } | null> {
+  async currentLead (): Promise<{ id: number; profile: Membership } | null> {
     const optLeadId = (await this.contentWorkingGroup.currentLeadId()) as Option<LeadId>;
     const leadId = optLeadId.unwrapOr(null);
 
     if (!leadId) return null;
 
-    const actorInRole = new ActorInRole({
-      role: new Role(RoleKeys.CuratorLead),
-      actor_id: leadId
-    });
-    const memberId = (await this.members.membershipIdByActorInRole(actorInRole)) as MemberId;
-    const profile = (await this.membersT.memberProfile(memberId)).unwrapOr(null);
+    const lead = new SingleLinkedMapEntry(Lead, await this.contentWorkingGroup.leadById(leadId)).value;
 
-    return profile && { id: memberId.toNumber(), profile };
+    if (!lead.stage.isOfType('Active')) {
+      return null;
+    }
+
+    const profile = await this.membersT.expectedMembership(lead.member_id);
+
+    return profile && { id: lead.member_id.toNumber(), profile };
   }
 }

+ 6 - 6
pioneer/packages/joy-utils/src/transport/council.ts

@@ -1,7 +1,7 @@
 import { ParsedMember } from '../types/members';
 import BaseTransport from './base';
 import { Seats, ElectionParameters } from '@joystream/types/council';
-import { MemberId, Profile } from '@joystream/types/members';
+import { MemberId, Membership } from '@joystream/types/members';
 import { u32, Vec } from '@polkadot/types/';
 import { Balance, BlockNumber } from '@polkadot/types/interfaces';
 import { FIRST_MEMBER_ID } from '../consts/members';
@@ -34,7 +34,7 @@ export default class CouncilTransport extends BaseTransport {
     return Promise.all(
       council.map(async seat => {
         const memberIds = (await this.members.memberIdsByControllerAccountId(seat.member)) as Vec<MemberId>;
-        const member = (await this.membersT.memberProfile(memberIds[0])).toJSON() as ParsedMember;
+        const member = (await this.membersT.expectedMembership(memberIds[0])).toJSON() as ParsedMember;
         return {
           ...member,
           memberId: memberIds[0]
@@ -43,13 +43,13 @@ export default class CouncilTransport extends BaseTransport {
     );
   }
 
-  async membersExceptCouncil (): Promise<{ id: number; profile: Profile }[]> {
+  async membersExceptCouncil (): Promise<{ id: number; profile: Membership }[]> {
     // Council members to filter out
     const activeCouncil = (await this.council.activeCouncil()) as Seats;
-    const membersCount = ((await this.members.membersCreated()) as MemberId).toNumber();
-    const profiles: { id: number; profile: Profile }[] = [];
+    const membersCount = ((await this.members.nextMemberId()) as MemberId).toNumber();
+    const profiles: { id: number; profile: Membership }[] = [];
     for (let id = FIRST_MEMBER_ID.toNumber(); id < membersCount; ++id) {
-      const profile = (await this.membersT.memberProfile(new MemberId(id))).unwrapOr(null);
+      const profile = (await this.membersT.membershipById(new MemberId(id)));
       if (
         !profile ||
         // Filter out council members

+ 0 - 3
pioneer/packages/joy-utils/src/transport/index.ts

@@ -4,7 +4,6 @@ import ContentWorkingGroupTransport from './contentWorkingGroup';
 import ProposalsTransport from './proposals';
 import MembersTransport from './members';
 import CouncilTransport from './council';
-import StorageProvidersTransport from './storageProviders';
 import ValidatorsTransport from './validators';
 import WorkingGroupsTransport from './workingGroups';
 import { APIQueryCache } from '../APIQueryCache';
@@ -18,7 +17,6 @@ export default class Transport {
   public council: CouncilTransport;
   public proposals: ProposalsTransport;
   public contentWorkingGroup: ContentWorkingGroupTransport;
-  public storageProviders: StorageProvidersTransport;
   public validators: ValidatorsTransport;
   public workingGroups: WorkingGroupsTransport;
 
@@ -27,7 +25,6 @@ export default class Transport {
     this.cacheApi = new APIQueryCache(api);
     this.chain = new ChainTransport(api, this.cacheApi);
     this.members = new MembersTransport(api, this.cacheApi);
-    this.storageProviders = new StorageProvidersTransport(api, this.cacheApi);
     this.validators = new ValidatorsTransport(api, this.cacheApi);
     this.council = new CouncilTransport(api, this.cacheApi, this.members, this.chain);
     this.contentWorkingGroup = new ContentWorkingGroupTransport(api, this.cacheApi, this.members);

+ 14 - 8
pioneer/packages/joy-utils/src/transport/members.ts

@@ -1,18 +1,24 @@
 import BaseTransport from './base';
-import { MemberId, Profile } from '@joystream/types/members';
-import { Option } from '@polkadot/types/';
+import { MemberId, Membership } from '@joystream/types/members';
 
 export default class MembersTransport extends BaseTransport {
-  memberProfile (id: MemberId | number): Promise<Option<Profile>> {
-    return this.members.memberProfile(id) as Promise<Option<Profile>>;
+  async membershipById (id: MemberId | number): Promise<Membership | null> {
+    const member = (await this.members.membershipById(id)) as Membership;
+    // Can't just use member.isEmpty because member.suspended is Bool (which isEmpty method always returns false)
+    return member.handle.isEmpty ? null : member;
   }
 
   // Throws if profile not found
-  async expectedMemberProfile (id: MemberId | number): Promise<Profile> {
-    return (await this.memberProfile(id)).unwrap();
+  async expectedMembership (id: MemberId | number): Promise<Membership> {
+    const member = await this.membershipById(id);
+    if (!member) {
+      throw new Error(`Expected member profile not found! ID: ${id.toString()}`);
+    }
+
+    return member;
   }
 
-  async membersCreated (): Promise<number> {
-    return (await this.members.membersCreated() as MemberId).toNumber();
+  async nextMemberId (): Promise<number> {
+    return (await this.members.nextMemberId() as MemberId).toNumber();
   }
 }

+ 4 - 4
pioneer/packages/joy-utils/src/transport/proposals.ts

@@ -108,7 +108,7 @@ export default class ProposalsTransport extends BaseTransport {
     if (!rawProposal) {
       rawProposal = await this.rawProposalById(id);
     }
-    const proposer = (await this.membersT.memberProfile(rawProposal.proposerId)).toJSON() as ParsedMember;
+    const proposer = (await this.membersT.expectedMembership(rawProposal.proposerId)).toJSON() as ParsedMember;
     const proposal = rawProposal.toJSON() as {
       title: string;
       description: string;
@@ -193,7 +193,7 @@ export default class ProposalsTransport extends BaseTransport {
       'proposalsEngine.voteExistsByProposalByVoter', // Double map of intrest
       proposalId, // First double-map key value
       (v) => new VoteKind(v), // Converter from hex
-      async () => (await this.membersT.membersCreated()), // A function that returns the number of iterations to go through when chekcing possible values for the second double-map key (memberId)
+      async () => (await this.membersT.nextMemberId()), // A function that returns the number of iterations to go through when chekcing possible values for the second double-map key (memberId)
       FIRST_MEMBER_ID.toNumber() // Min. possible value for second double-map key (memberId)
     );
 
@@ -201,7 +201,7 @@ export default class ProposalsTransport extends BaseTransport {
     for (const voteEntry of voteEntries) {
       const memberId = voteEntry.secondKey;
       const vote = voteEntry.value;
-      const parsedMember = (await this.membersT.memberProfile(memberId)).toJSON() as ParsedMember;
+      const parsedMember = (await this.membersT.expectedMembership(memberId)).toJSON() as ParsedMember;
       votesWithMembers.push({
         vote,
         member: {
@@ -270,7 +270,7 @@ export default class ProposalsTransport extends BaseTransport {
         updatedAt: await this.chainT.blockTimestamp(post.updated_at.toNumber()),
         updatedAtBlock: post.updated_at.toNumber(),
         authorId: post.author_id,
-        author: (await this.membersT.memberProfile(post.author_id)).unwrapOr(null),
+        author: (await this.membersT.expectedMembership(post.author_id)),
         editsCount: post.edition_number.toNumber()
       });
     }

+ 0 - 19
pioneer/packages/joy-utils/src/transport/storageProviders.ts

@@ -1,19 +0,0 @@
-import BaseTransport from './base';
-import { IStorageRoleParameters } from '../types/storageProviders';
-import { RoleKeys } from '@joystream/types/members';
-import { Vec } from '@polkadot/types/';
-import { AccountId } from '@polkadot/types/interfaces';
-
-export default class StorageProvidersTransport extends BaseTransport {
-  async roleParameters (): Promise<IStorageRoleParameters> {
-    const params = (
-      await this.api.query.actors.parameters(RoleKeys.StorageProvider)
-    ).toJSON() as IStorageRoleParameters;
-    return params;
-  }
-
-  async providers (): Promise<AccountId[]> {
-    const providers = (await this.actors.accountIdsByRole(RoleKeys.StorageProvider)) as Vec<AccountId>;
-    return providers.toArray();
-  }
-}

+ 2 - 2
pioneer/packages/joy-utils/src/transport/workingGroups.ts

@@ -46,7 +46,7 @@ export default class WorkingGroupsTransport extends BaseTransport {
       ? (await this.rewardRelationship(worker.reward_relationship.unwrap()))
       : undefined;
 
-    const profile = await this.membersT.expectedMemberProfile(worker.member_id);
+    const profile = await this.membersT.expectedMembership(worker.member_id);
 
     return { group, workerId, worker, profile, stake, reward };
   }
@@ -135,7 +135,7 @@ export default class WorkingGroupsTransport extends BaseTransport {
     return {
       wgApplicationId,
       applicationId: appId.toNumber(),
-      member: await this.membersT.expectedMemberProfile(wgApplication.member_id),
+      member: await this.membersT.expectedMembership(wgApplication.member_id),
       roleAccout: wgApplication.role_account_id,
       stakes: {
         application: appStakingId.isSome ? (await this.stakeValue(appStakingId.unwrap())).toNumber() : 0,

+ 0 - 1
pioneer/packages/joy-utils/src/types/members.ts

@@ -4,7 +4,6 @@ export type ParsedMember = {
   handle: string;
   registered_at_block: number;
   registered_at_time: number;
-  roles: any[];
   entry: { [k: string]: any };
   root_account: string;
   controller_account: string;

+ 2 - 2
pioneer/packages/joy-utils/src/types/proposals.ts

@@ -1,5 +1,5 @@
 import { ProposalId, VoteKind } from '@joystream/types/proposals';
-import { MemberId, Profile } from '@joystream/types/members';
+import { MemberId, Membership } from '@joystream/types/members';
 import { ThreadId, PostId } from '@joystream/types/common';
 import { ParsedMember } from './members';
 
@@ -98,7 +98,7 @@ export type ParsedPost = {
   createdAtBlock: number;
   updatedAt: Date;
   updatedAtBlock: number;
-  author: Profile | null;
+  author: Membership | null;
   authorId: MemberId;
   editsCount: number;
 };

+ 3 - 3
pioneer/packages/joy-utils/src/types/workingGroups.ts

@@ -1,5 +1,5 @@
 import { Worker, Opening as WGOpening } from '@joystream/types/working-group';
-import { Profile } from '@joystream/types/members';
+import { Membership } from '@joystream/types/members';
 import { OpeningId, Opening, ApplicationStage } from '@joystream/types/hiring';
 import { AccountId } from '@polkadot/types/interfaces';
 import { WorkingGroupKey } from '@joystream/types/common';
@@ -8,7 +8,7 @@ import { RewardRelationship } from '@joystream/types/recurring-rewards';
 export type WorkerData = {
   workerId: number;
   worker: Worker;
-  profile: Profile;
+  profile: Membership;
   stake?: number;
   reward?: RewardRelationship;
   group: WorkingGroupKey;
@@ -23,7 +23,7 @@ export type OpeningData = {
 export type ParsedApplication = {
   wgApplicationId: number;
   applicationId: number;
-  member: Profile;
+  member: Membership;
   roleAccout: AccountId;
   stakes: {
     application: number;

+ 2 - 1
tests/network-tests/package.json

@@ -14,7 +14,8 @@
   },
   "dependencies": {
     "@constantinople/types@npm:@joystream/types": "^0.10.0",
-    "@nicaea/types": "link:../../types",
+    "@nicaea/types@npm:@joystream/types": "^0.12.0",
+    "@alexandria/types": "link:../../types",
     "@polkadot/api": "^0.96.1",
     "@polkadot/keyring": "^1.7.0-beta.5",
     "@rome/types@npm:@joystream/types": "^0.7.0",

+ 1 - 1
tests/network-tests/src/iznik/tests/council/electingCouncilTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import BN from 'bn.js'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { closeApi } from '../../utils/closeApi'
 import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule'

+ 1 - 1
tests/network-tests/src/iznik/tests/fixtures/councilElectionModule.ts

@@ -2,7 +2,7 @@ import { ApiWrapper } from '../../utils/apiWrapper'
 import { KeyringPair } from '@polkadot/keyring/types'
 import BN from 'bn.js'
 import { assert } from 'chai'
-import { Seat } from '@nicaea/types/council'
+import { Seat } from '@alexandria/types/council'
 import { v4 as uuid } from 'uuid'
 import { Utils } from '../../utils/utils'
 import { Fixture } from './interfaces/fixture'

+ 4 - 4
tests/network-tests/src/iznik/tests/fixtures/proposalsModule.ts

@@ -2,7 +2,7 @@ import { KeyringPair } from '@polkadot/keyring/types'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { v4 as uuid } from 'uuid'
 import BN from 'bn.js'
-import { FillOpeningParameters, ProposalId } from '@nicaea/types/proposals'
+import { FillOpeningParameters, ProposalId } from '@alexandria/types/proposals'
 import { Fixture } from './interfaces/fixture'
 import { Bytes, Option, u32 } from '@polkadot/types'
 import { Balance, BlockNumber } from '@polkadot/types/interfaces'
@@ -13,9 +13,9 @@ import {
   ApplicationRationingPolicy,
   OpeningId,
   StakingPolicy,
-} from '@nicaea/types/hiring'
-import { RewardPolicy, SlashingTerms, WorkerId, WorkingGroupOpeningPolicyCommitment } from '@nicaea/types/working-group'
-import { WorkingGroup } from '@nicaea/types/common'
+} from '@alexandria/types/hiring'
+import { RewardPolicy, SlashingTerms, WorkerId, WorkingGroupOpeningPolicyCommitment } from '@alexandria/types/working-group'
+import { WorkingGroup } from '@alexandria/types/common'
 
 export class CreateWorkingGroupLeaderOpeningFixture implements Fixture {
   private apiWrapper: ApiWrapper

+ 3 - 3
tests/network-tests/src/iznik/tests/fixtures/workingGroupModule.ts

@@ -6,7 +6,7 @@ import { Balance, BlockNumber, Event } from '@polkadot/types/interfaces'
 import { Keyring } from '@polkadot/api'
 import { Option, u32 } from '@polkadot/types'
 import { v4 as uuid } from 'uuid'
-import { RewardRelationship } from '@nicaea/types/recurring-rewards'
+import { RewardRelationship } from '@alexandria/types/recurring-rewards'
 import {
   Application,
   ApplicationIdToWorkerIdMap,
@@ -14,7 +14,7 @@ import {
   Worker,
   WorkerId,
   WorkingGroupOpeningPolicyCommitment,
-} from '@nicaea/types/working-group'
+} from '@alexandria/types/working-group'
 import { Utils } from '../../utils/utils'
 import {
   ActivateOpeningAt,
@@ -23,7 +23,7 @@ import {
   Opening as HiringOpening,
   OpeningId,
   StakingPolicy,
-} from '@nicaea/types/hiring'
+} from '@alexandria/types/hiring'
 import { Fixture } from './interfaces/fixture'
 
 export class AddWorkerOpeningFixture implements Fixture {

+ 1 - 1
tests/network-tests/src/iznik/tests/membership/membershipCreationTest.ts

@@ -3,7 +3,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import { initConfig } from '../../utils/config'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { closeApi } from '../../utils/closeApi'
 import { BuyMembershipHappyCaseFixture, BuyMembershipWithInsufficienFundsFixture } from '../fixtures/membershipModule'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/contentWorkingGroupMintCapacityProposalTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { ContentWorkingGroupMintCapacityProposalFixture } from '../fixtures/proposalsModule'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/electionParametersProposalTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { Utils } from '../../utils/utils'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/manageLeaderRoleTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { BuyMembershipHappyCaseFixture } from '../fixtures/membershipModule'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/setLeadProposalTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { Utils } from '../../utils/utils'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/spendingProposalTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { Utils } from '../../utils/utils'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/textProposalTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { Utils } from '../../utils/utils'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/updateRuntime.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { Utils } from '../../utils/utils'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/validatorCountProposalTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper } from '../../utils/apiWrapper'
 import { Utils } from '../../utils/utils'

+ 1 - 1
tests/network-tests/src/iznik/tests/proposals/workingGroupMintCapacityProposalTest.ts

@@ -4,7 +4,7 @@ import { Keyring, WsProvider } from '@polkadot/api'
 import BN from 'bn.js'
 import { setTestTimeout } from '../../utils/setTestTimeout'
 import tap from 'tap'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { Utils } from '../../utils/utils'

+ 1 - 1
tests/network-tests/src/iznik/tests/workingGroup/atLeastValueBugTest.ts

@@ -1,5 +1,5 @@
 import { initConfig } from '../../utils/config'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { WsProvider, Keyring } from '@polkadot/api'

+ 1 - 1
tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsLeadTest.ts

@@ -1,5 +1,5 @@
 import { initConfig } from '../../utils/config'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { WsProvider, Keyring } from '@polkadot/api'

+ 1 - 1
tests/network-tests/src/iznik/tests/workingGroup/manageWorkerAsWorkerTest.ts

@@ -1,5 +1,5 @@
 import { initConfig } from '../../utils/config'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { WsProvider, Keyring } from '@polkadot/api'

+ 1 - 1
tests/network-tests/src/iznik/tests/workingGroup/workerApplicationHappyCaseTest.ts

@@ -1,5 +1,5 @@
 import { initConfig } from '../../utils/config'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { WsProvider, Keyring } from '@polkadot/api'

+ 1 - 1
tests/network-tests/src/iznik/tests/workingGroup/workerApplicationRejectionCaseTest.ts

@@ -1,5 +1,5 @@
 import { initConfig } from '../../utils/config'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { WsProvider, Keyring } from '@polkadot/api'

+ 1 - 1
tests/network-tests/src/iznik/tests/workingGroup/workerPayoutTest.ts

@@ -1,5 +1,5 @@
 import { initConfig } from '../../utils/config'
-import { registerJoystreamTypes } from '@nicaea/types'
+import { registerJoystreamTypes } from '@alexandria/types'
 import { closeApi } from '../../utils/closeApi'
 import { ApiWrapper, WorkingGroups } from '../../utils/apiWrapper'
 import { WsProvider, Keyring } from '@polkadot/api'

+ 25 - 14
tests/network-tests/src/iznik/utils/apiWrapper.ts

@@ -2,9 +2,9 @@ import { ApiPromise, WsProvider } from '@polkadot/api'
 import { Bytes, Option, u32, Vec } from '@polkadot/types'
 import { Codec } from '@polkadot/types/types'
 import { KeyringPair } from '@polkadot/keyring/types'
-import { MemberId, PaidMembershipTerms, PaidTermId, UserInfo } from '@nicaea/types/members'
-import { Mint, MintId } from '@nicaea/types/mint'
-import { Lead, LeadId } from '@nicaea/types/content-working-group'
+import { MemberId, PaidMembershipTerms, PaidTermId, UserInfo } from '@alexandria/types/members'
+import { Mint, MintId } from '@alexandria/types/mint'
+import { Lead, LeadId } from '@alexandria/types/content-working-group'
 import {
   Application,
   ApplicationIdToWorkerIdMap,
@@ -13,15 +13,15 @@ import {
   Worker,
   WorkerId,
   WorkingGroupOpeningPolicyCommitment,
-} from '@nicaea/types/working-group'
-import { ElectionStake, Seat } from '@nicaea/types/council'
+} from '@alexandria/types/working-group'
+import { ElectionStake, Seat } from '@alexandria/types/council'
 import { AccountId, Balance, BalanceOf, BlockNumber, Event, EventRecord } from '@polkadot/types/interfaces'
 import BN from 'bn.js'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
 import { Sender } from './sender'
 import { Utils } from './utils'
-import { Stake, StakedState, StakeId } from '@nicaea/types/stake'
-import { RewardRelationship, RewardRelationshipId } from '@nicaea/types/recurring-rewards'
+import { Stake, StakedState, StakeId } from '@alexandria/types/stake'
+import { RewardRelationship, RewardRelationshipId } from '@alexandria/types/recurring-rewards'
 import {
   ActivateOpeningAt,
   Application as HiringApplication,
@@ -30,9 +30,9 @@ import {
   Opening as HiringOpening,
   OpeningId,
   StakingPolicy,
-} from '@nicaea/types/hiring'
-import { FillOpeningParameters, ProposalId } from '@nicaea/types/proposals'
-import { WorkingGroup } from '@nicaea/types/common'
+} from '@alexandria/types/hiring'
+import { FillOpeningParameters, ProposalId } from '@alexandria/types/proposals'
+import { WorkingGroup } from '@alexandria/types/common'
 
 export enum WorkingGroups {
   StorageWorkingGroup = 'storageWorkingGroup',
@@ -74,7 +74,9 @@ export class ApiWrapper {
     return this.sender.signAndSend(
       (this.api.tx.members.buyMembership(
         paidTermsId,
-        new UserInfo({ 'handle': name, 'avatar_uri': '', 'about': '' })
+        /* Handle: */ name,
+        /* Avatar uri: */ '',
+        /* About: */ ''
       ) as unknown) as SubmittableExtrinsic<'promise'>,
       account,
       expectFailure
@@ -123,7 +125,9 @@ export class ApiWrapper {
     return this.estimateTxFee(
       (this.api.tx.members.buyMembership(
         paidTermsId,
-        new UserInfo({ 'handle': name, 'avatar_uri': '', 'about': '' })
+        /* Handle: */ name,
+        /* Avatar uri: */ '',
+        /* About: */ ''
       ) as unknown) as SubmittableExtrinsic<'promise'>
     )
   }
@@ -301,7 +305,12 @@ export class ApiWrapper {
     })
 
     return this.estimateTxFee(
-      this.api.tx[module].addOpening('CurrentBlock', commitment, 'Human readable text', 'Worker')
+      (this.api.tx[module].addOpening(
+        'CurrentBlock',
+        commitment,
+        'Human readable text',
+        'Worker'
+      ) as unknown) as SubmittableExtrinsic<'promise'>
     )
   }
 
@@ -1187,7 +1196,9 @@ export class ApiWrapper {
     type: string,
     module: WorkingGroups
   ): SubmittableExtrinsic<'promise'> {
-    return this.api.tx[module].addOpening(actiavteAt, commitment, text, type)
+    return (this.api.tx[module].addOpening(actiavteAt, commitment, text, type) as unknown) as SubmittableExtrinsic<
+      'promise'
+    >
   }
 
   public async acceptApplications(leader: KeyringPair, openingId: OpeningId, module: WorkingGroups): Promise<void> {

+ 1 - 1
tests/network-tests/src/iznik/utils/utils.ts

@@ -4,7 +4,7 @@ import { blake2AsHex } from '@polkadot/util-crypto'
 import BN from 'bn.js'
 import fs from 'fs'
 import Keyring, { decodeAddress } from '@polkadot/keyring'
-import { Seat } from '@nicaea/types/council'
+import { Seat } from '@alexandria/types/council'
 import { KeyringPair } from '@polkadot/keyring/types'
 import { v4 as uuid } from 'uuid'
 

+ 1 - 0
types/.prettierignore

@@ -4,3 +4,4 @@
 hiring/schemas/role.schema.json
 lib/
 build/
+src/definitions

+ 11 - 5
types/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@joystream/types",
-  "version": "0.12.0",
-  "description": "Types for Joystream Substrate Runtime - nicaea release",
+  "version": "0.13.0",
+  "description": "Types for Joystream Substrate Runtime - Alexandria release",
   "main": "index.js",
   "types": "index.d.ts",
   "scripts": {
@@ -9,13 +9,17 @@
     "build": "tsc --build tsconfig.json",
     "lint": "eslint ./ --quiet --ext .ts",
     "format": "prettier ./ --write",
-    "checks": "yarn build && madge --circular ./ && yarn lint && prettier ./ --check"
+    "checks": "yarn build && madge --circular ./ && yarn lint && prettier ./ --check",
+    "generate:defs": "ts-node node_modules/.bin/polkadot-types-from-defs --package ./src --input ./src/definitions",
+    "update:augment-types": "tsc --build tsconfig-scripts.json && node ./src/scripts/updateAugmentTypes.js",
+    "print:typedef": "tsc --build tsconfig-scripts.json && node ./src/scripts/defsFromTypes.js"
   },
   "author": "Joystream contributors",
   "maintainers": [],
   "dependencies": {
-    "@polkadot/types": "^0.96.1",
-    "@polkadot/keyring": "^1.7.0-beta.5",
+    "@polkadot/api": "^1.26.1",
+    "@polkadot/types": "^1.26.1",
+    "@polkadot/keyring": "^3.0.1",
     "@types/lodash": "^4.14.157",
     "@types/vfile": "^4.0.0",
     "ajv": "^6.11.0",
@@ -23,6 +27,8 @@
     "moment": "^2.24.0"
   },
   "devDependencies": {
+    "@polkadot/typegen": "^1.26.1",
+    "ts-node": "^8.6.2",
     "typescript": "^3.7.2"
   },
   "engines": {

+ 22 - 14
types/src/JoyEnum.ts

@@ -1,36 +1,44 @@
-import { Constructor } from '@polkadot/types/types'
+import { Constructor, Registry } from '@polkadot/types/types'
 import { Enum } from '@polkadot/types/codec'
 import { EnumConstructor } from '@polkadot/types/codec/Enum'
 
 export interface ExtendedEnum<Types extends Record<string, Constructor>> extends Enum {
   isOfType: (type: keyof Types) => boolean
   asType<TypeKey extends keyof Types>(type: TypeKey): InstanceType<Types[TypeKey]>
+  typeDefinitions: Types
+  type: keyof Types & string // More typesafe type for the original Enum property
 }
 
 export interface ExtendedEnumConstructor<Types extends Record<string, Constructor>>
   extends EnumConstructor<ExtendedEnum<Types>> {
-  create<TypeKey extends keyof Types>(typeKey: TypeKey, value: InstanceType<Types[TypeKey]>): ExtendedEnum<Types>
+  create<TypeKey extends keyof Types>(
+    registry: Registry,
+    typeKey: TypeKey,
+    value: InstanceType<Types[TypeKey]>
+  ): ExtendedEnum<Types>
+  typeDefinitions: Types
 }
 
 // Helper for creating extended Enum type with TS-compatible isOfType and asType helpers
 export function JoyEnum<Types extends Record<string, Constructor>>(types: Types): ExtendedEnumConstructor<Types> {
-  // Unique values check
-  if (Object.values(types).some((val, i) => Object.values(types).indexOf(val, i + 1) !== -1)) {
-    throw new Error('Values passed to JoyEnum are not unique. Create an individual class for each value.')
-  }
-
-  return class JoyEnumObject extends Enum {
-    public static create<TypeKey extends keyof Types>(typeKey: TypeKey, value: InstanceType<Types[TypeKey]>) {
-      return new JoyEnumObject({ [typeKey]: value })
+  return class JoyEnumObject extends Enum.with(types) {
+    static typeDefinitions = types
+    typeDefinitions = JoyEnumObject.typeDefinitions // Non-static version
+    public static create<TypeKey extends keyof Types>(
+      registry: Registry,
+      typeKey: TypeKey,
+      value: InstanceType<Types[TypeKey]>
+    ) {
+      return new JoyEnumObject(registry, { [typeKey]: value })
     }
-    constructor(value?: any, index?: number) {
-      super(types, value, index)
+    constructor(registry: Registry, value?: any, index?: number) {
+      super(registry, value, index)
     }
     public isOfType(typeKey: keyof Types) {
-      return this.value instanceof types[typeKey]
+      return this.type === typeKey
     }
     public asType<TypeKey extends keyof Types>(typeKey: TypeKey) {
-      if (!(this.value instanceof types[typeKey])) {
+      if (!this.isOfType(typeKey)) {
         throw new Error(`Enum.asType(${typeKey}) - value is not of type ${typeKey}`)
       }
       return this.value as InstanceType<Types[TypeKey]>

+ 63 - 34
types/src/JoyStruct.ts

@@ -1,44 +1,73 @@
-import { Option, Struct, Enum } from '@polkadot/types/codec'
-import { Text, bool as Bool } from '@polkadot/types'
-import { Codec } from '@polkadot/types/types'
+import { Struct } from '@polkadot/types/codec'
+import { Codec, Constructor, Registry } from '@polkadot/types/types'
 
-export class JoyStruct<
-  T extends {
-    [K: string]: Codec
-  }
-> extends Struct {
-  getField<C extends Codec>(name: keyof T): C {
-    return super.get(name as string) as C
-  }
-
-  getString(name: keyof T): string {
-    return this.getField<Text>(name).toString()
-  }
+export interface ExtendedStruct<FieldTypes extends Record<string, Constructor>> extends Struct<FieldTypes> {
+  getField<FieldKey extends keyof FieldTypes>(key: FieldKey): InstanceType<FieldTypes[FieldKey]>
+  getString<FieldKey extends keyof FieldTypes>(key: FieldKey): string
+  cloneValues(): { [k in keyof FieldTypes]: FieldTypes[k] }
+}
 
-  getBoolean(name: keyof T): boolean {
-    return this.getField<Bool>(name).valueOf()
-  }
+// Those getters are automatically added via Object.defineProperty when using Struct.with
+export type ExtendedStructGetters<FieldTypes extends Record<string, Constructor>> = {
+  [k in keyof FieldTypes]: InstanceType<FieldTypes[k]>
+}
+// More rich TypeScript definition of the Struct (includes automatically created getters)
+export type ExtendedStructDecorated<FieldTypes extends Record<string, Constructor>> = ExtendedStructGetters<
+  FieldTypes
+> &
+  ExtendedStruct<FieldTypes>
 
-  getEnumAsString<EnumValue extends string>(name: keyof T): EnumValue {
-    return this.getField<Enum>(name).toString() as EnumValue
-  }
+export interface StructConstructor<
+  FieldTypes extends Record<string, Constructor>,
+  StructType extends Struct<FieldTypes>
+> {
+  new (registry: Registry, value?: { [k in keyof FieldTypes]: InstanceType<FieldTypes[k]> }): StructType
+}
 
-  unwrapOrUndefined<C extends Codec>(name: keyof T): C | undefined {
-    return this.getField<Option<C>>(name).unwrapOr(undefined)
-  }
+export type ExtendedStructConstructor<FieldTypes extends Record<string, Constructor>> = StructConstructor<
+  FieldTypes,
+  ExtendedStruct<FieldTypes>
+>
 
-  getOptionalString(name: keyof T): string | undefined {
-    const text = this.unwrapOrUndefined<Text>(name)
-    return text ? text.toString() : undefined
-  }
+export type ExtendedStructDecoratedConstructor<FieldTypes extends Record<string, Constructor>> = StructConstructor<
+  FieldTypes,
+  ExtendedStructDecorated<FieldTypes>
+>
 
-  cloneValues(): T {
-    const objectClone = {} as { [K: string]: Codec }
+// Helper for creating extended Struct type with TS-compatible interface
+// It's called JoyStructCustom, because eventually we'd want to migrate all structs to JoyStructDecorated,
+// but the latter won't allow specifying getters that return different type than the original field type.
+// (ie. by using getString() instead of getField())
+export function JoyStructCustom<FieldTypes extends Record<string, Constructor>>(
+  fields: FieldTypes
+): ExtendedStructConstructor<FieldTypes> {
+  return class JoyStructObject extends Struct.with(fields) {
+    constructor(registry: Registry, value?: { [k in keyof FieldTypes]: InstanceType<FieldTypes[k]> }) {
+      super(registry, value)
+    }
+    getField<FieldKey extends keyof FieldTypes>(key: FieldKey): InstanceType<FieldTypes[FieldKey]> {
+      return this.get(key as string) as InstanceType<FieldTypes[FieldKey]>
+    }
+    getString<FieldKey extends keyof FieldTypes>(key: FieldKey): string {
+      return this.getField(key).toString()
+    }
+    // TODO: Check why would this ever be needed
+    cloneValues(): { [k in keyof FieldTypes]: FieldTypes[k] } {
+      const objectClone = {} as Partial<{ [k in keyof FieldTypes]: Codec }>
 
-    super.forEach((v, k) => {
-      objectClone[k] = v // shallow copy acceptable ?
-    })
+      super.forEach((v, k) => {
+        objectClone[k] = v // shallow copy acceptable ?
+      })
 
-    return objectClone as T
+      return (objectClone as unknown) as { [k in keyof FieldTypes]: FieldTypes[k] }
+    }
   }
 }
+
+// JoyStruct enriched with typescript definitions for getters automatically added by polkadot-js
+export function JoyStructDecorated<FieldTypes extends Record<string, Constructor>>(
+  fields: FieldTypes
+): ExtendedStructDecoratedConstructor<FieldTypes> {
+  // We need to cast here because there's no way to make TS aware of getters added with Object.defineProperty
+  return JoyStructCustom(fields) as ExtendedStructDecoratedConstructor<FieldTypes>
+}

+ 19 - 79
types/src/common.ts

@@ -1,15 +1,12 @@
-import { Struct, Option, Text, bool, Vec, u16, u32, u64, getTypeRegistry, Null } from '@polkadot/types'
+import { Struct, Option, Text, bool, Vec, u16, u32, u64, Null } from '@polkadot/types'
 import { BlockNumber, Moment } from '@polkadot/types/interfaces'
-import { Codec } from '@polkadot/types/types'
+import { Codec, RegistryTypes } from '@polkadot/types/types'
 // we get 'moment' because it is a dependency of @polkadot/util, via @polkadot/keyring
 import moment from 'moment'
-import { JoyStruct } from './JoyStruct'
+import { JoyStructCustom, JoyStructDecorated } from './JoyStruct'
 import { JoyEnum } from './JoyEnum'
-export { JoyStruct } from './JoyStruct'
-export { JoyEnum } from './JoyEnum'
 
-// Treat a BTreeSet as a Vec since it is encoded in the same way
-export class BTreeSet<T extends Codec> extends Vec<T> {}
+export { JoyEnum, JoyStructCustom, JoyStructDecorated }
 
 export class Credential extends u64 {}
 export class CredentialSet extends Vec.with(Credential) {} // BtreeSet ?
@@ -23,29 +20,7 @@ export type BlockAndTimeType = {
   time: Moment
 }
 
-export class BlockAndTime extends Struct {
-  constructor(value?: BlockAndTimeType) {
-    super(
-      {
-        block: u32, // BlockNumber
-        time: u64, // Moment
-      },
-      value
-    )
-  }
-
-  get block(): BlockNumber {
-    return this.get('block') as BlockNumber
-  }
-
-  get time(): Moment {
-    return this.get('time') as Moment
-  }
-
-  static newEmpty(): BlockAndTime {
-    return new BlockAndTime({} as BlockAndTime)
-  }
-
+export class BlockAndTime extends JoyStructDecorated({ block: u32, time: u64 }) implements BlockAndTimeType {
   get momentDate(): moment.Moment {
     const YEAR_2000_MILLISECONDS = 946684801000
 
@@ -76,49 +51,17 @@ export function getOptionPropOrUndefined<T extends Codec>(struct: Struct, fieldN
   return (struct.get(fieldName) as Option<T>).unwrapOr(undefined)
 }
 
-export class OptionText extends Option.with(Text) {
-  static none(): OptionText {
-    return new Option(Text, null)
-  }
-
-  static some(text: string): OptionText {
-    return new Option(Text, text)
-  }
-}
+export class OptionText extends Option.with(Text) {}
 
 export type InputValidationLengthConstraintType = {
   min: u16
   max_min_diff: u16
 }
 
-export class InputValidationLengthConstraint extends JoyStruct<InputValidationLengthConstraintType> {
-  constructor(value: InputValidationLengthConstraintType) {
-    super(
-      {
-        min: u16,
-        max_min_diff: u16,
-      },
-      value
-    )
-  }
-
-  static createWithMaxAllowed() {
-    return new InputValidationLengthConstraint({
-      min: new u16(1),
-      max_min_diff: new u16(65534), // Max allowed without causing u16 overflow
-    })
-  }
-
-  get min(): u16 {
-    return this.getField('min')
-  }
-
-  get max_min_diff(): u16 {
-    return this.getField('max_min_diff')
-  }
-
+export class InputValidationLengthConstraint extends JoyStructDecorated({ min: u16, max_min_diff: u16 })
+  implements InputValidationLengthConstraintType {
   get max(): u16 {
-    return new u16(this.min.add(this.max_min_diff))
+    return new u16(this.registry, this.min.add(this.max_min_diff))
   }
 }
 
@@ -128,17 +71,14 @@ export const WorkingGroupDef = {
 export type WorkingGroupKey = keyof typeof WorkingGroupDef
 export class WorkingGroup extends JoyEnum(WorkingGroupDef) {}
 
-export function registerCommonTypes() {
-  const typeRegistry = getTypeRegistry()
-
-  typeRegistry.register({
-    Credential,
-    CredentialSet,
-    BlockAndTime,
-    ThreadId,
-    PostId,
-    InputValidationLengthConstraint,
-    BTreeSet, // Is this even necessary?
-    WorkingGroup,
-  })
+export const commonTypes: RegistryTypes = {
+  Credential,
+  CredentialSet,
+  BlockAndTime,
+  ThreadId,
+  PostId,
+  InputValidationLengthConstraint,
+  WorkingGroup,
 }
+
+export default commonTypes

+ 204 - 452
types/src/content-working-group/index.ts

@@ -1,25 +1,13 @@
-import {
-  getTypeRegistry,
-  BTreeMap,
-  Enum,
-  bool,
-  u8,
-  u32,
-  Text,
-  GenericAccountId,
-  Null,
-  Option,
-  Vec,
-  u16,
-} from '@polkadot/types'
-import { BlockNumber, AccountId } from '@polkadot/types/interfaces'
-import { BTreeSet, JoyStruct, OptionText, Credential } from '../common'
+import { BTreeMap, BTreeSet, bool, u32, Text, Null, Option, Vec, u16 } from '@polkadot/types'
+import { BlockNumber } from '@polkadot/types/interfaces'
+import { OptionText, Credential, JoyEnum, JoyStructDecorated } from '../common'
 import { ActorId, MemberId } from '../members'
 import { StakeId } from '../stake'
 import { OpeningId, ApplicationId, ApplicationRationingPolicy, StakingPolicy } from '../hiring/index'
 import { RewardRelationshipId } from '../recurring-rewards'
-
 import ChannelId from './ChannelId'
+import AccountId from '@polkadot/types/generic/AccountId'
+
 export { ChannelId }
 export class CuratorId extends ActorId {}
 export class CuratorOpeningId extends OpeningId {}
@@ -29,35 +17,33 @@ export class PrincipalId extends Credential {}
 
 export class OptionalText extends OptionText {}
 
-export type ChannelContentTypeValue = 'Video' | 'Music' | 'Ebook'
-
-export const ChannelContentTypeAllValues: ChannelContentTypeValue[] = ['Video', 'Music', 'Ebook']
-
-export class ChannelContentType extends Enum {
-  constructor(value?: ChannelContentTypeValue, index?: number) {
-    super(ChannelContentTypeAllValues, value, index)
-  }
-}
-
-export type ChannelPublicationStatusValue = 'Public' | 'Unlisted'
-
-export const ChannelPublicationStatusAllValues: ChannelPublicationStatusValue[] = ['Public', 'Unlisted']
-
-export class ChannelPublicationStatus extends Enum {
-  constructor(value?: ChannelPublicationStatusValue, index?: number) {
-    super(ChannelPublicationStatusAllValues, value, index)
-  }
-}
-
-export type ChannelCurationStatusValue = 'Normal' | 'Censored'
-
-export const ChannelCurationStatusAllValues: ChannelCurationStatusValue[] = ['Normal', 'Censored']
-
-export class ChannelCurationStatus extends Enum {
-  constructor(value?: ChannelCurationStatusValue, index?: number) {
-    super(ChannelCurationStatusAllValues, value, index)
-  }
-}
+export const ChannelContentTypeAllValues = ['Video', 'Music', 'Ebook'] as const
+// FIXME: Naming conventions (Keys?)
+export type ChannelContentTypeValue = typeof ChannelContentTypeAllValues[number]
+export const ChannelContentTypeDef = {
+  Video: Null,
+  Music: Null,
+  Ebook: Null,
+} as const
+export class ChannelContentType extends JoyEnum(ChannelContentTypeDef) {}
+
+export const ChannelPublicationStatusAllValues = ['Public', 'Unlisted'] as const
+// FIXME: Naming conventions (Keys?)
+export type ChannelPublicationStatusValue = typeof ChannelPublicationStatusAllValues[number]
+export const ChannelPublicationStatusDef = {
+  Public: Null,
+  Unlisted: Null,
+} as const
+export class ChannelPublicationStatus extends JoyEnum(ChannelPublicationStatusDef) {}
+
+export const ChannelCurationStatusAllValues = ['Normal', 'Censored'] as const
+// FIXME: Naming conventions (Keys?)
+export type ChannelCurationStatusValue = typeof ChannelCurationStatusAllValues[number]
+export const ChannelCurationStatusDef = {
+  Normal: Null,
+  Censored: Null,
+} as const
+export class ChannelCurationStatus extends JoyEnum(ChannelCurationStatusDef) {}
 
 export type IChannel = {
   verified: bool
@@ -74,161 +60,95 @@ export type IChannel = {
   created: BlockNumber
   principal_id: PrincipalId
 }
-export class Channel extends JoyStruct<IChannel> {
-  constructor(value?: IChannel) {
-    super(
-      {
-        verified: bool,
-        handle: Text, // Vec.with(u8),
-        title: OptionalText,
-        description: OptionalText,
-        avatar: OptionalText,
-        banner: OptionalText,
-        content: ChannelContentType,
-        owner: MemberId,
-        role_account: GenericAccountId,
-        publication_status: ChannelPublicationStatus,
-        curation_status: ChannelCurationStatus,
-        created: u32, // BlockNumber,
-        principal_id: PrincipalId,
-      },
-      value
-    )
-  }
-}
-
-export class CurationActor extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Lead: Null,
-        Curator: CuratorId,
-      },
-      value,
-      index
-    )
-  }
-}
-
-export class Principal extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Lead: Null,
-        Curator: CuratorId,
-        ChannelOwner: ChannelId,
-      },
-      value,
-      index
-    )
-  }
-}
+export class Channel
+  extends JoyStructDecorated({
+    verified: bool,
+    handle: Text, // Vec.with(u8),
+    title: OptionalText,
+    description: OptionalText,
+    avatar: OptionalText,
+    banner: OptionalText,
+    content: ChannelContentType,
+    owner: MemberId,
+    role_account: AccountId,
+    publication_status: ChannelPublicationStatus,
+    curation_status: ChannelCurationStatus,
+    created: u32, // BlockNumber,
+    principal_id: PrincipalId,
+  })
+  implements IChannel {}
+
+export class CurationActor extends JoyEnum({
+  Lead: Null,
+  Curator: CuratorId,
+} as const) {}
+
+export class Principal extends JoyEnum({
+  Lead: Null,
+  Curator: CuratorId,
+  ChannelOwner: ChannelId,
+} as const) {}
 
 export type ICuratorRoleStakeProfile = {
   stake_id: StakeId
   termination_unstaking_period: Option<BlockNumber>
   exit_unstaking_period: Option<BlockNumber>
 }
-export class CuratorRoleStakeProfile extends JoyStruct<ICuratorRoleStakeProfile> {
-  constructor(value?: ICuratorRoleStakeProfile) {
-    super(
-      {
-        stake_id: StakeId,
-        termination_unstaking_period: Option.with(u32),
-        exit_unstaking_period: Option.with(u32),
-      },
-      value
-    )
-  }
+export class CuratorRoleStakeProfile
+  extends JoyStructDecorated({
+    stake_id: StakeId,
+    termination_unstaking_period: Option.with(u32),
+    exit_unstaking_period: Option.with(u32),
+  })
+  implements ICuratorRoleStakeProfile {}
 
-  get stake_id(): StakeId {
-    return this.getField<StakeId>('stake_id')
-  }
-}
-
-export class CuratorExitInitiationOrigin extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Lead: Null,
-        Curator: Null,
-      },
-      value,
-      index
-    )
-  }
-}
+export class CuratorExitInitiationOrigin extends JoyEnum({
+  Lead: Null,
+  Curator: Null,
+} as const) {}
 
 export type ICuratorExitSummary = {
   origin: CuratorExitInitiationOrigin
   initiated_at_block_number: BlockNumber
-  rationale_text: Vec<u8>
-}
-export class CuratorExitSummary extends JoyStruct<ICuratorExitSummary> {
-  constructor(value?: ICuratorExitSummary) {
-    super(
-      {
-        origin: CuratorExitInitiationOrigin,
-        initiated_at_block_number: u32,
-        rationale_text: Text,
-      },
-      value
-    )
-  }
-}
-
-export enum CuratorRoleStakeKeys {
+  rationale_text: Text
+}
+export class CuratorExitSummary
+  extends JoyStructDecorated({
+    origin: CuratorExitInitiationOrigin,
+    initiated_at_block_number: u32,
+    rationale_text: Text, // FIXME: Should be: Bytes
+  })
+  implements ICuratorExitSummary {}
+
+// FIXME: Replace usages with isOfType, asType wherever possible
+export enum CuratorRoleStageKeys {
   Active = 'Active',
   Unstaking = 'Unstaking',
   Exited = 'Exited',
 }
-export class CuratorRoleStage extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        [CuratorRoleStakeKeys.Active]: Null,
-        [CuratorRoleStakeKeys.Unstaking]: CuratorExitSummary,
-        [CuratorRoleStakeKeys.Exited]: CuratorExitSummary,
-      },
-      value,
-      index
-    )
-  }
-}
+export const CuratorRoleStageDef = {
+  Active: Null,
+  Unstaking: CuratorExitSummary,
+  Exited: CuratorExitSummary,
+} as const
+export class CuratorRoleStage extends JoyEnum(CuratorRoleStageDef) {}
 
 export type ICuratorInduction = {
   lead: LeadId
   curator_application_id: CuratorApplicationId
   at_block: BlockNumber
 }
-export class CuratorInduction extends JoyStruct<ICuratorInduction> {
-  constructor(value?: ICuratorInduction) {
-    super(
-      {
-        lead: LeadId,
-        curator_application_id: CuratorApplicationId,
-        at_block: u32,
-      },
-      value
-    )
-  }
-
-  get lead(): LeadId {
-    return this.getField<LeadId>('lead')
-  }
-
-  get curator_application_id(): CuratorApplicationId {
-    return this.getField<CuratorApplicationId>('curator_application_id')
-  }
-
+export class CuratorInduction
+  extends JoyStructDecorated({
+    lead: LeadId,
+    curator_application_id: CuratorApplicationId,
+    at_block: u32,
+  })
+  implements ICuratorInduction {
   // Helper for working-group compatibility
   get worker_application_id(): CuratorApplicationId {
     return this.curator_application_id
   }
-
-  get at_block(): u32 {
-    return this.getField<u32>('at_block')
-  }
 }
 
 export type ICurator = {
@@ -239,52 +159,23 @@ export type ICurator = {
   induction: CuratorInduction
   principal_id: PrincipalId
 }
-export class Curator extends JoyStruct<ICurator> {
-  constructor(value?: ICurator) {
-    super(
-      {
-        role_account: GenericAccountId,
-        reward_relationship: Option.with(RewardRelationshipId),
-        role_stake_profile: Option.with(CuratorRoleStakeProfile),
-        stage: CuratorRoleStage,
-        induction: CuratorInduction,
-        principal_id: PrincipalId,
-      },
-      value
-    )
-  }
-
-  get role_account(): GenericAccountId {
-    return this.getField<GenericAccountId>('role_account')
-  }
-
+export class Curator
+  extends JoyStructDecorated({
+    role_account: AccountId,
+    reward_relationship: Option.with(RewardRelationshipId),
+    role_stake_profile: Option.with(CuratorRoleStakeProfile),
+    stage: CuratorRoleStage,
+    induction: CuratorInduction,
+    principal_id: PrincipalId,
+  })
+  implements ICurator {
   // Helper for working-group compatibility
-  get role_account_id(): GenericAccountId {
+  get role_account_id(): AccountId {
     return this.role_account
   }
 
-  get reward_relationship(): Option<RewardRelationshipId> {
-    return this.getField<Option<RewardRelationshipId>>('reward_relationship')
-  }
-
-  get role_stake_profile(): Option<CuratorRoleStakeProfile> {
-    return this.getField<Option<CuratorRoleStakeProfile>>('role_stake_profile')
-  }
-
-  get stage(): CuratorRoleStage {
-    return this.getField<CuratorRoleStage>('stage')
-  }
-
-  get induction(): CuratorInduction {
-    return this.getField<CuratorInduction>('induction')
-  }
-
-  get principal_id(): PrincipalId {
-    return this.getField<PrincipalId>('principal_id')
-  }
-
   get is_active(): boolean {
-    return this.stage.type == CuratorRoleStakeKeys.Active
+    return this.stage.type == CuratorRoleStageKeys.Active
   }
 }
 
@@ -294,74 +185,39 @@ export type ICuratorApplication = {
   member_id: MemberId
   application_id: ApplicationId
 }
-export class CuratorApplication extends JoyStruct<ICuratorApplication> {
-  constructor(value?: ICuratorApplication) {
-    super(
-      {
-        role_account: GenericAccountId,
-        curator_opening_id: CuratorOpeningId,
-        member_id: MemberId,
-        application_id: ApplicationId,
-      },
-      value
-    )
-  }
-
-  get role_account(): GenericAccountId {
-    return this.getField<GenericAccountId>('role_account')
-  }
-
+export class CuratorApplication
+  extends JoyStructDecorated({
+    role_account: AccountId,
+    curator_opening_id: CuratorOpeningId,
+    member_id: MemberId,
+    application_id: ApplicationId,
+  })
+  implements ICuratorApplication {
   // Helper for working-group compatibility
-  get role_account_id(): GenericAccountId {
+  get role_account_id(): AccountId {
     return this.role_account
   }
-
-  get curator_opening_id(): CuratorOpeningId {
-    return this.getField<CuratorOpeningId>('curator_opening_id')
-  }
-
   // Helper for working-group compatibility
   get opening_id(): CuratorOpeningId {
     return this.curator_opening_id
   }
-
-  get member_id(): MemberId {
-    return this.getField<MemberId>('member_id')
-  }
-
-  get application_id(): ApplicationId {
-    return this.getField<ApplicationId>('application_id')
-  }
 }
 
 export type ISlashableTerms = {
   max_count: u16
   max_percent_pts_per_time: u16
 }
-export class SlashableTerms extends JoyStruct<ISlashableTerms> {
-  constructor(value?: ISlashableTerms) {
-    super(
-      {
-        max_count: u16,
-        max_percent_pts_per_time: u16,
-      },
-      value
-    )
-  }
-}
+export class SlashableTerms
+  extends JoyStructDecorated({
+    max_count: u16,
+    max_percent_pts_per_time: u16,
+  })
+  implements ISlashableTerms {}
 
-export class SlashingTerms extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Unslashable: Null,
-        Slashable: SlashableTerms,
-      },
-      value,
-      index
-    )
-  }
-}
+export class SlashingTerms extends JoyEnum({
+  Unslashable: Null,
+  Slashable: SlashableTerms,
+} as const) {}
 
 export type IOpeningPolicyCommitment = {
   application_rationing_policy: Option<ApplicationRationingPolicy>
@@ -377,75 +233,22 @@ export type IOpeningPolicyCommitment = {
   exit_curator_role_application_stake_unstaking_period: Option<BlockNumber>
   exit_curator_role_stake_unstaking_period: Option<BlockNumber>
 }
-export class OpeningPolicyCommitment extends JoyStruct<IOpeningPolicyCommitment> {
-  constructor(value?: IOpeningPolicyCommitment) {
-    super(
-      {
-        application_rationing_policy: Option.with(ApplicationRationingPolicy),
-        max_review_period_length: u32, // BlockNumber,
-        application_staking_policy: Option.with(StakingPolicy),
-        role_staking_policy: Option.with(StakingPolicy),
-        role_slashing_terms: SlashingTerms,
-        fill_opening_successful_applicant_application_stake_unstaking_period: Option.with(u32),
-        fill_opening_failed_applicant_application_stake_unstaking_period: Option.with(u32),
-        fill_opening_failed_applicant_role_stake_unstaking_period: Option.with(u32),
-        terminate_curator_application_stake_unstaking_period: Option.with(u32),
-        terminate_curator_role_stake_unstaking_period: Option.with(u32),
-        exit_curator_role_application_stake_unstaking_period: Option.with(u32),
-        exit_curator_role_stake_unstaking_period: Option.with(u32),
-      },
-      value
-    )
-  }
-
-  get application_rationing_policy(): Option<ApplicationRationingPolicy> {
-    return this.getField<Option<ApplicationRationingPolicy>>('application_rationing_policy')
-  }
-
-  get max_review_period_length(): u32 {
-    return this.getField<u32>('max_review_period_length')
-  }
-
-  get application_staking_policy(): Option<StakingPolicy> {
-    return this.getField<Option<StakingPolicy>>('application_staking_policy')
-  }
-
-  get role_staking_policy(): Option<StakingPolicy> {
-    return this.getField<Option<StakingPolicy>>('role_staking_policy')
-  }
-
-  get role_slashing_terms(): SlashingTerms {
-    return this.getField<SlashingTerms>('role_slashing_terms')
-  }
-
-  get fill_opening_successful_applicant_application_stake_unstaking_period(): Option<u32> {
-    return this.getField<Option<u32>>('fill_opening_successful_applicant_application_stake_unstaking_period')
-  }
-
-  get fill_opening_failed_applicant_application_stake_unstaking_period(): Option<u32> {
-    return this.getField<Option<u32>>('fill_opening_failed_applicant_application_stake_unstaking_period')
-  }
-
-  get fill_opening_failed_applicant_role_stake_unstaking_period(): Option<u32> {
-    return this.getField<Option<u32>>('fill_opening_failed_applicant_role_stake_unstaking_period')
-  }
-
-  get terminate_curator_application_stake_unstaking_period(): Option<u32> {
-    return this.getField<Option<u32>>('terminate_curator_application_stake_unstaking_period')
-  }
-
-  get terminate_curator_role_stake_unstaking_period(): Option<u32> {
-    return this.getField<Option<u32>>('terminate_curator_role_stake_unstaking_period')
-  }
-
-  get exit_curator_role_application_stake_unstaking_period(): Option<u32> {
-    return this.getField<Option<u32>>('exit_curator_role_application_stake_unstaking_period')
-  }
-
-  get exit_curator_role_stake_unstaking_period(): Option<u32> {
-    return this.getField<Option<u32>>('exit_curator_role_stake_unstaking_period')
-  }
-}
+export class OpeningPolicyCommitment
+  extends JoyStructDecorated({
+    application_rationing_policy: Option.with(ApplicationRationingPolicy),
+    max_review_period_length: u32, // BlockNumber,
+    application_staking_policy: Option.with(StakingPolicy),
+    role_staking_policy: Option.with(StakingPolicy),
+    role_slashing_terms: SlashingTerms,
+    fill_opening_successful_applicant_application_stake_unstaking_period: Option.with(u32),
+    fill_opening_failed_applicant_application_stake_unstaking_period: Option.with(u32),
+    fill_opening_failed_applicant_role_stake_unstaking_period: Option.with(u32),
+    terminate_curator_application_stake_unstaking_period: Option.with(u32),
+    terminate_curator_role_stake_unstaking_period: Option.with(u32),
+    exit_curator_role_application_stake_unstaking_period: Option.with(u32),
+    exit_curator_role_stake_unstaking_period: Option.with(u32),
+  })
+  implements IOpeningPolicyCommitment {}
 
 // Not entierly sure that using BTreeSet will work correctly when reading/decoding this type from chain state
 export type ICuratorOpening = {
@@ -453,22 +256,13 @@ export type ICuratorOpening = {
   curator_applications: BTreeSet<CuratorApplicationId>
   policy_commitment: OpeningPolicyCommitment
 }
-export class CuratorOpening extends JoyStruct<ICuratorOpening> {
-  constructor(value?: ICuratorOpening) {
-    super(
-      {
-        opening_id: OpeningId,
-        curator_applications: BTreeSet.with(CuratorApplicationId),
-        policy_commitment: OpeningPolicyCommitment,
-      },
-      value
-    )
-  }
-
-  get opening_id(): OpeningId {
-    return this.getField<OpeningId>('opening_id')
-  }
-
+export class CuratorOpening
+  extends JoyStructDecorated({
+    opening_id: OpeningId,
+    curator_applications: BTreeSet.with(CuratorApplicationId),
+    policy_commitment: OpeningPolicyCommitment,
+  })
+  implements ICuratorOpening {
   // Helper for working-group compatibility
   get hiring_opening_id(): OpeningId {
     return this.opening_id
@@ -478,112 +272,70 @@ export class CuratorOpening extends JoyStruct<ICuratorOpening> {
 export type IExitedLeadRole = {
   initiated_at_block_number: BlockNumber
 }
-export class ExitedLeadRole extends JoyStruct<IExitedLeadRole> {
-  constructor(value?: IExitedLeadRole) {
-    super(
-      {
-        initiated_at_block_number: u32,
-      },
-      value
-    )
-  }
-}
+export class ExitedLeadRole
+  extends JoyStructDecorated({
+    initiated_at_block_number: u32,
+  })
+  implements IExitedLeadRole {}
 
-export class LeadRoleState extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Active: Null,
-        Exited: ExitedLeadRole,
-      },
-      value,
-      index
-    )
-  }
-}
+export class LeadRoleState extends JoyEnum({
+  Active: Null,
+  Exited: ExitedLeadRole,
+} as const) {}
 
 export type ILead = {
+  member_id: MemberId
   role_account: AccountId
   reward_relationship: Option<RewardRelationshipId>
   inducted: BlockNumber
   stage: LeadRoleState
 }
-export class Lead extends JoyStruct<ILead> {
-  constructor(value?: ILead) {
-    super(
-      {
-        role_account: GenericAccountId,
-        reward_relationship: Option.with(RewardRelationshipId),
-        inducted: u32,
-        stage: LeadRoleState,
-      },
-      value
-    )
-  }
-
-  get role_account(): GenericAccountId {
-    return this.getField<GenericAccountId>('role_account')
-  }
-
+export class Lead
+  extends JoyStructDecorated({
+    member_id: MemberId,
+    role_account: AccountId,
+    reward_relationship: Option.with(RewardRelationshipId),
+    inducted: u32,
+    stage: LeadRoleState,
+  })
+  implements ILead {
   // Helper for working-group compatibility
-  get role_account_id(): GenericAccountId {
+  get role_account_id(): AccountId {
     return this.role_account
   }
-
-  get reward_relationship(): Option<RewardRelationshipId> {
-    return this.getField<Option<RewardRelationshipId>>('reward_relationship')
-  }
-
-  get stage(): LeadRoleState {
-    return this.getField('stage')
-  }
 }
 
-export class WorkingGroupUnstaker extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Lead: LeadId,
-        Curator: CuratorId,
-      },
-      value,
-      index
-    )
-  }
-}
-
-export class CuratorApplicationIdToCuratorIdMap extends BTreeMap<ApplicationId, CuratorId> {
-  constructor(value?: any) {
-    super(ApplicationId, CuratorId, value)
-  }
-}
-
-export function registerContentWorkingGroupTypes() {
-  try {
-    getTypeRegistry().register({
-      ChannelId: 'u64',
-      CuratorId: 'u64',
-      CuratorOpeningId: 'u64',
-      CuratorApplicationId: 'u64',
-      LeadId: 'u64',
-      PrincipalId: 'u64',
-      OptionalText,
-      Channel,
-      ChannelContentType,
-      ChannelCurationStatus,
-      ChannelPublicationStatus,
-      CurationActor,
-      Curator,
-      CuratorApplication,
-      CuratorOpening,
-      Lead,
-      OpeningPolicyCommitment,
-      Principal,
-      WorkingGroupUnstaker,
-      CuratorApplicationIdToCuratorIdMap,
-      CuratorApplicationIdSet: Vec.with(CuratorApplicationId),
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of content working group module', err)
-  }
-}
+export class WorkingGroupUnstaker extends JoyEnum({
+  Lead: LeadId,
+  Curator: CuratorId,
+}) {}
+
+export class CuratorApplicationIdToCuratorIdMap extends BTreeMap.with(ApplicationId, CuratorId) {}
+
+export class CuratorApplicationIdSet extends Vec.with(CuratorApplicationId) {}
+
+export const contentWorkingGroupTypes = {
+  ChannelId: 'u64',
+  CuratorId: 'u64',
+  CuratorOpeningId: 'u64',
+  CuratorApplicationId: 'u64',
+  LeadId: 'u64',
+  PrincipalId: 'u64',
+  OptionalText,
+  Channel,
+  ChannelContentType,
+  ChannelCurationStatus,
+  ChannelPublicationStatus,
+  CurationActor,
+  Curator,
+  CuratorApplication,
+  CuratorOpening,
+  Lead,
+  OpeningPolicyCommitment,
+  Principal,
+  WorkingGroupUnstaker,
+  CuratorApplicationIdToCuratorIdMap,
+  CuratorApplicationIdSet,
+}
+
+export default contentWorkingGroupTypes

+ 72 - 166
types/src/council/index.ts

@@ -1,110 +1,72 @@
-import { Enum, Option } from '@polkadot/types/codec'
-import { getTypeRegistry, Struct, Vec } from '@polkadot/types'
-import { BlockNumber, AccountId, Balance, Hash } from '@polkadot/types/interfaces'
-import { u32 } from '@polkadot/types/primitive'
-import { MemberId } from '../members'
-
-export type TransferableStake = {
+import { Option, U8aFixed } from '@polkadot/types/codec'
+import { Vec } from '@polkadot/types'
+import { BlockNumber, Balance, Hash } from '@polkadot/types/interfaces'
+import { u32, u128 } from '@polkadot/types/primitive'
+import { RegistryTypes } from '@polkadot/types/types'
+import { JoyStructDecorated } from '../JoyStruct'
+import { JoyEnum } from '../JoyEnum'
+import AccountId from '@polkadot/types/generic/AccountId'
+
+export type ITransferableStake = {
   seat: Balance
   backing: Balance
 }
-
-export type ElectionStake = {
+export class TransferableStake
+  extends JoyStructDecorated({
+    seat: u128,
+    backing: u128,
+  })
+  implements ITransferableStake {}
+
+export type IElectionStake = {
   new: Balance
   transferred: Balance
 }
-
-export class Backer extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        member: 'AccountId',
-        stake: 'Balance',
-      },
-      value
-    )
-  }
-
-  get member(): MemberId {
-    return this.get('member') as MemberId
-  }
-
-  get stake(): Balance {
-    return this.get('stake') as Balance
-  }
-}
+export class ElectionStake
+  extends JoyStructDecorated({
+    new: u128,
+    transferred: u128,
+  })
+  implements IElectionStake {}
+
+export class Backer extends JoyStructDecorated({
+  member: AccountId,
+  stake: u128, // Balance
+}) {}
 
 export class Backers extends Vec.with(Backer) {}
-export class Seat extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        member: 'AccountId',
-        stake: 'Balance',
-        backers: Backers,
-      },
-      value
-    )
-  }
-
-  get member(): AccountId {
-    return this.get('member') as AccountId
-  }
-
-  get stake(): Balance {
-    return this.get('stake') as Balance
-  }
-
-  get backers(): Backers {
-    return this.get('backers') as Backers
-  }
-}
+export class Seat extends JoyStructDecorated({
+  member: AccountId,
+  stake: u128, // Balance
+  backers: Backers,
+}) {}
 
 export class Seats extends Vec.with(Seat) {}
 
-export type SealedVote = {
+export type ISealedVote = {
   voter: AccountId
   commitment: Hash
   stake: ElectionStake
   vote: Option<AccountId>
 }
+export class SealedVote
+  extends JoyStructDecorated({
+    voter: AccountId,
+    commitment: U8aFixed, // Hash
+    stake: ElectionStake,
+    vote: Option.with(AccountId),
+  })
+  implements ISealedVote {}
 
 export class Announcing extends u32 {}
 export class Voting extends u32 {}
 export class Revealing extends u32 {}
 
-export class ElectionStage extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Announcing,
-        Voting,
-        Revealing,
-      },
-      value,
-      index
-    )
-  }
-
-  /** Create a new Announcing stage. */
-  static Announcing(endsAt: BlockNumber | number): ElectionStage {
-    return this.newElectionStage('Announcing', endsAt)
-  }
-
-  /** Create a new Voting stage. */
-  static Voting(endsAt: BlockNumber | number): ElectionStage {
-    return this.newElectionStage('Voting', endsAt)
-  }
-
-  /** Create a new Revealing stage. */
-  static Revealing(endsAt: BlockNumber | number): ElectionStage {
-    return this.newElectionStage('Revealing', endsAt)
-  }
-
-  static newElectionStage(stageName: string, endsAt: BlockNumber | number) {
-    return new ElectionStage({ [stageName]: endsAt })
-  }
-}
+export class ElectionStage extends JoyEnum({
+  Announcing,
+  Voting,
+  Revealing,
+} as const) {}
 
 export type AnyElectionStage = Announcing | Voting | Revealing
 
@@ -119,85 +81,29 @@ export type IElectionParameters = {
   min_voting_stake: Balance
 }
 
-export class ElectionParameters extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        announcing_period: 'BlockNumber',
-        voting_period: 'BlockNumber',
-        revealing_period: 'BlockNumber',
-        council_size: 'u32',
-        candidacy_limit: 'u32',
-        new_term_duration: 'BlockNumber',
-        min_council_stake: 'Balance',
-        min_voting_stake: 'Balance',
-      },
-      value
-    )
-  }
-  get announcing_period() {
-    return this.get('announcing_period') as BlockNumber
-  }
-  get voting_period() {
-    return this.get('voting_period') as BlockNumber
-  }
-  get revealing_period() {
-    return this.get('revealing_period') as BlockNumber
-  }
-  get council_size() {
-    return this.get('council_size') as u32
-  }
-  get candidacy_limit() {
-    return this.get('candidacy_limit') as u32
-  }
-  get new_term_duration() {
-    return this.get('new_term_duration') as BlockNumber
-  }
-  get min_council_stake() {
-    return this.get('min_council_stake') as Balance
-  }
-  get min_voting_stake() {
-    return this.get('min_voting_stake') as Balance
-  }
+export class ElectionParameters
+  extends JoyStructDecorated({
+    announcing_period: u32, // BlockNumber
+    voting_period: u32, // BlockNumber
+    revealing_period: u32, // BlockNumber
+    council_size: u32,
+    candidacy_limit: u32,
+    new_term_duration: u32, // BlockNumber
+    min_council_stake: u128, // Balance
+    min_voting_stake: u128, // Balance
+  })
+  implements IElectionParameters {}
+
+export const councilTypes: RegistryTypes = {
+  ElectionStage,
+  ElectionStake,
+  SealedVote,
+  TransferableStake,
+  ElectionParameters,
+  Seat,
+  Seats,
+  Backer,
+  Backers,
 }
 
-// TODO Refactor: split this function and move to corresponding modules: election and proposals.
-export function registerCouncilAndElectionTypes() {
-  try {
-    const typeRegistry = getTypeRegistry()
-
-    typeRegistry.register({
-      ElectionStage,
-      ElectionStake: {
-        new: 'Balance',
-        transferred: 'Balance',
-      },
-      SealedVote: {
-        voter: 'AccountId',
-        commitment: 'Hash',
-        stake: 'ElectionStake',
-        vote: 'Option<AccountId>',
-      },
-      TransferableStake: {
-        seat: 'Balance',
-        backing: 'Balance',
-      },
-      ElectionParameters: {
-        announcing_period: 'BlockNumber',
-        voting_period: 'BlockNumber',
-        revealing_period: 'BlockNumber',
-        council_size: 'u32',
-        candidacy_limit: 'u32',
-        new_term_duration: 'BlockNumber',
-        min_council_stake: 'Balance',
-        min_voting_stake: 'Balance',
-      },
-      Seat,
-      Seats,
-      Backer,
-      Backers,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types for council and election modules', err)
-  }
-}
+export default councilTypes

Dosya farkı çok büyük olduğundan ihmal edildi
+ 26 - 0
types/src/definitions/augment-types.ts


+ 2 - 0
types/src/definitions/definitions.ts

@@ -0,0 +1,2 @@
+// We can put our types definitions here in the future (in order to use @polkadot/typegen)
+export {}

+ 12 - 32
types/src/discovery.ts

@@ -1,39 +1,19 @@
-import { Struct } from '@polkadot/types/codec'
-import { getTypeRegistry, Text, u32 } from '@polkadot/types'
-import { BlockNumber } from '@polkadot/types/interfaces'
+import { Text, u32 } from '@polkadot/types'
+import { RegistryTypes } from '@polkadot/types/types'
+import { JoyStructDecorated } from './common'
 
 export class IPNSIdentity extends Text {}
 export class Url extends Text {}
 
-export class AccountInfo extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        identity: IPNSIdentity,
-        expires_at: u32, // BlockNumber
-      },
-      value
-    )
-  }
+export class AccountInfo extends JoyStructDecorated({
+  identity: IPNSIdentity,
+  expires_at: u32, // BlockNumber
+}) {}
 
-  get identity(): IPNSIdentity {
-    return this.get('identity') as IPNSIdentity
-  }
-
-  get expires_at(): BlockNumber {
-    return this.get('expires_at') as BlockNumber
-  }
+export const discoveryTypes: RegistryTypes = {
+  Url,
+  IPNSIdentity,
+  // AccountInfo, FIXME: This overrides core Substrate type
 }
 
-export function registerDiscoveryTypes() {
-  try {
-    const typeRegistry = getTypeRegistry()
-    typeRegistry.register({
-      Url,
-      IPNSIdentity,
-      AccountInfo,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of discovery module', err)
-  }
-}
+export default discoveryTypes

+ 90 - 148
types/src/forum.ts

@@ -1,6 +1,7 @@
-import { getTypeRegistry, bool, u32, u64, Text, Option, Vec as Vector, GenericAccountId } from '@polkadot/types'
-import { AccountId } from '@polkadot/types/interfaces'
-import { BlockAndTime, JoyStruct, ThreadId, PostId } from './common'
+import { bool, u32, u64, Text, Option, Vec as Vector } from '@polkadot/types'
+import { BlockAndTime, ThreadId, PostId, JoyStructCustom, JoyStructDecorated } from './common'
+import { RegistryTypes } from '@polkadot/types/types'
+import AccountId from '@polkadot/types/generic/AccountId'
 
 export type ModerationActionType = {
   moderated_at: BlockAndTime
@@ -8,18 +9,13 @@ export type ModerationActionType = {
   rationale: Text
 }
 
-export class ModerationAction extends JoyStruct<ModerationActionType> {
-  constructor(value: ModerationActionType) {
-    super(
-      {
-        moderated_at: BlockAndTime,
-        moderator_id: GenericAccountId,
-        rationale: Text,
-      },
-      value
-    )
-  }
-
+export class ModerationAction extends JoyStructCustom({
+  moderated_at: BlockAndTime,
+  moderator_id: AccountId,
+  rationale: Text,
+})
+// FIXME: Make it JoyStructDecorated compatible
+{
   get moderated_at(): BlockAndTime {
     return this.getField('moderated_at')
   }
@@ -38,17 +34,12 @@ export type PostTextChangeType = {
   text: Text
 }
 
-export class PostTextChange extends JoyStruct<PostTextChangeType> {
-  constructor(value: PostTextChangeType) {
-    super(
-      {
-        expired_at: BlockAndTime,
-        text: Text,
-      },
-      value
-    )
-  }
-
+export class PostTextChange extends JoyStructCustom({
+  expired_at: BlockAndTime,
+  text: Text,
+})
+// FIXME: Make it JoyStructDecorated compatible
+{
   get expired_at(): BlockAndTime {
     return this.getField('expired_at')
   }
@@ -78,25 +69,12 @@ export type ChildPositionInParentCategoryType = {
   child_nr_in_parent_category: u32
 }
 
-export class ChildPositionInParentCategory extends JoyStruct<ChildPositionInParentCategoryType> {
-  constructor(value: ChildPositionInParentCategoryType) {
-    super(
-      {
-        parent_id: CategoryId,
-        child_nr_in_parent_category: u32,
-      },
-      value
-    )
-  }
-
-  get parent_id(): CategoryId {
-    return this.getField('parent_id')
-  }
-
-  get child_nr_in_parent_category(): u32 {
-    return this.getField('child_nr_in_parent_category')
-  }
-}
+export class ChildPositionInParentCategory
+  extends JoyStructDecorated({
+    parent_id: CategoryId,
+    child_nr_in_parent_category: u32,
+  })
+  implements ChildPositionInParentCategoryType {}
 
 export class OptionChildPositionInParentCategory extends Option.with(ChildPositionInParentCategory) {}
 
@@ -114,30 +92,21 @@ export type CategoryType = {
   moderator_id: AccountId
 }
 
-export class Category extends JoyStruct<CategoryType> {
-  constructor(value: CategoryType) {
-    super(
-      {
-        id: CategoryId,
-        title: Text,
-        description: Text,
-        created_at: BlockAndTime,
-        deleted: bool,
-        archived: bool,
-        num_direct_subcategories: u32,
-        num_direct_unmoderated_threads: u32,
-        num_direct_moderated_threads: u32,
-        position_in_parent_category: OptionChildPositionInParentCategory,
-        moderator_id: GenericAccountId,
-      },
-      value
-    )
-  }
-
-  static newEmpty(): Category {
-    return new Category({} as CategoryType)
-  }
-
+export class Category extends JoyStructCustom({
+  id: CategoryId,
+  title: Text,
+  description: Text,
+  created_at: BlockAndTime,
+  deleted: bool,
+  archived: bool,
+  num_direct_subcategories: u32,
+  num_direct_unmoderated_threads: u32,
+  num_direct_moderated_threads: u32,
+  position_in_parent_category: OptionChildPositionInParentCategory,
+  moderator_id: AccountId,
+})
+// FIXME: Make it JoyStructDecorated compatible
+{
   get id(): CategoryId {
     return this.getField('id')
   }
@@ -155,11 +124,11 @@ export class Category extends JoyStruct<CategoryType> {
   }
 
   get deleted(): boolean {
-    return this.getBoolean('deleted')
+    return this.getField('deleted').valueOf()
   }
 
   get archived(): boolean {
-    return this.getBoolean('archived')
+    return this.getField('archived').valueOf()
   }
 
   get num_direct_subcategories(): u32 {
@@ -175,7 +144,7 @@ export class Category extends JoyStruct<CategoryType> {
   }
 
   get num_threads_created(): u32 {
-    return new u32(this.num_direct_unmoderated_threads.add(this.num_direct_moderated_threads))
+    return new u32(this.registry, this.num_direct_unmoderated_threads.add(this.num_direct_moderated_threads))
   }
 
   get hasSubcategories(): boolean {
@@ -221,28 +190,19 @@ export type ThreadType = {
   author_id: AccountId
 }
 
-export class Thread extends JoyStruct<ThreadType> {
-  constructor(value: ThreadType) {
-    super(
-      {
-        id: ThreadId,
-        title: Text,
-        category_id: CategoryId,
-        nr_in_category: u32,
-        moderation: OptionModerationAction,
-        num_unmoderated_posts: u32,
-        num_moderated_posts: u32,
-        created_at: BlockAndTime,
-        author_id: GenericAccountId,
-      },
-      value
-    )
-  }
-
-  static newEmpty(): Thread {
-    return new Thread({} as ThreadType)
-  }
-
+export class Thread extends JoyStructCustom({
+  id: ThreadId,
+  title: Text,
+  category_id: CategoryId,
+  nr_in_category: u32,
+  moderation: OptionModerationAction,
+  num_unmoderated_posts: u32,
+  num_moderated_posts: u32,
+  created_at: BlockAndTime,
+  author_id: AccountId,
+})
+// FIXME: Make it JoyStructDecorated compatible
+{
   get id(): ThreadId {
     return this.getField('id')
   }
@@ -260,7 +220,7 @@ export class Thread extends JoyStruct<ThreadType> {
   }
 
   get moderation(): ModerationAction | undefined {
-    return this.unwrapOrUndefined('moderation')
+    return this.getField('moderation').unwrapOr(undefined)
   }
 
   get moderated(): boolean {
@@ -276,7 +236,7 @@ export class Thread extends JoyStruct<ThreadType> {
   }
 
   get num_posts_ever_created(): u32 {
-    return new u32(this.num_unmoderated_posts.add(this.num_moderated_posts))
+    return new u32(this.registry, this.num_unmoderated_posts.add(this.num_moderated_posts))
   }
 
   get created_at(): BlockAndTime {
@@ -300,27 +260,18 @@ export type PostType = {
 }
 
 // TODO deprectated: replaced w/ Post
-export class Post extends JoyStruct<PostType> {
-  constructor(value: PostType) {
-    super(
-      {
-        id: PostId,
-        thread_id: ThreadId,
-        nr_in_thread: u32,
-        current_text: Text,
-        moderation: OptionModerationAction,
-        text_change_history: VecPostTextChange,
-        created_at: BlockAndTime,
-        author_id: GenericAccountId,
-      },
-      value
-    )
-  }
-
-  static newEmpty(): Post {
-    return new Post({} as PostType)
-  }
-
+export class Post extends JoyStructCustom({
+  id: PostId,
+  thread_id: ThreadId,
+  nr_in_thread: u32,
+  current_text: Text,
+  moderation: OptionModerationAction,
+  text_change_history: VecPostTextChange,
+  created_at: BlockAndTime,
+  author_id: AccountId,
+})
+// FIXME: Make it JoyStructDecorated compatible
+{
   get id(): PostId {
     return this.getField('id')
   }
@@ -338,7 +289,7 @@ export class Post extends JoyStruct<PostType> {
   }
 
   get moderation(): ModerationAction | undefined {
-    return this.unwrapOrUndefined('moderation')
+    return this.getField('moderation').unwrapOr(undefined)
   }
 
   get moderated(): boolean {
@@ -366,19 +317,14 @@ export type ReplyType = {
 }
 
 // TODO deprectated: replaced w/ Post
-export class Reply extends JoyStruct<ReplyType> {
-  constructor(value: ReplyType) {
-    super(
-      {
-        owner: GenericAccountId,
-        thread_id: ThreadId,
-        text: Text,
-        moderation: OptionModerationAction,
-      },
-      value
-    )
-  }
-
+export class Reply extends JoyStructCustom({
+  owner: AccountId,
+  thread_id: ThreadId,
+  text: Text,
+  moderation: OptionModerationAction,
+})
+// FIXME: Make it JoyStructDecorated compatible
+{
   get owner(): AccountId {
     return this.getField('owner')
   }
@@ -392,7 +338,7 @@ export class Reply extends JoyStruct<ReplyType> {
   }
 
   get moderation(): ModerationAction | undefined {
-    return this.unwrapOrUndefined('moderation')
+    return this.getField('moderation').unwrapOr(undefined)
   }
 
   get moderated(): boolean {
@@ -400,20 +346,16 @@ export class Reply extends JoyStruct<ReplyType> {
   }
 }
 
-export function registerForumTypes() {
-  try {
-    getTypeRegistry().register({
-      PostTextChange,
-      ModerationAction,
-      ChildPositionInParentCategory,
-      CategoryId,
-      Category,
-      Thread,
-      Post,
-      ReplyId,
-      Reply,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of forum module', err)
-  }
+export const forumTypes: RegistryTypes = {
+  PostTextChange,
+  ModerationAction,
+  ChildPositionInParentCategory,
+  CategoryId,
+  Category,
+  Thread,
+  Post,
+  ReplyId,
+  Reply,
 }
+
+export default forumTypes

+ 132 - 316
types/src/hiring/index.ts

@@ -1,7 +1,7 @@
-import { getTypeRegistry, Null, u128, u64, u32, Vec, Option, Text } from '@polkadot/types'
-import { Enum } from '@polkadot/types/codec'
+import { Null, u128, u64, u32, Option, Text } from '@polkadot/types'
+import { BTreeSet } from '@polkadot/types/codec'
 import { BlockNumber, Balance } from '@polkadot/types/interfaces'
-import { JoyStruct, JoyEnum } from '../common'
+import { JoyEnum, JoyStructDecorated } from '../common'
 import { StakeId } from '../stake'
 
 import { GenericJoyStreamRoleSchema } from './schemas/role.schema.typings'
@@ -9,6 +9,7 @@ import { GenericJoyStreamRoleSchema } from './schemas/role.schema.typings'
 import ajv from 'ajv'
 
 import * as role_schema_json from './schemas/role.schema.json'
+import { RegistryTypes } from '@polkadot/types/types'
 
 export class ApplicationId extends u64 {}
 export class OpeningId extends u64 {}
@@ -25,9 +26,9 @@ export const ActivateOpeningAtKeys: { [k in keyof typeof ActivateOpeningAtDef]:
   ExactBlock: 'ExactBlock',
 } as const
 export type ActivateOpeningAtKey = keyof typeof ActivateOpeningAtDef
-// TODO: Replace with JoyEnum
-export class ActivateOpeningAt extends Enum.with(ActivateOpeningAtDef) {}
+export class ActivateOpeningAt extends JoyEnum(ActivateOpeningAtDef) {}
 
+// FIXME: Replace usages with isOfType, asType wherever possible
 export enum ApplicationDeactivationCauseKeys {
   External = 'External',
   Hired = 'Hired',
@@ -37,118 +38,74 @@ export enum ApplicationDeactivationCauseKeys {
   ReviewPeriodExpired = 'ReviewPeriodExpired',
   OpeningFilled = 'OpeningFilled',
 }
-
-export class ApplicationDeactivationCause extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      [
-        ApplicationDeactivationCauseKeys.External,
-        ApplicationDeactivationCauseKeys.Hired,
-        ApplicationDeactivationCauseKeys.NotHired,
-        ApplicationDeactivationCauseKeys.CrowdedOut,
-        ApplicationDeactivationCauseKeys.OpeningCancelled,
-        ApplicationDeactivationCauseKeys.ReviewPeriodExpired,
-        ApplicationDeactivationCauseKeys.OpeningFilled,
-      ],
-      value,
-      index
-    )
-  }
-}
+const ApplicationDeactivationCauseDef = {
+  External: Null,
+  Hired: Null,
+  NotHired: Null,
+  CrowdedOut: Null,
+  OpeningCancelled: Null,
+  ReviewPeriodExpired: Null,
+  OpeningFilled: Null,
+} as const
+export class ApplicationDeactivationCause extends JoyEnum(ApplicationDeactivationCauseDef) {}
 
 export type UnstakingApplicationStageType = {
   deactivation_initiated: BlockNumber
   cause: ApplicationDeactivationCause
 }
-export class UnstakingApplicationStage extends JoyStruct<UnstakingApplicationStageType> {
-  constructor(value?: UnstakingApplicationStageType) {
-    super(
-      {
-        deactivation_initiated: u32, // BlockNumber
-        cause: ApplicationDeactivationCause,
-      },
-      value
-    )
-  }
-
-  get cause(): ApplicationDeactivationCause {
-    return this.getField<ApplicationDeactivationCause>('cause')
-  }
-}
+export class UnstakingApplicationStage
+  extends JoyStructDecorated({
+    deactivation_initiated: u32, // BlockNumber
+    cause: ApplicationDeactivationCause,
+  })
+  implements UnstakingApplicationStageType {}
 
 export type InactiveApplicationStageType = {
   deactivation_initiated: BlockNumber
   deactivated: BlockNumber
   cause: ApplicationDeactivationCause
 }
-export class InactiveApplicationStage extends JoyStruct<InactiveApplicationStageType> {
-  constructor(value?: InactiveApplicationStageType) {
-    super(
-      {
-        deactivation_initiated: u32, // BlockNumber
-        deactivated: u32,
-        cause: ApplicationDeactivationCause,
-      },
-      value
-    )
-  }
-
-  get cause(): ApplicationDeactivationCause {
-    return this.getField<ApplicationDeactivationCause>('cause')
-  }
-}
+export class InactiveApplicationStage
+  extends JoyStructDecorated({
+    deactivation_initiated: u32, // BlockNumber
+    deactivated: u32,
+    cause: ApplicationDeactivationCause,
+  })
+  implements InactiveApplicationStageType {}
 
 export class ActiveApplicationStage extends Null {}
-
-// TODO: Find usages and replace with "JoyEnum-standard"
+// FIXME: Replace usages with isOfType, asType wherever possible
 export enum ApplicationStageKeys {
   Active = 'Active',
   Unstaking = 'Unstaking',
   Inactive = 'Inactive',
 }
-export class ApplicationStage extends JoyEnum({
+export const ApplicationStageDef = {
   Active: ActiveApplicationStage,
   Unstaking: UnstakingApplicationStage,
   Inactive: InactiveApplicationStage,
-} as const) {}
+} as const
+export class ApplicationStage extends JoyEnum(ApplicationStageDef) {}
 
 export type IApplicationRationingPolicy = {
   max_active_applicants: u32
 }
-export class ApplicationRationingPolicy extends JoyStruct<IApplicationRationingPolicy> {
-  constructor(value?: IApplicationRationingPolicy) {
-    super(
-      {
-        max_active_applicants: u32,
-      },
-      value
-    )
-  }
-
-  get max_active_applicants(): u32 {
-    return this.getField<u32>('max_active_applicants')
-  }
-}
+export class ApplicationRationingPolicy
+  extends JoyStructDecorated({
+    max_active_applicants: u32,
+  })
+  implements IApplicationRationingPolicy {}
 
 export type WaitingToBeingOpeningStageVariantType = {
   begins_at_block: BlockNumber
 }
-export class WaitingToBeingOpeningStageVariant extends JoyStruct<WaitingToBeingOpeningStageVariantType> {
-  constructor(value?: WaitingToBeingOpeningStageVariantType) {
-    super(
-      {
-        begins_at_block: u32,
-      },
-      value
-    )
-  }
-
-  get begins_at_block(): BlockNumber {
-    return this.getField<BlockNumber>('begins_at_block')
-  }
-}
+export class WaitingToBeingOpeningStageVariant
+  extends JoyStructDecorated({
+    begins_at_block: u32,
+  })
+  implements WaitingToBeingOpeningStageVariantType {}
 
-// TODO: Find usages and replace them with JoyEnum helpers
+// FIXME: Replace usages with isOfType, asType wherever possible
 export enum OpeningDeactivationCauseKeys {
   CancelledBeforeActivation = 'CancelledBeforeActivation',
   CancelledAcceptingApplications = 'CancelledAcceptingApplications',
@@ -156,62 +113,34 @@ export enum OpeningDeactivationCauseKeys {
   ReviewPeriodExpired = 'ReviewPeriodExpired',
   Filled = 'Filled',
 }
-
-class OpeningDeactivationCause_CancelledBeforeActivation extends Null {}
-class OpeningDeactivationCause_CancelledAcceptingApplications extends Null {}
-class OpeningDeactivationCause_CancelledInReviewPeriod extends Null {}
-class OpeningDeactivationCause_ReviewPeriodExpired extends Null {}
-class OpeningDeactivationCause_Filled extends Null {}
-
-export class OpeningDeactivationCause extends JoyEnum({
-  CancelledBeforeActivation: OpeningDeactivationCause_CancelledBeforeActivation,
-  CancelledAcceptingApplications: OpeningDeactivationCause_CancelledAcceptingApplications,
-  CancelledInReviewPeriod: OpeningDeactivationCause_CancelledInReviewPeriod,
-  ReviewPeriodExpired: OpeningDeactivationCause_ReviewPeriodExpired,
-  Filled: OpeningDeactivationCause_Filled,
-} as const) {}
+const OpeningDeactivationCauseDef = {
+  CancelledBeforeActivation: Null,
+  CancelledAcceptingApplications: Null,
+  CancelledInReviewPeriod: Null,
+  ReviewPeriodExpired: Null,
+  Filled: Null,
+} as const
+export class OpeningDeactivationCause extends JoyEnum(OpeningDeactivationCauseDef) {}
 
 export type IAcceptingApplications = {
   started_accepting_applicants_at_block: BlockNumber
 }
-export class AcceptingApplications extends JoyStruct<IAcceptingApplications> {
-  constructor(value?: IAcceptingApplications) {
-    super(
-      {
-        started_accepting_applicants_at_block: u32,
-      },
-      value
-    )
-  }
-
-  get started_accepting_applicants_at_block(): BlockNumber {
-    return this.getField<BlockNumber>('started_accepting_applicants_at_block')
-  }
-}
+export class AcceptingApplications
+  extends JoyStructDecorated({
+    started_accepting_applicants_at_block: u32,
+  })
+  implements IAcceptingApplications {}
 
 export type IReviewPeriod = {
   started_accepting_applicants_at_block: BlockNumber
   started_review_period_at_block: BlockNumber
 }
-export class ReviewPeriod extends JoyStruct<IReviewPeriod> {
-  constructor(value?: IReviewPeriod) {
-    super(
-      {
-        started_accepting_applicants_at_block: u32,
-        started_review_period_at_block: u32,
-      },
-      value
-    )
-  }
-
-  get started_accepting_applicants_at_block(): BlockNumber {
-    return this.getField<BlockNumber>('started_accepting_applicants_at_block')
-  }
-
-  get started_review_period_at_block(): BlockNumber {
-    return this.getField<BlockNumber>('started_review_period_at_block')
-  }
-}
+export class ReviewPeriod
+  extends JoyStructDecorated({
+    started_accepting_applicants_at_block: u32,
+    started_review_period_at_block: u32,
+  })
+  implements IReviewPeriod {}
 
 export type IDeactivated = {
   cause: OpeningDeactivationCause
@@ -219,35 +148,14 @@ export type IDeactivated = {
   started_accepting_applicants_at_block: BlockNumber
   started_review_period_at_block: Option<BlockNumber>
 }
-export class Deactivated extends JoyStruct<IDeactivated> {
-  constructor(value?: IDeactivated) {
-    super(
-      {
-        cause: OpeningDeactivationCause,
-        deactivated_at_block: u32,
-        started_accepting_applicants_at_block: u32,
-        started_review_period_at_block: Option.with(u32),
-      },
-      value
-    )
-  }
-
-  get cause(): OpeningDeactivationCause {
-    return this.getField<OpeningDeactivationCause>('cause')
-  }
-
-  get deactivated_at_block(): BlockNumber {
-    return this.getField<BlockNumber>('deactivated_at_block')
-  }
-
-  get started_accepting_applicants_at_block(): BlockNumber {
-    return this.getField<BlockNumber>('started_accepting_applicants_at_block')
-  }
-
-  get started_review_period_at_block(): BlockNumber {
-    return this.getField<BlockNumber>('started_review_period_at_block')
-  }
-}
+export class Deactivated
+  extends JoyStructDecorated({
+    cause: OpeningDeactivationCause,
+    deactivated_at_block: u32,
+    started_accepting_applicants_at_block: u32,
+    started_review_period_at_block: Option.with(u32),
+  })
+  implements IDeactivated {}
 
 export const ActiveOpeningStageDef = {
   AcceptingApplications: AcceptingApplications,
@@ -255,60 +163,48 @@ export const ActiveOpeningStageDef = {
   Deactivated: Deactivated,
 } as const
 export type ActiveOpeningStageKey = keyof typeof ActiveOpeningStageDef
-
 export class ActiveOpeningStage extends JoyEnum(ActiveOpeningStageDef) {}
 
 export type ActiveOpeningStageVariantType = {
   stage: ActiveOpeningStage
-  applications_added: Vec<ApplicationId> //BTreeSet<ApplicationId>,
+  applications_added: BTreeSet<ApplicationId>
   active_application_count: u32
   unstaking_application_count: u32
   deactivated_application_count: u32
 }
-export class ActiveOpeningStageVariant extends JoyStruct<ActiveOpeningStageVariantType> {
-  constructor(value?: ActiveOpeningStageVariantType) {
-    super(
-      {
-        stage: ActiveOpeningStage,
-        applications_added: Vec.with(ApplicationId), //BTreeSet<ApplicationId>,
-        active_application_count: u32,
-        unstaking_application_count: u32,
-        deactivated_application_count: u32,
-      },
-      value
-    )
-  }
-
-  get stage(): ActiveOpeningStage {
-    return this.getField<ActiveOpeningStage>('stage')
-  }
-
+export class ActiveOpeningStageVariant extends JoyStructDecorated({
+  stage: ActiveOpeningStage,
+  applications_added: BTreeSet.with(ApplicationId),
+  active_application_count: u32,
+  unstaking_application_count: u32,
+  deactivated_application_count: u32,
+}) {
   get is_active(): boolean {
     return this.stage.isOfType('AcceptingApplications')
   }
 }
 
-// TODO: Find usages and replace them with JoyEnum helpers
+// FIXME: Replace usages with isOfType, asType wherever possible
 export enum OpeningStageKeys {
   WaitingToBegin = 'WaitingToBegin',
   Active = 'Active',
 }
-
-export class OpeningStage extends JoyEnum({
+export const OpeningStageDef = {
   WaitingToBegin: WaitingToBeingOpeningStageVariant,
   Active: ActiveOpeningStageVariant,
-} as const) {}
+} as const
+export class OpeningStage extends JoyEnum(OpeningStageDef) {}
 
+// FIXME: Replace usages with isOfType, asType wherever possible
 export enum StakingAmountLimitModeKeys {
   AtLeast = 'AtLeast',
   Exact = 'Exact',
 }
-
-export class StakingAmountLimitMode extends Enum {
-  constructor(value?: any, index?: number) {
-    super([StakingAmountLimitModeKeys.AtLeast, StakingAmountLimitModeKeys.Exact], value, index)
-  }
-}
+export const StakingAmountLimitModeDef = {
+  AtLeast: Null,
+  Exact: Null,
+} as const
+export class StakingAmountLimitMode extends JoyEnum(StakingAmountLimitModeDef) {}
 
 export type IStakingPolicy = {
   amount: Balance
@@ -316,35 +212,14 @@ export type IStakingPolicy = {
   crowded_out_unstaking_period_length: Option<BlockNumber>
   review_period_expired_unstaking_period_length: Option<BlockNumber>
 }
-export class StakingPolicy extends JoyStruct<IStakingPolicy> {
-  constructor(value?: IStakingPolicy) {
-    super(
-      {
-        amount: u128,
-        amount_mode: StakingAmountLimitMode,
-        crowded_out_unstaking_period_length: Option.with(u32),
-        review_period_expired_unstaking_period_length: Option.with(u32),
-      },
-      value
-    )
-  }
-
-  get amount(): u128 {
-    return this.getField<u128>('amount')
-  }
-
-  get amount_mode(): StakingAmountLimitMode {
-    return this.getField<StakingAmountLimitMode>('amount_mode')
-  }
-
-  get crowded_out_unstaking_period_length(): Option<u32> {
-    return this.getField<Option<u32>>('crowded_out_unstaking_period_length')
-  }
-
-  get review_period_expired_unstaking_period_length(): Option<u32> {
-    return this.getField<Option<u32>>('review_period_expired_unstaking_period_length')
-  }
-}
+export class StakingPolicy
+  extends JoyStructDecorated({
+    amount: u128,
+    amount_mode: StakingAmountLimitMode,
+    crowded_out_unstaking_period_length: Option.with(u32),
+    review_period_expired_unstaking_period_length: Option.with(u32),
+  })
+  implements IStakingPolicy {}
 export const schemaValidator: ajv.ValidateFunction = new ajv({ allErrors: true }).compile(role_schema_json)
 
 const OpeningHRTFallback: GenericJoyStreamRoleSchema = {
@@ -373,24 +248,19 @@ export type IOpening = {
   human_readable_text: Text // Vec<u8>,
 }
 
-export class Opening extends JoyStruct<IOpening> {
-  constructor(value?: IOpening) {
-    super(
-      {
-        created: u32,
-        stage: OpeningStage,
-        max_review_period_length: u32,
-        application_rationing_policy: Option.with(ApplicationRationingPolicy),
-        application_staking_policy: Option.with(StakingPolicy),
-        role_staking_policy: Option.with(StakingPolicy),
-        human_readable_text: Text, // Vec.with(u8),
-      },
-      value
-    )
-  }
-
+export class Opening
+  extends JoyStructDecorated({
+    created: u32,
+    stage: OpeningStage,
+    max_review_period_length: u32,
+    application_rationing_policy: Option.with(ApplicationRationingPolicy),
+    application_staking_policy: Option.with(StakingPolicy),
+    role_staking_policy: Option.with(StakingPolicy),
+    human_readable_text: Text, // Vec.with(u8),
+  })
+  implements IOpening {
   parse_human_readable_text(): GenericJoyStreamRoleSchema | string | undefined {
-    const hrt = this.getField<Text>('human_readable_text')
+    const hrt = this.human_readable_text
 
     if (!hrt) {
       return undefined
@@ -421,34 +291,6 @@ export class Opening extends JoyStruct<IOpening> {
     return hrt
   }
 
-  get created(): BlockNumber {
-    return this.getField<BlockNumber>('created')
-  }
-
-  get stage(): OpeningStage {
-    return this.getField<OpeningStage>('stage')
-  }
-
-  get max_review_period_length(): BlockNumber {
-    return this.getField<BlockNumber>('max_review_period_length')
-  }
-
-  get application_rationing_policy(): Option<ApplicationRationingPolicy> {
-    return this.getField<Option<ApplicationRationingPolicy>>('application_rationing_policy')
-  }
-
-  get application_staking_policy(): Option<StakingPolicy> {
-    return this.getField<Option<StakingPolicy>>('application_staking_policy')
-  }
-
-  get role_staking_policy(): Option<StakingPolicy> {
-    return this.getField<Option<StakingPolicy>>('role_staking_policy')
-  }
-
-  get human_readable_text(): Text {
-    return this.getField<Text>('human_readable_text')
-  }
-
   get max_applicants(): number {
     const appPolicy = this.application_rationing_policy
     if (appPolicy.isNone) {
@@ -480,54 +322,28 @@ export type IApplication = {
   human_readable_text: Text
 }
 
-export class Application extends JoyStruct<IApplication> {
-  constructor(value?: IOpening) {
-    super(
-      {
-        opening_id: OpeningId,
-        application_index_in_opening: u32,
-        add_to_opening_in_block: u32,
-        active_role_staking_id: Option.with(StakeId),
-        active_application_staking_id: Option.with(StakeId),
-        stage: ApplicationStage,
-        human_readable_text: Text,
-      },
-      value
-    )
-  }
-
-  get stage(): ApplicationStage {
-    return this.getField<ApplicationStage>('stage')
-  }
-
-  get active_role_staking_id(): Option<StakeId> {
-    return this.getField<Option<StakeId>>('active_role_staking_id')
-  }
-
-  get active_application_staking_id(): Option<StakeId> {
-    return this.getField<Option<StakeId>>('active_application_staking_id')
-  }
-
-  get human_readable_text(): Text {
-    return this.getField<Text>('human_readable_text')
-  }
+export class Application
+  extends JoyStructDecorated({
+    opening_id: OpeningId,
+    application_index_in_opening: u32,
+    add_to_opening_in_block: u32,
+    active_role_staking_id: Option.with(StakeId),
+    active_application_staking_id: Option.with(StakeId),
+    stage: ApplicationStage,
+    human_readable_text: Text,
+  })
+  implements IApplication {}
+
+export const hiringTypes: RegistryTypes = {
+  ApplicationId: 'u64',
+  OpeningId: 'u64',
+  Application,
+  ApplicationStage,
+  ActivateOpeningAt,
+  ApplicationRationingPolicy,
+  OpeningStage,
+  StakingPolicy,
+  Opening,
 }
 
-export function registerHiringTypes() {
-  try {
-    getTypeRegistry().register({
-      ApplicationId: 'u64',
-      OpeningId: 'u64',
-      Application,
-      ApplicationStage,
-      // why the prefix? is there some other identically named type?
-      'hiring::ActivateOpeningAt': ActivateOpeningAt,
-      ApplicationRationingPolicy,
-      OpeningStage,
-      StakingPolicy,
-      Opening,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of hiring module', err)
-  }
-}
+export default hiringTypes

+ 35 - 41
types/src/index.ts

@@ -1,21 +1,20 @@
-import { getTypeRegistry } from '@polkadot/types'
-
-import * as common from './common'
-import * as members from './members'
-import * as council from './council'
-import * as roles from './roles'
-import * as forum from './forum'
-import * as stake from './stake'
-import * as mint from './mint'
-import * as recurringRewards from './recurring-rewards'
-import * as hiring from './hiring'
-import * as versionedStore from './versioned-store'
-import * as versionedStorePermissions from './versioned-store/permissions'
-import * as contentWorkingGroup from './content-working-group'
-import * as workingGroup from './working-group'
-import * as discovery from './discovery'
-import * as media from './media'
-import * as proposals from './proposals'
+import { RegistryTypes } from '@polkadot/types/types'
+import common from './common'
+import members from './members'
+import council from './council'
+import roles from './roles'
+import forum from './forum'
+import stake from './stake'
+import mint from './mint'
+import recurringRewards from './recurring-rewards'
+import hiring from './hiring'
+import versionedStore from './versioned-store'
+import versionedStorePermissions from './versioned-store/permissions'
+import contentWorkingGroup from './content-working-group'
+import workingGroup from './working-group'
+import discovery from './discovery'
+import media from './media'
+import proposals from './proposals'
 
 export {
   common,
@@ -36,27 +35,22 @@ export {
   proposals,
 }
 
-export function registerJoystreamTypes() {
-  const typeRegistry = getTypeRegistry()
-
-  typeRegistry.register({
-    MemoText: 'Text', // for the memo module
-  })
-
-  common.registerCommonTypes()
-  members.registerMembershipTypes()
-  council.registerCouncilAndElectionTypes()
-  roles.registerRolesTypes()
-  forum.registerForumTypes()
-  stake.registerStakeTypes()
-  mint.registerMintTypes()
-  recurringRewards.registerRecurringRewardsTypes()
-  hiring.registerHiringTypes()
-  versionedStore.registerVersionedStoreTypes()
-  versionedStorePermissions.registerVersionedStorePermissionsTypes()
-  contentWorkingGroup.registerContentWorkingGroupTypes()
-  workingGroup.registerWorkingGroupTypes()
-  discovery.registerDiscoveryTypes()
-  media.registerMediaTypes()
-  proposals.registerProposalTypes()
+export const types: RegistryTypes = {
+  MemoText: 'Text', // for the memo module
+  ...common,
+  ...members,
+  ...council,
+  ...roles,
+  ...forum,
+  ...stake,
+  ...mint,
+  ...recurringRewards,
+  ...hiring,
+  ...versionedStore,
+  ...versionedStorePermissions,
+  ...contentWorkingGroup,
+  ...workingGroup,
+  ...discovery,
+  ...media,
+  ...proposals,
 }

+ 47 - 114
types/src/media.ts

@@ -1,20 +1,21 @@
-import { Enum, Struct, Option, Vec as Vector, H256, BTreeMap, getTypeRegistry, u64, bool, Text } from '@polkadot/types'
-import { BlockAndTime } from './common'
+import { Option, Vec as Vector, BTreeMap, u64, bool, Text, Null } from '@polkadot/types'
+import { U8aFixed } from '@polkadot/types/codec'
+import { H256 } from '@polkadot/types/interfaces'
+import { BlockAndTime, JoyEnum, JoyStructDecorated } from './common'
 import { MemberId } from './members'
 import { StorageProviderId } from './working-group' // this should be in discovery really
-
 import { randomAsU8a } from '@polkadot/util-crypto'
 import { encodeAddress, decodeAddress } from '@polkadot/keyring'
-// import { u8aToString, stringToU8a } from '@polkadot/util';
+import { RegistryTypes, Registry } from '@polkadot/types/types'
 
-export class ContentId extends H256 {
-  static generate(): ContentId {
+export class ContentId extends U8aFixed implements H256 {
+  static generate(registry: Registry): ContentId {
     // randomAsU8a uses https://www.npmjs.com/package/tweetnacl#random-bytes-generation
-    return new ContentId(randomAsU8a())
+    return new ContentId(registry, randomAsU8a())
   }
 
-  static decode(contentId: string): ContentId {
-    return new ContentId(decodeAddress(contentId))
+  static decode(registry: Registry, contentId: string): ContentId {
+    return new ContentId(registry, decodeAddress(contentId))
   }
 
   static encode(contentId: Uint8Array): string {
@@ -32,120 +33,52 @@ export class DataObjectStorageRelationshipId extends u64 {}
 
 export class VecContentId extends Vector.with(ContentId) {}
 export class OptionVecContentId extends Option.with(VecContentId) {}
-export type LiaisonJudgementKey = 'Pending' | 'Accepted' | 'Rejected'
-
-export class LiaisonJudgement extends Enum {
-  constructor(value?: LiaisonJudgementKey) {
-    super(['Pending', 'Accepted', 'Rejected'], value)
-  }
-}
-
-export class DataObject extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        owner: MemberId,
-        added_at: BlockAndTime,
-        type_id: DataObjectTypeId,
-        size: u64,
-        liaison: StorageProviderId,
-        liaison_judgement: LiaisonJudgement,
-        ipfs_content_id: Text,
-      },
-      value
-    )
-  }
-
-  get owner(): MemberId {
-    return this.get('owner') as MemberId
-  }
-
-  get added_at(): BlockAndTime {
-    return this.get('added_at') as BlockAndTime
-  }
-
-  get type_id(): DataObjectTypeId {
-    return this.get('type_id') as DataObjectTypeId
-  }
 
+export const LiaisonJudgementDef = {
+  Pending: Null,
+  Accepted: Null,
+  Rejected: Null,
+} as const
+export type LiaisonJudgementKey = keyof typeof LiaisonJudgementDef
+export class LiaisonJudgement extends JoyEnum(LiaisonJudgementDef) {}
+
+export class DataObject extends JoyStructDecorated({
+  owner: MemberId,
+  added_at: BlockAndTime,
+  type_id: DataObjectTypeId,
+  size: u64,
+  liaison: StorageProviderId,
+  liaison_judgement: LiaisonJudgement,
+  ipfs_content_id: Text,
+}) {
   /** Actually it's 'size', but 'size' is already reserved by a parent class. */
   get size_in_bytes(): u64 {
     return this.get('size') as u64
   }
-
-  get liaison(): StorageProviderId {
-    return this.get('liaison') as StorageProviderId
-  }
-
-  get liaison_judgement(): LiaisonJudgement {
-    return this.get('liaison_judgement') as LiaisonJudgement
-  }
-
-  get ipfs_content_id(): Text {
-    return this.get('ipfs_content_id') as Text
-  }
 }
 
-export class DataObjectStorageRelationship extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        content_id: ContentId,
-        storage_provider: StorageProviderId,
-        ready: bool,
-      },
-      value
-    )
-  }
+export class DataObjectStorageRelationship extends JoyStructDecorated({
+  content_id: ContentId,
+  storage_provider: StorageProviderId,
+  ready: bool,
+}) {}
 
-  get content_id(): ContentId {
-    return this.get('content_id') as ContentId
-  }
-
-  get storage_provider(): StorageProviderId {
-    return this.get('storage_provider') as StorageProviderId
-  }
-
-  get ready(): bool {
-    return this.get('ready') as bool
-  }
-}
-
-export class DataObjectType extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        description: Text,
-        active: bool,
-      },
-      value
-    )
-  }
-
-  get description(): Text {
-    return this.get('description') as Text
-  }
-
-  get active(): bool {
-    return this.get('active') as bool
-  }
-}
+export class DataObjectType extends JoyStructDecorated({
+  description: Text,
+  active: bool,
+}) {}
 
 export class DataObjectsMap extends BTreeMap.with(ContentId, DataObject) {}
 
-export function registerMediaTypes() {
-  try {
-    getTypeRegistry().register({
-      ContentId,
-      LiaisonJudgement,
-      DataObject,
-      DataObjectStorageRelationshipId,
-      DataObjectStorageRelationship,
-      DataObjectTypeId,
-      DataObjectType,
-      DataObjectsMap,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of media module', err)
-  }
+export const mediaTypes: RegistryTypes = {
+  ContentId,
+  LiaisonJudgement,
+  DataObject,
+  DataObjectStorageRelationshipId,
+  DataObjectStorageRelationship,
+  DataObjectTypeId,
+  DataObjectType,
+  DataObjectsMap,
 }
+
+export default mediaTypes

+ 43 - 205
types/src/members.ts

@@ -1,20 +1,8 @@
-import {
-  Enum,
-  getTypeRegistry,
-  Option,
-  Struct,
-  Null,
-  bool,
-  u32,
-  u64,
-  u128,
-  Text,
-  GenericAccountId,
-  Vec,
-} from '@polkadot/types'
-import { BlockNumber, Moment, BalanceOf } from '@polkadot/types/interfaces'
-import { OptionText, JoyStruct } from './common'
-import AccountId from '@polkadot/types/primitive/Generic/AccountId'
+import { Option, Null, bool, u32, u64, u128, Text } from '@polkadot/types'
+import { BlockNumber, Moment } from '@polkadot/types/interfaces'
+import AccountId from '@polkadot/types/generic/AccountId'
+import { RegistryTypes } from '@polkadot/types/types'
+import { JoyEnum, JoyStructDecorated } from './common'
 
 export class MemberId extends u64 {}
 export class PaidTermId extends u64 {}
@@ -22,64 +10,15 @@ export class SubscriptionId extends u64 {}
 export class ActorId extends u64 {}
 
 export class Paid extends PaidTermId {}
-export class Screening extends GenericAccountId {}
+export class Screening extends AccountId {}
 export class Genesis extends Null {}
-export class EntryMethod extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Paid,
-        Screening,
-        Genesis,
-      },
-      value,
-      index
-    )
-  }
-}
-
-export enum RoleKeys {
-  StorageProvider = 'StorageProvider',
-  ChannelOwner = 'ChannelOwner',
-  CuratorLead = 'CuratorLead',
-  Curator = 'Curator',
-}
-
-export class Role extends Enum {
-  constructor(value?: any, index?: number) {
-    super([RoleKeys.StorageProvider, RoleKeys.ChannelOwner, RoleKeys.CuratorLead, RoleKeys.Curator], value, index)
-  }
-}
-
-export class ActorInRole extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        role: Role,
-        actor_id: ActorId,
-      },
-      value
-    )
-  }
-
-  get role(): Role {
-    return this.get('role') as Role
-  }
-
-  get actor_id(): ActorId {
-    return this.get('actor_id') as ActorId
-  }
-
-  get isContentLead(): boolean {
-    return this.role.eq(RoleKeys.CuratorLead)
-  }
+export class EntryMethod extends JoyEnum({
+  Paid,
+  Screening,
+  Genesis,
+}) {}
 
-  get isCurator(): boolean {
-    return this.role.eq(RoleKeys.Curator)
-  }
-}
-
-export type IProfile = {
+export type IMembership = {
   handle: Text
   avatar_uri: Text
   about: Text
@@ -90,136 +29,35 @@ export type IProfile = {
   subscription: Option<SubscriptionId>
   root_account: AccountId
   controller_account: AccountId
-  roles: Vec<ActorInRole>
-}
-export class Profile extends JoyStruct<IProfile> {
-  constructor(value?: IProfile) {
-    super(
-      {
-        handle: Text,
-        avatar_uri: Text,
-        about: Text,
-        registered_at_block: u32,
-        registered_at_time: u64,
-        entry: EntryMethod,
-        suspended: bool,
-        subscription: Option.with(SubscriptionId),
-        root_account: AccountId,
-        controller_account: AccountId,
-        roles: Vec.with(ActorInRole),
-      },
-      value
-    )
-  }
-
-  get handle(): Text {
-    return this.get('handle') as Text
-  }
-
-  get avatar_uri(): Text {
-    return this.get('avatar_uri') as Text
-  }
-
-  get about(): Text {
-    return this.get('about') as Text
-  }
-
-  get registered_at_block(): u32 {
-    return this.get('registered_at_block') as u32
-  }
-
-  get registered_at_time(): u64 {
-    return this.get('registered_at_time') as u64
-  }
-
-  get entry(): EntryMethod {
-    return this.get('entry') as EntryMethod
-  }
-
-  get suspended(): bool {
-    return this.get('suspended') as bool
-  }
-
-  get subscription(): Option<SubscriptionId> {
-    return this.get('subscription') as Option<SubscriptionId>
-  }
-
-  get root_account(): AccountId {
-    return this.get('root_account') as AccountId
-  }
-
-  get controller_account(): AccountId {
-    return this.get('controller_account') as AccountId
-  }
-
-  get roles(): Vec<ActorInRole> {
-    return this.get('roles') as Vec<ActorInRole>
-  }
-}
-
-export class UserInfo extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        handle: OptionText,
-        avatar_uri: OptionText,
-        about: OptionText,
-      },
-      value
-    )
-  }
-}
-
-export type CheckedUserInfo = {
-  handle: Text
-  avatar_uri: Text
-  about: Text
-}
-
-export class PaidMembershipTerms extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        fee: u128, // BalanceOf
-        text: Text,
-      },
-      value
-    )
-  }
-
-  get fee(): BalanceOf {
-    return this.get('fee') as BalanceOf
-  }
-
-  get text(): Text {
-    return this.get('text') as Text
-  }
-}
-
-export function registerMembershipTypes() {
-  try {
-    const typeRegistry = getTypeRegistry()
-    typeRegistry.register({
-      EntryMethod,
-      MemberId,
-      PaidTermId,
-      SubscriptionId,
-      Profile,
-      UserInfo,
-      CheckedUserInfo: {
-        handle: 'Text',
-        avatar_uri: 'Text',
-        about: 'Text',
-      },
-      PaidMembershipTerms: {
-        fee: 'BalanceOf',
-        text: 'Text',
-      },
-      Role,
-      ActorId,
-      ActorInRole,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of membership module', err)
-  }
 }
+export class Membership
+  extends JoyStructDecorated({
+    handle: Text,
+    avatar_uri: Text,
+    about: Text,
+    registered_at_block: u32,
+    registered_at_time: u64,
+    entry: EntryMethod,
+    suspended: bool,
+    subscription: Option.with(SubscriptionId),
+    root_account: AccountId,
+    controller_account: AccountId,
+  })
+  implements IMembership {}
+
+export class PaidMembershipTerms extends JoyStructDecorated({
+  fee: u128, // BalanceOf
+  text: Text,
+}) {}
+
+export const membersTypes: RegistryTypes = {
+  EntryMethod,
+  MemberId,
+  PaidTermId,
+  SubscriptionId,
+  Membership,
+  PaidMembershipTerms,
+  ActorId,
+}
+
+export default membersTypes

+ 32 - 63
types/src/mint/index.ts

@@ -1,6 +1,7 @@
-import { getTypeRegistry, u32, u64, u128, Option, Enum } from '@polkadot/types'
+import { u32, u64, u128, Option } from '@polkadot/types'
 import { Balance, BlockNumber } from '@polkadot/types/interfaces'
-import { JoyStruct } from '../common'
+import { JoyEnum, JoyStructDecorated } from '../common'
+import { RegistryTypes } from '@polkadot/types/types'
 
 export class MintId extends u64 {}
 
@@ -8,51 +9,29 @@ export class Setting extends u128 {}
 export class Adding extends u128 {}
 export class Reducing extends u128 {}
 
-export class AdjustCapacityBy extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Setting,
-        Adding,
-        Reducing,
-      },
-      value,
-      index
-    )
-  }
-}
+export class AdjustCapacityBy extends JoyEnum({ Setting, Adding, Reducing } as const) {}
 
 export type IAdjustOnInterval = {
   block_interval: BlockNumber
   adjustment_type: AdjustCapacityBy
 }
-export class AdjustOnInterval extends JoyStruct<IAdjustOnInterval> {
-  constructor(value?: IAdjustOnInterval) {
-    super(
-      {
-        block_interval: u32,
-        adjustment_type: AdjustCapacityBy,
-      },
-      value
-    )
-  }
-}
+export class AdjustOnInterval
+  extends JoyStructDecorated({
+    block_interval: u32,
+    adjustment_type: AdjustCapacityBy,
+  })
+  implements IAdjustOnInterval {}
 
 export type INextAdjustment = {
   adjustment: AdjustOnInterval
   at_block: BlockNumber
 }
-export class NextAdjustment extends JoyStruct<INextAdjustment> {
-  constructor(value?: INextAdjustment) {
-    super(
-      {
-        adjustment: AdjustOnInterval,
-        at_block: u32,
-      },
-      value
-    )
-  }
-}
+export class NextAdjustment
+  extends JoyStructDecorated({
+    adjustment: AdjustOnInterval,
+    at_block: u32,
+  })
+  implements INextAdjustment {}
 
 export type IMint = {
   capacity: Balance
@@ -60,30 +39,20 @@ export type IMint = {
   created_at: BlockNumber
   total_minted: Balance
 }
-export class Mint extends JoyStruct<IMint> {
-  constructor(value?: IMint) {
-    super(
-      {
-        capacity: u128,
-        next_adjustment: Option.with(NextAdjustment),
-        created_at: u32,
-        total_minted: u128,
-      },
-      value
-    )
-  }
-}
+export class Mint
+  extends JoyStructDecorated({
+    capacity: u128,
+    next_adjustment: Option.with(NextAdjustment),
+    created_at: u32,
+    total_minted: u128,
+  })
+  implements IMint {}
 
-export function registerMintTypes() {
-  try {
-    getTypeRegistry().register({
-      MintId: 'u64',
-      Mint,
-      MintBalanceOf: 'Balance',
-      BalanceOfMint: 'Balance',
-      'minting::BalanceOf': 'Balance',
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of mint module', err)
-  }
-}
+export const mintTypes: RegistryTypes = {
+  MintId: 'u64',
+  Mint,
+  MintBalanceOf: 'Balance',
+  BalanceOfMint: 'Balance',
+  'minting::BalanceOf': 'Balance',
+}
+export default mintTypes

+ 172 - 485
types/src/proposals.ts

@@ -1,21 +1,8 @@
-import {
-  Text,
-  u32,
-  Enum,
-  getTypeRegistry,
-  Tuple,
-  GenericAccountId,
-  u8,
-  Vec,
-  Option,
-  Struct,
-  Null,
-  Bytes,
-} from '@polkadot/types'
-import { bool } from '@polkadot/types/primitive'
+import { Text, u32, Tuple, u8, Vec, Option, Null, Bytes } from '@polkadot/types'
+import { bool, u128 } from '@polkadot/types/primitive'
 import { BlockNumber, Balance } from '@polkadot/types/interfaces'
-import AccountId from '@polkadot/types/primitive/Generic/AccountId'
-import { ThreadId, JoyStruct, WorkingGroup } from './common'
+import AccountId from '@polkadot/types/generic/AccountId'
+import { ThreadId, WorkingGroup, JoyEnum, JoyStructDecorated } from './common'
 import { MemberId } from './members'
 import { RoleParameters } from './roles'
 import { StakeId } from './stake'
@@ -30,110 +17,42 @@ export type IVotingResults = {
   slashes: u32
 }
 
-export class VotingResults extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        abstensions: 'u32',
-        approvals: 'u32',
-        rejections: 'u32',
-        slashes: 'u32',
-      },
-      value
-    )
-  }
-}
+export class VotingResults extends JoyStructDecorated({
+  abstensions: u32,
+  approvals: u32,
+  rejections: u32,
+  slashes: u32,
+}) {}
 
 export type ProposalParametersType = {
   // During this period, votes can be accepted
   votingPeriod: BlockNumber
-
   /* A pause before execution of the approved proposal. Zero means approved proposal would be
      executed immediately. */
   gracePeriod: BlockNumber
-
   // Quorum percentage of approving voters required to pass the proposal.
   approvalQuorumPercentage: u32
-
   // Approval votes percentage threshold to pass the proposal.
   approvalThresholdPercentage: u32
-
   // Quorum percentage of voters required to slash the proposal.
   slashingQuorumPercentage: u32
-
   // Slashing votes percentage threshold to slash the proposal.
   slashingThresholdPercentage: u32
-
   // Proposal stake
-  requiredStake: Balance
+  requiredStake: Option<Balance>
 }
 
-class ProposalParameters extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        // During this period, votes can be accepted
-        votingPeriod: 'BlockNumber',
-
-        /* A pause before execution of the approved proposal. Zero means approved proposal would be
-     executed immediately. */
-        gracePeriod: 'BlockNumber',
-
-        // Quorum percentage of approving voters required to pass the proposal.
-        approvalQuorumPercentage: 'u32',
-
-        // Approval votes percentage threshold to pass the proposal.
-        approvalThresholdPercentage: 'u32',
-
-        // Quorum percentage of voters required to slash the proposal.
-        slashingQuorumPercentage: 'u32',
-
-        // Slashing votes percentage threshold to slash the proposal.
-        slashingThresholdPercentage: 'u32',
-
-        // Proposal stake
-        requiredStake: 'Option<Balance>',
-      },
-      value
-    )
-  }
-
-  // During this period, votes can be accepted
-  get votingPeriod(): BlockNumber {
-    return this.get('votingPeriod') as BlockNumber
-  }
-
-  /* A pause before execution of the approved proposal. Zero means approved proposal would be
-     executed immediately. */
-  get gracePeriod(): BlockNumber {
-    return this.get('gracePeriod') as BlockNumber
-  }
-
-  // Quorum percentage of approving voters required to pass the proposal.
-  get approvalQuorumPercentage(): u32 {
-    return this.get('approvalQuorumPercentage') as u32
-  }
-
-  // Approval votes percentage threshold to pass the proposal.
-  get approvalThresholdPercentage(): u32 {
-    return this.get('approvalThresholdPercentage') as u32
-  }
-
-  // Quorum percentage of voters required to slash the proposal.
-  get slashingQuorumPercentage(): u32 {
-    return this.get('slashingQuorumPercentage') as u32
-  }
-
-  // Slashing votes percentage threshold to slash the proposal.
-  get slashingThresholdPercentage(): u32 {
-    return this.get('slashingThresholdPercentage') as u32
-  }
-
-  // Proposal stake
-  get requiredStake(): Option<Balance> {
-    return this.get('requiredStake') as Option<Balance>
-  }
-}
+export class ProposalParameters
+  extends JoyStructDecorated({
+    votingPeriod: u32,
+    gracePeriod: u32,
+    approvalQuorumPercentage: u32,
+    approvalThresholdPercentage: u32,
+    slashingQuorumPercentage: u32,
+    slashingThresholdPercentage: u32,
+    requiredStake: Option.with(u128),
+  })
+  implements ProposalParametersType {}
 
 export type IProposal = {
   parameters: ProposalParameters
@@ -163,70 +82,35 @@ export type IActiveStake = {
   stake_id: StakeId
   source_account_id: AccountId
 }
-export class ActiveStake extends JoyStruct<IActiveStake> {
-  constructor(value?: IActiveStake) {
-    super(
-      {
-        stakeId: StakeId,
-        sourceAccountId: GenericAccountId,
-      },
-      value
-    )
-  }
-}
+export class ActiveStake extends JoyStructDecorated({ stake_id: StakeId, source_account_id: AccountId })
+  implements IActiveStake {}
 
-export class ExecutionFailedStatus extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        error: 'Vec<u8>',
-      },
-      value
-    )
-  }
-
-  get error() {
-    return this.get('error') as Vec<u8>
-  }
-}
+export class ExecutionFailedStatus extends JoyStructDecorated({
+  error: Vec.with(u8),
+}) {}
 
 class ExecutionFailed extends ExecutionFailedStatus {}
 
-export type ApprovedProposalStatuses = 'PendingExecution' | 'Executed' | 'ExecutionFailed'
-
-export class ApprovedProposalStatus extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        PendingExecution: Null,
-        Executed: Null,
-        ExecutionFailed,
-      },
-      value,
-      index
-    )
-  }
-}
+export const ApprovedProposalDef = {
+  PendingExecution: Null,
+  Executed: Null,
+  ExecutionFailed,
+} as const
+export type ApprovedProposalStatuses = keyof typeof ApprovedProposalDef
+export class ApprovedProposalStatus extends JoyEnum(ApprovedProposalDef) {}
+
 export class Approved extends ApprovedProposalStatus {}
 
-export type ProposalDecisionStatuses = 'Canceled' | 'Vetoed' | 'Rejected' | 'Slashed' | 'Expired' | 'Approved'
-
-export class ProposalDecisionStatus extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Canceled: Null,
-        Vetoed: Null,
-        Rejected: Null,
-        Slashed: Null,
-        Expired: Null,
-        Approved,
-      },
-      value,
-      index
-    )
-  }
-}
+export const ProposalDecisionStatusesDef = {
+  Canceled: Null,
+  Vetoed: Null,
+  Rejected: Null,
+  Slashed: Null,
+  Expired: Null,
+  Approved,
+} as const
+export type ProposalDecisionStatuses = keyof typeof ProposalDecisionStatusesDef
+export class ProposalDecisionStatus extends JoyEnum(ProposalDecisionStatusesDef) {}
 
 export type IFinalizationData = {
   proposalStatus: ProposalDecisionStatus
@@ -235,209 +119,87 @@ export type IFinalizationData = {
   stakeDataAfterUnstakingError: Option<ActiveStake>
 }
 
-export class FinalizationData extends JoyStruct<IFinalizationData> {
-  constructor(value?: IFinalizationData) {
-    super(
-      {
-        proposalStatus: ProposalDecisionStatus,
-        finalizedAt: u32,
-        encodedUnstakingErrorDueToBrokenRuntime: Option.with(Vec.with(u8)),
-        stakeDataAfterUnstakingError: Option.with(ActiveStake),
-      },
-      value
-    )
-  }
-}
+export class FinalizationData
+  // FIXME: Snake case for consistency?
+  extends JoyStructDecorated({
+    proposalStatus: ProposalDecisionStatus,
+    finalizedAt: u32,
+    encodedUnstakingErrorDueToBrokenRuntime: Option.with(Vec.with(u8)),
+    stakeDataAfterUnstakingError: Option.with(ActiveStake),
+  })
+  implements IFinalizationData {}
 
 export class Active extends Option.with(ActiveStake) {}
 export class Finalized extends FinalizationData {}
 
-export class ProposalStatus extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Active,
-        Finalized,
-      },
-      value,
-      index
-    )
-  }
-}
+export class ProposalStatus extends JoyEnum({
+  Active,
+  Finalized,
+} as const) {}
 
 export const VoteKinds = ['Approve', 'Reject', 'Slash', 'Abstain'] as const
-
-export class VoteKind extends Enum {
-  constructor(value?: any, index?: number) {
-    super(['Approve', 'Reject', 'Slash', 'Abstain'], value, index)
-  }
-}
+export type VoteKindKey = typeof VoteKinds[number]
+export const VoteKindDef = {
+  Approve: Null,
+  Reject: Null,
+  Slash: Null,
+  Abstain: Null,
+} as const
+export class VoteKind extends JoyEnum(VoteKindDef) {}
 
 export type ProposalVotes = [MemberId, VoteKind][]
 
 export class ProposalId extends u32 {}
 
-export class SpendingParams extends Tuple {
-  constructor(value?: any) {
-    super(['Balance', 'AccountId'], value)
-  }
-}
+export class SpendingParams extends Tuple.with(['Balance', 'AccountId']) {}
 
-class SetLeadParams extends Tuple {
-  constructor(value?: any) {
-    super([MemberId, AccountId], value)
-  }
-}
+class SetLeadParams extends Tuple.with([MemberId, AccountId]) {}
 
 export class SetLead extends Option.with(SetLeadParams) {}
 
-export class Proposal extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        // Proposals parameter, characterize different proposal types.
-        parameters: ProposalParameters,
-
-        // Identifier of member proposing.
-        proposerId: MemberId,
-
-        // Proposal description
-        title: Text,
-
-        // Proposal body
-        description: Text,
-
-        // When it was created.
-        createdAt: 'BlockNumber',
-
-        /// Current proposal status
-        status: ProposalStatus,
-
-        /// Curring voting result for the proposal
-        votingResults: VotingResults,
-      },
-      value
-    )
-  }
-
-  get parameters(): ProposalParameters {
-    return this.get('parameters') as ProposalParameters
-  }
-
-  get proposerId(): MemberId {
-    return this.get('proposerId') as MemberId
-  }
-
-  get title(): Text {
-    return this.get('description') as Text
-  }
-
-  get description(): Text {
-    return this.get('description') as Text
-  }
-
-  get createdAt(): BlockNumber {
-    return this.get('createdAt') as BlockNumber
-  }
-
-  get status(): ProposalStatus {
-    return this.get('status') as ProposalStatus
-  }
-
-  get votingResults(): VotingResults {
-    return this.get('votingResults') as VotingResults
-  }
-}
-
-export class ThreadCounter extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        author_id: MemberId,
-        counter: 'u32',
-      },
-      value
-    )
-  }
-
-  get author_id(): MemberId {
-    return this.get('author_id') as MemberId
-  }
-
-  get counter(): u32 {
-    return this.get('counter') as u32
-  }
-}
-
-export class DiscussionThread extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        title: Bytes,
-        created_at: 'BlockNumber',
-        author_id: MemberId,
-      },
-      value
-    )
-  }
-
-  get title(): Bytes {
-    return this.get('title') as Bytes
-  }
-
-  get created_at(): BlockNumber {
-    return this.get('created_ad') as BlockNumber
-  }
-
-  get author_id(): MemberId {
-    return this.get('author_id') as MemberId
-  }
-}
-
-export class DiscussionPost extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        text: Bytes,
-        /// When post was added.
-        created_at: 'BlockNumber',
-        /// When post was updated last time.
-        updated_at: 'BlockNumber',
-        /// Author of the post.
-        author_id: MemberId,
-        /// Parent thread id for this post
-        thread_id: ThreadId,
-        /// Defines how many times this post was edited. Zero on creation.
-        edition_number: u32,
-      },
-      value
-    )
-  }
-
-  get text(): Bytes {
-    return this.get('text') as Bytes
-  }
-
-  get created_at(): BlockNumber {
-    return this.get('created_at') as BlockNumber
-  }
-
-  get updated_at(): BlockNumber {
-    return this.get('updated_at') as BlockNumber
-  }
-
-  get author_id(): MemberId {
-    return this.get('author_id') as MemberId
-  }
-
-  get thread_id(): ThreadId {
-    return this.get('thread_id') as ThreadId
-  }
-
-  get edition_number(): u32 {
-    return this.get('edition_number') as u32
-  }
-}
+export class Proposal
+  // FIXME: Snake case for consistency?
+  extends JoyStructDecorated({
+    // Proposals parameter, characterize different proposal types.
+    parameters: ProposalParameters,
+    // Identifier of member proposing.
+    proposerId: MemberId,
+    // Proposal description
+    title: Text,
+    // Proposal body
+    description: Text,
+    // When it was created.
+    createdAt: u32, // BlockNumber
+    /// Current proposal status
+    status: ProposalStatus,
+    /// Curring voting result for the proposal
+    votingResults: VotingResults,
+  }) {}
+
+export class ThreadCounter extends JoyStructDecorated({
+  author_id: MemberId,
+  counter: u32,
+}) {}
+
+export class DiscussionThread extends JoyStructDecorated({
+  title: Bytes,
+  created_at: u32, // BlockNumber
+  author_id: MemberId,
+}) {}
+
+export class DiscussionPost extends JoyStructDecorated({
+  text: Bytes,
+  /// When post was added.
+  created_at: u32, // BlockNumber
+  /// When post was updated last time.
+  updated_at: u32, // BlockNumber
+  /// Author of the post.
+  author_id: MemberId,
+  /// Parent thread id for this post
+  thread_id: ThreadId,
+  /// Defines how many times this post was edited. Zero on creation.
+  edition_number: u32,
+}) {}
 
 export type IAddOpeningParameters = {
   activate_at: ActivateOpeningAt
@@ -446,35 +208,14 @@ export type IAddOpeningParameters = {
   working_group: WorkingGroup
 }
 
-export class AddOpeningParameters extends JoyStruct<IAddOpeningParameters> {
-  constructor(value?: IAddOpeningParameters) {
-    super(
-      {
-        activate_at: ActivateOpeningAt,
-        commitment: WorkingGroupOpeningPolicyCommitment,
-        human_readable_text: Bytes,
-        working_group: WorkingGroup,
-      },
-      value
-    )
-  }
-
-  get activate_at(): ActivateOpeningAt {
-    return this.getField<ActivateOpeningAt>('activate_at')
-  }
-
-  get commitment(): WorkingGroupOpeningPolicyCommitment {
-    return this.getField<WorkingGroupOpeningPolicyCommitment>('commitment')
-  }
-
-  get human_readable_text(): Bytes {
-    return this.getField<Bytes>('human_readable_text')
-  }
-
-  get working_group(): WorkingGroup {
-    return this.getField<WorkingGroup>('working_group')
-  }
-}
+export class AddOpeningParameters
+  extends JoyStructDecorated({
+    activate_at: ActivateOpeningAt,
+    commitment: WorkingGroupOpeningPolicyCommitment,
+    human_readable_text: Bytes,
+    working_group: WorkingGroup,
+  })
+  implements IAddOpeningParameters {}
 
 export type IFillOpeningParameters = {
   opening_id: OpeningId
@@ -483,35 +224,14 @@ export type IFillOpeningParameters = {
   working_group: WorkingGroup
 }
 
-export class FillOpeningParameters extends JoyStruct<IFillOpeningParameters> {
-  constructor(value?: IFillOpeningParameters) {
-    super(
-      {
-        opening_id: OpeningId,
-        successful_application_id: ApplicationId,
-        reward_policy: Option.with(RewardPolicy),
-        working_group: WorkingGroup,
-      },
-      value
-    )
-  }
-
-  get opening_id(): OpeningId {
-    return this.getField<OpeningId>('opening_id')
-  }
-
-  get successful_application_id(): ApplicationId {
-    return this.getField<ApplicationId>('successful_application_id')
-  }
-
-  get reward_policy(): Option<RewardPolicy> {
-    return this.getField<Option<RewardPolicy>>('reward_policy')
-  }
-
-  get working_group(): WorkingGroup {
-    return this.getField<WorkingGroup>('working_group')
-  }
-}
+export class FillOpeningParameters
+  extends JoyStructDecorated({
+    opening_id: OpeningId,
+    successful_application_id: ApplicationId,
+    reward_policy: Option.with(RewardPolicy),
+    working_group: WorkingGroup,
+  })
+  implements IFillOpeningParameters {}
 
 export type ITerminateRoleParameters = {
   worker_id: WorkerId
@@ -520,84 +240,51 @@ export type ITerminateRoleParameters = {
   working_group: WorkingGroup
 }
 
-export class TerminateRoleParameters extends JoyStruct<ITerminateRoleParameters> {
-  constructor(value?: ITerminateRoleParameters) {
-    super(
-      {
-        worker_id: WorkerId,
-        rationale: Bytes,
-        slash: bool,
-        working_group: WorkingGroup,
-      },
-      value
-    )
-  }
-
-  get worker_id(): WorkerId {
-    return this.getField<WorkerId>('worker_id')
-  }
-
-  get rationale(): Bytes {
-    return this.getField<Bytes>('rationale')
-  }
-
-  get slash(): bool {
-    return this.getField<bool>('slash')
-  }
-
-  get working_group(): WorkingGroup {
-    return this.getField<WorkingGroup>('working_group')
-  }
-}
-
-export class ProposalDetails extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Text: 'Text',
-        RuntimeUpgrade: 'Vec<u8>',
-        SetElectionParameters: ElectionParameters,
-        Spending: SpendingParams,
-        SetLead: SetLead,
-        SetContentWorkingGroupMintCapacity: 'Balance',
-        EvictStorageProvider: 'AccountId',
-        SetValidatorCount: 'u32',
-        SetStorageRoleParameters: RoleParameters,
-        AddWorkingGroupLeaderOpening: AddOpeningParameters,
-        BeginReviewWorkingGroupLeaderApplication: Tuple.with([OpeningId, WorkingGroup]),
-        FillWorkingGroupLeaderOpening: FillOpeningParameters,
-        SetWorkingGroupMintCapacity: Tuple.with(['Balance', WorkingGroup]),
-        DecreaseWorkingGroupLeaderStake: Tuple.with([WorkerId, 'Balance', WorkingGroup]),
-        SlashWorkingGroupLeaderStake: Tuple.with([WorkerId, 'Balance', WorkingGroup]),
-        SetWorkingGroupLeaderReward: Tuple.with([WorkerId, 'Balance', WorkingGroup]),
-        TerminateWorkingGroupLeaderRole: TerminateRoleParameters,
-      },
-      value,
-      index
-    )
-  }
-}
+export class TerminateRoleParameters
+  extends JoyStructDecorated({
+    worker_id: WorkerId,
+    rationale: Bytes,
+    slash: bool,
+    working_group: WorkingGroup,
+  })
+  implements ITerminateRoleParameters {}
+
+export class ProposalDetails extends JoyEnum({
+  Text: Text,
+  RuntimeUpgrade: Vec.with(u8),
+  SetElectionParameters: ElectionParameters,
+  Spending: SpendingParams,
+  SetLead: SetLead,
+  SetContentWorkingGroupMintCapacity: u128,
+  EvictStorageProvider: AccountId,
+  SetValidatorCount: u32,
+  SetStorageRoleParameters: RoleParameters,
+  AddWorkingGroupLeaderOpening: AddOpeningParameters,
+  BeginReviewWorkingGroupLeaderApplication: Tuple.with([OpeningId, WorkingGroup]),
+  FillWorkingGroupLeaderOpening: FillOpeningParameters,
+  SetWorkingGroupMintCapacity: Tuple.with(['Balance', WorkingGroup]),
+  DecreaseWorkingGroupLeaderStake: Tuple.with([WorkerId, 'Balance', WorkingGroup]),
+  SlashWorkingGroupLeaderStake: Tuple.with([WorkerId, 'Balance', WorkingGroup]),
+  SetWorkingGroupLeaderReward: Tuple.with([WorkerId, 'Balance', WorkingGroup]),
+  TerminateWorkingGroupLeaderRole: TerminateRoleParameters,
+} as const) {}
 
 // export default proposalTypes;
-export function registerProposalTypes() {
-  try {
-    getTypeRegistry().register({
-      ProposalId,
-      ProposalStatus,
-      ProposalOf: Proposal,
-      ProposalDetails,
-      ProposalDetailsOf: ProposalDetails, // Runtime alias
-      VotingResults,
-      ProposalParameters,
-      VoteKind,
-      ThreadCounter,
-      DiscussionThread,
-      DiscussionPost,
-      AddOpeningParameters,
-      FillOpeningParameters,
-      TerminateRoleParameters,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of proposals module', err)
-  }
+export const proposalsTypes = {
+  ProposalId,
+  ProposalStatus,
+  ProposalOf: Proposal,
+  ProposalDetails,
+  ProposalDetailsOf: ProposalDetails, // Runtime alias
+  VotingResults,
+  ProposalParameters,
+  VoteKind,
+  ThreadCounter,
+  DiscussionThread,
+  DiscussionPost,
+  AddOpeningParameters,
+  FillOpeningParameters,
+  TerminateRoleParameters,
 }
+
+export default proposalsTypes

+ 30 - 75
types/src/recurring-rewards/index.ts

@@ -1,7 +1,9 @@
-import { getTypeRegistry, u64, u128, Option } from '@polkadot/types'
-import { AccountId, Balance, BlockNumber } from '@polkadot/types/interfaces'
-import { JoyStruct } from '../common'
+import { u64, u128, Option, u32 } from '@polkadot/types'
+import { Balance, BlockNumber } from '@polkadot/types/interfaces'
 import { MintId } from '../mint'
+import { JoyStructDecorated } from '../common'
+import AccountId from '@polkadot/types/generic/AccountId'
+import { RegistryTypes } from '@polkadot/types/types'
 
 export class RecipientId extends u64 {}
 export class RewardRelationshipId extends u64 {}
@@ -10,25 +12,12 @@ export type IRecipient = {
   total_reward_received: Balance
   total_reward_missed: Balance
 }
-export class Recipient extends JoyStruct<IRecipient> {
-  constructor(value?: IRecipient) {
-    super(
-      {
-        total_reward_received: u128,
-        total_reward_missed: u128,
-      },
-      value
-    )
-  }
-
-  get total_reward_received(): u128 {
-    return this.getField<u128>('total_reward_received')
-  }
-
-  get total_reward_missed(): u128 {
-    return this.getField<u128>('total_reward_missed')
-  }
-}
+export class Recipient
+  extends JoyStructDecorated({
+    total_reward_received: u128,
+    total_reward_missed: u128,
+  })
+  implements IRecipient {}
 
 export type IRewardRelationship = {
   recipient: RecipientId
@@ -40,57 +29,23 @@ export type IRewardRelationship = {
   total_reward_received: Balance
   total_reward_missed: Balance
 }
-export class RewardRelationship extends JoyStruct<IRewardRelationship> {
-  constructor(value?: IRecipient) {
-    super(
-      {
-        recipient: RecipientId,
-        mint_id: MintId,
-        account: 'AccountId',
-        amount_per_payout: 'Balance',
-        next_payment_at_block: Option.with('BlockNumber'),
-        payout_interval: Option.with('BlockNumber'),
-        total_reward_received: 'Balance',
-        total_reward_missed: 'Balance',
-      },
-      value
-    )
-  }
-
-  get recipient(): RecipientId {
-    return this.getField<RecipientId>('recipient')
-  }
-
-  get total_reward_received(): Balance {
-    return this.getField<Balance>('total_reward_received')
-  }
-
-  get total_reward_missed(): Balance {
-    return this.getField<Balance>('total_reward_missed')
-  }
-
-  get amount_per_payout(): Balance {
-    return this.getField<Balance>('amount_per_payout')
-  }
-
-  get payout_interval(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('payout_interval')
-  }
-
-  get next_payment_at_block(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('next_payment_at_block')
-  }
-}
-
-export function registerRecurringRewardsTypes() {
-  try {
-    getTypeRegistry().register({
-      RecipientId: 'u64',
-      RewardRelationshipId: 'u64',
-      Recipient,
-      RewardRelationship,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of recurring rewards module', err)
-  }
+export class RewardRelationship
+  extends JoyStructDecorated({
+    recipient: RecipientId,
+    mint_id: MintId,
+    account: AccountId,
+    amount_per_payout: u128, // Balance
+    next_payment_at_block: Option.with(u32), // BlockNumber
+    payout_interval: Option.with(u32), // BlockNumber
+    total_reward_received: u128, // Balance
+    total_reward_missed: u128, // Balance
+  })
+  implements IRewardRelationship {}
+
+export const recurringRewardsTypes: RegistryTypes = {
+  RecipientId: 'u64',
+  RewardRelationshipId: 'u64',
+  Recipient,
+  RewardRelationship,
 }
+export default recurringRewardsTypes

+ 17 - 62
types/src/roles.ts

@@ -1,67 +1,22 @@
-import { Struct } from '@polkadot/types/codec'
-import { getTypeRegistry, u32, u128 } from '@polkadot/types'
-import { BlockNumber, Balance } from '@polkadot/types/interfaces'
+import { u32, u128 } from '@polkadot/types'
+import { JoyStructDecorated } from './common'
 
 // We only need this type for historic reasons to read old proposal state
 // that was related to the now defunct actors module
-export class RoleParameters extends Struct {
-  constructor(value?: any) {
-    super(
-      {
-        min_stake: u128, // Balance,
-        min_actors: u32,
-        max_actors: u32,
-        reward: u128, // Balance,
-        reward_period: u32, // BlockNumber,
-        bonding_period: u32, // BlockNumber,
-        unbonding_period: u32, // BlockNumber,
-        min_service_period: u32, // BlockNumber,
-        startup_grace_period: u32, // BlockNumber,
-        entry_request_fee: u128, // Balance
-      },
-      value
-    )
-  }
+export class RoleParameters extends JoyStructDecorated({
+  min_stake: u128, // Balance,
+  min_actors: u32,
+  max_actors: u32,
+  reward: u128, // Balance,
+  reward_period: u32, // BlockNumber,
+  bonding_period: u32, // BlockNumber,
+  unbonding_period: u32, // BlockNumber,
+  min_service_period: u32, // BlockNumber,
+  startup_grace_period: u32, // BlockNumber,
+  entry_request_fee: u128, // Balance
+}) {}
 
-  get min_stake(): Balance {
-    return this.get('min_stake') as Balance
-  }
-  get max_actors(): u32 {
-    return this.get('max_actors') as u32
-  }
-  get min_actors(): u32 {
-    return this.get('min_actors') as u32
-  }
-  get reward(): Balance {
-    return this.get('reward') as Balance
-  }
-  get reward_period(): BlockNumber {
-    return this.get('reward_period') as BlockNumber
-  }
-  get unbonding_period(): BlockNumber {
-    return this.get('unbonding_period') as BlockNumber
-  }
-  get bonding_period(): BlockNumber {
-    return this.get('bonding_period') as BlockNumber
-  }
-  get min_service_period(): BlockNumber {
-    return this.get('min_service_period') as BlockNumber
-  }
-  get startup_grace_period(): BlockNumber {
-    return this.get('startup_grace_period') as BlockNumber
-  }
-  get entry_request_fee(): Balance {
-    return this.get('entry_request_fee') as Balance
-  }
-}
-
-export function registerRolesTypes() {
-  try {
-    const typeRegistry = getTypeRegistry()
-    typeRegistry.register({
-      RoleParameters,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of roles module', err)
-  }
+export const rolesTypes = {
+  RoleParameters,
 }
+export default rolesTypes

+ 41 - 0
types/src/scripts/defsFromTypes.ts

@@ -0,0 +1,41 @@
+// Conversion of Joystream types into @polkadot/typegen compatible RegistryTypes object
+// (will require a few additonal tweaks to work, currently just logs the output in the console)
+
+import { types } from '../index'
+import { Constructor, Codec, RegistryTypes, Registry } from '@polkadot/types/types'
+import { TypeRegistry } from '@polkadot/types'
+
+function normalizeDef(registry: Registry, defOrConstructor: any, typeName: string) {
+  if (typeof defOrConstructor === 'string') {
+    return defOrConstructor
+  } else if (typeof defOrConstructor === 'function') {
+    const defString = new (defOrConstructor as Constructor<Codec>)(registry).toRawType().toString()
+    try {
+      const obj = JSON.parse(defString)
+      // def is an object:
+      return obj
+    } catch (e) {
+      // def if just a type name:
+      return defString
+    }
+  }
+
+  throw new Error(`Unkown type entry for ${typeName} found in registry!`)
+}
+
+async function defsFromTypes() {
+  const registry = new TypeRegistry()
+  registry.setKnownTypes({ types })
+  registry.register(types)
+  const defs: RegistryTypes = {}
+  Object.entries(registry.knownTypes.types as any).forEach(([typeName, defOrConstructor]) => {
+    const def = normalizeDef(registry, defOrConstructor, typeName)
+    defs[typeName] = def
+  })
+
+  return defs
+}
+
+defsFromTypes()
+  .then((defs) => console.log(defs))
+  .catch(console.error)

+ 108 - 0
types/src/scripts/updateAugmentTypes.ts

@@ -0,0 +1,108 @@
+// Adds Joystream types to /definitions/augment-types.ts allowing better api.createType TS support
+
+import common from '../common'
+import members from '../members'
+import council from '../council'
+import roles from '../roles'
+import forum from '../forum'
+import stake from '../stake'
+import mint from '../mint'
+import recurringRewards from '../recurring-rewards'
+import hiring from '../hiring'
+import versionedStore from '../versioned-store'
+import versionedStorePermissions from '../versioned-store/permissions'
+import contentWorkingGroup from '../content-working-group'
+import workingGroup from '../working-group'
+import discovery from '../discovery'
+import media from '../media'
+import proposals from '../proposals'
+import fs from 'fs'
+import path from 'path'
+
+const typesByModule = {
+  'common': common,
+  'members': members,
+  'council': council,
+  'roles': roles,
+  'forum': forum,
+  'stake': stake,
+  'mint': mint,
+  'recurring-rewards': recurringRewards,
+  'hiring': hiring,
+  'versioned-store': versionedStore,
+  'versioned-store/permissions': versionedStorePermissions,
+  'content-working-group': contentWorkingGroup,
+  'working-group': workingGroup,
+  'discovery': discovery,
+  'media': media,
+  'proposals': proposals,
+}
+
+type Imports = { [moduleName: string]: string[] }
+type AugmentTypes = { [typeName: string]: string }
+
+const imports: Imports = {}
+const augmentTypes: AugmentTypes = {}
+
+const populateFileByTemplateTag = (fileContent: string, tag: string, lines: string[]) => {
+  const fileLines = fileContent.split('\n')
+  const startIndex = fileLines.findIndex((line) => line.includes(`/** ${tag} **/`))
+  const endIndex = fileLines.findIndex((line) => line.includes(`/** /${tag} **/`))
+
+  if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
+    throw new Error(`populateFileByTemplateTag: Invalid tag (${tag})`)
+  }
+
+  const whitespaceMatch = fileLines[startIndex].match(/^(\s)+/)
+  const whitespace = whitespaceMatch ? whitespaceMatch[0] : ''
+
+  fileLines.splice(startIndex + 1, endIndex - (startIndex + 1), ...lines.map((line) => `${whitespace}${line}`))
+
+  return fileLines.join('\n')
+}
+
+const updateAugmentTypesFile = (filePath: string, imports: Imports, augmentTypes: AugmentTypes) => {
+  let fileContent = fs.readFileSync(filePath).toString()
+  fileContent = populateFileByTemplateTag(
+    fileContent,
+    'CUSTOMIMPORTS',
+    Object.entries(imports).map(
+      ([moduleName, importStatements]) =>
+        // import as to avoid namespace clashes
+        `import { ${importStatements.join(', ')} } from '../${moduleName}'`
+    )
+  )
+  fileContent = populateFileByTemplateTag(
+    fileContent,
+    'CUSTOMTYPES',
+    Object.entries(augmentTypes).map(([typeName, constructorName]) => `"${typeName}": ${constructorName};`)
+  )
+
+  fs.writeFileSync(filePath, fileContent)
+}
+
+const addAugmentTypes = (typeName: string, constructorName: string) => {
+  augmentTypes[typeName] = constructorName
+  augmentTypes[`Option<${typeName}>`] = `Option<${constructorName}>`
+  augmentTypes[`Vec<${typeName}>`] = `Vec<${constructorName}>`
+}
+
+Object.entries(typesByModule).forEach(([moduleName, types]) => {
+  Object.entries(types).forEach(([typeName, codecOrName]) => {
+    if (typeof codecOrName === 'function') {
+      const constructorName = codecOrName.name
+      if (!constructorName) {
+        throw new Error(`Codec constructor doesn't have a name: ${typeName}`)
+      }
+      const normalizedTypeName = typeName.replace(/[^A-Za-z0-9_]/g, '_')
+      // Add "as" to avoid namespace clashes
+      const importStatement = `${constructorName} as ${normalizedTypeName}`
+      !imports[moduleName] ? (imports[moduleName] = [importStatement]) : imports[moduleName].push(importStatement)
+      addAugmentTypes(typeName, normalizedTypeName)
+    } else if (typeof codecOrName === 'string') {
+      addAugmentTypes(typeName, codecOrName)
+    }
+  })
+})
+
+updateAugmentTypesFile(path.join(__dirname, '../definitions/augment-types.ts'), imports, augmentTypes)

+ 47 - 100
types/src/stake/index.ts

@@ -1,6 +1,7 @@
-import { getTypeRegistry, u32, u64, u128, Enum, Null, BTreeMap, bool } from '@polkadot/types'
-import { JoyStruct } from '../common'
+import { u32, u64, u128, Null, BTreeMap, bool } from '@polkadot/types'
 import { BlockNumber, Balance } from '@polkadot/types/interfaces'
+import { RegistryTypes } from '@polkadot/types/types'
+import { JoyEnum, JoyStructDecorated } from '../common'
 
 export class StakeId extends u64 {}
 export class SlashId extends u64 {}
@@ -11,52 +12,34 @@ export type ISlash = {
   blocks_remaining_in_active_period_for_slashing: BlockNumber
   slash_amount: Balance
 }
-export class Slash extends JoyStruct<ISlash> {
-  constructor(value?: ISlash) {
-    super(
-      {
-        started_at_block: u32,
-        is_active: bool,
-        blocks_remaining_in_active_period_for_slashing: u32,
-        slash_amount: u128,
-      },
-      value
-    )
-  }
-}
+export class Slash
+  extends JoyStructDecorated({
+    started_at_block: u32,
+    is_active: bool,
+    blocks_remaining_in_active_period_for_slashing: u32,
+    slash_amount: u128,
+  })
+  implements ISlash {}
 
 export type IUnstakingState = {
   started_at_block: BlockNumber
   is_active: bool
   blocks_remaining_in_active_period_for_unstaking: BlockNumber
 }
-export class UnstakingState extends JoyStruct<IUnstakingState> {
-  constructor(value?: IUnstakingState) {
-    super(
-      {
-        started_at_block: u32,
-        is_active: bool,
-        blocks_remaining_in_active_period_for_unstaking: u32,
-      },
-      value
-    )
-  }
-}
+export class UnstakingState
+  extends JoyStructDecorated({
+    started_at_block: u32,
+    is_active: bool,
+    blocks_remaining_in_active_period_for_unstaking: u32,
+  })
+  implements IUnstakingState {}
 
 export class Normal extends Null {}
 export class Unstaking extends UnstakingState {}
-export class StakedStatus extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        Normal,
-        Unstaking,
-      },
-      value,
-      index
-    )
-  }
-}
+export class StakedStatus extends JoyEnum({
+  Normal,
+  Unstaking,
+} as const) {}
 
 export type IStakedState = {
   staked_amount: Balance
@@ -64,81 +47,45 @@ export type IStakedState = {
   next_slash_id: SlashId
   ongoing_slashes: BTreeMap<SlashId, Slash>
 }
-export class StakedState extends JoyStruct<IStakedState> {
-  constructor(value?: IStakedState) {
-    super(
-      {
-        staked_amount: u128,
-        staked_status: StakedStatus,
-        next_slash_id: SlashId,
-        ongoing_slashes: BTreeMap.with(SlashId, Slash),
-      },
-      value
-    )
-  }
-
-  get staked_amount(): u128 {
-    return this.getField<u128>('staked_amount')
-  }
-}
+export class StakedState
+  extends JoyStructDecorated({
+    staked_amount: u128,
+    staked_status: StakedStatus,
+    next_slash_id: SlashId,
+    ongoing_slashes: BTreeMap.with(SlashId, Slash),
+  })
+  implements IStakedState {}
 
 export class NotStaked extends Null {}
 export class Staked extends StakedState {}
 
-export class StakingStatus extends Enum {
-  constructor(value?: any, index?: number) {
-    super(
-      {
-        NotStaked,
-        Staked,
-      },
-      value,
-      index
-    )
-  }
-}
+export class StakingStatus extends JoyEnum({
+  NotStaked,
+  Staked,
+} as const) {}
 
 export type IStake = {
   created: BlockNumber
   staking_status: StakingStatus
 }
 
-export class Stake extends JoyStruct<IStake> {
-  constructor(value?: IStake) {
-    super(
-      {
-        created: u32,
-        staking_status: StakingStatus,
-      },
-      value
-    )
-  }
-
-  get created(): u32 {
-    return this.getField<u32>('created')
-  }
-
-  get staking_status(): StakingStatus {
-    return this.getField<StakingStatus>('staking_status')
-  }
-
+export class Stake
+  extends JoyStructDecorated({
+    created: u32,
+    staking_status: StakingStatus,
+  })
+  implements IStake {
   get value(): Balance {
-    switch (this.staking_status.type) {
-      case 'Staked':
-        return (this.staking_status.value as Staked).staked_amount
+    if (this.staking_status.isOfType('Staked')) {
+      return this.staking_status.asType('Staked').staked_amount
     }
 
-    return new u128(0)
+    return new u128(this.registry, 0)
   }
 }
 
-export function registerStakeTypes() {
-  try {
-    getTypeRegistry().register({
-      StakeId: 'u64',
-      Stake,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of stake module', err)
-  }
+export const stakeTypes: RegistryTypes = {
+  StakeId: 'u64',
+  Stake,
 }
+export default stakeTypes

+ 36 - 34
types/src/versioned-store/EntityCodec.ts

@@ -1,10 +1,10 @@
 import BN from 'bn.js'
 import { Text, bool, Vec, u16 } from '@polkadot/types'
-import { Codec } from '@polkadot/types/types'
+import { Codec, Registry } from '@polkadot/types/types'
 import { Class, Entity, VecClassPropertyValue, ClassPropertyValue, EntityId, ClassId, unifyPropName } from '.'
 import * as PV from './PropertyValue'
 import { PropertyValue } from './PropertyValue'
-import { PropertyTypeName } from './PropertyTypeName'
+import { PropertyTypeKeys } from './PropertyType'
 import ChannelId from '../content-working-group/ChannelId'
 
 /**
@@ -40,9 +40,9 @@ function substrateToPlain<T>(x: Codec): T | undefined {
  *
  * @throws Error
  */
-function plainToSubstrate(propType: string, value: any): PropertyValue {
+function plainToSubstrate(registry: Registry, propType: string, value: any): PropertyValue {
   const ok = (typeEnum: PV.PropertyValueEnum) => {
-    return new PropertyValue({ [propType]: typeEnum })
+    return new PropertyValue(registry, { [propType]: typeEnum }) // FIXME: createType?
   }
 
   const valueAsBool = (): boolean => {
@@ -79,51 +79,48 @@ function plainToSubstrate(propType: string, value: any): PropertyValue {
     return valueAsArr() as string[]
   }
 
+  // FIXME: use createType?
   switch (propType) {
     // Primitives:
-
     case 'None':
-      return ok(new PV.None())
+      return ok(new PV.None(registry))
     case 'Bool':
-      return ok(new PV.Bool(valueAsBool()))
+      return ok(new PV.Bool(registry, valueAsBool()))
     case 'Uint16':
-      return ok(new PV.Uint16(value as string))
+      return ok(new PV.Uint16(registry, value as string))
     case 'Uint32':
-      return ok(new PV.Uint32(value as string))
+      return ok(new PV.Uint32(registry, value as string))
     case 'Uint64':
-      return ok(new PV.Uint64(value as string))
+      return ok(new PV.Uint64(registry, value as string))
     case 'Int16':
-      return ok(new PV.Int16(value as string))
+      return ok(new PV.Int16(registry, value as string))
     case 'Int32':
-      return ok(new PV.Int32(value as string))
+      return ok(new PV.Int32(registry, value as string))
     case 'Int64':
-      return ok(new PV.Int64(value as string))
+      return ok(new PV.Int64(registry, value as string))
     case 'Text':
-      return ok(new PV.Text(value as string))
+      return ok(new PV.Text(registry, value as string))
     case 'Internal':
-      return ok(new PV.Internal(value as string))
-
+      return ok(new PV.Internal(registry, value as string))
     // Vectors:
-
     case 'BoolVec':
-      return ok(new PV.BoolVec(valueAsBoolArr()))
+      return ok(new PV.BoolVec(registry, valueAsBoolArr()))
     case 'Uint16Vec':
-      return ok(new PV.Uint16Vec(valueAsStrArr()))
+      return ok(new PV.Uint16Vec(registry, valueAsStrArr()))
     case 'Uint32Vec':
-      return ok(new PV.Uint32Vec(valueAsStrArr()))
+      return ok(new PV.Uint32Vec(registry, valueAsStrArr()))
     case 'Uint64Vec':
-      return ok(new PV.Uint64Vec(valueAsStrArr()))
+      return ok(new PV.Uint64Vec(registry, valueAsStrArr()))
     case 'Int16Vec':
-      return ok(new PV.Int16Vec(valueAsStrArr()))
+      return ok(new PV.Int16Vec(registry, valueAsStrArr()))
     case 'Int32Vec':
-      return ok(new PV.Int32Vec(valueAsStrArr()))
+      return ok(new PV.Int32Vec(registry, valueAsStrArr()))
     case 'Int64Vec':
-      return ok(new PV.Int64Vec(valueAsStrArr()))
+      return ok(new PV.Int64Vec(registry, valueAsStrArr()))
     case 'TextVec':
-      return ok(new PV.TextVec(valueAsStrArr()))
+      return ok(new PV.TextVec(registry, valueAsStrArr()))
     case 'InternalVec':
-      return ok(new PV.InternalVec(valueAsArr()))
-
+      return ok(new PV.InternalVec(registry, valueAsArr()))
     default: {
       throw new Error(`Unknown property type name: ${propType}`)
     }
@@ -174,8 +171,10 @@ export interface ToPlainObjectProps {
 export abstract class EntityCodec<T extends PlainEntity> {
   private propNameToMetaMap: Map<string, PropMeta> = new Map()
   private propIndexToNameMap: Map<number, string> = new Map()
+  private registry: Registry
 
   public constructor(entityClass: Class) {
+    this.registry = entityClass.registry
     entityClass.properties.map((p, index) => {
       const propName = unifyPropName(p.name.toString())
       const propMeta = { index, type: p.prop_type.type.toString() }
@@ -244,26 +243,29 @@ export abstract class EntityCodec<T extends PlainEntity> {
     // console.log('propNameToMetaMap propNameToMetaMap', this.propNameToMetaMap)
     // console.log('toSubstrateUpdate updatedProps', updatedProps)
 
-    const res = new VecClassPropertyValue()
+    const res = new VecClassPropertyValue(this.registry) // FIXME: createType?
     Object.keys(updatedProps).map((propName) => {
       const meta = this.propNameToMetaMap.get(propName)
       if (meta) {
-        const propType = meta.type as PropertyTypeName
+        const propType = meta.type as PropertyTypeKeys
         const plainValue = (updatedProps as any)[propName]
 
         let codecValue: PropertyValue | undefined
         try {
-          codecValue = plainToSubstrate(propType, plainValue)
+          codecValue = plainToSubstrate(this.registry, propType, plainValue)
         } catch (err) {
           console.error(`Failed to convert plain value '${plainValue}' to Substrate codec. Error:`, err)
         }
 
         if (codecValue) {
           res.push(
-            new ClassPropertyValue({
-              in_class_index: new u16(meta.index),
-              value: codecValue,
-            })
+            new ClassPropertyValue(
+              this.registry, // FIXME: createType?
+              {
+                in_class_index: new u16(this.registry, meta.index),
+                value: codecValue,
+              }
+            )
           )
         }
       }

+ 27 - 76
types/src/versioned-store/PropertyType.ts

@@ -1,11 +1,11 @@
 import { u16, Null } from '@polkadot/types'
-import { Enum, Tuple } from '@polkadot/types/codec'
+import { Tuple } from '@polkadot/types/codec'
 import ClassId from './ClassId'
+import { JoyEnum } from '../common'
 
 export class None extends Null {}
 
 // Single values:
-
 export class Bool extends Null {}
 export class Uint16 extends Null {}
 export class Uint32 extends Null {}
@@ -15,9 +15,7 @@ export class Int32 extends Null {}
 export class Int64 extends Null {}
 export class Text extends u16 {}
 export class Internal extends ClassId {}
-
 // Vectors:
-
 export class BoolVec extends u16 {}
 export class Uint16Vec extends u16 {}
 export class Uint32Vec extends u16 {}
@@ -25,80 +23,33 @@ export class Uint64Vec extends u16 {}
 export class Int16Vec extends u16 {}
 export class Int32Vec extends u16 {}
 export class Int64Vec extends u16 {}
+export class TextVec extends Tuple.with([u16, u16]) {} // [maxItems, maxTextLength]
+export class InternalVec extends Tuple.with([u16, ClassId]) {} // [maxItems, classId]
 
-export class TextVec extends Tuple.with([u16, u16]) {
-  static newTypesafe(maxItems: u16 | number, maxTextLength: u16 | number) {
-    return new this([maxItems, maxTextLength])
-  }
-}
-
-export class InternalVec extends Tuple.with([u16, ClassId]) {
-  static newTypesafe(maxItems: u16 | number, classId: ClassId | number) {
-    return new this([maxItems, classId])
-  }
-}
-
-export type PropertyTypeEnum =
-  | None
-
+export const PropertyTypeDef = {
+  None,
   // Single values:
-  | Bool
-  | Uint16
-  | Uint32
-  | Uint64
-  | Int16
-  | Int32
-  | Int64
-  | Text
-  | Internal
-
+  Bool,
+  Uint16,
+  Uint32,
+  Uint64,
+  Int16,
+  Int32,
+  Int64,
+  Text,
+  Internal,
   // Vectors:
-  | BoolVec
-  | Uint16Vec
-  | Uint32Vec
-  | Uint64Vec
-  | Int16Vec
-  | Int32Vec
-  | Int64Vec
-  | TextVec
-  | InternalVec
-
-type PropertyTypeEnumValue = {
-  [typeName: string]: PropertyTypeEnum
-}
-
-export class PropertyType extends Enum {
-  constructor(value?: PropertyTypeEnumValue, index?: number) {
-    super(
-      {
-        None,
-
-        // Single values:
-        Bool,
-        Uint16,
-        Uint32,
-        Uint64,
-        Int16,
-        Int32,
-        Int64,
-        Text,
-        Internal,
-
-        // Vectors:
-        BoolVec,
-        Uint16Vec,
-        Uint32Vec,
-        Uint64Vec,
-        Int16Vec,
-        Int32Vec,
-        Int64Vec,
-        TextVec,
-        InternalVec,
-      },
-      value,
-      index
-    )
-  }
-}
+  BoolVec,
+  Uint16Vec,
+  Uint32Vec,
+  Uint64Vec,
+  Int16Vec,
+  Int32Vec,
+  Int64Vec,
+  TextVec,
+  InternalVec,
+} as const
+export type PropertyTypeKeys = keyof typeof PropertyTypeDef
+export class PropertyType extends JoyEnum(PropertyTypeDef) {}
 
 export default PropertyType

+ 0 - 22
types/src/versioned-store/PropertyTypeName.ts

@@ -1,22 +0,0 @@
-export type PropertyTypeName =
-  | 'None'
-  | 'Bool'
-  | 'Uint16'
-  | 'Uint32'
-  | 'Uint64'
-  | 'Int16'
-  | 'Int32'
-  | 'Int64'
-  | 'Text'
-  | 'Internal'
-
-  // Vectors:
-  | 'BoolVec'
-  | 'Uint16Vec'
-  | 'Uint32Vec'
-  | 'Uint64Vec'
-  | 'Int16Vec'
-  | 'Int32Vec'
-  | 'Int64Vec'
-  | 'TextVec'
-  | 'InternalVec'

+ 25 - 56
types/src/versioned-store/PropertyValue.ts

@@ -10,8 +10,8 @@ import {
   i32,
   i64,
 } from '@polkadot/types'
-import { Enum } from '@polkadot/types/codec'
 import EntityId from './EntityId'
+import { JoyEnum } from '../common'
 
 export class None extends Null {}
 
@@ -40,67 +40,36 @@ export class Int64Vec extends Vector.with(i64) {}
 export class TextVec extends Vector.with(PolkaText) {}
 export class InternalVec extends Vector.with(EntityId) {}
 
-export type PropertyValueEnum =
-  | None
-
+export const PropertyValueDef = {
+  None,
   // Single values:
-  | Bool
-  | Uint16
-  | Uint32
-  | Uint64
-  | Int16
-  | Int32
-  | Int64
-  | Text
-  | Internal
-
+  Bool,
+  Uint16,
+  Uint32,
+  Uint64,
+  Int16,
+  Int32,
+  Int64,
+  Text,
+  Internal,
   // Vectors:
-  | BoolVec
-  | Uint16Vec
-  | Uint32Vec
-  | Uint64Vec
-  | Int16Vec
-  | Int32Vec
-  | Int64Vec
-  | TextVec
-  | InternalVec
+  BoolVec,
+  Uint16Vec,
+  Uint32Vec,
+  Uint64Vec,
+  Int16Vec,
+  Int32Vec,
+  Int64Vec,
+  TextVec,
+  InternalVec,
+} as const
 
+// FIXME: Fix naming conventions, or remove those later?
+export type PropertyValueEnum = InstanceType<typeof PropertyValueDef[keyof typeof PropertyValueDef]>
 export type PropertyValueEnumValue = {
   [typeName: string]: PropertyValueEnum
 }
 
-export class PropertyValue extends Enum {
-  constructor(value?: PropertyValueEnumValue, index?: number) {
-    super(
-      {
-        None,
-
-        // Single values:
-        Bool,
-        Uint16,
-        Uint32,
-        Uint64,
-        Int16,
-        Int32,
-        Int64,
-        Text,
-        Internal,
-
-        // Vectors:
-        BoolVec,
-        Uint16Vec,
-        Uint32Vec,
-        Uint64Vec,
-        Int16Vec,
-        Int32Vec,
-        Int64Vec,
-        TextVec,
-        InternalVec,
-      },
-      value,
-      index
-    )
-  }
-}
+export class PropertyValue extends JoyEnum(PropertyValueDef) {}
 
 export default PropertyValue

+ 51 - 105
types/src/versioned-store/index.ts

@@ -1,11 +1,12 @@
-import { getTypeRegistry, u16, Text, bool as Bool } from '@polkadot/types'
+import { u16, Text, bool as Bool } from '@polkadot/types'
 import { Vec as Vector } from '@polkadot/types/codec'
-import { JoyStruct } from '../common'
 import EntityId from './EntityId'
 import ClassId from './ClassId'
 import PropertyType from './PropertyType'
 import PropertyValue from './PropertyValue'
 import { camelCase, upperFirst } from 'lodash'
+import { RegistryTypes } from '@polkadot/types/types'
+import { JoyStructDecorated, JoyStructCustom } from '../common'
 
 export { ClassId, EntityId, PropertyType, PropertyValue }
 
@@ -16,25 +17,20 @@ export type PropertyTsType = {
   description: Text
 }
 
-export class Property extends JoyStruct<PropertyTsType> {
-  constructor(value: PropertyTsType) {
-    super(
-      {
-        prop_type: PropertyType,
-        required: Bool,
-        name: Text,
-        description: Text,
-      },
-      value
-    )
-  }
-
+export class Property extends JoyStructCustom({
+  prop_type: PropertyType,
+  required: Bool,
+  name: Text,
+  description: Text,
+})
+// FIXME: Make it JoyStructDecorated compatible
+{
   get prop_type(): PropertyType {
     return this.getField('prop_type')
   }
 
   get required(): boolean {
-    return this.getBoolean('required')
+    return this.getField('required').valueOf()
   }
 
   get name(): string {
@@ -54,20 +50,11 @@ export type ClassSchemaType = {
   properties: VecU16
 }
 
-export class ClassSchema extends JoyStruct<ClassSchemaType> {
-  constructor(value: ClassSchemaType) {
-    super(
-      {
-        properties: VecU16,
-      },
-      value
-    )
-  }
-
-  get properties(): VecU16 {
-    return this.getField('properties')
-  }
-}
+export class ClassSchema
+  extends JoyStructDecorated({
+    properties: VecU16,
+  })
+  implements ClassSchemaType {}
 
 export class VecClassSchema extends Vector.with(ClassSchema) {}
 
@@ -76,25 +63,12 @@ export type ClassPropertyValueType = {
   value: PropertyValue
 }
 
-export class ClassPropertyValue extends JoyStruct<ClassPropertyValueType> {
-  constructor(value: ClassPropertyValueType) {
-    super(
-      {
-        in_class_index: u16,
-        value: PropertyValue,
-      },
-      value
-    )
-  }
-
-  get in_class_index(): u16 {
-    return this.getField('in_class_index')
-  }
-
-  get value(): PropertyValue {
-    return this.getField('value')
-  }
-}
+export class ClassPropertyValue
+  extends JoyStructDecorated({
+    in_class_index: u16,
+    value: PropertyValue,
+  })
+  implements ClassPropertyValueType {}
 
 export class VecClassPropertyValue extends Vector.with(ClassPropertyValue) {}
 
@@ -106,20 +80,15 @@ export type ClassType = {
   description: Text
 }
 
-export class Class extends JoyStruct<ClassType> {
-  constructor(value: ClassType) {
-    super(
-      {
-        id: ClassId,
-        properties: VecProperty,
-        schemas: VecClassSchema,
-        name: Text,
-        description: Text,
-      },
-      value
-    )
-  }
-
+export class Class extends JoyStructCustom({
+  id: ClassId,
+  properties: VecProperty,
+  schemas: VecClassSchema,
+  name: Text,
+  description: Text,
+})
+// FIXME: Make it JoyStructDecorated compatible
+{
   get id(): ClassId {
     return this.getField('id')
   }
@@ -148,32 +117,13 @@ export type EntityType = {
   values: VecClassPropertyValue
 }
 
-export class Entity extends JoyStruct<EntityType> {
-  constructor(value: EntityType) {
-    super(
-      {
-        id: EntityId,
-        class_id: ClassId,
-        in_class_schema_indexes: VecU16,
-        values: VecClassPropertyValue,
-      },
-      value
-    )
-  }
-
-  get id(): EntityId {
-    return this.getField('id')
-  }
-
-  get class_id(): ClassId {
-    return this.getField('class_id')
-  }
-
-  get in_class_schema_indexes(): VecU16 {
-    return this.getField('in_class_schema_indexes')
-  }
-
-  /** NOTE: Renamed to `entity_values` because `values` is already in use. */
+export class Entity extends JoyStructDecorated({
+  id: EntityId,
+  class_id: ClassId,
+  in_class_schema_indexes: VecU16,
+  values: VecClassPropertyValue,
+}) {
+  /** NOTE: Renamed to `entity_values` because `values` is already in use (Map's original method). */
   get entity_values(): VecClassPropertyValue {
     return this.getField('values')
   }
@@ -205,20 +155,16 @@ export function unifyPropName(propName: string): string {
   return camelCase(propName)
 }
 
-export function registerVersionedStoreTypes() {
-  try {
-    getTypeRegistry().register({
-      ClassId: 'u64',
-      EntityId: 'u64',
-      Class,
-      Entity,
-      ClassSchema,
-      Property,
-      PropertyType,
-      PropertyValue,
-      ClassPropertyValue,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of Versioned Store module', err)
-  }
+export const versionedStoreTypes: RegistryTypes = {
+  ClassId: 'u64',
+  EntityId: 'u64',
+  Class,
+  Entity,
+  ClassSchema,
+  Property,
+  PropertyType,
+  PropertyValue,
+  ClassPropertyValue,
 }
+
+export default versionedStoreTypes

+ 12 - 45
types/src/versioned-store/permissions/ClassPermissions.ts

@@ -1,6 +1,6 @@
 import { u32, bool } from '@polkadot/types'
 import { BlockNumber } from '@polkadot/types/interfaces'
-import { JoyStruct, CredentialSet } from '../../common'
+import { CredentialSet, JoyStructDecorated } from '../../common'
 import EntityPermissions from './EntityPermissions'
 import { ReferenceConstraint } from './reference-constraint'
 
@@ -14,47 +14,14 @@ type IClassPermissions = {
   last_permissions_update: BlockNumber
 }
 
-export default class ClassPermissionsType extends JoyStruct<IClassPermissions> {
-  constructor(value: IClassPermissions) {
-    super(
-      {
-        entity_permissions: EntityPermissions,
-        entities_can_be_created: bool,
-        add_schemas: CredentialSet,
-        create_entities: CredentialSet,
-        reference_constraint: ReferenceConstraint,
-        admins: CredentialSet,
-        last_permissions_update: u32, // BlockNumber,
-      },
-      value
-    )
-  }
-
-  get entity_permissions(): EntityPermissions {
-    return this.getField('entity_permissions')
-  }
-
-  get entities_can_be_created(): bool {
-    return this.getField('entities_can_be_created')
-  }
-
-  get add_schemas(): CredentialSet {
-    return this.getField('add_schemas')
-  }
-
-  get create_entities(): CredentialSet {
-    return this.getField('create_entities')
-  }
-
-  get reference_constraint(): ReferenceConstraint {
-    return this.getField('reference_constraint')
-  }
-
-  get admins(): CredentialSet {
-    return this.getField('admins')
-  }
-
-  get last_permissions_update(): u32 {
-    return this.getField('last_permissions_update')
-  }
-}
+export default class ClassPermissionsType
+  extends JoyStructDecorated({
+    entity_permissions: EntityPermissions,
+    entities_can_be_created: bool,
+    add_schemas: CredentialSet,
+    create_entities: CredentialSet,
+    reference_constraint: ReferenceConstraint,
+    admins: CredentialSet,
+    last_permissions_update: u32, // BlockNumber,
+  })
+  implements IClassPermissions {}

+ 7 - 20
types/src/versioned-store/permissions/EntityPermissions.ts

@@ -1,4 +1,4 @@
-import { JoyStruct } from '../../JoyStruct'
+import { JoyStructDecorated } from '../../JoyStruct'
 import { bool } from '@polkadot/types'
 import { CredentialSet } from '../../common'
 
@@ -7,22 +7,9 @@ type IEntityPermissions = {
   maintainer_has_all_permissions: bool
 }
 
-export default class EntityPermissions extends JoyStruct<IEntityPermissions> {
-  constructor(value: IEntityPermissions) {
-    super(
-      {
-        update: CredentialSet,
-        maintainer_has_all_permissions: bool,
-      },
-      value
-    )
-  }
-
-  get update(): CredentialSet {
-    return this.getField('update')
-  }
-
-  get maintainer_has_all_permissions(): bool {
-    return this.getField('maintainer_has_all_permissions')
-  }
-}
+export default class EntityPermissions
+  extends JoyStructDecorated({
+    update: CredentialSet,
+    maintainer_has_all_permissions: bool,
+  })
+  implements IEntityPermissions {}

+ 7 - 20
types/src/versioned-store/permissions/PropertyOfClass.ts

@@ -1,28 +1,15 @@
 import { u16 } from '@polkadot/types'
-import { JoyStruct } from '../../common'
 import ClassId from '../ClassId'
+import { JoyStructDecorated } from '../../common'
 
 type IPropertyOfClass = {
   class_id: ClassId
   property_index: u16
 }
 
-export default class PropertyOfClass extends JoyStruct<IPropertyOfClass> {
-  constructor(value: IPropertyOfClass) {
-    super(
-      {
-        class_id: ClassId,
-        property_index: u16,
-      },
-      value
-    )
-  }
-
-  get class_id(): ClassId {
-    return this.getField('class_id')
-  }
-
-  get property_index(): u16 {
-    return this.getField('property_index')
-  }
-}
+export default class PropertyOfClass
+  extends JoyStructDecorated({
+    class_id: ClassId,
+    property_index: u16,
+  })
+  implements IPropertyOfClass {}

+ 7 - 20
types/src/versioned-store/permissions/batching/ParametrizedClassPropertyValue.ts

@@ -1,28 +1,15 @@
 import { ParametrizedPropertyValue } from './parametrized-property-value'
-import { JoyStruct } from '../../../common'
 import { u16 } from '@polkadot/types'
+import { JoyStructDecorated } from '../../../common'
 
 type IParametrizedClassPropertyValue = {
   in_class_index: u16
   value: ParametrizedPropertyValue
 }
 
-export default class ParametrizedClassPropertyValue extends JoyStruct<IParametrizedClassPropertyValue> {
-  constructor(value: IParametrizedClassPropertyValue) {
-    super(
-      {
-        in_class_index: u16,
-        value: ParametrizedPropertyValue,
-      },
-      value
-    )
-  }
-
-  get in_class_index(): u16 {
-    return this.getField('in_class_index')
-  }
-
-  get value(): ParametrizedPropertyValue {
-    return this.getField('value')
-  }
-}
+export default class ParametrizedClassPropertyValue
+  extends JoyStructDecorated({
+    in_class_index: u16,
+    value: ParametrizedPropertyValue,
+  })
+  implements IParametrizedClassPropertyValue {}

+ 8 - 25
types/src/versioned-store/permissions/batching/index.ts

@@ -1,4 +1,4 @@
-import { JoyStruct, Credential } from '../../../common'
+import { Credential, JoyStructDecorated } from '../../../common'
 import { OperationType } from './operation-types'
 import { bool, Option } from '@polkadot/types'
 
@@ -8,27 +8,10 @@ type IOperation = {
   operation_type: OperationType
 }
 
-export class Operation extends JoyStruct<IOperation> {
-  constructor(value: IOperation) {
-    super(
-      {
-        with_credential: Option.with(Credential),
-        as_entity_maintainer: bool,
-        operation_type: OperationType,
-      },
-      value
-    )
-  }
-
-  get with_credential(): Option<Credential> {
-    return this.getField('with_credential')
-  }
-
-  get as_entity_maintainer(): bool {
-    return this.getField('as_entity_maintainer')
-  }
-
-  get operation_type(): OperationType {
-    return this.getField('operation_type')
-  }
-}
+export class Operation
+  extends JoyStructDecorated({
+    with_credential: Option.with(Credential),
+    as_entity_maintainer: bool,
+    operation_type: OperationType,
+  })
+  implements IOperation {}

+ 19 - 26
types/src/versioned-store/permissions/batching/operation-types.ts

@@ -1,4 +1,4 @@
-import { Enum, u16 } from '@polkadot/types'
+import { u16 } from '@polkadot/types'
 import {
   CreateEntityOperation,
   UpdatePropertyValuesOperation,
@@ -7,56 +7,49 @@ import {
 } from './operations'
 import ClassId from '../../ClassId'
 import { ParametrizedEntity } from './parametrized-entity'
+import { JoyEnum } from '../../../common'
+import { Registry } from '@polkadot/types/types'
 
 export class CreateEntity extends CreateEntityOperation {}
 export class UpdatePropertyValues extends UpdatePropertyValuesOperation {}
 export class AddSchemaSupportToEntity extends AddSchemaSupportToEntityOperation {}
 
-export type OperationTypeVariant = CreateEntity | UpdatePropertyValues | AddSchemaSupportToEntity
+export const OperationTypeDef = {
+  CreateEntity,
+  UpdatePropertyValues,
+  AddSchemaSupportToEntity,
+} as const
 
-type OperationTypeVariantValue = {
-  [typeName: string]: OperationTypeVariant
-}
-
-export class OperationType extends Enum {
-  constructor(value?: OperationTypeVariantValue, index?: number) {
-    super(
-      {
-        CreateEntity,
-        UpdatePropertyValues,
-        AddSchemaSupportToEntity,
-      },
-      value,
-      index
-    )
-  }
-
-  static CreateEntity(class_id: ClassId): OperationType {
-    const value = new CreateEntity({ class_id })
-    return new OperationType({ CreateEntity: value })
+export class OperationType extends JoyEnum(OperationTypeDef) {
+  // TODO: Are those are worth preserving?
+  static CreateEntity(registry: Registry, class_id: ClassId): OperationType {
+    const value = new CreateEntity(registry, { class_id })
+    return new OperationType(registry, { CreateEntity: value })
   }
 
   static UpdatePropertyValues(
+    registry: Registry,
     entity_id: ParametrizedEntity,
     parametrized_property_values: ParameterizedClassPropertyValues
   ): OperationType {
-    const value = new UpdatePropertyValues({
+    const value = new UpdatePropertyValues(registry, {
       entity_id,
       parametrized_property_values,
     })
-    return new OperationType({ UpdatePropertyValues: value })
+    return new OperationType(registry, { UpdatePropertyValues: value })
   }
 
   static AddSchemaSupportToEntity(
+    registry: Registry,
     entity_id: ParametrizedEntity,
     schema_id: u16,
     parametrized_property_values: ParameterizedClassPropertyValues
   ): OperationType {
-    const value = new AddSchemaSupportToEntity({
+    const value = new AddSchemaSupportToEntity(registry, {
       entity_id,
       schema_id,
       parametrized_property_values,
     })
-    return new OperationType({ AddSchemaSupportToEntity: value })
+    return new OperationType(registry, { AddSchemaSupportToEntity: value })
   }
 }

+ 23 - 57
types/src/versioned-store/permissions/batching/operations.ts

@@ -1,8 +1,8 @@
 import ClassId from '../../ClassId'
-import { JoyStruct } from '../../../common'
 import { ParametrizedEntity } from './parametrized-entity'
 import { Vec, u16 } from '@polkadot/types'
 import ParametrizedClassPropertyValue from './ParametrizedClassPropertyValue'
+import { JoyStructDecorated } from '../../../common'
 
 // TODO Rename to ParametrizedClassPropertyValue
 export class ParameterizedClassPropertyValues extends Vec.with(ParametrizedClassPropertyValue) {}
@@ -22,62 +22,28 @@ export type IAddSchemaSupportToEntityOperation = {
   parametrized_property_values: ParameterizedClassPropertyValues
 }
 
-export class CreateEntityOperation extends JoyStruct<ICreateEntityOperation> {
-  constructor(value: ICreateEntityOperation) {
-    super(
-      {
-        class_id: ClassId,
-      },
-      value
-    )
-  }
-
-  get class_id(): ClassId {
-    return this.getField('class_id')
-  }
-}
-
-export class UpdatePropertyValuesOperation extends JoyStruct<IUpdatePropertyValuesOperation> {
-  constructor(value: IUpdatePropertyValuesOperation) {
-    super(
-      {
-        entity_id: ParametrizedEntity,
-        parametrized_property_values: ParameterizedClassPropertyValues,
-      },
-      value
-    )
-  }
-
-  get entity_id(): ParametrizedEntity {
-    return this.getField('entity_id')
-  }
-
+export class CreateEntityOperation
+  extends JoyStructDecorated({
+    class_id: ClassId,
+  })
+  implements ICreateEntityOperation {}
+
+export class UpdatePropertyValuesOperation
+  extends JoyStructDecorated({
+    entity_id: ParametrizedEntity,
+    parametrized_property_values: ParameterizedClassPropertyValues,
+  })
+  implements IUpdatePropertyValuesOperation {}
+
+export class AddSchemaSupportToEntityOperation
+  extends JoyStructDecorated({
+    entity_id: ParametrizedEntity,
+    schema_id: u16,
+    parametrized_property_values: ParameterizedClassPropertyValues,
+  })
+  implements IAddSchemaSupportToEntityOperation {
+  // Additional helper
   get property_values(): ParameterizedClassPropertyValues {
-    return this.getField('parametrized_property_values')
-  }
-}
-
-export class AddSchemaSupportToEntityOperation extends JoyStruct<IAddSchemaSupportToEntityOperation> {
-  constructor(value: IAddSchemaSupportToEntityOperation) {
-    super(
-      {
-        entity_id: ParametrizedEntity,
-        schema_id: u16,
-        parametrized_property_values: ParameterizedClassPropertyValues,
-      },
-      value
-    )
-  }
-
-  get entity_id(): ParametrizedEntity {
-    return this.getField('entity_id')
-  }
-
-  get property_values(): ParameterizedClassPropertyValues {
-    return this.getField('parametrized_property_values')
-  }
-
-  get schema_id(): u16 {
-    return this.getField('schema_id')
+    return this.parametrized_property_values
   }
 }

+ 13 - 22
types/src/versioned-store/permissions/batching/parametrized-entity.ts

@@ -1,32 +1,23 @@
-import { Enum, u32 } from '@polkadot/types'
+import { u32 } from '@polkadot/types'
 import EntityId from '../../EntityId'
+import { Registry } from '@polkadot/types/types'
+import { JoyEnum } from '../../../common'
 
 export class InternalEntityJustAdded extends u32 {}
 export class ExistingEntity extends EntityId {}
 
-export type ParametrizedEntityVariant = InternalEntityJustAdded | ExistingEntity
+export const ParametrizedEntityDef = {
+  InternalEntityJustAdded,
+  ExistingEntity,
+} as const
 
-type ParametrizedEntityValue = {
-  [typeName: string]: ParametrizedEntityVariant
-}
-
-export class ParametrizedEntity extends Enum {
-  constructor(value?: ParametrizedEntityValue, index?: number) {
-    super(
-      {
-        InternalEntityJustAdded,
-        ExistingEntity,
-      },
-      value,
-      index
-    )
-  }
-
-  static InternalEntityJustAdded(index: u32): ParametrizedEntity {
-    return new ParametrizedEntity({ InternalEntityJustAdded: new InternalEntityJustAdded(index) })
+export class ParametrizedEntity extends JoyEnum(ParametrizedEntityDef) {
+  // TODO: Are those worth preserving?
+  static InternalEntityJustAdded(registry: Registry, index: u32): ParametrizedEntity {
+    return new ParametrizedEntity(registry, { InternalEntityJustAdded: new InternalEntityJustAdded(registry, index) })
   }
 
-  static ExistingEntity(entity_id: EntityId): ParametrizedEntity {
-    return new ParametrizedEntity({ ExistingEntity: new ExistingEntity(entity_id) })
+  static ExistingEntity(registry: Registry, entity_id: EntityId): ParametrizedEntity {
+    return new ParametrizedEntity(registry, { ExistingEntity: new ExistingEntity(registry, entity_id) })
   }
 }

+ 21 - 26
types/src/versioned-store/permissions/batching/parametrized-property-value.ts

@@ -1,39 +1,34 @@
-import { Enum, u32, Vec } from '@polkadot/types'
+import { u32, Vec } from '@polkadot/types'
 import { PropertyValue as VersionedStorePropertyValue, PropertyValueEnumValue } from '../../PropertyValue'
 import { ParametrizedEntity } from './parametrized-entity'
+import { Registry } from '@polkadot/types/types'
+import { JoyEnum } from '../../../common'
 
 export class PropertyValue extends VersionedStorePropertyValue {}
 export class InternalEntityJustAdded extends u32 {}
 export class InternalEntityVec extends Vec.with(ParametrizedEntity) {}
 
-export type ParametrizedPropertyValueVariant = PropertyValue | InternalEntityJustAdded | InternalEntityVec
-
-type ParametrizedPropertyValueType = {
-  [typeName: string]: ParametrizedPropertyValueVariant
-}
-
-export class ParametrizedPropertyValue extends Enum {
-  constructor(value?: ParametrizedPropertyValueType, index?: number) {
-    super(
-      {
-        PropertyValue,
-        InternalEntityJustAdded,
-        InternalEntityVec,
-      },
-      value,
-      index
-    )
-  }
-
-  static PropertyValue(value: PropertyValueEnumValue): ParametrizedPropertyValue {
-    return new ParametrizedPropertyValue({ PropertyValue: new VersionedStorePropertyValue(value) })
+export const ParametrizedPropertyValueDef = {
+  PropertyValue,
+  InternalEntityJustAdded,
+  InternalEntityVec,
+} as const
+export class ParametrizedPropertyValue extends JoyEnum(ParametrizedPropertyValueDef) {
+  // TODO: Are those worth preserving?
+  static PropertyValue(registry: Registry, value: PropertyValueEnumValue): ParametrizedPropertyValue {
+    return new ParametrizedPropertyValue(registry, { PropertyValue: new VersionedStorePropertyValue(registry, value) })
   }
 
-  static InternalEntityJustAdded(index: number | u32): ParametrizedPropertyValue {
-    return new ParametrizedPropertyValue({ InternalEntityJustAdded: new InternalEntityJustAdded(index) })
+  static InternalEntityJustAdded(registry: Registry, index: number | u32): ParametrizedPropertyValue {
+    return new ParametrizedPropertyValue(registry, {
+      InternalEntityJustAdded: new InternalEntityJustAdded(registry, index),
+    })
   }
 
-  static InternalEntityVec(entities: ParametrizedEntity[] | Vec<ParametrizedEntity>): ParametrizedPropertyValue {
-    return new ParametrizedPropertyValue({ InternalEntityVec: new InternalEntityVec(entities) })
+  static InternalEntityVec(
+    registry: Registry,
+    entities: ParametrizedEntity[] | Vec<ParametrizedEntity>
+  ): ParametrizedPropertyValue {
+    return new ParametrizedPropertyValue(registry, { InternalEntityVec: new InternalEntityVec(registry, entities) })
   }
 }

+ 8 - 13
types/src/versioned-store/permissions/index.ts

@@ -1,21 +1,16 @@
-import { getTypeRegistry } from '@polkadot/types'
-
 import EntityPermissions from './EntityPermissions'
 import { ReferenceConstraint } from './reference-constraint'
 import ClassPermissionsType from './ClassPermissions'
 import { Operation } from './batching/'
+import { RegistryTypes } from '@polkadot/types/types'
 
 export { EntityPermissions, ReferenceConstraint, ClassPermissionsType, Operation }
 
-export function registerVersionedStorePermissionsTypes() {
-  try {
-    getTypeRegistry().register({
-      EntityPermissions,
-      ReferenceConstraint,
-      ClassPermissionsType,
-      Operation,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of versioned store module', err)
-  }
+export const versionedStorePermissionsTypes: RegistryTypes = {
+  EntityPermissions,
+  ReferenceConstraint,
+  ClassPermissionsType,
+  Operation,
 }
+
+export default versionedStorePermissionsTypes

+ 9 - 32
types/src/versioned-store/permissions/reference-constraint.ts

@@ -1,38 +1,15 @@
-import { Enum, Null, Vec } from '@polkadot/types'
+import { Null, BTreeSet } from '@polkadot/types'
 import PropertyOfClass from './PropertyOfClass'
+import { JoyEnum } from '../../common'
 
 export class NoReferencingAllowed extends Null {}
 export class NoConstraint extends Null {}
-export class Restricted extends Vec.with(PropertyOfClass) {} // BtreeSet ?
+export class Restricted extends BTreeSet.with(PropertyOfClass) {}
 
-export type ReferenceConstraintVariant = NoReferencingAllowed | NoConstraint | Restricted
+export const ReferenceConstraintDef = {
+  NoReferencingAllowed,
+  NoConstraint,
+  Restricted,
+} as const
 
-export type ReferenceConstraintValue = {
-  [typeName: string]: ReferenceConstraintVariant
-}
-
-export class ReferenceConstraint extends Enum {
-  constructor(value?: ReferenceConstraintValue, index?: number) {
-    super(
-      {
-        NoReferencingAllowed,
-        NoConstraint,
-        Restricted,
-      },
-      value,
-      index
-    )
-  }
-
-  static NoReferencingAllowed(): ReferenceConstraint {
-    return new ReferenceConstraint({ NoReferencingAllowed: new NoReferencingAllowed() })
-  }
-
-  static NoConstraint(): ReferenceConstraint {
-    return new ReferenceConstraint({ NoConstraint: new NoConstraint() })
-  }
-
-  static Restricted(restrictions: Vec<PropertyOfClass>): ReferenceConstraint {
-    return new ReferenceConstraint({ Restricted: new Restricted(restrictions) })
-  }
-}
+export class ReferenceConstraint extends JoyEnum(ReferenceConstraintDef) {}

+ 87 - 238
types/src/working-group/index.ts

@@ -1,12 +1,13 @@
-import { getTypeRegistry, Bytes, BTreeMap, Option } from '@polkadot/types'
-import { u16, Null } from '@polkadot/types/primitive'
-import { AccountId, BlockNumber, Balance } from '@polkadot/types/interfaces'
-import { BTreeSet, JoyStruct } from '../common'
+import { Bytes, BTreeMap, BTreeSet, Option } from '@polkadot/types'
+import { u16, Null, u32, u128 } from '@polkadot/types/primitive'
+import AccountId from '@polkadot/types/generic/AccountId'
+import { BlockNumber, Balance } from '@polkadot/types/interfaces'
 import { MemberId, ActorId } from '../members'
 import { RewardRelationshipId } from '../recurring-rewards'
 import { StakeId } from '../stake'
 import { ApplicationId, OpeningId, ApplicationRationingPolicy, StakingPolicy } from '../hiring'
-import { JoyEnum } from '../JoyEnum'
+import { JoyEnum, JoyStructDecorated } from '../common'
+import { RegistryTypes } from '@polkadot/types/types'
 
 export class RationaleText extends Bytes {}
 
@@ -20,35 +21,14 @@ export type IApplication = {
 // This type is also defined in /hiring (and those are incosistent), but here
 // it is beeing registered as "ApplicationOf" (which is an alias used by the runtime working-group module),
 // so it shouldn't cause any conflicts
-export class Application extends JoyStruct<IApplication> {
-  constructor(value?: IApplication) {
-    super(
-      {
-        role_account_id: 'AccountId',
-        opening_id: OpeningId,
-        member_id: MemberId,
-        application_id: ApplicationId,
-      },
-      value
-    )
-  }
-
-  get role_account_id(): AccountId {
-    return this.getField<AccountId>('role_account_id')
-  }
-
-  get opening_id(): OpeningId {
-    return this.getField<OpeningId>('opening_id')
-  }
-
-  get member_id(): MemberId {
-    return this.getField<MemberId>('member_id')
-  }
-
-  get application_id(): ApplicationId {
-    return this.getField<ApplicationId>('application_id')
-  }
-}
+export class Application
+  extends JoyStructDecorated({
+    role_account_id: AccountId,
+    opening_id: OpeningId,
+    member_id: MemberId,
+    application_id: ApplicationId,
+  })
+  implements IApplication {}
 
 export class WorkerId extends ActorId {}
 
@@ -64,30 +44,13 @@ export type IRoleStakeProfile = {
   exit_unstaking_period: Option<BlockNumber>
 }
 
-export class RoleStakeProfile extends JoyStruct<IRoleStakeProfile> {
-  constructor(value?: IRoleStakeProfile) {
-    super(
-      {
-        stake_id: StakeId,
-        termination_unstaking_period: 'Option<BlockNumber>',
-        exit_unstaking_period: 'Option<BlockNumber>',
-      },
-      value
-    )
-  }
-
-  get stake_id(): StakeId {
-    return this.getField<StakeId>('stake_id')
-  }
-
-  get termination_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('termination_unstaking_period')
-  }
-
-  get exit_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('exit_unstaking_period')
-  }
-}
+export class RoleStakeProfile
+  extends JoyStructDecorated({
+    stake_id: StakeId,
+    termination_unstaking_period: Option.with(u32), // Option<BlockNumber>
+    exit_unstaking_period: Option.with(u32), // Option<BlockNumber>
+  })
+  implements IRoleStakeProfile {}
 
 export type IWorker = {
   member_id: MemberId
@@ -96,35 +59,15 @@ export type IWorker = {
   role_stake_profile: Option<RoleStakeProfile>
 }
 
-export class Worker extends JoyStruct<IWorker> {
-  constructor(value?: IWorker) {
-    super(
-      {
-        member_id: MemberId,
-        role_account_id: 'AccountId',
-        reward_relationship: Option.with(RewardRelationshipId),
-        role_stake_profile: Option.with(RoleStakeProfile),
-      },
-      value
-    )
-  }
-
-  get member_id(): MemberId {
-    return this.getField<MemberId>('member_id')
-  }
-
-  get role_account_id(): AccountId {
-    return this.getField<AccountId>('role_account_id')
-  }
-
-  get reward_relationship(): Option<RewardRelationshipId> {
-    return this.getField<Option<RewardRelationshipId>>('reward_relationship')
-  }
-
-  get role_stake_profile(): Option<RoleStakeProfile> {
-    return this.getField<Option<RoleStakeProfile>>('role_stake_profile')
-  }
-
+export class Worker
+  extends JoyStructDecorated({
+    member_id: MemberId,
+    role_account_id: AccountId,
+    reward_relationship: Option.with(RewardRelationshipId),
+    role_stake_profile: Option.with(RoleStakeProfile),
+  })
+  implements IWorker {
+  // FIXME: Won't be needed soon?
   get is_active(): boolean {
     return !this.isEmpty
   }
@@ -138,17 +81,12 @@ export type ISlashableTerms = {
 // This type is also defined in /content-working-group, but currently both those definitions are identical
 // (I added this defininition here too, because techinicaly those are 2 different types in the runtime.
 // Later the definition in /content-working-group will be removed and we can just register this type here)
-export class SlashableTerms extends JoyStruct<ISlashableTerms> {
-  constructor(value?: ISlashableTerms) {
-    super(
-      {
-        max_count: u16,
-        max_percent_pts_per_time: u16,
-      },
-      value
-    )
-  }
-}
+export class SlashableTerms
+  extends JoyStructDecorated({
+    max_count: u16,
+    max_percent_pts_per_time: u16,
+  })
+  implements ISlashableTerms {}
 
 export class UnslashableTerms extends Null {}
 
@@ -184,75 +122,22 @@ export type IWorkingGroupOpeningPolicyCommitment = {
 // Since both those types are basically the same structs (only filed names are different) nothing seems to break, but it's
 // very fragile atm and any change to this type in working-group module could result in "unsolvable" inconsistencies
 // (this won't be an issue after CWG gets refactored to use the working-grpup module too)
-export class WorkingGroupOpeningPolicyCommitment extends JoyStruct<IWorkingGroupOpeningPolicyCommitment> {
-  constructor(value?: IWorkingGroupOpeningPolicyCommitment) {
-    super(
-      {
-        application_rationing_policy: Option.with(ApplicationRationingPolicy),
-        max_review_period_length: 'BlockNumber',
-        application_staking_policy: Option.with(StakingPolicy),
-        role_staking_policy: Option.with(StakingPolicy),
-        role_slashing_terms: SlashingTerms,
-        fill_opening_successful_applicant_application_stake_unstaking_period: 'Option<BlockNumber>',
-        fill_opening_failed_applicant_application_stake_unstaking_period: 'Option<BlockNumber>',
-        fill_opening_failed_applicant_role_stake_unstaking_period: 'Option<BlockNumber>',
-        terminate_application_stake_unstaking_period: 'Option<BlockNumber>',
-        terminate_role_stake_unstaking_period: 'Option<BlockNumber>',
-        exit_role_application_stake_unstaking_period: 'Option<BlockNumber>',
-        exit_role_stake_unstaking_period: 'Option<BlockNumber>',
-      },
-      value
-    )
-  }
-
-  get application_rationing_policy(): Option<ApplicationRationingPolicy> {
-    return this.getField<Option<ApplicationRationingPolicy>>('application_rationing_policy')
-  }
-
-  get max_review_period_length(): BlockNumber {
-    return this.getField<BlockNumber>('max_review_period_length')
-  }
-
-  get application_staking_policy(): Option<StakingPolicy> {
-    return this.getField<Option<StakingPolicy>>('application_staking_policy')
-  }
-
-  get role_staking_policy(): Option<StakingPolicy> {
-    return this.getField<Option<StakingPolicy>>('role_staking_policy')
-  }
-
-  get role_slashing_terms(): SlashingTerms {
-    return this.getField<SlashingTerms>('role_slashing_terms')
-  }
-
-  get fill_opening_successful_applicant_application_stake_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('fill_opening_successful_applicant_application_stake_unstaking_period')
-  }
-
-  get fill_opening_failed_applicant_application_stake_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('fill_opening_failed_applicant_application_stake_unstaking_period')
-  }
-
-  get fill_opening_failed_applicant_role_stake_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('fill_opening_failed_applicant_role_stake_unstaking_period')
-  }
-
-  get terminate_application_stake_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('terminate_application_stake_unstaking_period')
-  }
-
-  get terminate_role_stake_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('terminate_role_stake_unstaking_period')
-  }
-
-  get exit_role_application_stake_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('exit_role_application_stake_unstaking_period')
-  }
-
-  get exit_role_stake_unstaking_period(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('exit_role_stake_unstaking_period')
-  }
-}
+export class WorkingGroupOpeningPolicyCommitment
+  extends JoyStructDecorated({
+    application_rationing_policy: Option.with(ApplicationRationingPolicy),
+    max_review_period_length: u32, // BlockNumber
+    application_staking_policy: Option.with(StakingPolicy),
+    role_staking_policy: Option.with(StakingPolicy),
+    role_slashing_terms: SlashingTerms,
+    fill_opening_successful_applicant_application_stake_unstaking_period: Option.with(u32),
+    fill_opening_failed_applicant_application_stake_unstaking_period: Option.with(u32),
+    fill_opening_failed_applicant_role_stake_unstaking_period: Option.with(u32),
+    terminate_application_stake_unstaking_period: Option.with(u32),
+    terminate_role_stake_unstaking_period: Option.with(u32),
+    exit_role_application_stake_unstaking_period: Option.with(u32),
+    exit_role_stake_unstaking_period: Option.with(u32),
+  })
+  implements IWorkingGroupOpeningPolicyCommitment {}
 
 export class OpeningType_Leader extends Null {}
 export class OpeningType_Worker extends Null {}
@@ -273,35 +158,14 @@ export type IOpening = {
 // This type is also defined in /hiring (and those are incosistent), but here
 // it is beeing registered as "OpeningOf" (which is an alias used by the runtime working-group module),
 // so it shouldn't cause any conflicts
-export class Opening extends JoyStruct<IOpening> {
-  constructor(value?: IWorker) {
-    super(
-      {
-        hiring_opening_id: OpeningId,
-        applications: BTreeSet.with(ApplicationId),
-        policy_commitment: WorkingGroupOpeningPolicyCommitment,
-        opening_type: OpeningType,
-      },
-      value
-    )
-  }
-
-  get hiring_opening_id(): OpeningId {
-    return this.getField<OpeningId>('hiring_opening_id')
-  }
-
-  get applications(): BTreeSet<ApplicationId> {
-    return this.getField<BTreeSet<ApplicationId>>('applications')
-  }
-
-  get policy_commitment(): WorkingGroupOpeningPolicyCommitment {
-    return this.getField<WorkingGroupOpeningPolicyCommitment>('policy_commitment')
-  }
-
-  get opening_type(): OpeningType {
-    return this.getField<OpeningType>('opening_type')
-  }
-}
+export class Opening
+  extends JoyStructDecorated({
+    hiring_opening_id: OpeningId,
+    applications: BTreeSet.with(ApplicationId),
+    policy_commitment: WorkingGroupOpeningPolicyCommitment,
+    opening_type: OpeningType,
+  })
+  implements IOpening {}
 
 // Also defined in "content-working-group" runtime module, but those definitions are the consistent
 export type IRewardPolicy = {
@@ -310,47 +174,32 @@ export type IRewardPolicy = {
   payout_interval: Option<BlockNumber>
 }
 
-export class RewardPolicy extends JoyStruct<IRewardPolicy> {
-  constructor(value?: IRewardPolicy) {
-    super(
-      {
-        amount_per_payout: 'Balance',
-        next_payment_at_block: 'BlockNumber',
-        payout_interval: 'Option<BlockNumber>',
-      },
-      value
-    )
-  }
-  get amount_per_payout(): Balance {
-    return this.getField<Balance>('amount_per_payout')
-  }
-  get next_payment_at_block(): BlockNumber {
-    return this.getField<BlockNumber>('next_payment_at_block')
-  }
-  get payout_interval(): Option<BlockNumber> {
-    return this.getField<Option<BlockNumber>>('payout_interval')
-  }
+export class RewardPolicy
+  extends JoyStructDecorated({
+    amount_per_payout: u128, // Balance
+    next_payment_at_block: u32, // BlockNumber
+    payout_interval: Option.with(u32), // Option<BlockNumber>
+  })
+  implements IRewardPolicy {}
+
+// Needed for types augment tool
+export { OpeningId, ApplicationId }
+
+export const workingGroupTypes: RegistryTypes = {
+  RationaleText,
+  ApplicationOf: Application,
+  ApplicationIdSet,
+  ApplicationIdToWorkerIdMap,
+  WorkerId,
+  WorkerOf: Worker,
+  OpeningOf: Opening,
+  StorageProviderId,
+  OpeningType,
+  /// Alias used by the runtime working-group module
+  HiringApplicationId: ApplicationId,
+  RewardPolicy,
+  'working_group::OpeningId': OpeningId,
+  'working_group::WorkerId': WorkerId,
 }
 
-export function registerWorkingGroupTypes() {
-  try {
-    getTypeRegistry().register({
-      RationaleText,
-      ApplicationOf: Application,
-      ApplicationIdSet,
-      ApplicationIdToWorkerIdMap,
-      WorkerId,
-      WorkerOf: Worker,
-      OpeningOf: Opening,
-      StorageProviderId,
-      OpeningType,
-      /// Alias used by the runtime working-group module
-      HiringApplicationId: ApplicationId,
-      RewardPolicy,
-      'working_group::OpeningId': OpeningId,
-      'working_group::WorkerId': WorkerId,
-    })
-  } catch (err) {
-    console.error('Failed to register custom types of working-group module', err)
-  }
-}
+export default workingGroupTypes

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor