accounts.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { ActionStatus } from '@polkadot/react-components/Status/types';
  2. import { CreateResult } from '@polkadot/ui-keyring/types';
  3. import { KeypairType } from '@polkadot/util-crypto/types';
  4. import FileSaver from 'file-saver';
  5. import { DEV_PHRASE } from '@polkadot/keyring/defaults';
  6. import keyring from '@polkadot/ui-keyring';
  7. import { isHex, u8aToHex } from '@polkadot/util';
  8. import { keyExtractSuri, mnemonicGenerate, mnemonicValidate, randomAsU8a } from '@polkadot/util-crypto';
  9. export type SeedType = 'bip' | 'raw' | 'dev';
  10. export interface AddressState {
  11. address: string | null;
  12. deriveError: string | null;
  13. derivePath: string;
  14. isSeedValid: boolean;
  15. pairType: KeypairType;
  16. seed: string;
  17. seedType: SeedType;
  18. }
  19. const DEFAULT_PAIR_TYPE = 'ed25519'; // 'sr25519';
  20. function deriveValidate (seed: string, derivePath: string, pairType: KeypairType): string | null {
  21. try {
  22. const { path } = keyExtractSuri(`${seed}${derivePath}`);
  23. // we don't allow soft for ed25519
  24. if (pairType === 'ed25519' && path.some(({ isSoft }): boolean => isSoft)) {
  25. return 'Soft derivation paths are not allowed on ed25519';
  26. }
  27. } catch (error) {
  28. return error.message;
  29. }
  30. return null;
  31. }
  32. function isHexSeed (seed: string): boolean {
  33. return isHex(seed) && seed.length === 66;
  34. }
  35. function rawValidate (seed: string): boolean {
  36. return ((seed.length > 0) && (seed.length <= 32)) || isHexSeed(seed);
  37. }
  38. function addressFromSeed (phrase: string, derivePath: string, pairType: KeypairType): string {
  39. return keyring
  40. .createFromUri(`${phrase.trim()}${derivePath}`, {}, pairType)
  41. .address;
  42. }
  43. function newSeed (seed: string | undefined | null, seedType: SeedType): string {
  44. switch (seedType) {
  45. case 'bip':
  46. return mnemonicGenerate();
  47. case 'dev':
  48. return DEV_PHRASE;
  49. default:
  50. return seed || u8aToHex(randomAsU8a());
  51. }
  52. }
  53. export function generateSeed (_seed: string | undefined | null, derivePath: string, seedType: SeedType, pairType: KeypairType = DEFAULT_PAIR_TYPE): AddressState {
  54. const seed = newSeed(_seed, seedType);
  55. const address = addressFromSeed(seed, derivePath, pairType);
  56. return {
  57. address,
  58. deriveError: null,
  59. derivePath,
  60. isSeedValid: true,
  61. pairType,
  62. seedType,
  63. seed
  64. };
  65. }
  66. export function updateAddress (seed: string, derivePath: string, seedType: SeedType, pairType: KeypairType): AddressState {
  67. const deriveError = deriveValidate(seed, derivePath, pairType);
  68. let isSeedValid = seedType === 'raw'
  69. ? rawValidate(seed)
  70. : mnemonicValidate(seed);
  71. let address: string | null = null;
  72. if (!deriveError && isSeedValid) {
  73. try {
  74. address = addressFromSeed(seed, derivePath, pairType);
  75. } catch (error) {
  76. isSeedValid = false;
  77. }
  78. }
  79. return {
  80. address,
  81. deriveError,
  82. derivePath,
  83. isSeedValid,
  84. pairType,
  85. seedType,
  86. seed
  87. };
  88. }
  89. export function downloadAccount ({ json, pair }: CreateResult): void {
  90. const blob = new Blob([JSON.stringify(json)], { type: 'application/json; charset=utf-8' });
  91. FileSaver.saveAs(blob, `${pair.address}.json`);
  92. }
  93. export function createAccount (suri: string, pairType: KeypairType, name: string, password: string, success: string): ActionStatus {
  94. // we will fill in all the details below
  95. const status = { action: 'create' } as ActionStatus;
  96. try {
  97. const result = keyring.addUri(suri, password, { name: name.trim(), tags: [] }, pairType);
  98. const { address } = result.pair;
  99. status.account = address;
  100. status.status = 'success';
  101. status.message = success;
  102. downloadAccount(result);
  103. } catch (error) {
  104. status.status = 'error';
  105. status.message = error.message;
  106. }
  107. return status;
  108. }
  109. export function isPasswordValid (password: string): boolean {
  110. return password.length === 0 || keyring.isPassValid(password);
  111. }