@toruslabs/ethereum-controllers 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/ethereumControllers.cjs.js +6153 -0
  2. package/dist/ethereumControllers.cjs.js.map +1 -0
  3. package/dist/ethereumControllers.esm.js +5570 -0
  4. package/dist/ethereumControllers.esm.js.map +1 -0
  5. package/dist/ethereumControllers.umd.min.js +3 -0
  6. package/dist/ethereumControllers.umd.min.js.LICENSE.txt +38 -0
  7. package/dist/ethereumControllers.umd.min.js.map +1 -0
  8. package/dist/types/Account/AccountTrackerController.d.ts +35 -0
  9. package/dist/types/Block/PollingBlockTracker.d.ts +14 -0
  10. package/dist/types/Currency/CurrencyController.d.ts +30 -0
  11. package/dist/types/Gas/GasFeeController.d.ts +64 -0
  12. package/dist/types/Gas/IGasFeeController.d.ts +49 -0
  13. package/dist/types/Gas/gasUtil.d.ts +21 -0
  14. package/dist/types/Keyring/KeyringController.d.ts +20 -0
  15. package/dist/types/Message/AbstractMessageController.d.ts +36 -0
  16. package/dist/types/Message/DecryptMessageController.d.ts +20 -0
  17. package/dist/types/Message/EncryptionPublicKeyController.d.ts +20 -0
  18. package/dist/types/Message/MessageController.d.ts +20 -0
  19. package/dist/types/Message/PersonalMessageController.d.ts +20 -0
  20. package/dist/types/Message/TypedMessageController.d.ts +21 -0
  21. package/dist/types/Message/utils.d.ts +10 -0
  22. package/dist/types/Network/NetworkController.d.ts +40 -0
  23. package/dist/types/Network/createEthereumMiddleware.d.ts +66 -0
  24. package/dist/types/Network/createJsonRpcClient.d.ts +9 -0
  25. package/dist/types/Nfts/INftsController.d.ts +10 -0
  26. package/dist/types/Nfts/NftHandler.d.ts +35 -0
  27. package/dist/types/Nfts/NftsController.d.ts +40 -0
  28. package/dist/types/Preferences/PreferencesController.d.ts +53 -0
  29. package/dist/types/Tokens/ITokensController.d.ts +10 -0
  30. package/dist/types/Tokens/TokenHandler.d.ts +20 -0
  31. package/dist/types/Tokens/TokenRatesController.d.ts +42 -0
  32. package/dist/types/Tokens/TokensController.d.ts +42 -0
  33. package/dist/types/Transaction/NonceTracker.d.ts +37 -0
  34. package/dist/types/Transaction/PendingTransactionTracker.d.ts +32 -0
  35. package/dist/types/Transaction/TransactionController.d.ts +67 -0
  36. package/dist/types/Transaction/TransactionGasUtil.d.ts +21 -0
  37. package/dist/types/Transaction/TransactionStateHistoryHelper.d.ts +16 -0
  38. package/dist/types/Transaction/TransactionStateManager.d.ts +30 -0
  39. package/dist/types/Transaction/TransactionUtils.d.ts +70 -0
  40. package/dist/types/index.d.ts +43 -0
  41. package/dist/types/utils/abiDecoder.d.ts +17 -0
  42. package/dist/types/utils/abis.d.ts +84 -0
  43. package/dist/types/utils/constants.d.ts +81 -0
  44. package/dist/types/utils/contractAddresses.d.ts +1 -0
  45. package/dist/types/utils/conversionUtils.d.ts +42 -0
  46. package/dist/types/utils/helpers.d.ts +24 -0
  47. package/dist/types/utils/interfaces.d.ts +384 -0
  48. package/package.json +71 -0
  49. package/src/Account/AccountTrackerController.ts +157 -0
  50. package/src/Block/PollingBlockTracker.ts +89 -0
  51. package/src/Currency/CurrencyController.ts +117 -0
  52. package/src/Gas/GasFeeController.ts +254 -0
  53. package/src/Gas/IGasFeeController.ts +56 -0
  54. package/src/Gas/gasUtil.ts +163 -0
  55. package/src/Keyring/KeyringController.ts +118 -0
  56. package/src/Message/AbstractMessageController.ts +136 -0
  57. package/src/Message/DecryptMessageController.ts +81 -0
  58. package/src/Message/EncryptionPublicKeyController.ts +83 -0
  59. package/src/Message/MessageController.ts +74 -0
  60. package/src/Message/PersonalMessageController.ts +74 -0
  61. package/src/Message/TypedMessageController.ts +112 -0
  62. package/src/Message/utils.ts +107 -0
  63. package/src/Network/NetworkController.ts +184 -0
  64. package/src/Network/createEthereumMiddleware.ts +307 -0
  65. package/src/Network/createJsonRpcClient.ts +59 -0
  66. package/src/Nfts/INftsController.ts +13 -0
  67. package/src/Nfts/NftHandler.ts +191 -0
  68. package/src/Nfts/NftsController.ts +230 -0
  69. package/src/Preferences/PreferencesController.ts +409 -0
  70. package/src/Tokens/ITokensController.ts +13 -0
  71. package/src/Tokens/TokenHandler.ts +60 -0
  72. package/src/Tokens/TokenRatesController.ts +134 -0
  73. package/src/Tokens/TokensController.ts +278 -0
  74. package/src/Transaction/NonceTracker.ts +152 -0
  75. package/src/Transaction/PendingTransactionTracker.ts +235 -0
  76. package/src/Transaction/TransactionController.ts +558 -0
  77. package/src/Transaction/TransactionGasUtil.ts +74 -0
  78. package/src/Transaction/TransactionStateHistoryHelper.ts +41 -0
  79. package/src/Transaction/TransactionStateManager.ts +315 -0
  80. package/src/Transaction/TransactionUtils.ts +333 -0
  81. package/src/index.ts +45 -0
  82. package/src/utils/abiDecoder.ts +195 -0
  83. package/src/utils/abis.ts +677 -0
  84. package/src/utils/constants.ts +379 -0
  85. package/src/utils/contractAddresses.ts +21 -0
  86. package/src/utils/conversionUtils.ts +269 -0
  87. package/src/utils/helpers.ts +177 -0
  88. package/src/utils/interfaces.ts +454 -0
@@ -0,0 +1,163 @@
1
+ import { get } from "@toruslabs/http-helpers";
2
+ import { SafeEventEmitterProvider } from "@toruslabs/openlogin-jrpc";
3
+ import BigNumber from "bignumber.js";
4
+
5
+ import { METHOD_TYPES } from "../utils/constants";
6
+ import { decGWEIToHexWEI, hexWEIToDecGWEI } from "../utils/conversionUtils";
7
+ import {
8
+ EthereumGasFeeEstimates,
9
+ EthereumLegacyGasFeeEstimates,
10
+ GasEstimatesAPIResponse,
11
+ GasEstimatesLegacyAPIResponse,
12
+ GasFeeTimeBounds,
13
+ } from "./IGasFeeController";
14
+
15
+ export function normalizeGWEIDecimalNumbers(n: string | number): number | BigNumber {
16
+ const numberAsWEIHex = decGWEIToHexWEI(new BigNumber(n));
17
+ const numberAsGWEI = hexWEIToDecGWEI(numberAsWEIHex);
18
+ return numberAsGWEI;
19
+ }
20
+
21
+ export async function fetchGasEstimates(url: string): Promise<EthereumGasFeeEstimates> {
22
+ const estimates = await get<GasEstimatesAPIResponse>(url);
23
+ const normalizedEstimates = {
24
+ estimatedBaseFee: normalizeGWEIDecimalNumbers(estimates.estimatedBaseFee).toString(10),
25
+ low: {
26
+ ...estimates.low,
27
+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(estimates.low.suggestedMaxPriorityFeePerGas).toString(10),
28
+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(estimates.low.suggestedMaxFeePerGas).toString(10),
29
+ },
30
+ medium: {
31
+ ...estimates.medium,
32
+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(estimates.medium.suggestedMaxPriorityFeePerGas).toString(10),
33
+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(estimates.medium.suggestedMaxFeePerGas).toString(10),
34
+ },
35
+ high: {
36
+ ...estimates.high,
37
+ suggestedMaxPriorityFeePerGas: normalizeGWEIDecimalNumbers(estimates.high.suggestedMaxPriorityFeePerGas).toString(10),
38
+ suggestedMaxFeePerGas: normalizeGWEIDecimalNumbers(estimates.high.suggestedMaxFeePerGas).toString(10),
39
+ },
40
+ };
41
+ return normalizedEstimates;
42
+ }
43
+
44
+ export interface FeeHistoryResponse {
45
+ baseFeePerGas: string[];
46
+ gasUsedRatio: string[];
47
+ oldestBlock: string;
48
+ reward: string[][];
49
+ }
50
+
51
+ export async function fetchGasEstimatesViaEthFeeHistory(provider: SafeEventEmitterProvider): Promise<EthereumGasFeeEstimates> {
52
+ const noOfBlocks = 10;
53
+ const newestBlock = "latest";
54
+ // get the 10, 50 and 95th percentile of the tip fees from the last 10 blocks
55
+ const percentileValues = [10, 50, 95];
56
+ const feeHistory = await provider.request<[number, string, number[]], FeeHistoryResponse>({
57
+ method: "eth_feeHistory",
58
+ params: [noOfBlocks, newestBlock, percentileValues],
59
+ });
60
+ // this is in hex wei
61
+ const finalBaseFeePerGas = feeHistory.baseFeePerGas[feeHistory.baseFeePerGas.length - 1];
62
+ // this is in hex wei
63
+ const priorityFeeCalcs = feeHistory.reward.reduce(
64
+ (acc, curr) => {
65
+ return {
66
+ slow: acc.slow.plus(new BigNumber(curr[0], 16)),
67
+ average: acc.average.plus(new BigNumber(curr[1], 16)),
68
+ fast: acc.fast.plus(new BigNumber(curr[2], 16)),
69
+ };
70
+ },
71
+ { slow: new BigNumber(0), average: new BigNumber(0), fast: new BigNumber(0) }
72
+ );
73
+ return {
74
+ estimatedBaseFee: hexWEIToDecGWEI(finalBaseFeePerGas).toString(10),
75
+ high: {
76
+ maxWaitTimeEstimate: 30_000,
77
+ minWaitTimeEstimate: 15_000,
78
+ suggestedMaxFeePerGas: hexWEIToDecGWEI(priorityFeeCalcs.fast.plus(finalBaseFeePerGas).toString(16)).toString(),
79
+ suggestedMaxPriorityFeePerGas: hexWEIToDecGWEI(priorityFeeCalcs.fast.toString(16)).toString(),
80
+ },
81
+ medium: {
82
+ maxWaitTimeEstimate: 45_000,
83
+ minWaitTimeEstimate: 15_000,
84
+ suggestedMaxFeePerGas: hexWEIToDecGWEI(priorityFeeCalcs.average.plus(finalBaseFeePerGas).toString(16)).toString(),
85
+ suggestedMaxPriorityFeePerGas: hexWEIToDecGWEI(priorityFeeCalcs.average.toString(16)).toString(),
86
+ },
87
+ low: {
88
+ maxWaitTimeEstimate: 60_000,
89
+ minWaitTimeEstimate: 15_000,
90
+ suggestedMaxFeePerGas: hexWEIToDecGWEI(priorityFeeCalcs.slow.plus(finalBaseFeePerGas).toString(16)).toString(),
91
+ suggestedMaxPriorityFeePerGas: hexWEIToDecGWEI(priorityFeeCalcs.slow.toString(16)).toString(),
92
+ },
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Hit the legacy MetaSwaps gasPrices estimate api and return the low, medium
98
+ * high values from that API.
99
+ */
100
+ export async function fetchLegacyGasPriceEstimates(url: string): Promise<EthereumLegacyGasFeeEstimates> {
101
+ const result = await get<GasEstimatesLegacyAPIResponse>(url, {
102
+ referrer: url,
103
+ referrerPolicy: "no-referrer-when-downgrade",
104
+ method: "GET",
105
+ });
106
+ // this returns decimal gwei
107
+ return {
108
+ low: result.SafeGasPrice,
109
+ medium: result.ProposeGasPrice,
110
+ high: result.FastGasPrice,
111
+ };
112
+ }
113
+
114
+ export async function fetchEthGasPriceEstimate(provider: SafeEventEmitterProvider): Promise<{ gasPrice: string }> {
115
+ const gasPrice = await provider.request<never, string>({ method: METHOD_TYPES.ETH_GET_GAS_PRICE });
116
+ return {
117
+ gasPrice: hexWEIToDecGWEI(gasPrice).toString(),
118
+ };
119
+ }
120
+
121
+ export function calculateTimeEstimate(
122
+ maxPriorityFeePerGas: string,
123
+ maxFeePerGas: string,
124
+ gasFeeEstimates: EthereumGasFeeEstimates
125
+ ): GasFeeTimeBounds {
126
+ // all are in dec gwei
127
+ const { low, medium, high, estimatedBaseFee } = gasFeeEstimates;
128
+
129
+ const maxPriorityFeePerGasInWEI = new BigNumber(decGWEIToHexWEI(new BigNumber(maxPriorityFeePerGas)), 16);
130
+ const maxFeePerGasInWEI = new BigNumber(decGWEIToHexWEI(new BigNumber(maxFeePerGas)), 16);
131
+ const estimatedBaseFeeInWEI = new BigNumber(decGWEIToHexWEI(new BigNumber(estimatedBaseFee)), 16);
132
+
133
+ const effectiveMaxPriorityFee = BigNumber.min(maxPriorityFeePerGasInWEI, maxFeePerGasInWEI.minus(estimatedBaseFeeInWEI));
134
+
135
+ const lowMaxPriorityFeeInWEI = new BigNumber(decGWEIToHexWEI(new BigNumber(low.suggestedMaxPriorityFeePerGas)), 16);
136
+ const mediumMaxPriorityFeeInWEI = new BigNumber(decGWEIToHexWEI(new BigNumber(medium.suggestedMaxPriorityFeePerGas)), 16);
137
+ const highMaxPriorityFeeInWEI = new BigNumber(decGWEIToHexWEI(new BigNumber(high.suggestedMaxPriorityFeePerGas)), 16);
138
+
139
+ let lowerTimeBound: null | number;
140
+ let upperTimeBound: string | number;
141
+
142
+ if (effectiveMaxPriorityFee.lt(lowMaxPriorityFeeInWEI)) {
143
+ lowerTimeBound = null;
144
+ upperTimeBound = "unknown";
145
+ } else if (effectiveMaxPriorityFee.gte(lowMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(mediumMaxPriorityFeeInWEI)) {
146
+ lowerTimeBound = low.minWaitTimeEstimate;
147
+ upperTimeBound = low.maxWaitTimeEstimate;
148
+ } else if (effectiveMaxPriorityFee.gte(mediumMaxPriorityFeeInWEI) && effectiveMaxPriorityFee.lt(highMaxPriorityFeeInWEI)) {
149
+ lowerTimeBound = medium.minWaitTimeEstimate;
150
+ upperTimeBound = medium.maxWaitTimeEstimate;
151
+ } else if (effectiveMaxPriorityFee.eq(highMaxPriorityFeeInWEI)) {
152
+ lowerTimeBound = high.minWaitTimeEstimate;
153
+ upperTimeBound = high.maxWaitTimeEstimate;
154
+ } else {
155
+ lowerTimeBound = 0;
156
+ upperTimeBound = high.maxWaitTimeEstimate;
157
+ }
158
+
159
+ return {
160
+ lowerTimeBound,
161
+ upperTimeBound,
162
+ };
163
+ }
@@ -0,0 +1,118 @@
1
+ import { TypedTransaction } from "@ethereumjs/tx";
2
+ import { addHexPrefix, bigIntToBytes, ecsign, stripHexPrefix } from "@ethereumjs/util";
3
+ import {
4
+ concatSig,
5
+ decrypt,
6
+ EthEncryptedData,
7
+ getEncryptionPublicKey,
8
+ MessageTypes,
9
+ personalSign,
10
+ signTypedData,
11
+ SignTypedDataVersion,
12
+ TypedDataV1,
13
+ TypedMessage,
14
+ } from "@metamask/eth-sig-util";
15
+ import { BaseConfig, BaseKeyringController, IKeyringController, KeyringControllerState } from "@toruslabs/base-controllers";
16
+ import { SigningKey, Wallet } from "ethers";
17
+
18
+ export default class KeyringController extends BaseKeyringController<Partial<BaseConfig>, KeyringControllerState> implements IKeyringController {
19
+ constructor({ config, state }: { config: Partial<BaseConfig>; state: Partial<KeyringControllerState> }) {
20
+ super({ config, state });
21
+ this.defaultState = { wallets: [] };
22
+ this.initialize();
23
+ }
24
+
25
+ async signTransaction<T, U>(tx: T, address: string): Promise<U> {
26
+ const txPayload = tx as unknown as TypedTransaction;
27
+ const wallet = this._getWalletForAccount(address);
28
+ const privKey = this.getBufferPrivateKey(wallet.privateKey);
29
+ const signedTx = txPayload.sign(privKey);
30
+ // Newer versions of Ethereumjs-tx are immutable and return a new tx object
31
+ return signedTx === undefined ? (tx as unknown as U) : (signedTx as unknown as U);
32
+ }
33
+
34
+ getAccounts(): string[] {
35
+ return this.state.wallets.map((w) => w.publicKey);
36
+ }
37
+
38
+ importAccount(accountPrivateKey: string): string {
39
+ const hexPrivateKey = addHexPrefix(accountPrivateKey);
40
+ const signingKey = new SigningKey(hexPrivateKey);
41
+ const wallet = new Wallet(signingKey.privateKey);
42
+ const { address } = wallet;
43
+
44
+ const existingWallet = this.state.wallets.find((w) => w.address === address);
45
+ if (existingWallet) return existingWallet.address;
46
+
47
+ this.update({
48
+ wallets: [
49
+ ...this.state.wallets,
50
+ {
51
+ publicKey: signingKey.publicKey,
52
+ privateKey: accountPrivateKey,
53
+ address,
54
+ },
55
+ ],
56
+ });
57
+ return address;
58
+ }
59
+
60
+ removeAccount(address: string): void {
61
+ const newWallets = [...this.state.wallets];
62
+ const idx = newWallets.findIndex((w) => w.address === address);
63
+ if (idx !== -1) {
64
+ newWallets.splice(idx, 1);
65
+ this.update({ wallets: newWallets });
66
+ }
67
+ }
68
+
69
+ getBufferPrivateKey(privateKey: string) {
70
+ const stripped = stripHexPrefix(privateKey);
71
+ return Buffer.from(stripped, "hex");
72
+ }
73
+
74
+ // For eth_sign, we need to sign arbitrary data:
75
+ signMessage(data: string, address: string) {
76
+ const wallet = this._getWalletForAccount(address);
77
+ const privKey = this.getBufferPrivateKey(wallet.privateKey);
78
+ const messageSig = ecsign(Buffer.from(stripHexPrefix(data), "hex"), privKey);
79
+ const sig = concatSig(Buffer.from(bigIntToBytes(messageSig.v)), Buffer.from(messageSig.r), Buffer.from(messageSig.s));
80
+ return sig;
81
+ }
82
+
83
+ // For personal_sign, we need to prefix the message: ensure input is hashed and not buffer of utf-8
84
+ async signPersonalMessage(data: string, address: string) {
85
+ const wallet = this._getWalletForAccount(address);
86
+ const privKey = this.getBufferPrivateKey(wallet.privateKey);
87
+ const sig = personalSign({ privateKey: privKey, data });
88
+ return sig;
89
+ }
90
+
91
+ // personal_signTypedData, signs data along with the schema
92
+ async signTypedData<V extends SignTypedDataVersion, T extends MessageTypes>(
93
+ typedData: V extends "V1" ? TypedDataV1 : TypedMessage<T>,
94
+ address: string,
95
+ version: V
96
+ ) {
97
+ const wallet = this._getWalletForAccount(address);
98
+ const privKey = this.getBufferPrivateKey(wallet.privateKey);
99
+ return signTypedData({ privateKey: privKey, data: typedData, version });
100
+ }
101
+
102
+ signEncryptionPublicKey(address: string) {
103
+ const wallet = this._getWalletForAccount(address);
104
+ return getEncryptionPublicKey(stripHexPrefix(wallet.privateKey));
105
+ }
106
+
107
+ decryptMessage(data: EthEncryptedData, address: string) {
108
+ const wallet = this._getWalletForAccount(address);
109
+ return decrypt({ encryptedData: data, privateKey: stripHexPrefix(wallet.privateKey) });
110
+ }
111
+
112
+ private _getWalletForAccount(account: string) {
113
+ const address = account.toLowerCase();
114
+ const wallet = this.state.wallets.find((w) => w.address.toLowerCase() === address);
115
+ if (!wallet) throw new Error("Torus Keyring - Unable to find matching address.");
116
+ return wallet;
117
+ }
118
+ }
@@ -0,0 +1,136 @@
1
+ import { SignTypedDataVersion } from "@metamask/eth-sig-util";
2
+ import { providerErrors, rpcErrors } from "@metamask/rpc-errors";
3
+ import { BaseConfig, BaseController, BaseState } from "@toruslabs/base-controllers";
4
+ import { JRPCRequest, Json } from "@toruslabs/openlogin-jrpc";
5
+
6
+ import NetworkController from "../Network/NetworkController";
7
+ import { MessageStatus } from "../utils/constants";
8
+ import { AbstractMessage, BaseRequestParams, MessageStatusType, UserRequestApprovalParams } from "../utils/interfaces";
9
+
10
+ export interface MessageControllerState<M extends AbstractMessage> extends BaseState {
11
+ unapprovedMessages: Record<string, M>;
12
+ unapprovedMessagesCount: number;
13
+ }
14
+
15
+ export default abstract class AbstractMessageController<M extends AbstractMessage, P extends BaseRequestParams> extends BaseController<
16
+ BaseConfig,
17
+ MessageControllerState<M>
18
+ > {
19
+ protected messages: M[];
20
+
21
+ protected getNetworkIdentifier: NetworkController["getNetworkIdentifier"];
22
+
23
+ /**
24
+ * Controller in charge of managing - storing, adding, removing, updating - Messages.
25
+ *
26
+ */
27
+ constructor({
28
+ config,
29
+ state,
30
+ getNetworkIdentifier,
31
+ }: {
32
+ config?: Partial<BaseConfig>;
33
+ state?: Partial<MessageControllerState<M>>;
34
+ getNetworkIdentifier: NetworkController["getNetworkIdentifier"];
35
+ }) {
36
+ super({ config, state });
37
+ this.defaultState = {
38
+ unapprovedMessages: {},
39
+ unapprovedMessagesCount: 0,
40
+ };
41
+ this.messages = [];
42
+ this.defaultConfig = {};
43
+ this.getNetworkIdentifier = getNetworkIdentifier;
44
+ super.initialize();
45
+ }
46
+
47
+ getMessage(messageId: string) {
48
+ return this.messages.find((message) => message.id === messageId);
49
+ }
50
+
51
+ getAllMessages() {
52
+ return this.messages;
53
+ }
54
+
55
+ setMetadata(messageId: string, metadata: Json) {
56
+ const message = this.getMessage(messageId);
57
+ if (!message) {
58
+ throw new Error(`${this.name}: Message not found for id: ${messageId}.`);
59
+ }
60
+ message.metadata = metadata;
61
+ this.updateMessage(message);
62
+ }
63
+
64
+ getUnapprovedMessages() {
65
+ return this.messages
66
+ .filter((message) => message.status === MessageStatus.UNAPPROVED)
67
+ .reduce((result: { [key: string]: M }, message: M) => {
68
+ result[message.id] = message;
69
+ return result;
70
+ }, {}) as { [key: string]: M };
71
+ }
72
+
73
+ async addMessage(message: M) {
74
+ this.messages.push(message);
75
+ this.saveMessageList();
76
+ }
77
+
78
+ approveMessage(messageParams: P): Promise<P> {
79
+ this.setMessageStatus(messageParams.id, MessageStatus.APPROVED);
80
+ return this.prepMessageForSigning(messageParams);
81
+ }
82
+
83
+ setMessageStatus(messageId: string, status: MessageStatusType) {
84
+ const message = this.getMessage(messageId);
85
+ if (!message) {
86
+ throw new Error(`${this.name}: Message not found for id: ${messageId}.`);
87
+ }
88
+ message.status = status;
89
+ this.updateMessage(message);
90
+ this.emit(`${messageId}:${status}`, message);
91
+ if (status === MessageStatus.REJECTED || status === MessageStatus.SIGNED || status === MessageStatus.FAILED) {
92
+ this.emit(`${messageId}:finished`, message);
93
+ }
94
+ }
95
+
96
+ async waitForFinishStatus(msgParams: P, messageName: string): Promise<string> {
97
+ return new Promise((resolve, reject) => {
98
+ const handleFinished = (msg: M) => {
99
+ if (msg.status === MessageStatus.REJECTED) {
100
+ return reject(providerErrors.userRejectedRequest(`${messageName} Signature: User denied message signature`));
101
+ }
102
+ if (msg.status === MessageStatus.FAILED) {
103
+ return reject(rpcErrors.internal(`${messageName} Signature: failed to sign message ${msg.error}`));
104
+ }
105
+ if (msg.status === MessageStatus.SIGNED) {
106
+ return resolve(msg.rawSig as string);
107
+ }
108
+
109
+ return reject(rpcErrors.internal(`${messageName} Signature: Unknown problem: ${JSON.stringify(msgParams)}`));
110
+ };
111
+ this.once(`${msgParams.id}:finished`, handleFinished);
112
+ });
113
+ }
114
+
115
+ protected updateMessage(message: M) {
116
+ const index = this.messages.findIndex((msg) => message.id === msg.id);
117
+ if (index !== -1) {
118
+ this.messages[index] = message;
119
+ }
120
+ this.saveMessageList();
121
+ }
122
+
123
+ protected saveMessageList() {
124
+ const unapprovedMessages = this.getUnapprovedMessages();
125
+ const unapprovedMessagesCount = Object.keys(unapprovedMessages).length;
126
+ this.update({ unapprovedMessages, unapprovedMessagesCount });
127
+ }
128
+
129
+ abstract prepMessageForSigning(messageParams: P): Promise<P>;
130
+
131
+ abstract addUnapprovedMessage(
132
+ messageParams: P,
133
+ request: JRPCRequest<P> & UserRequestApprovalParams,
134
+ version?: SignTypedDataVersion
135
+ ): Promise<string>;
136
+ }
@@ -0,0 +1,81 @@
1
+ // import { isValidAddress } from "@ethereumjs/util";
2
+ import { BaseConfig, randomId } from "@toruslabs/base-controllers";
3
+ import { JRPCRequest } from "@toruslabs/openlogin-jrpc";
4
+ import log from "loglevel";
5
+
6
+ import KeyringController from "../Keyring/KeyringController";
7
+ import NetworkController from "../Network/NetworkController";
8
+ import { MessageStatus, METHOD_TYPES } from "../utils/constants";
9
+ import { DecryptMessage, DecryptMessageParams, Message, UserRequestApprovalParams } from "../utils/interfaces";
10
+ import AbstractMessageController, { MessageControllerState } from "./AbstractMessageController";
11
+ import { normalizeMessageData, parseDecryptMessageData, validateDecryptedMessageData } from "./utils";
12
+
13
+ export class DecryptMessageController extends AbstractMessageController<DecryptMessage, DecryptMessageParams> {
14
+ override name = "DecryptMessageController";
15
+
16
+ protected decryptMessage: KeyringController["decryptMessage"];
17
+
18
+ constructor({
19
+ config,
20
+ state,
21
+ decryptMessage,
22
+ getNetworkIdentifier,
23
+ }: {
24
+ config: Partial<BaseConfig>;
25
+ state: Partial<MessageControllerState<Message>>;
26
+ decryptMessage: KeyringController["decryptMessage"];
27
+ getNetworkIdentifier: NetworkController["getNetworkIdentifier"];
28
+ }) {
29
+ super({ config, state, getNetworkIdentifier });
30
+ this.decryptMessage = decryptMessage;
31
+ this.initialize();
32
+ }
33
+
34
+ async processDecryptMessage(messageId: string): Promise<string> {
35
+ try {
36
+ const msgObject = this.getMessage(messageId);
37
+ const cleanMsgParams = await this.approveMessage(msgObject.messageParams);
38
+ const parsedData = parseDecryptMessageData(cleanMsgParams.data);
39
+ const rawSig = this.decryptMessage(parsedData, cleanMsgParams.from);
40
+ this.updateMessage({ ...msgObject, rawSig });
41
+ this.setMessageStatus(messageId, MessageStatus.SIGNED);
42
+ return rawSig;
43
+ } catch (error) {
44
+ log.error(error);
45
+ this.setMessageStatus(messageId, MessageStatus.FAILED);
46
+ }
47
+ }
48
+
49
+ async addNewUnapprovedMessage(
50
+ messageParams: DecryptMessageParams,
51
+ req: JRPCRequest<DecryptMessageParams> & UserRequestApprovalParams
52
+ ): Promise<string> {
53
+ await this.addUnapprovedMessage(messageParams, req);
54
+ return this.waitForFinishStatus(messageParams, this.name);
55
+ }
56
+
57
+ async addUnapprovedMessage(messageParams: DecryptMessageParams, req?: JRPCRequest<DecryptMessageParams> & UserRequestApprovalParams) {
58
+ validateDecryptedMessageData(messageParams);
59
+ if (req) {
60
+ messageParams.origin = req.origin;
61
+ }
62
+ messageParams.data = normalizeMessageData(messageParams.data);
63
+ const messageId = randomId();
64
+ const messageData: DecryptMessage = {
65
+ id: messageId,
66
+ messageParams,
67
+ status: MessageStatus.UNAPPROVED,
68
+ time: Date.now(),
69
+ type: METHOD_TYPES.ETH_DECRYPT,
70
+ };
71
+ await this.addMessage(messageData);
72
+ this.emit(`unapprovedMessage`, {
73
+ ...messageParams,
74
+ });
75
+ return messageId;
76
+ }
77
+
78
+ prepMessageForSigning(messageParams: DecryptMessageParams): Promise<DecryptMessageParams> {
79
+ return Promise.resolve({ ...messageParams });
80
+ }
81
+ }
@@ -0,0 +1,83 @@
1
+ // import { isValidAddress } from "@ethereumjs/util";
2
+ import { BaseConfig, randomId } from "@toruslabs/base-controllers";
3
+ import { JRPCRequest } from "@toruslabs/openlogin-jrpc";
4
+ import log from "loglevel";
5
+
6
+ import KeyringController from "../Keyring/KeyringController";
7
+ import NetworkController from "../Network/NetworkController";
8
+ import { MessageStatus, METHOD_TYPES } from "../utils/constants";
9
+ import { EncryptionPublicKey, EncryptionPublicKeyParams, UserRequestApprovalParams } from "../utils/interfaces";
10
+ import AbstractMessageController, { MessageControllerState } from "./AbstractMessageController";
11
+ import { validateEncryptionPublicKeyMessageData } from "./utils";
12
+
13
+ export class EncryptionPublicKeyController extends AbstractMessageController<EncryptionPublicKey, EncryptionPublicKeyParams> {
14
+ override name = "EncryptionPublicKeyController";
15
+
16
+ protected signEncryptionPublicKey: KeyringController["signEncryptionPublicKey"];
17
+
18
+ constructor({
19
+ config,
20
+ state,
21
+ signEncryptionPublicKey,
22
+ getNetworkIdentifier,
23
+ }: {
24
+ config: Partial<BaseConfig>;
25
+ state: Partial<MessageControllerState<EncryptionPublicKey>>;
26
+ signEncryptionPublicKey: KeyringController["signEncryptionPublicKey"];
27
+ getNetworkIdentifier: NetworkController["getNetworkIdentifier"];
28
+ }) {
29
+ super({ config, state, getNetworkIdentifier });
30
+ this.signEncryptionPublicKey = signEncryptionPublicKey;
31
+ this.initialize();
32
+ }
33
+
34
+ async processGetEncryptionPublicKey(messageId: string): Promise<string> {
35
+ try {
36
+ const msgObject = this.getMessage(messageId);
37
+ const cleanMsgParams = await this.approveMessage(msgObject.messageParams);
38
+ const publicKey = this.signEncryptionPublicKey(cleanMsgParams.from);
39
+ this.updateMessage({ ...msgObject, rawSig: publicKey });
40
+ this.setMessageStatus(messageId, MessageStatus.SIGNED);
41
+ return publicKey;
42
+ } catch (error) {
43
+ log.error(error);
44
+ this.setMessageStatus(messageId, MessageStatus.FAILED);
45
+ }
46
+ }
47
+
48
+ async addNewUnapprovedMessage(
49
+ messageParams: EncryptionPublicKeyParams,
50
+ req: JRPCRequest<EncryptionPublicKeyParams> & UserRequestApprovalParams
51
+ ): Promise<string> {
52
+ await this.addUnapprovedMessage(messageParams, req);
53
+ return this.waitForFinishStatus(messageParams, this.name);
54
+ }
55
+
56
+ async addUnapprovedMessage(
57
+ messageParams: EncryptionPublicKeyParams,
58
+ req?: JRPCRequest<EncryptionPublicKeyParams> & UserRequestApprovalParams
59
+ ): Promise<string> {
60
+ validateEncryptionPublicKeyMessageData(messageParams);
61
+ if (req) {
62
+ messageParams.origin = req.origin;
63
+ }
64
+ const messageId = randomId();
65
+ const messageData: EncryptionPublicKey = {
66
+ id: messageId,
67
+ messageParams,
68
+ status: MessageStatus.UNAPPROVED,
69
+ time: Date.now(),
70
+ type: METHOD_TYPES.ETH_GET_ENCRYPTION_PUBLIC_KEY,
71
+ };
72
+ await this.addMessage(messageData);
73
+ this.emit(`unapprovedMessage`, {
74
+ ...messageParams,
75
+ });
76
+ return messageId;
77
+ }
78
+
79
+ prepMessageForSigning(messageParams: EncryptionPublicKeyParams): Promise<EncryptionPublicKeyParams> {
80
+ // From should be the public key for the encryption
81
+ return Promise.resolve({ ...messageParams, from: messageParams.data });
82
+ }
83
+ }
@@ -0,0 +1,74 @@
1
+ import { BaseConfig, randomId } from "@toruslabs/base-controllers";
2
+ import { JRPCRequest } from "@toruslabs/openlogin-jrpc";
3
+ import log from "loglevel";
4
+
5
+ import KeyringController from "../Keyring/KeyringController";
6
+ import NetworkController from "../Network/NetworkController";
7
+ import { MessageStatus, METHOD_TYPES } from "../utils/constants";
8
+ import { Message, MessageParams, UserRequestApprovalParams } from "../utils/interfaces";
9
+ import AbstractMessageController, { MessageControllerState } from "./AbstractMessageController";
10
+ import { normalizeMessageData, validateSignMessageData } from "./utils";
11
+
12
+ export class MessageController extends AbstractMessageController<Message, MessageParams> {
13
+ override name = "MessageController";
14
+
15
+ protected signMessage: KeyringController["signMessage"];
16
+
17
+ constructor({
18
+ config,
19
+ state,
20
+ signMessage,
21
+ getNetworkIdentifier,
22
+ }: {
23
+ config: Partial<BaseConfig>;
24
+ state: Partial<MessageControllerState<Message>>;
25
+ signMessage: KeyringController["signMessage"];
26
+ getNetworkIdentifier: NetworkController["getNetworkIdentifier"];
27
+ }) {
28
+ super({ config, state, getNetworkIdentifier });
29
+ this.signMessage = signMessage;
30
+ this.initialize();
31
+ }
32
+
33
+ async processSignMessage(messageId: string): Promise<string> {
34
+ try {
35
+ const msgObject = this.getMessage(messageId);
36
+ const cleanMsgParams = await this.approveMessage(msgObject.messageParams);
37
+ const rawSig = this.signMessage(cleanMsgParams.data, cleanMsgParams.from);
38
+ this.updateMessage({ ...msgObject, rawSig });
39
+ this.setMessageStatus(messageId, MessageStatus.SIGNED);
40
+ return rawSig;
41
+ } catch (error) {
42
+ log.error(error);
43
+ this.setMessageStatus(messageId, MessageStatus.FAILED);
44
+ }
45
+ }
46
+
47
+ async addNewUnapprovedMessage(messageParams: MessageParams, req: JRPCRequest<MessageParams> & UserRequestApprovalParams): Promise<string> {
48
+ await this.addUnapprovedMessage(messageParams, req);
49
+ return this.waitForFinishStatus(messageParams, this.name);
50
+ }
51
+
52
+ async addUnapprovedMessage(messageParams: MessageParams, req: JRPCRequest<MessageParams> & UserRequestApprovalParams): Promise<string> {
53
+ validateSignMessageData(messageParams);
54
+ if (req) {
55
+ messageParams.origin = req.origin;
56
+ }
57
+ messageParams.data = normalizeMessageData(messageParams.data);
58
+ const messageId = randomId();
59
+ const messageData: Message = {
60
+ id: messageId,
61
+ messageParams,
62
+ status: MessageStatus.UNAPPROVED,
63
+ time: Date.now(),
64
+ type: METHOD_TYPES.ETH_SIGN,
65
+ };
66
+ await this.addMessage(messageData);
67
+ this.emit(`unapprovedMessage`, messageParams);
68
+ return messageId;
69
+ }
70
+
71
+ prepMessageForSigning(messageParams: MessageParams): Promise<MessageParams> {
72
+ return Promise.resolve(messageParams);
73
+ }
74
+ }