initialize-lead.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { registry, types } from '@joystream/types'
  2. import { JoyBTreeSet, MemberId } from '@joystream/types/common'
  3. import { ApplicationId, OpeningId } from '@joystream/types/working-group'
  4. import { ApiPromise, WsProvider } from '@polkadot/api'
  5. import { SubmittableExtrinsic } from '@polkadot/api/types'
  6. import { ExtrinsicsHelper, getAlicePair, getKeyFromSuri } from './helpers/extrinsics'
  7. import BN from 'bn.js'
  8. const workingGroupModules = [
  9. 'storageWorkingGroup',
  10. 'contentDirectoryWorkingGroup',
  11. 'forumWorkingGroup',
  12. 'membershipWorkingGroup',
  13. 'operationsWorkingGroup',
  14. 'gatewayWorkingGroup',
  15. ] as const
  16. type WorkingGroupModuleName = typeof workingGroupModules[number]
  17. const MIN_APPLICATION_STAKE = new BN(2000)
  18. const STAKING_ACCOUNT_CANDIDATE_STAKE = new BN(200)
  19. async function main() {
  20. // Init api
  21. const WS_URI = process.env.WS_URI || 'ws://127.0.0.1:9944'
  22. console.log(`Initializing the api (${WS_URI})...`)
  23. const provider = new WsProvider(WS_URI)
  24. const api = await ApiPromise.create({ provider, types })
  25. const Group = process.env.GROUP || 'contentDirectoryWorkingGroup'
  26. const LeadKeyPair = process.env.LEAD_URI ? getKeyFromSuri(process.env.LEAD_URI) : getAlicePair()
  27. const SudoKeyPair = process.env.SUDO_URI ? getKeyFromSuri(process.env.SUDO_URI) : getAlicePair()
  28. const StakeKeyPair = LeadKeyPair.derive(`//stake${Date.now()}`)
  29. if (!workingGroupModules.includes(Group as WorkingGroupModuleName)) {
  30. throw new Error(`Invalid working group: ${Group}`)
  31. }
  32. const groupModule = Group as WorkingGroupModuleName
  33. const txHelper = new ExtrinsicsHelper(api)
  34. const sudo = (tx: SubmittableExtrinsic<'promise'>) => api.tx.sudo.sudo(tx)
  35. // Create membership if not already created
  36. const memberEntries = await api.query.members.membershipById.entries()
  37. const matchingEntry = memberEntries.find(
  38. ([storageKey, member]) => member.controller_account.toString() === LeadKeyPair.address
  39. )
  40. let memberId: MemberId | undefined = matchingEntry?.[0].args[0] as MemberId | undefined
  41. // Only buy membership if LEAD_URI is not provided - ie for Alice
  42. if (!memberId && process.env.LEAD_URI) {
  43. throw new Error('Make sure Controller key LEAD_URI is for a member')
  44. }
  45. if (!memberId) {
  46. console.log('Buying new membership...')
  47. const [memberRes] = await txHelper.sendAndCheck(
  48. LeadKeyPair,
  49. [
  50. api.tx.members.buyMembership({
  51. root_account: LeadKeyPair.address,
  52. controller_account: LeadKeyPair.address,
  53. handle: 'alice',
  54. }),
  55. ],
  56. 'Failed to setup member account'
  57. )
  58. memberId = memberRes.findRecord('members', 'MembershipBought')!.event.data[0] as MemberId
  59. }
  60. // Create a new lead opening
  61. if ((await api.query[groupModule].currentLead()).isSome) {
  62. console.log(`${groupModule} lead already exists, aborting...`)
  63. } else {
  64. console.log(`Making member id: ${memberId} the ${groupModule} lead.`)
  65. // Create lead opening
  66. console.log(`Creating ${groupModule} lead opening...`)
  67. const [openingRes] = await txHelper.sendAndCheck(
  68. SudoKeyPair,
  69. [
  70. sudo(
  71. api.tx[groupModule].addOpening(
  72. '',
  73. 'Leader',
  74. {
  75. stake_amount: MIN_APPLICATION_STAKE,
  76. leaving_unstaking_period: 99999,
  77. },
  78. null
  79. )
  80. ),
  81. ],
  82. `Failed to create ${groupModule} lead opening!`
  83. )
  84. const openingId = openingRes.findRecord(groupModule, 'OpeningAdded')!.event.data[0] as OpeningId
  85. // Set up stake account
  86. const addCandidateTx = api.tx.members.addStakingAccountCandidate(memberId)
  87. const addCandidateFee = (await addCandidateTx.paymentInfo(StakeKeyPair.address)).partialFee
  88. const stakingAccountBalance = MIN_APPLICATION_STAKE.add(STAKING_ACCOUNT_CANDIDATE_STAKE).add(addCandidateFee)
  89. console.log('Setting up staking account...')
  90. await txHelper.sendAndCheck(
  91. LeadKeyPair,
  92. [api.tx.balances.transfer(StakeKeyPair.address, stakingAccountBalance)],
  93. `Failed to send funds to staing account (${stakingAccountBalance})`
  94. )
  95. await txHelper.sendAndCheck(StakeKeyPair, [addCandidateTx], 'Failed to add staking candidate')
  96. await txHelper.sendAndCheck(
  97. LeadKeyPair,
  98. [api.tx.members.confirmStakingAccount(memberId, StakeKeyPair.address)],
  99. 'Failed to confirm staking account'
  100. )
  101. console.log((await api.query.system.account(StakeKeyPair.address)).toHuman())
  102. // Apply to lead opening
  103. console.log(`Applying to ${groupModule} lead opening...`)
  104. const [applicationRes] = await txHelper.sendAndCheck(
  105. LeadKeyPair,
  106. [
  107. api.tx[groupModule].applyOnOpening({
  108. member_id: memberId,
  109. role_account_id: LeadKeyPair.address,
  110. opening_id: openingId,
  111. stake_parameters: {
  112. stake: MIN_APPLICATION_STAKE,
  113. staking_account_id: StakeKeyPair.address,
  114. },
  115. }),
  116. ],
  117. 'Failed to apply on lead opening!'
  118. )
  119. const applicationId = applicationRes.findRecord(groupModule, 'AppliedOnOpening')!.event.data[1] as ApplicationId
  120. // Fill opening
  121. console.log('Filling the opening...')
  122. await txHelper.sendAndCheck(
  123. LeadKeyPair,
  124. [sudo(api.tx[groupModule].fillOpening(openingId, new (JoyBTreeSet(ApplicationId))(registry, [applicationId])))],
  125. 'Failed to fill the opening'
  126. )
  127. }
  128. }
  129. main()
  130. .then(() => process.exit())
  131. .catch((e) => console.error(e))