wallet.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { SigningKey } from "../crypto/index.js";
  2. import { assertArgument } from "../utils/index.js";
  3. import { BaseWallet } from "./base-wallet.js";
  4. import { HDNodeWallet } from "./hdwallet.js";
  5. import { decryptCrowdsaleJson, isCrowdsaleJson } from "./json-crowdsale.js";
  6. import {
  7. decryptKeystoreJson, decryptKeystoreJsonSync,
  8. encryptKeystoreJson, encryptKeystoreJsonSync,
  9. isKeystoreJson
  10. } from "./json-keystore.js";
  11. import { Mnemonic } from "./mnemonic.js";
  12. import type { ProgressCallback } from "../crypto/index.js";
  13. import type { Provider } from "../providers/index.js";
  14. import type { CrowdsaleAccount } from "./json-crowdsale.js";
  15. import type { KeystoreAccount } from "./json-keystore.js";
  16. function stall(duration: number): Promise<void> {
  17. return new Promise((resolve) => { setTimeout(() => { resolve(); }, duration); });
  18. }
  19. /**
  20. * A **Wallet** manages a single private key which is used to sign
  21. * transactions, messages and other common payloads.
  22. *
  23. * This class is generally the main entry point for developers
  24. * that wish to use a private key directly, as it can create
  25. * instances from a large variety of common sources, including
  26. * raw private key, [[link-bip-39]] mnemonics and encrypte JSON
  27. * wallets.
  28. */
  29. export class Wallet extends BaseWallet {
  30. /**
  31. * Create a new wallet for the private %%key%%, optionally connected
  32. * to %%provider%%.
  33. */
  34. constructor(key: string | SigningKey, provider?: null | Provider) {
  35. if (typeof(key) === "string" && !key.startsWith("0x")) {
  36. key = "0x" + key;
  37. }
  38. let signingKey = (typeof(key) === "string") ? new SigningKey(key): key;
  39. super(signingKey, provider);
  40. }
  41. connect(provider: null | Provider): Wallet {
  42. return new Wallet(this.signingKey, provider);
  43. }
  44. /**
  45. * Resolves to a [JSON Keystore Wallet](json-wallets) encrypted with
  46. * %%password%%.
  47. *
  48. * If %%progressCallback%% is specified, it will receive periodic
  49. * updates as the encryption process progreses.
  50. */
  51. async encrypt(password: Uint8Array | string, progressCallback?: ProgressCallback): Promise<string> {
  52. const account = { address: this.address, privateKey: this.privateKey };
  53. return await encryptKeystoreJson(account, password, { progressCallback });
  54. }
  55. /**
  56. * Returns a [JSON Keystore Wallet](json-wallets) encryped with
  57. * %%password%%.
  58. *
  59. * It is preferred to use the [async version](encrypt) instead,
  60. * which allows a [[ProgressCallback]] to keep the user informed.
  61. *
  62. * This method will block the event loop (freezing all UI) until
  63. * it is complete, which may be a non-trivial duration.
  64. */
  65. encryptSync(password: Uint8Array | string): string {
  66. const account = { address: this.address, privateKey: this.privateKey };
  67. return encryptKeystoreJsonSync(account, password);
  68. }
  69. static #fromAccount(account: null | CrowdsaleAccount | KeystoreAccount): HDNodeWallet | Wallet {
  70. assertArgument(account, "invalid JSON wallet", "json", "[ REDACTED ]");
  71. if ("mnemonic" in account && account.mnemonic && account.mnemonic.locale === "en") {
  72. const mnemonic = Mnemonic.fromEntropy(account.mnemonic.entropy);
  73. const wallet = HDNodeWallet.fromMnemonic(mnemonic, account.mnemonic.path);
  74. if (wallet.address === account.address && wallet.privateKey === account.privateKey) {
  75. return wallet;
  76. }
  77. console.log("WARNING: JSON mismatch address/privateKey != mnemonic; fallback onto private key");
  78. }
  79. const wallet = new Wallet(account.privateKey);
  80. assertArgument(wallet.address === account.address,
  81. "address/privateKey mismatch", "json", "[ REDACTED ]");
  82. return wallet;
  83. }
  84. /**
  85. * Creates (asynchronously) a **Wallet** by decrypting the %%json%%
  86. * with %%password%%.
  87. *
  88. * If %%progress%% is provided, it is called periodically during
  89. * decryption so that any UI can be updated.
  90. */
  91. static async fromEncryptedJson(json: string, password: Uint8Array | string, progress?: ProgressCallback): Promise<HDNodeWallet | Wallet> {
  92. let account: null | CrowdsaleAccount | KeystoreAccount = null;
  93. if (isKeystoreJson(json)) {
  94. account = await decryptKeystoreJson(json, password, progress);
  95. } else if (isCrowdsaleJson(json)) {
  96. if (progress) { progress(0); await stall(0); }
  97. account = decryptCrowdsaleJson(json, password);
  98. if (progress) { progress(1); await stall(0); }
  99. }
  100. return Wallet.#fromAccount(account);
  101. }
  102. /**
  103. * Creates a **Wallet** by decrypting the %%json%% with %%password%%.
  104. *
  105. * The [[fromEncryptedJson]] method is preferred, as this method
  106. * will lock up and freeze the UI during decryption, which may take
  107. * some time.
  108. */
  109. static fromEncryptedJsonSync(json: string, password: Uint8Array | string): HDNodeWallet | Wallet {
  110. let account: null | CrowdsaleAccount | KeystoreAccount = null;
  111. if (isKeystoreJson(json)) {
  112. account = decryptKeystoreJsonSync(json, password);
  113. } else if (isCrowdsaleJson(json)) {
  114. account = decryptCrowdsaleJson(json, password);
  115. } else {
  116. assertArgument(false, "invalid JSON wallet", "json", "[ REDACTED ]");
  117. }
  118. return Wallet.#fromAccount(account);
  119. }
  120. /**
  121. * Creates a new random [[HDNodeWallet]] using the available
  122. * [cryptographic random source](randomBytes).
  123. *
  124. * If there is no crytographic random source, this will throw.
  125. */
  126. static createRandom(provider?: null | Provider): HDNodeWallet {
  127. const wallet = HDNodeWallet.createRandom();
  128. if (provider) { return wallet.connect(provider); }
  129. return wallet;
  130. }
  131. /**
  132. * Creates a [[HDNodeWallet]] for %%phrase%%.
  133. */
  134. static fromPhrase(phrase: string, provider?: Provider): HDNodeWallet {
  135. const wallet = HDNodeWallet.fromPhrase(phrase);
  136. if (provider) { return wallet.connect(provider); }
  137. return wallet;
  138. }
  139. }