ApiCommandBase.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { Command, flags } from '@oclif/command'
  2. import { createApi } from '../services/runtime/api'
  3. import {
  4. getAccountFromJsonFile,
  5. getAlicePair,
  6. } from '../services/runtime/accounts'
  7. import { KeyringPair } from '@polkadot/keyring/types'
  8. import { ApiPromise } from '@polkadot/api'
  9. import logger from '../services/logger'
  10. import ExitCodes from './ExitCodes'
  11. import { CLIError } from '@oclif/errors'
  12. import { Input } from '@oclif/parser'
  13. export default abstract class ApiCommandBase extends Command {
  14. private api: ApiPromise | null = null
  15. static flags = {
  16. help: flags.help({ char: 'h' }),
  17. dev: flags.boolean({ char: 'm', description: 'Use development mode' }),
  18. apiUrl: flags.string({
  19. char: 'u',
  20. description:
  21. 'Runtime API URL. Mandatory in non-dev environment. Default is ws://localhost:9944',
  22. }),
  23. keyfile: flags.string({
  24. char: 'k',
  25. description:
  26. 'Key file for the account. Mandatory in non-dev environment.',
  27. }),
  28. password: flags.string({
  29. char: 'p',
  30. description: 'Key file password (optional).',
  31. }),
  32. }
  33. async finally(err: Error | undefined): Promise<void> {
  34. // called after run and catch regardless of whether or not the command errored
  35. // We'll force exit here, in case there is no error, to prevent console.log from hanging the process
  36. if (!err) this.exit(0)
  37. super.finally(err)
  38. }
  39. protected async getApi(): Promise<ApiPromise> {
  40. if (this.api === null) {
  41. throw new CLIError('Runtime Api is uninitialized.', {
  42. exit: ExitCodes.ApiError,
  43. })
  44. }
  45. return this.api
  46. }
  47. async init(): Promise<void> {
  48. // Oclif hack: https://github.com/oclif/oclif/issues/225#issuecomment-490555119
  49. /* eslint-disable @typescript-eslint/no-explicit-any */
  50. const { flags } = this.parse(<Input<any>>this.constructor)
  51. const apiUrl = flags.apiUrl ?? 'ws://localhost:9944'
  52. this.api = await createApi(apiUrl)
  53. await this.getApi()
  54. }
  55. async ensureDevelopmentChain(): Promise<void> {
  56. const api = await this.getApi()
  57. const developmentChainName = 'Development'
  58. const runningChainName = await api.rpc.system.chain()
  59. if (runningChainName.toString() !== developmentChainName) {
  60. throw new CLIError(
  61. 'This command should only be run on a Development chain.',
  62. { exit: ExitCodes.DevelopmentModeOnly }
  63. )
  64. }
  65. logger.info('Development mode is ON.')
  66. }
  67. getAccount(flags: {
  68. dev?: boolean
  69. keyfile?: string
  70. password?: string
  71. }): KeyringPair {
  72. const keyfile = flags.keyfile ?? ''
  73. const password = flags.password
  74. let account: KeyringPair
  75. if (flags.dev) {
  76. account = getAlicePair()
  77. } else {
  78. if (keyfile === '') {
  79. this.error('Keyfile must be set.')
  80. }
  81. account = getAccountFromJsonFile(keyfile)
  82. account.unlock(password)
  83. }
  84. return account
  85. }
  86. exitAfterRuntimeCall(success: boolean): never {
  87. let exitCode = ExitCodes.OK
  88. if (!success) {
  89. exitCode = ExitCodes.UnsuccessfulRuntimeCall
  90. }
  91. this.exit(exitCode)
  92. }
  93. }