浏览代码

merged iznik

Gleb Urvanov 4 年之前
父节点
当前提交
b2f97f42d7

+ 2 - 1
package.json

@@ -22,7 +22,8 @@
     "pioneer",
     "pioneer/packages/apps*",
     "pioneer/packages/page*",
-    "pioneer/packages/react*"
+    "pioneer/packages/react*",
+    "utils/api-examples"
   ],
   "resolutions": {
     "@polkadot/api": "1.26.1",

+ 3 - 11
pioneer/packages/joy-proposals/src/Proposal/ProposalDetails.tsx

@@ -5,7 +5,7 @@ import Body from './Body';
 import VotingSection from './VotingSection';
 import Votes from './Votes';
 import { MyAccountProps, withMyAccount } from '@polkadot/joy-utils/MyAccount';
-import { ParsedProposal, ProposalVotes } from '@polkadot/joy-utils/types/proposals';
+import { ParsedProposal } from '@polkadot/joy-utils/types/proposals';
 import { withCalls } from '@polkadot/react-api';
 import { withMulti } from '@polkadot/react-api/with';
 
@@ -14,7 +14,6 @@ import { ProposalId, ProposalDecisionStatuses, ApprovedProposalStatuses, Executi
 import { BlockNumber } from '@polkadot/types/interfaces';
 import { MemberId } from '@joystream/types/members';
 import { Seat } from '@joystream/types/council';
-import { PromiseComponent } from '@polkadot/joy-utils/react/components';
 import ProposalDiscussion from './discussion/ProposalDiscussion';
 
 import styled from 'styled-components';
@@ -115,7 +114,6 @@ export function getExtendedStatus (proposal: ParsedProposal, bestNumber: BlockNu
 type ProposalDetailsProps = MyAccountProps & {
   proposal: ParsedProposal;
   proposalId: ProposalId;
-  votesListState: { data: ProposalVotes | null; error: any; loading: boolean };
   bestNumber?: BlockNumber;
   council?: Seat[];
 };
@@ -127,8 +125,7 @@ function ProposalDetails ({
   myMemberId,
   iAmMember,
   council,
-  bestNumber,
-  votesListState
+  bestNumber
 }: ProposalDetailsProps) {
   const iAmCouncilMember = Boolean(iAmMember && council && council.some(seat => seat.member.toString() === myAddress));
   const iAmProposer = Boolean(iAmMember && myMemberId !== undefined && proposal.proposerId === myMemberId.toNumber());
@@ -156,12 +153,7 @@ function ProposalDetails ({
               memberId={ myMemberId as MemberId }
               isVotingPeriod={ isVotingPeriod }/>
           ) }
-          <PromiseComponent
-            error={votesListState.error}
-            loading={votesListState.loading}
-            message="Fetching the votes...">
-            <Votes votes={votesListState.data as ProposalVotes} />
-          </PromiseComponent>
+          <Votes proposal={proposal}/>
         </ProposalDetailsVoting>
       </ProposalDetailsMain>
       <ProposalDetailsDiscussion>

+ 2 - 2
pioneer/packages/joy-proposals/src/Proposal/ProposalFromId.tsx

@@ -12,14 +12,14 @@ export default function ProposalFromId (props: RouteComponentProps<any>) {
     }
   } = props;
 
-  const { proposal: proposalState, votes: votesState } = useProposalSubscription(new ProposalId(id));
+  const proposalState = useProposalSubscription(new ProposalId(id));
 
   return (
     <PromiseComponent
       error={proposalState.error}
       loading={proposalState.loading}
       message={'Fetching proposal...'}>
-      <ProposalDetails proposal={ proposalState.data } proposalId={ id } votesListState={ votesState }/>
+      <ProposalDetails proposal={ proposalState.data } proposalId={ id }/>
     </PromiseComponent>
   );
 }

+ 53 - 36
pioneer/packages/joy-proposals/src/Proposal/Votes.tsx

@@ -1,50 +1,67 @@
 import React from 'react';
 import { Header, Divider, Table, Icon } from 'semantic-ui-react';
 import useVoteStyles from './useVoteStyles';
-import { ProposalVotes } from '@polkadot/joy-utils/types/proposals';
 import { VoteKind } from '@joystream/types/proposals';
 import { VoteKindStr } from './VotingSection';
 import ProfilePreview from '@polkadot/joy-utils/MemberProfilePreview';
+import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks';
+import { ParsedProposal, ProposalVotes } from '@polkadot/joy-utils/types/proposals';
+import { PromiseComponent } from '@polkadot/joy-utils/react/components';
 
 type VotesProps = {
-  votes: ProposalVotes;
+  proposal: ParsedProposal;
 };
 
-export default function Votes ({ votes }: VotesProps) {
-  if (!votes.votes.length) {
-    return <Header as="h4">No votes have been submitted!</Header>;
-  }
+export default function Votes ({ proposal: { id, votingResults } }: VotesProps) {
+  const transport = useTransport();
+  const [votes, error, loading] = usePromise<ProposalVotes | null>(
+    () => transport.proposals.votes(id),
+    null,
+    [votingResults]
+  );
 
   return (
-    <>
-      <Header as="h3">
-        All Votes: ({votes.votes.length}/{votes.councilMembersLength})
-      </Header>
-      <Divider />
-      <Table basic="very">
-        <Table.Body>
-          {votes.votes.map((proposalVote, idx) => {
-            const { vote, member } = proposalVote;
-            const voteStr = (vote as VoteKind).type.toString() as VoteKindStr;
-            const { icon, textColor } = useVoteStyles(voteStr);
-            return (
-              <Table.Row key={`${member.handle}-${idx}`}>
-                <Table.Cell className={textColor}>
-                  <Icon name={icon} />
-                  {voteStr}
-                </Table.Cell>
-                <Table.Cell>
-                  <ProfilePreview
-                    handle={member.handle}
-                    avatar_uri={member.avatar_uri}
-                    root_account={member.root_account}
-                  />
-                </Table.Cell>
-              </Table.Row>
-            );
-          })}
-        </Table.Body>
-      </Table>
-    </>
+    <PromiseComponent
+      error={error}
+      loading={loading}
+      message="Fetching the votes...">
+      { (votes && votes.votes.length > 0)
+        ? (
+          <>
+            <Header as="h3">
+              All Votes: ({votes.votes.length}/{votes.councilMembersLength})
+            </Header>
+            <Divider />
+            <Table basic="very">
+              <Table.Body>
+                {votes.votes.map((proposalVote, idx) => {
+                  const { vote, member } = proposalVote;
+                  const voteStr = (vote as VoteKind).type.toString() as VoteKindStr;
+                  const { icon, textColor } = useVoteStyles(voteStr);
+                  return (
+                    <Table.Row key={`${member.handle}-${idx}`}>
+                      <Table.Cell className={textColor}>
+                        <Icon name={icon} />
+                        {voteStr}
+                      </Table.Cell>
+                      <Table.Cell>
+                        <ProfilePreview
+                          handle={member.handle}
+                          avatar_uri={member.avatar_uri}
+                          root_account={member.root_account}
+                        />
+                      </Table.Cell>
+                    </Table.Row>
+                  );
+                })}
+              </Table.Body>
+            </Table>
+          </>
+        )
+        : (
+          <Header as="h4">No votes have been submitted!</Header>
+        )
+      }
+    </PromiseComponent>
   );
 }

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

@@ -548,7 +548,7 @@ export class Transport extends TransportBase implements ITransport {
   }
 
   async blockHash (height: number): Promise<string> {
-    const blockHash = await this.cachedApi.query.system.blockHash(height);
+    const blockHash = await this.api.rpc.chain.getBlockHash(height);
     return blockHash.toString();
   }
 

+ 27 - 26
pioneer/packages/joy-utils-old/src/react/hooks/proposals/useProposalSubscription.tsx

@@ -1,39 +1,41 @@
 import { useState, useEffect } from 'react';
-import { ParsedProposal, ProposalVotes } from '../../../types/proposals';
-import { useTransport, usePromise } from '../';
+import { useTransport } from '../';
 import { ProposalId } from '@joystream/types/proposals';
+import { ParsedProposal } from '@polkadot/joy-utils/types/proposals';
 
 // Take advantage of polkadot api subscriptions to re-fetch proposal data and votes
 // each time there is some runtime change in the proposal
 const useProposalSubscription = (id: ProposalId) => {
   const transport = useTransport();
-  // State holding an "unsubscribe method"
-  const [unsubscribeProposal, setUnsubscribeProposal] = useState<(() => void) | null>(null);
-
-  const [proposal, proposalError, proposalLoading, refreshProposal] = usePromise<ParsedProposal>(
-    () => transport.proposals.proposalById(id),
-    {} as ParsedProposal
-  );
-
-  const [votes, votesError, votesLoading, refreshVotes] = usePromise<ProposalVotes | null>(
-    () => transport.proposals.votes(id),
-    null
-  );
-
-  // Function to re-fetch the data using transport
-  const refreshProposalData = () => {
-    refreshProposal();
-    refreshVotes();
-  };
+  // State holding current proposal data
+  const [data, setData] = useState<ParsedProposal | null>(null);
+  const [error, setError] = useState<any>(null);
+  const [loading, setLoading] = useState<boolean>(true);
 
   useEffect(() => {
     // onMount...
     let unmounted = false;
+    let unsubscribeProposal: (() => void) | undefined;
+    const refreshProposalData = () => {
+      transport.proposals.proposalById(id)
+        .then(newData => {
+          if (!unmounted) {
+            setData(newData);
+            setLoading(false);
+          }
+        })
+        .catch(error => {
+          if (!unmounted) {
+            setError(error);
+            setLoading(false);
+          }
+        });
+    };
     // Create the subscription
     transport.proposals.subscribeProposal(id, refreshProposalData)
       .then(unsubscribe => {
         if (!unmounted) {
-          setUnsubscribeProposal(() => unsubscribe);
+          unsubscribeProposal = unsubscribe;
         } else {
           unsubscribe(); // If already unmounted - unsubscribe immedietally!
         }
@@ -42,14 +44,13 @@ const useProposalSubscription = (id: ProposalId) => {
       // onUnmount...
       // Clean the subscription
       unmounted = true;
-      if (unsubscribeProposal !== null) unsubscribeProposal();
+      if (unsubscribeProposal) {
+        unsubscribeProposal();
+      }
     };
   }, []);
 
-  return {
-    proposal: { data: proposal, error: proposalError, loading: proposalLoading },
-    votes: { data: votes, error: votesError, loading: votesLoading }
-  };
+  return { data, error, loading };
 };
 
 export default useProposalSubscription;

+ 4 - 1
types/src/definitions/augment-types.ts

@@ -59,7 +59,7 @@ import { Class as Class, Entity as Entity, ClassSchema as ClassSchema, Property
 import { EntityPermissions as EntityPermissions, ReferenceConstraint as ReferenceConstraint, ClassPermissionsType as ClassPermissionsType, Operation as Operation, OperationType as OperationType, CreateEntity as CreateEntity, UpdatePropertyValues as UpdatePropertyValues, AddSchemaSupportToEntity as AddSchemaSupportToEntity, ParametrizedEntity as ParametrizedEntity, ParametrizedClassPropertyValue as ParametrizedClassPropertyValue, ParametrizedPropertyValue as ParametrizedPropertyValue } from '../versioned-store/permissions'
 import { OptionalText as OptionalText, Channel as Channel, ChannelContentType as ChannelContentType, ChannelCurationStatus as ChannelCurationStatus, ChannelPublicationStatus as ChannelPublicationStatus, CurationActor as CurationActor, Curator as Curator, CuratorApplication as CuratorApplication, CuratorOpening as CuratorOpening, Lead as Lead, OpeningPolicyCommitment as OpeningPolicyCommitment, Principal as Principal, WorkingGroupUnstaker as WorkingGroupUnstaker, CuratorApplicationIdToCuratorIdMap as CuratorApplicationIdToCuratorIdMap, CuratorApplicationIdSet as CuratorApplicationIdSet, CuratorRoleStakeProfile as CuratorRoleStakeProfile, CuratorRoleStage as CuratorRoleStage, CuratorExitSummary as CuratorExitSummary, CuratorExitInitiationOrigin as CuratorExitInitiationOrigin, ExitedLeadRole as ExitedLeadRole, CuratorInduction as CuratorInduction } from '../content-working-group'
 import { RationaleText as RationaleText, Application as ApplicationOf, ApplicationIdSet as ApplicationIdSet, ApplicationIdToWorkerIdMap as ApplicationIdToWorkerIdMap, WorkerId as WorkerId, Worker as WorkerOf, Opening as OpeningOf, StorageProviderId as StorageProviderId, OpeningType as OpeningType, ApplicationId as HiringApplicationId, RewardPolicy as RewardPolicy, OpeningId as working_group__OpeningId, WorkerId as working_group__WorkerId, WorkingGroupOpeningPolicyCommitment as WorkingGroupOpeningPolicyCommitment, RoleStakeProfile as RoleStakeProfile } from '../working-group'
-import { Url as Url, IPNSIdentity as IPNSIdentity } from '../discovery'
+import { Url as Url, IPNSIdentity as IPNSIdentity, ServiceProviderRecord as ServiceProviderRecord } from '../discovery'
 import { ContentId as ContentId, LiaisonJudgement as LiaisonJudgement, DataObject as DataObject, DataObjectStorageRelationshipId as DataObjectStorageRelationshipId, DataObjectStorageRelationship as DataObjectStorageRelationship, DataObjectTypeId as DataObjectTypeId, DataObjectType as DataObjectType, DataObjectsMap as DataObjectsMap } from '../media'
 import { ProposalId as ProposalId, ProposalStatus as ProposalStatus, Proposal as ProposalOf, ProposalDetails as ProposalDetails, ProposalDetails as ProposalDetailsOf, VotingResults as VotingResults, ProposalParameters as ProposalParameters, VoteKind as VoteKind, ThreadCounter as ThreadCounter, DiscussionThread as DiscussionThread, DiscussionPost as DiscussionPost, AddOpeningParameters as AddOpeningParameters, FillOpeningParameters as FillOpeningParameters, TerminateRoleParameters as TerminateRoleParameters, ActiveStake as ActiveStake, FinalizationData as FinalizationData, ProposalDecisionStatus as ProposalDecisionStatus, ExecutionFailed as ExecutionFailed } from '../proposals'
 /** /CUSTOMIMPORTS **/
@@ -2282,6 +2282,9 @@ declare module '@polkadot/types/types/registry' {
     "IPNSIdentity": IPNSIdentity;
     "Option<IPNSIdentity>": Option<IPNSIdentity>;
     "Vec<IPNSIdentity>": Vec<IPNSIdentity>;
+    "ServiceProviderRecord": ServiceProviderRecord;
+    "Option<ServiceProviderRecord>": Option<ServiceProviderRecord>;
+    "Vec<ServiceProviderRecord>": Vec<ServiceProviderRecord>;
     "ContentId": ContentId;
     "Option<ContentId>": Option<ContentId>;
     "Vec<ContentId>": Vec<ContentId>;

+ 46 - 0
utils/api-examples/README.md

@@ -0,0 +1,46 @@
+# Joystream API Examples
+
+Repo with examples on how to use the @joystream/types package along with @polkadot/api to communicate with a joystream full node.
+
+## Examples
+
+```
+yarn
+yarn run status
+```
+
+## Example code
+
+```javascript
+import { types } from '@joystream/types'
+import { ApiPromise, WsProvider } from '@polkadot/api'
+
+async function main() {
+  // Initialise the provider to connect to the local node
+  const provider = new WsProvider('ws://127.0.0.1:9944')
+
+  // Create the API and wait until ready
+  const api = await ApiPromise.create({ provider, types })
+
+  await api.isReady
+
+  // Retrieve the chain & node information information via rpc calls
+  const [chain, nodeName, nodeVersion] = await Promise.all([
+    api.rpc.system.chain(),
+    api.rpc.system.name(),
+    api.rpc.system.version(),
+  ])
+
+  console.log(`Chain ${chain} using ${nodeName} v${nodeVersion}`)
+}
+
+main()
+```
+
+### Scripts
+
+You can run scripts that are found in the [./scripts/](./scripts) folder:
+
+```sh
+yarn script example
+```

+ 25 - 0
utils/api-examples/package.json

@@ -0,0 +1,25 @@
+{
+  "name": "api-examples",
+  "private": true,
+  "version": "0.1.0",
+  "license": "GPL-3.0-only",
+  "scripts": {
+    "status": "ts-node src/status",
+    "script": "ts-node src/script"
+  },
+  "dependencies": {
+    "@joystream/types": "^0.13.0",
+    "@polkadot/api": "^1.26.1",
+    "@polkadot/types": "^1.26.1",
+    "@polkadot/keyring": "^3.0.1",
+    "@polkadot/util": "^3.0.1",
+    "@polkadot/util-crypto": "^3.0.1",
+    "@types/bn.js": "^4.11.5",
+    "bn.js": "^4.11.8"
+  },
+  "devDependencies": {
+    "@polkadot/ts": "^0.1.56",
+    "typescript": "^3.9.7",
+    "ts-node": "^8.6.2"
+  }
+}

+ 32 - 0
utils/api-examples/scripts/example.js

@@ -0,0 +1,32 @@
+/* global api, hashing, keyring, types, util, joy */
+
+// run this script with:
+// yarn script example
+//
+// or copy and paste the code into the pioneer javascript toolbox at:
+// https://testnet.joystream.org/#/js
+//
+// Example works on nicaea release+
+
+const script = async ({ api, hashing, keyring, types, util, joy }) => {
+  // Retrieve the chain & node information information via rpc calls
+  const [chain, nodeName, nodeVersion, runtimeVersion] = await Promise.all([
+    api.rpc.system.chain(),
+    api.rpc.system.name(),
+    api.rpc.system.version(),
+    api.runtimeVersion,
+  ])
+
+  console.log(`Chain: ${chain}`)
+  console.log(`Software: ${nodeName} v${nodeVersion}`)
+  console.log(`Runtime specVersion: ${runtimeVersion.specVersion}`)
+}
+
+if (typeof module === 'undefined') {
+  // Pioneer js-toolbox
+  // Available globals [api, hashing, keyring, types, util]
+  script({ api, hashing, keyring, types, util, joy })
+} else {
+  // Node
+  module.exports = script
+}

+ 49 - 0
utils/api-examples/scripts/export-data-directory.js

@@ -0,0 +1,49 @@
+/* global api, hashing, keyring, types, util */
+
+// run this script with:
+// yarn script exportDataDirectory
+//
+// or copy and paste the code into the pioneer javascript toolbox at:
+// https://testnet.joystream.org/#/js
+
+const script = async ({ api }) => {
+  const ids = await api.query.dataDirectory.knownContentIds()
+
+  // When a BTreeMap is constructed for injection the node will fail to decode
+  // it if its not sorted.
+  ids.sort()
+
+  const transformed = await Promise.all(
+    ids.map(async (id) => {
+      let obj = await api.query.dataDirectory.dataObjectByContentId(id)
+      if (obj.isNone) {
+        return null
+      }
+      obj = obj.unwrap()
+
+      return [
+        id,
+        {
+          owner: obj.owner,
+          added_at: obj.added_at,
+          type_id: obj.type_id,
+          size: obj.size_in_bytes,
+          liaison: obj.liaison,
+          liaison_judgement: obj.liaison_judgement,
+          ipfs_content_id: obj.ipfs_content_id,
+        },
+      ]
+    })
+  )
+
+  console.log(JSON.stringify(transformed))
+  console.error(`Exported ${transformed.length} objects`)
+}
+
+if (typeof module === 'undefined') {
+  // Pioneer js-toolbox
+  script({ api, hashing, keyring, types, util })
+} else {
+  // Node
+  module.exports = script
+}

+ 9 - 0
utils/api-examples/scripts/index.js

@@ -0,0 +1,9 @@
+const exportedScripts = {}
+
+exportedScripts.example = require('./example.js')
+exportedScripts.exportDataDirectory = require('./export-data-directory.js')
+exportedScripts.injectDataObjects = require('./inject-data-objects.js')
+exportedScripts.listDataDirectory = require('./list-data-directory.js')
+exportedScripts.testTransfer = require('./transfer.js')
+
+module.exports = exportedScripts

文件差异内容过多而无法显示
+ 15 - 0
utils/api-examples/scripts/inject-data-objects.js


+ 33 - 0
utils/api-examples/scripts/list-data-directory.js

@@ -0,0 +1,33 @@
+/* global api, hashing, keyring, types, util, joy */
+
+// run this script with:
+// yarn script listDataDirectory
+//
+// or copy and paste the code into the pioneer javascript toolbox at:
+// https://testnet.joystream.org/#/js
+// requires nicaea release+
+
+const script = async ({ api, joy }) => {
+  const ids = await api.query.dataDirectory.knownContentIds()
+
+  await Promise.all(
+    ids.map(async (id) => {
+      let obj = await api.query.dataDirectory.dataObjectByContentId(id)
+      if (obj.isNone) {
+        return
+      }
+      obj = obj.unwrap()
+      console.log(`contentId: ${api.createType('ContentId', id).encode()}, ipfs: ${obj.ipfs_content_id}`)
+    })
+  )
+
+  console.error(`Data Directory contains ${ids.length} objects`)
+}
+
+if (typeof module === 'undefined') {
+  // Pioneer js-toolbox
+  script({ api, hashing, keyring, types, util, joy })
+} else {
+  // Node
+  module.exports = script
+}

+ 31 - 0
utils/api-examples/scripts/transfer.js

@@ -0,0 +1,31 @@
+/* global api, hashing, keyring, types, util, window */
+
+// run this script with:
+// yarn script testTransfer
+//
+// or copy and paste the code into the pioneer javascript toolbox at:
+// https://testnet.joystream.org/#/js
+//
+
+const script = async ({ api, keyring }) => {
+  const sudoAddress = (await api.query.sudo.key()).toString()
+  let sudo
+  if (typeof window === 'undefined') {
+    // In node, get the keyPair if the keyring was provided
+    sudo = keyring.getPair(sudoAddress)
+  } else {
+    // Pioneer: let the UI Signer handle it
+    sudo = sudoAddress
+  }
+
+  const transfer = api.tx.balances.transfer(sudoAddress, 100)
+  await transfer.signAndSend(sudo)
+}
+
+if (typeof module === 'undefined') {
+  // Pioneer js-toolbox
+  script({ api, hashing, keyring, types, util })
+} else {
+  // Node
+  module.exports = script
+}

+ 20 - 0
utils/api-examples/src/get-code.ts

@@ -0,0 +1,20 @@
+import { ApiPromise, WsProvider } from '@polkadot/api'
+import { types } from '@joystream/types'
+
+async function main() {
+  const provider = new WsProvider('ws://127.0.0.1:9944')
+
+  const api = await ApiPromise.create({ provider, types })
+
+  const currentBlockHash = await api.rpc.chain.getBlockHash(1)
+
+  console.log('getting code as of block hash', currentBlockHash.toString())
+
+  const substrateWasm = await api.query.substrate.code.at(currentBlockHash)
+
+  console.log(substrateWasm.toHex())
+
+  api.disconnect()
+}
+
+main()

+ 52 - 0
utils/api-examples/src/script.ts

@@ -0,0 +1,52 @@
+// @ts-check
+
+import { ApiPromise, WsProvider } from '@polkadot/api'
+import * as types from '@polkadot/types'
+import * as util from '@polkadot/util'
+import joy, { types as joyTypes } from '@joystream/types'
+import * as hashing from '@polkadot/util-crypto'
+import { Keyring } from '@polkadot/keyring'
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const scripts = require('../scripts')
+
+async function main() {
+  const scriptArg = process.argv[2]
+  const script = scripts[scriptArg]
+
+  if (!scriptArg || !script) {
+    console.error('Please specify valid script name.')
+    console.error('Available scripts:', Object.keys(scripts))
+    return
+  }
+
+  const provider = new WsProvider('ws://127.0.0.1:9944')
+
+  const api = await ApiPromise.create({ provider, types: joyTypes })
+
+  // We don't pass a custom signer to the api so we must use a keyPair
+  // when calling signAndSend on transactions
+  const keyring = new Keyring()
+
+  // Optional last argument is a SURI for account to use for signing transactions
+  const suri = process.argv[3]
+  if (suri) {
+    keyring.addFromUri(suri, undefined, 'sr25519')
+  }
+
+  // Add development well known keys to keyring
+  if ((await api.rpc.system.chain()).toString() === 'Development') {
+    keyring.addFromUri('//Alice', undefined, 'sr25519')
+    keyring.addFromUri('//Bob', undefined, 'sr25519')
+  }
+
+  try {
+    await script({ api, types, util, hashing, keyring, joy })
+  } catch (err) {
+    console.error(err)
+  }
+
+  api.disconnect()
+}
+
+main()

+ 56 - 0
utils/api-examples/src/status.ts

@@ -0,0 +1,56 @@
+// @ts-check
+
+import { ApiPromise, WsProvider } from '@polkadot/api'
+import { types } from '@joystream/types'
+import { Seat } from '@joystream/types/council'
+import { ValidatorId } from '@polkadot/types/interfaces'
+
+import BN from 'bn.js'
+
+async function main() {
+  // Initialise the provider to connect to the local node
+  const provider = new WsProvider('ws://127.0.0.1:9944')
+
+  // Create the API and wait until ready
+  const api = await ApiPromise.create({ provider, types })
+
+  // Retrieve the chain & node information information via rpc calls
+  const [chain, nodeName, nodeVersion] = await Promise.all([
+    api.rpc.system.chain(),
+    api.rpc.system.name(),
+    api.rpc.system.version(),
+  ])
+
+  console.log(`Chain ${chain} using ${nodeName} v${nodeVersion}`)
+
+  const council = ((await api.query.council.activeCouncil()) as unknown) as Seat[]
+  const validators = ((await api.query.session.validators()) as unknown) as ValidatorId[]
+  const version = (await api.rpc.state.getRuntimeVersion()) as any
+
+  console.log(`Runtime Version: ${version.authoringVersion}.${version.specVersion}.${version.implVersion}`)
+
+  // let council: QueryableStorageFunction<Seat[], SubscriptionResult> = (await api.query.council.activeCouncil()) as unknown as Seat[]
+  // let council = (await api.query.council.activeCouncil()) as unknown as Seat[];
+
+  // number of council members
+  console.log('Council size:', council.length)
+
+  console.log('Validator count:', validators.length)
+
+  if (validators && validators.length > 0) {
+    // Retrieve the free balances for all validators
+    const validatorBalances = await Promise.all(
+      validators.map((authorityId) => api.query.balances.account(authorityId))
+    )
+
+    const totalValidatorBalances = validatorBalances.reduce((total, value) => total.add(value.free), new BN(0))
+
+    // TODO: to get the staked amounts we need to read the account lock information.
+
+    console.log('Total Validator Free Balance:', totalValidatorBalances.toString())
+  }
+
+  api.disconnect()
+}
+
+main()

+ 30 - 0
utils/api-examples/src/tohex.ts

@@ -0,0 +1,30 @@
+import { CuratorApplicationId } from '@joystream/types/content-working-group'
+import { BTreeSet, createType, TypeRegistry } from '@polkadot/types'
+import { types } from '@joystream/types'
+
+async function main() {
+  const wgId = [1, 2]
+
+  const registry = new TypeRegistry()
+  registry.register(types)
+
+  const set = new BTreeSet<CuratorApplicationId>(registry, CuratorApplicationId, [])
+
+  wgId.forEach((id) => {
+    set.add(createType(registry, 'CuratorApplicationId', id))
+  })
+
+  /*
+    Replace the integers inside the bracket in:
+    let wgId:number[] = [1, 2];
+    With the "WG ID"s of the curators you wish to hire, in ascending order.
+
+    To hire "WG ID" 18 21 and 16:
+    let wgId:number[] = [16, 18, 21];
+    */
+
+  console.log('copy/paste the output below to hire curator applicant(s) with WG IDs:', wgId)
+  console.log(set.toHex())
+}
+
+main()

+ 19 - 0
utils/api-examples/tsconfig.json

@@ -0,0 +1,19 @@
+{
+  "compilerOptions": {
+    "declaration": true,
+    "importHelpers": true,
+    "module": "commonjs",
+    "outDir": "lib",
+    "rootDir": "src",
+    "strict": true,
+    "target": "es2017",
+    "esModuleInterop": true,
+    "types": ["node"],
+    "noUnusedLocals": true,
+    "baseUrl": "./",
+    "paths": {
+      "@polkadot/types/augment": ["../../types/src/definitions/augment-types.ts"]
+    }
+  },
+  "include": ["src/**/*"]
+}

+ 1 - 1
yarn.lock

@@ -3383,7 +3383,7 @@
     "@polkadot/util" "^3.0.1"
     bn.js "^5.1.2"
 
-"@polkadot/types@1.26.1", "@polkadot/types@^0.96.1":
+"@polkadot/types@1.26.1", "@polkadot/types@^0.96.1", "@polkadot/types@^1.26.1":
   version "1.26.1"
   resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-1.26.1.tgz#e58a823da22bd526b298f7d42384bf59b8994fad"
   integrity sha512-mrA3+qYyDvfOIOMkY8lg2ziCYpwOl3N1LUxKdiyBDtKM7Dl8ZWQ0nLUCDW5MhbzDlThmYjE4feBRA+2eBShfyA==

部分文件因为文件数量过多而无法显示