@metamask-previews/eth-money-keyring 2.0.0-8ff7ba2
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.
- package/CHANGELOG.md +44 -0
- package/LICENSE +15 -0
- package/README.md +38 -0
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/money-keyring.cjs +178 -0
- package/dist/money-keyring.cjs.map +1 -0
- package/dist/money-keyring.d.cts +89 -0
- package/dist/money-keyring.d.cts.map +1 -0
- package/dist/money-keyring.d.mts +89 -0
- package/dist/money-keyring.d.mts.map +1 -0
- package/dist/money-keyring.mjs +174 -0
- package/dist/money-keyring.mjs.map +1 -0
- package/package.json +76 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.0.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Support custom cryptographic functions ([#491](https://github.com/MetaMask/accounts/pull/491))
|
|
15
|
+
- Those functions are forwarded to the inner `HdKeyring`.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **BREAKING:** Replace inheritance with composition; `MoneyKeyring` now wraps an inner `HdKeyring` instead of extending it ([#484](https://github.com/MetaMask/accounts/pull/484)), ([#492](https://github.com/MetaMask/accounts/pull/488), [#488](https://github.com/MetaMask/accounts/pull/492))
|
|
20
|
+
- Constructor now requires a `MoneyKeyringOptions` object with a `getMnemonic` callback. The `entropySource` is set by `deserialize()` from the serialized state.
|
|
21
|
+
- The inner `HdKeyring` is created on the first signing call (lazily), protected by a mutex to ensure single initialization under concurrency.
|
|
22
|
+
- Serialized state now stores `entropySource` instead of `mnemonic`; the mnemonic is resolved at deserialization time via the callback.
|
|
23
|
+
- Serialized state now stores `account` (the derived address) instead of `numberOfAccounts`; the account is restored directly from state without calling `getMnemonic`.
|
|
24
|
+
- `hdPath` is no longer part of the serialized state; it is always `MONEY_DERIVATION_PATH`.
|
|
25
|
+
- `signMessage`, `getEncryptionPublicKey`, `decryptMessage`, `exportAccount`, and `getAppKeyAddress` are no longer exposed.
|
|
26
|
+
- `removeAccount()` has been removed; accounts are permanent once added.
|
|
27
|
+
- `getAccounts()` is now a cheap synchronous-like read that never triggers `getMnemonic`.
|
|
28
|
+
- New exports: `GetMnemonicCallback`, `MoneyKeyringOptions` types.
|
|
29
|
+
- New dependency: `@metamask/keyring-utils` (for `Keyring` interface).
|
|
30
|
+
- New dependency: `async-mutex`.
|
|
31
|
+
|
|
32
|
+
## [1.0.0] - 2026-04-01 [DEPRECATED]
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Add initial implementation of `MoneyKeyring` ([#472](https://github.com/MetaMask/accounts/pull/472), [#474](https://github.com/MetaMask/accounts/pull/474))
|
|
37
|
+
- Extends `HdKeyring` from `@metamask/eth-hd-keyring`.
|
|
38
|
+
- Uses keyring type `"Money Keyring"`.
|
|
39
|
+
- Uses derivation path `"m/44'/4392018'/0'/0"`.
|
|
40
|
+
- Enforces that at most one Money account can exist.
|
|
41
|
+
|
|
42
|
+
[Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/eth-money-keyring@2.0.0...HEAD
|
|
43
|
+
[2.0.0]: https://github.com/MetaMask/accounts/compare/@metamask/eth-money-keyring@1.0.0...@metamask/eth-money-keyring@2.0.0
|
|
44
|
+
[1.0.0]: https://github.com/MetaMask/accounts/releases/tag/@metamask/eth-money-keyring@1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 MetaMask
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Money Keyring
|
|
2
|
+
|
|
3
|
+
An Ethereum keyring that extends [`@metamask/eth-hd-keyring`](../keyring-eth-hd) with a distinct keyring type and derivation path for money accounts.
|
|
4
|
+
|
|
5
|
+
Money accounts use a separate HD derivation path to keep funds isolated from the primary HD keyring, while reusing the same seed phrase and signing infrastructure.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
`yarn add @metamask/eth-money-keyring`
|
|
10
|
+
|
|
11
|
+
or
|
|
12
|
+
|
|
13
|
+
`npm install @metamask/eth-money-keyring`
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { MoneyKeyring } from '@metamask/eth-money-keyring';
|
|
19
|
+
|
|
20
|
+
const keyring = new MoneyKeyring();
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The `MoneyKeyring` class implements the same `Keyring` interface as `HdKeyring` — see the [HD Keyring README](../keyring-eth-hd/README.md) for full API documentation.
|
|
24
|
+
|
|
25
|
+
## Contributing
|
|
26
|
+
|
|
27
|
+
### Setup
|
|
28
|
+
|
|
29
|
+
- Install [Node.js](https://nodejs.org) version 18
|
|
30
|
+
- If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you.
|
|
31
|
+
- Install [Yarn v4](https://yarnpkg.com/getting-started/install)
|
|
32
|
+
- Run `yarn install` to install dependencies and run any required post-install scripts
|
|
33
|
+
|
|
34
|
+
### Testing and Linting
|
|
35
|
+
|
|
36
|
+
Run `yarn test` to run the tests once.
|
|
37
|
+
|
|
38
|
+
Run `yarn lint` to run the linter, or run `yarn lint:fix` to run the linter and fix any automatically fixable issues.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MONEY_DERIVATION_PATH = exports.MoneyKeyring = void 0;
|
|
4
|
+
var money_keyring_1 = require("./money-keyring.cjs");
|
|
5
|
+
Object.defineProperty(exports, "MoneyKeyring", { enumerable: true, get: function () { return money_keyring_1.MoneyKeyring; } });
|
|
6
|
+
Object.defineProperty(exports, "MONEY_DERIVATION_PATH", { enumerable: true, get: function () { return money_keyring_1.MONEY_DERIVATION_PATH; } });
|
|
7
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAMyB;AALvB,6GAAA,YAAY,OAAA;AACZ,sHAAA,qBAAqB,OAAA","sourcesContent":["export {\n MoneyKeyring,\n MONEY_DERIVATION_PATH,\n type GetMnemonicCallback,\n type MoneyKeyringOptions,\n type MoneyKeyringSerializedState,\n} from './money-keyring';\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,GACjC,4BAAwB"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,2BAA2B,GACjC,4BAAwB"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,qBAAqB,EAItB,4BAAwB","sourcesContent":["export {\n MoneyKeyring,\n MONEY_DERIVATION_PATH,\n type GetMnemonicCallback,\n type MoneyKeyringOptions,\n type MoneyKeyringSerializedState,\n} from './money-keyring';\n"]}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _MoneyKeyring_instances, _MoneyKeyring_getMnemonic, _MoneyKeyring_cryptographicFunctions, _MoneyKeyring_entropySource, _MoneyKeyring_account, _MoneyKeyring_inner, _MoneyKeyring_lock, _MoneyKeyring_withLockedInner, _MoneyKeyring_getSigner;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.MoneyKeyring = exports.MONEY_KEYRING_TYPE = exports.MONEY_DERIVATION_PATH = void 0;
|
|
16
|
+
const eth_hd_keyring_1 = require("@metamask/eth-hd-keyring");
|
|
17
|
+
const keyring_api_1 = require("@metamask/keyring-api");
|
|
18
|
+
const superstruct_1 = require("@metamask/superstruct");
|
|
19
|
+
const async_mutex_1 = require("async-mutex");
|
|
20
|
+
/**
|
|
21
|
+
* Based on the SLIP-10 coin type: https://github.com/satoshilabs/slips/pull/1983
|
|
22
|
+
*/
|
|
23
|
+
exports.MONEY_DERIVATION_PATH = `m/44'/4392018'/0'/0`;
|
|
24
|
+
/**
|
|
25
|
+
* The unique type identifier for the {@link MoneyKeyring}.
|
|
26
|
+
*/
|
|
27
|
+
exports.MONEY_KEYRING_TYPE = 'Money Keyring';
|
|
28
|
+
/**
|
|
29
|
+
* Struct for validating the serialized state of a {@link MoneyKeyring}.
|
|
30
|
+
*
|
|
31
|
+
* @property entropySource - The entropy source identifier.
|
|
32
|
+
* @property account - The account address, if one has been added.
|
|
33
|
+
*/
|
|
34
|
+
const MoneyKeyringSerializedStateStruct = (0, superstruct_1.object)({
|
|
35
|
+
entropySource: (0, superstruct_1.string)(),
|
|
36
|
+
account: (0, superstruct_1.optional)(keyring_api_1.EthAddressStrictStruct),
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Keyring used to create and manage Money Accounts. It wraps an inner {@link HdKeyring}
|
|
40
|
+
* and enforces a specific HD path and a limit on the number of accounts. The mnemonic
|
|
41
|
+
* is never stored in the serialized state; instead, it is resolved at deserialization
|
|
42
|
+
* time via a callback.
|
|
43
|
+
*
|
|
44
|
+
* The inner {@link HdKeyring} is instantiated lazily on the first signing operation
|
|
45
|
+
* (via {@link MoneyKeyring.#getSigner}). A mutex ensures that concurrent callers never
|
|
46
|
+
* trigger more than one initialization.
|
|
47
|
+
*/
|
|
48
|
+
class MoneyKeyring {
|
|
49
|
+
constructor({ getMnemonic, cryptographicFunctions }) {
|
|
50
|
+
_MoneyKeyring_instances.add(this);
|
|
51
|
+
this.type = exports.MONEY_KEYRING_TYPE;
|
|
52
|
+
_MoneyKeyring_getMnemonic.set(this, void 0);
|
|
53
|
+
_MoneyKeyring_cryptographicFunctions.set(this, void 0);
|
|
54
|
+
_MoneyKeyring_entropySource.set(this, void 0);
|
|
55
|
+
_MoneyKeyring_account.set(this, void 0);
|
|
56
|
+
_MoneyKeyring_inner.set(this, void 0);
|
|
57
|
+
_MoneyKeyring_lock.set(this, new async_mutex_1.Mutex());
|
|
58
|
+
__classPrivateFieldSet(this, _MoneyKeyring_getMnemonic, getMnemonic, "f");
|
|
59
|
+
__classPrivateFieldSet(this, _MoneyKeyring_cryptographicFunctions, cryptographicFunctions, "f");
|
|
60
|
+
}
|
|
61
|
+
get entropySource() {
|
|
62
|
+
return __classPrivateFieldGet(this, _MoneyKeyring_entropySource, "f");
|
|
63
|
+
}
|
|
64
|
+
async serialize() {
|
|
65
|
+
const state = {
|
|
66
|
+
entropySource: __classPrivateFieldGet(this, _MoneyKeyring_entropySource, "f"),
|
|
67
|
+
account: __classPrivateFieldGet(this, _MoneyKeyring_account, "f"),
|
|
68
|
+
};
|
|
69
|
+
(0, superstruct_1.assert)(state, MoneyKeyringSerializedStateStruct);
|
|
70
|
+
return state;
|
|
71
|
+
}
|
|
72
|
+
async deserialize(state) {
|
|
73
|
+
(0, superstruct_1.assert)(state, MoneyKeyringSerializedStateStruct);
|
|
74
|
+
if (__classPrivateFieldGet(this, _MoneyKeyring_entropySource, "f") !== undefined) {
|
|
75
|
+
throw new Error('MoneyKeyring: cannot deserialize after initialization');
|
|
76
|
+
}
|
|
77
|
+
__classPrivateFieldSet(this, _MoneyKeyring_entropySource, state.entropySource, "f");
|
|
78
|
+
__classPrivateFieldSet(this, _MoneyKeyring_account, state.account, "f");
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Add the single Money account to this keyring.
|
|
82
|
+
*
|
|
83
|
+
* @param numberOfAccounts - Must be 1 (the only supported value).
|
|
84
|
+
* @returns The address of the newly added account.
|
|
85
|
+
* @throws If `numberOfAccounts` is not 1 or an account already exists.
|
|
86
|
+
*/
|
|
87
|
+
async addAccounts(numberOfAccounts = 1) {
|
|
88
|
+
if (numberOfAccounts !== 1) {
|
|
89
|
+
throw new Error('MoneyKeyring: supports adding exactly one account');
|
|
90
|
+
}
|
|
91
|
+
return await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_withLockedInner).call(this, async (inner) => {
|
|
92
|
+
if (__classPrivateFieldGet(this, _MoneyKeyring_account, "f") !== undefined) {
|
|
93
|
+
throw new Error('MoneyKeyring: already has an account');
|
|
94
|
+
}
|
|
95
|
+
const [account] = await inner.addAccounts(1);
|
|
96
|
+
if (!account) {
|
|
97
|
+
throw new Error('MoneyKeyring: failed to add account');
|
|
98
|
+
}
|
|
99
|
+
__classPrivateFieldSet(this, _MoneyKeyring_account, account, "f");
|
|
100
|
+
return [account];
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
// -----------------------------------------------------------------------------------
|
|
104
|
+
// The methods below are pass-throughs to the inner HdKeyring.
|
|
105
|
+
async getAccounts() {
|
|
106
|
+
return __classPrivateFieldGet(this, _MoneyKeyring_account, "f") ? [__classPrivateFieldGet(this, _MoneyKeyring_account, "f")] : [];
|
|
107
|
+
}
|
|
108
|
+
async signTransaction(...args) {
|
|
109
|
+
return (await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_getSigner).call(this)).signTransaction(...args);
|
|
110
|
+
}
|
|
111
|
+
async signPersonalMessage(...args) {
|
|
112
|
+
return (await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_getSigner).call(this)).signPersonalMessage(...args);
|
|
113
|
+
}
|
|
114
|
+
async signTypedData(...args) {
|
|
115
|
+
return (await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_getSigner).call(this)).signTypedData(...args);
|
|
116
|
+
}
|
|
117
|
+
async signEip7702Authorization(...args) {
|
|
118
|
+
return (await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_getSigner).call(this)).signEip7702Authorization(...args);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.MoneyKeyring = MoneyKeyring;
|
|
122
|
+
_MoneyKeyring_getMnemonic = new WeakMap(), _MoneyKeyring_cryptographicFunctions = new WeakMap(), _MoneyKeyring_entropySource = new WeakMap(), _MoneyKeyring_account = new WeakMap(), _MoneyKeyring_inner = new WeakMap(), _MoneyKeyring_lock = new WeakMap(), _MoneyKeyring_instances = new WeakSet(), _MoneyKeyring_withLockedInner =
|
|
123
|
+
/**
|
|
124
|
+
* Runs a callback with the initialized inner {@link HdKeyring}, always within
|
|
125
|
+
* the mutex lock. Initializes {@link HdKeyring} on the first call.
|
|
126
|
+
*
|
|
127
|
+
* The initialization is deferred until first needed so that the potentially
|
|
128
|
+
* expensive {@link GetMnemonicCallback} is not invoked during
|
|
129
|
+
* {@link MoneyKeyring.deserialize} or {@link MoneyKeyring.getAccounts}.
|
|
130
|
+
*
|
|
131
|
+
* @param callback - Function to execute with the initialized inner keyring.
|
|
132
|
+
* @returns The result of the callback.
|
|
133
|
+
*/
|
|
134
|
+
async function _MoneyKeyring_withLockedInner(callback) {
|
|
135
|
+
const entropySource = __classPrivateFieldGet(this, _MoneyKeyring_entropySource, "f");
|
|
136
|
+
if (!entropySource) {
|
|
137
|
+
throw new Error('MoneyKeyring: not yet deserialized');
|
|
138
|
+
}
|
|
139
|
+
return __classPrivateFieldGet(this, _MoneyKeyring_lock, "f").runExclusive(async () => {
|
|
140
|
+
if (!__classPrivateFieldGet(this, _MoneyKeyring_inner, "f")) {
|
|
141
|
+
const mnemonic = await __classPrivateFieldGet(this, _MoneyKeyring_getMnemonic, "f").call(this, entropySource);
|
|
142
|
+
const inner = new eth_hd_keyring_1.HdKeyring({
|
|
143
|
+
cryptographicFunctions: __classPrivateFieldGet(this, _MoneyKeyring_cryptographicFunctions, "f"),
|
|
144
|
+
});
|
|
145
|
+
await inner.deserialize({
|
|
146
|
+
mnemonic,
|
|
147
|
+
numberOfAccounts: __classPrivateFieldGet(this, _MoneyKeyring_account, "f") ? 1 : 0,
|
|
148
|
+
hdPath: exports.MONEY_DERIVATION_PATH,
|
|
149
|
+
});
|
|
150
|
+
if (__classPrivateFieldGet(this, _MoneyKeyring_account, "f")) {
|
|
151
|
+
const [derivedAccount] = await inner.getAccounts();
|
|
152
|
+
if (derivedAccount?.toLowerCase() !== __classPrivateFieldGet(this, _MoneyKeyring_account, "f").toLowerCase()) {
|
|
153
|
+
throw new Error('MoneyKeyring: signer account mismatch');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
__classPrivateFieldSet(this, _MoneyKeyring_inner, inner, "f");
|
|
157
|
+
}
|
|
158
|
+
return callback(__classPrivateFieldGet(this, _MoneyKeyring_inner, "f"));
|
|
159
|
+
});
|
|
160
|
+
}, _MoneyKeyring_getSigner =
|
|
161
|
+
/**
|
|
162
|
+
* Returns the initialized inner {@link HdKeyring}.
|
|
163
|
+
*
|
|
164
|
+
* Uses a fast path if already initialized, otherwise delegates to
|
|
165
|
+
* {@link MoneyKeyring.#withLockedInner}.
|
|
166
|
+
*
|
|
167
|
+
* @returns The EVM signer instance.
|
|
168
|
+
*/
|
|
169
|
+
async function _MoneyKeyring_getSigner() {
|
|
170
|
+
if (__classPrivateFieldGet(this, _MoneyKeyring_inner, "f")) {
|
|
171
|
+
return __classPrivateFieldGet(this, _MoneyKeyring_inner, "f");
|
|
172
|
+
}
|
|
173
|
+
// We use the mutex-protected method to initialize the inner keyring if needed, but once
|
|
174
|
+
// initialized, we can return it directly and use it as the signer instance.
|
|
175
|
+
return await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_withLockedInner).call(this, (inner) => inner);
|
|
176
|
+
};
|
|
177
|
+
MoneyKeyring.type = exports.MONEY_KEYRING_TYPE;
|
|
178
|
+
//# sourceMappingURL=money-keyring.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money-keyring.cjs","sourceRoot":"","sources":["../src/money-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { HdKeyring } from '@metamask/eth-hd-keyring';\nimport { type CryptographicFunctions } from '@metamask/key-tree';\nimport { EthAddressStrictStruct } from '@metamask/keyring-api';\nimport type { Keyring } from '@metamask/keyring-utils';\nimport {\n assert,\n type Infer,\n object,\n optional,\n string as stringStruct,\n} from '@metamask/superstruct';\nimport { type Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\n/**\n * Based on the SLIP-10 coin type: https://github.com/satoshilabs/slips/pull/1983\n */\nexport const MONEY_DERIVATION_PATH = `m/44'/4392018'/0'/0`;\n\n/**\n * The unique type identifier for the {@link MoneyKeyring}.\n */\nexport const MONEY_KEYRING_TYPE = 'Money Keyring';\n\n/**\n * Callback used to resolve a mnemonic from an entropy source ID.\n *\n * @param entropySource - The entropy source identifier.\n * @returns The mnemonic as an array of UTF-8 byte values.\n */\nexport type GetMnemonicCallback = (entropySource: string) => Promise<number[]>;\n\n/**\n * EVM signer interface, a subset of {@link HdKeyring} methods.\n */\nexport type EvmSigner = {\n signTransaction: HdKeyring['signTransaction'];\n signPersonalMessage: HdKeyring['signPersonalMessage'];\n signTypedData: HdKeyring['signTypedData'];\n signEip7702Authorization: HdKeyring['signEip7702Authorization'];\n};\n\n/**\n * Options for constructing a {@link MoneyKeyring}.\n *\n * @property getMnemonic - Callback to resolve the mnemonic from an entropy source ID.\n */\nexport type MoneyKeyringOptions = {\n getMnemonic: GetMnemonicCallback;\n cryptographicFunctions?: CryptographicFunctions;\n};\n\n/**\n * Struct for validating the serialized state of a {@link MoneyKeyring}.\n *\n * @property entropySource - The entropy source identifier.\n * @property account - The account address, if one has been added.\n */\nconst MoneyKeyringSerializedStateStruct = object({\n entropySource: stringStruct(),\n account: optional(EthAddressStrictStruct),\n});\n\n/**\n * The serialized state of a {@link MoneyKeyring} instance.\n */\nexport type MoneyKeyringSerializedState = Infer<\n typeof MoneyKeyringSerializedStateStruct\n>;\n\n/**\n * Keyring used to create and manage Money Accounts. It wraps an inner {@link HdKeyring}\n * and enforces a specific HD path and a limit on the number of accounts. The mnemonic\n * is never stored in the serialized state; instead, it is resolved at deserialization\n * time via a callback.\n *\n * The inner {@link HdKeyring} is instantiated lazily on the first signing operation\n * (via {@link MoneyKeyring.#getSigner}). A mutex ensures that concurrent callers never\n * trigger more than one initialization.\n */\nexport class MoneyKeyring implements Keyring {\n static type: string = MONEY_KEYRING_TYPE;\n\n readonly type: string = MONEY_KEYRING_TYPE;\n\n readonly #getMnemonic: GetMnemonicCallback;\n\n readonly #cryptographicFunctions: CryptographicFunctions | undefined;\n\n #entropySource: string | undefined;\n\n #account: Hex | undefined;\n\n #inner: HdKeyring | undefined;\n\n readonly #lock = new Mutex();\n\n constructor({ getMnemonic, cryptographicFunctions }: MoneyKeyringOptions) {\n this.#getMnemonic = getMnemonic;\n this.#cryptographicFunctions = cryptographicFunctions;\n }\n\n get entropySource(): string | undefined {\n return this.#entropySource;\n }\n\n async serialize(): Promise<MoneyKeyringSerializedState> {\n const state = {\n entropySource: this.#entropySource,\n account: this.#account,\n };\n assert(state, MoneyKeyringSerializedStateStruct);\n\n return state;\n }\n\n async deserialize(state: MoneyKeyringSerializedState): Promise<void> {\n assert(state, MoneyKeyringSerializedStateStruct);\n\n if (this.#entropySource !== undefined) {\n throw new Error('MoneyKeyring: cannot deserialize after initialization');\n }\n\n this.#entropySource = state.entropySource;\n this.#account = state.account;\n }\n\n /**\n * Runs a callback with the initialized inner {@link HdKeyring}, always within\n * the mutex lock. Initializes {@link HdKeyring} on the first call.\n *\n * The initialization is deferred until first needed so that the potentially\n * expensive {@link GetMnemonicCallback} is not invoked during\n * {@link MoneyKeyring.deserialize} or {@link MoneyKeyring.getAccounts}.\n *\n * @param callback - Function to execute with the initialized inner keyring.\n * @returns The result of the callback.\n */\n async #withLockedInner<ReturnType>(\n callback: (inner: HdKeyring) => Promise<ReturnType> | ReturnType,\n ): Promise<ReturnType> {\n const entropySource = this.#entropySource;\n if (!entropySource) {\n throw new Error('MoneyKeyring: not yet deserialized');\n }\n\n return this.#lock.runExclusive(async () => {\n if (!this.#inner) {\n const mnemonic = await this.#getMnemonic(entropySource);\n const inner = new HdKeyring({\n cryptographicFunctions: this.#cryptographicFunctions,\n });\n await inner.deserialize({\n mnemonic,\n numberOfAccounts: this.#account ? 1 : 0,\n hdPath: MONEY_DERIVATION_PATH,\n });\n\n if (this.#account) {\n const [derivedAccount] = await inner.getAccounts();\n if (derivedAccount?.toLowerCase() !== this.#account.toLowerCase()) {\n throw new Error('MoneyKeyring: signer account mismatch');\n }\n }\n\n this.#inner = inner;\n }\n\n return callback(this.#inner);\n });\n }\n\n /**\n * Returns the initialized inner {@link HdKeyring}.\n *\n * Uses a fast path if already initialized, otherwise delegates to\n * {@link MoneyKeyring.#withLockedInner}.\n *\n * @returns The EVM signer instance.\n */\n async #getSigner(): Promise<EvmSigner> {\n if (this.#inner) {\n return this.#inner;\n }\n\n // We use the mutex-protected method to initialize the inner keyring if needed, but once\n // initialized, we can return it directly and use it as the signer instance.\n return await this.#withLockedInner((inner) => inner);\n }\n\n /**\n * Add the single Money account to this keyring.\n *\n * @param numberOfAccounts - Must be 1 (the only supported value).\n * @returns The address of the newly added account.\n * @throws If `numberOfAccounts` is not 1 or an account already exists.\n */\n async addAccounts(numberOfAccounts = 1): Promise<Hex[]> {\n if (numberOfAccounts !== 1) {\n throw new Error('MoneyKeyring: supports adding exactly one account');\n }\n\n return await this.#withLockedInner(async (inner) => {\n if (this.#account !== undefined) {\n throw new Error('MoneyKeyring: already has an account');\n }\n\n const [account] = await inner.addAccounts(1);\n if (!account) {\n throw new Error('MoneyKeyring: failed to add account');\n }\n this.#account = account;\n\n return [account];\n });\n }\n\n // -----------------------------------------------------------------------------------\n // The methods below are pass-throughs to the inner HdKeyring.\n\n async getAccounts(): Promise<Hex[]> {\n return this.#account ? [this.#account] : [];\n }\n\n async signTransaction(\n ...args: Parameters<HdKeyring['signTransaction']>\n ): ReturnType<HdKeyring['signTransaction']> {\n return (await this.#getSigner()).signTransaction(...args);\n }\n\n async signPersonalMessage(\n ...args: Parameters<HdKeyring['signPersonalMessage']>\n ): ReturnType<HdKeyring['signPersonalMessage']> {\n return (await this.#getSigner()).signPersonalMessage(...args);\n }\n\n async signTypedData(\n ...args: Parameters<HdKeyring['signTypedData']>\n ): ReturnType<HdKeyring['signTypedData']> {\n return (await this.#getSigner()).signTypedData(...args);\n }\n\n async signEip7702Authorization(\n ...args: Parameters<HdKeyring['signEip7702Authorization']>\n ): ReturnType<HdKeyring['signEip7702Authorization']> {\n return (await this.#getSigner()).signEip7702Authorization(...args);\n }\n}\n"]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { HdKeyring } from "@metamask/eth-hd-keyring";
|
|
2
|
+
import { type CryptographicFunctions } from "@metamask/key-tree";
|
|
3
|
+
import type { Keyring } from "@metamask/keyring-utils";
|
|
4
|
+
import { type Infer } from "@metamask/superstruct";
|
|
5
|
+
import { type Hex } from "@metamask/utils";
|
|
6
|
+
/**
|
|
7
|
+
* Based on the SLIP-10 coin type: https://github.com/satoshilabs/slips/pull/1983
|
|
8
|
+
*/
|
|
9
|
+
export declare const MONEY_DERIVATION_PATH = "m/44'/4392018'/0'/0";
|
|
10
|
+
/**
|
|
11
|
+
* The unique type identifier for the {@link MoneyKeyring}.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MONEY_KEYRING_TYPE = "Money Keyring";
|
|
14
|
+
/**
|
|
15
|
+
* Callback used to resolve a mnemonic from an entropy source ID.
|
|
16
|
+
*
|
|
17
|
+
* @param entropySource - The entropy source identifier.
|
|
18
|
+
* @returns The mnemonic as an array of UTF-8 byte values.
|
|
19
|
+
*/
|
|
20
|
+
export type GetMnemonicCallback = (entropySource: string) => Promise<number[]>;
|
|
21
|
+
/**
|
|
22
|
+
* EVM signer interface, a subset of {@link HdKeyring} methods.
|
|
23
|
+
*/
|
|
24
|
+
export type EvmSigner = {
|
|
25
|
+
signTransaction: HdKeyring['signTransaction'];
|
|
26
|
+
signPersonalMessage: HdKeyring['signPersonalMessage'];
|
|
27
|
+
signTypedData: HdKeyring['signTypedData'];
|
|
28
|
+
signEip7702Authorization: HdKeyring['signEip7702Authorization'];
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Options for constructing a {@link MoneyKeyring}.
|
|
32
|
+
*
|
|
33
|
+
* @property getMnemonic - Callback to resolve the mnemonic from an entropy source ID.
|
|
34
|
+
*/
|
|
35
|
+
export type MoneyKeyringOptions = {
|
|
36
|
+
getMnemonic: GetMnemonicCallback;
|
|
37
|
+
cryptographicFunctions?: CryptographicFunctions;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Struct for validating the serialized state of a {@link MoneyKeyring}.
|
|
41
|
+
*
|
|
42
|
+
* @property entropySource - The entropy source identifier.
|
|
43
|
+
* @property account - The account address, if one has been added.
|
|
44
|
+
*/
|
|
45
|
+
declare const MoneyKeyringSerializedStateStruct: import("@metamask/superstruct").Struct<{
|
|
46
|
+
entropySource: string;
|
|
47
|
+
account?: `0x${string}` | undefined;
|
|
48
|
+
}, {
|
|
49
|
+
entropySource: import("@metamask/superstruct").Struct<string, null>;
|
|
50
|
+
account: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* The serialized state of a {@link MoneyKeyring} instance.
|
|
54
|
+
*/
|
|
55
|
+
export type MoneyKeyringSerializedState = Infer<typeof MoneyKeyringSerializedStateStruct>;
|
|
56
|
+
/**
|
|
57
|
+
* Keyring used to create and manage Money Accounts. It wraps an inner {@link HdKeyring}
|
|
58
|
+
* and enforces a specific HD path and a limit on the number of accounts. The mnemonic
|
|
59
|
+
* is never stored in the serialized state; instead, it is resolved at deserialization
|
|
60
|
+
* time via a callback.
|
|
61
|
+
*
|
|
62
|
+
* The inner {@link HdKeyring} is instantiated lazily on the first signing operation
|
|
63
|
+
* (via {@link MoneyKeyring.#getSigner}). A mutex ensures that concurrent callers never
|
|
64
|
+
* trigger more than one initialization.
|
|
65
|
+
*/
|
|
66
|
+
export declare class MoneyKeyring implements Keyring {
|
|
67
|
+
#private;
|
|
68
|
+
static type: string;
|
|
69
|
+
readonly type: string;
|
|
70
|
+
constructor({ getMnemonic, cryptographicFunctions }: MoneyKeyringOptions);
|
|
71
|
+
get entropySource(): string | undefined;
|
|
72
|
+
serialize(): Promise<MoneyKeyringSerializedState>;
|
|
73
|
+
deserialize(state: MoneyKeyringSerializedState): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Add the single Money account to this keyring.
|
|
76
|
+
*
|
|
77
|
+
* @param numberOfAccounts - Must be 1 (the only supported value).
|
|
78
|
+
* @returns The address of the newly added account.
|
|
79
|
+
* @throws If `numberOfAccounts` is not 1 or an account already exists.
|
|
80
|
+
*/
|
|
81
|
+
addAccounts(numberOfAccounts?: number): Promise<Hex[]>;
|
|
82
|
+
getAccounts(): Promise<Hex[]>;
|
|
83
|
+
signTransaction(...args: Parameters<HdKeyring['signTransaction']>): ReturnType<HdKeyring['signTransaction']>;
|
|
84
|
+
signPersonalMessage(...args: Parameters<HdKeyring['signPersonalMessage']>): ReturnType<HdKeyring['signPersonalMessage']>;
|
|
85
|
+
signTypedData(...args: Parameters<HdKeyring['signTypedData']>): ReturnType<HdKeyring['signTypedData']>;
|
|
86
|
+
signEip7702Authorization(...args: Parameters<HdKeyring['signEip7702Authorization']>): ReturnType<HdKeyring['signEip7702Authorization']>;
|
|
87
|
+
}
|
|
88
|
+
export {};
|
|
89
|
+
//# sourceMappingURL=money-keyring.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money-keyring.d.cts","sourceRoot":"","sources":["../src/money-keyring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,iCAAiC;AACrD,OAAO,EAAE,KAAK,sBAAsB,EAAE,2BAA2B;AAEjE,OAAO,KAAK,EAAE,OAAO,EAAE,gCAAgC;AACvD,OAAO,EAEL,KAAK,KAAK,EAIX,8BAA8B;AAC/B,OAAO,EAAE,KAAK,GAAG,EAAE,wBAAwB;AAG3C;;GAEG;AACH,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,eAAe,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC9C,mBAAmB,EAAE,SAAS,CAAC,qBAAqB,CAAC,CAAC;IACtD,aAAa,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;IAC1C,wBAAwB,EAAE,SAAS,CAAC,0BAA0B,CAAC,CAAC;CACjE,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,mBAAmB,CAAC;IACjC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;CACjD,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,iCAAiC;;;;;;EAGrC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,KAAK,CAC7C,OAAO,iCAAiC,CACzC,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,YAAa,YAAW,OAAO;;IAC1C,MAAM,CAAC,IAAI,EAAE,MAAM,CAAsB;IAEzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAsB;gBAc/B,EAAE,WAAW,EAAE,sBAAsB,EAAE,EAAE,mBAAmB;IAKxE,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAEtC;IAEK,SAAS,IAAI,OAAO,CAAC,2BAA2B,CAAC;IAUjD,WAAW,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IA0EpE;;;;;;OAMG;IACG,WAAW,CAAC,gBAAgB,SAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAuBjD,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAI7B,eAAe,CACnB,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,GAChD,UAAU,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAIrC,mBAAmB,CACvB,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC,GACpD,UAAU,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAIzC,aAAa,CACjB,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,GAC9C,UAAU,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAInC,wBAAwB,CAC5B,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC,GACzD,UAAU,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;CAGrD"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { HdKeyring } from "@metamask/eth-hd-keyring";
|
|
2
|
+
import { type CryptographicFunctions } from "@metamask/key-tree";
|
|
3
|
+
import type { Keyring } from "@metamask/keyring-utils";
|
|
4
|
+
import { type Infer } from "@metamask/superstruct";
|
|
5
|
+
import { type Hex } from "@metamask/utils";
|
|
6
|
+
/**
|
|
7
|
+
* Based on the SLIP-10 coin type: https://github.com/satoshilabs/slips/pull/1983
|
|
8
|
+
*/
|
|
9
|
+
export declare const MONEY_DERIVATION_PATH = "m/44'/4392018'/0'/0";
|
|
10
|
+
/**
|
|
11
|
+
* The unique type identifier for the {@link MoneyKeyring}.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MONEY_KEYRING_TYPE = "Money Keyring";
|
|
14
|
+
/**
|
|
15
|
+
* Callback used to resolve a mnemonic from an entropy source ID.
|
|
16
|
+
*
|
|
17
|
+
* @param entropySource - The entropy source identifier.
|
|
18
|
+
* @returns The mnemonic as an array of UTF-8 byte values.
|
|
19
|
+
*/
|
|
20
|
+
export type GetMnemonicCallback = (entropySource: string) => Promise<number[]>;
|
|
21
|
+
/**
|
|
22
|
+
* EVM signer interface, a subset of {@link HdKeyring} methods.
|
|
23
|
+
*/
|
|
24
|
+
export type EvmSigner = {
|
|
25
|
+
signTransaction: HdKeyring['signTransaction'];
|
|
26
|
+
signPersonalMessage: HdKeyring['signPersonalMessage'];
|
|
27
|
+
signTypedData: HdKeyring['signTypedData'];
|
|
28
|
+
signEip7702Authorization: HdKeyring['signEip7702Authorization'];
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Options for constructing a {@link MoneyKeyring}.
|
|
32
|
+
*
|
|
33
|
+
* @property getMnemonic - Callback to resolve the mnemonic from an entropy source ID.
|
|
34
|
+
*/
|
|
35
|
+
export type MoneyKeyringOptions = {
|
|
36
|
+
getMnemonic: GetMnemonicCallback;
|
|
37
|
+
cryptographicFunctions?: CryptographicFunctions;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Struct for validating the serialized state of a {@link MoneyKeyring}.
|
|
41
|
+
*
|
|
42
|
+
* @property entropySource - The entropy source identifier.
|
|
43
|
+
* @property account - The account address, if one has been added.
|
|
44
|
+
*/
|
|
45
|
+
declare const MoneyKeyringSerializedStateStruct: import("@metamask/superstruct").Struct<{
|
|
46
|
+
entropySource: string;
|
|
47
|
+
account?: `0x${string}` | undefined;
|
|
48
|
+
}, {
|
|
49
|
+
entropySource: import("@metamask/superstruct").Struct<string, null>;
|
|
50
|
+
account: import("@metamask/superstruct").Struct<`0x${string}` | undefined, null>;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* The serialized state of a {@link MoneyKeyring} instance.
|
|
54
|
+
*/
|
|
55
|
+
export type MoneyKeyringSerializedState = Infer<typeof MoneyKeyringSerializedStateStruct>;
|
|
56
|
+
/**
|
|
57
|
+
* Keyring used to create and manage Money Accounts. It wraps an inner {@link HdKeyring}
|
|
58
|
+
* and enforces a specific HD path and a limit on the number of accounts. The mnemonic
|
|
59
|
+
* is never stored in the serialized state; instead, it is resolved at deserialization
|
|
60
|
+
* time via a callback.
|
|
61
|
+
*
|
|
62
|
+
* The inner {@link HdKeyring} is instantiated lazily on the first signing operation
|
|
63
|
+
* (via {@link MoneyKeyring.#getSigner}). A mutex ensures that concurrent callers never
|
|
64
|
+
* trigger more than one initialization.
|
|
65
|
+
*/
|
|
66
|
+
export declare class MoneyKeyring implements Keyring {
|
|
67
|
+
#private;
|
|
68
|
+
static type: string;
|
|
69
|
+
readonly type: string;
|
|
70
|
+
constructor({ getMnemonic, cryptographicFunctions }: MoneyKeyringOptions);
|
|
71
|
+
get entropySource(): string | undefined;
|
|
72
|
+
serialize(): Promise<MoneyKeyringSerializedState>;
|
|
73
|
+
deserialize(state: MoneyKeyringSerializedState): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Add the single Money account to this keyring.
|
|
76
|
+
*
|
|
77
|
+
* @param numberOfAccounts - Must be 1 (the only supported value).
|
|
78
|
+
* @returns The address of the newly added account.
|
|
79
|
+
* @throws If `numberOfAccounts` is not 1 or an account already exists.
|
|
80
|
+
*/
|
|
81
|
+
addAccounts(numberOfAccounts?: number): Promise<Hex[]>;
|
|
82
|
+
getAccounts(): Promise<Hex[]>;
|
|
83
|
+
signTransaction(...args: Parameters<HdKeyring['signTransaction']>): ReturnType<HdKeyring['signTransaction']>;
|
|
84
|
+
signPersonalMessage(...args: Parameters<HdKeyring['signPersonalMessage']>): ReturnType<HdKeyring['signPersonalMessage']>;
|
|
85
|
+
signTypedData(...args: Parameters<HdKeyring['signTypedData']>): ReturnType<HdKeyring['signTypedData']>;
|
|
86
|
+
signEip7702Authorization(...args: Parameters<HdKeyring['signEip7702Authorization']>): ReturnType<HdKeyring['signEip7702Authorization']>;
|
|
87
|
+
}
|
|
88
|
+
export {};
|
|
89
|
+
//# sourceMappingURL=money-keyring.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money-keyring.d.mts","sourceRoot":"","sources":["../src/money-keyring.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,iCAAiC;AACrD,OAAO,EAAE,KAAK,sBAAsB,EAAE,2BAA2B;AAEjE,OAAO,KAAK,EAAE,OAAO,EAAE,gCAAgC;AACvD,OAAO,EAEL,KAAK,KAAK,EAIX,8BAA8B;AAC/B,OAAO,EAAE,KAAK,GAAG,EAAE,wBAAwB;AAG3C;;GAEG;AACH,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,eAAe,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC9C,mBAAmB,EAAE,SAAS,CAAC,qBAAqB,CAAC,CAAC;IACtD,aAAa,EAAE,SAAS,CAAC,eAAe,CAAC,CAAC;IAC1C,wBAAwB,EAAE,SAAS,CAAC,0BAA0B,CAAC,CAAC;CACjE,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,mBAAmB,CAAC;IACjC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;CACjD,CAAC;AAEF;;;;;GAKG;AACH,QAAA,MAAM,iCAAiC;;;;;;EAGrC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,KAAK,CAC7C,OAAO,iCAAiC,CACzC,CAAC;AAEF;;;;;;;;;GASG;AACH,qBAAa,YAAa,YAAW,OAAO;;IAC1C,MAAM,CAAC,IAAI,EAAE,MAAM,CAAsB;IAEzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAsB;gBAc/B,EAAE,WAAW,EAAE,sBAAsB,EAAE,EAAE,mBAAmB;IAKxE,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAEtC;IAEK,SAAS,IAAI,OAAO,CAAC,2BAA2B,CAAC;IAUjD,WAAW,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IA0EpE;;;;;;OAMG;IACG,WAAW,CAAC,gBAAgB,SAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAuBjD,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAI7B,eAAe,CACnB,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC,GAChD,UAAU,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAIrC,mBAAmB,CACvB,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC,GACpD,UAAU,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAIzC,aAAa,CACjB,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,GAC9C,UAAU,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAInC,wBAAwB,CAC5B,GAAG,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC,GACzD,UAAU,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;CAGrD"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _MoneyKeyring_instances, _MoneyKeyring_getMnemonic, _MoneyKeyring_cryptographicFunctions, _MoneyKeyring_entropySource, _MoneyKeyring_account, _MoneyKeyring_inner, _MoneyKeyring_lock, _MoneyKeyring_withLockedInner, _MoneyKeyring_getSigner;
|
|
13
|
+
import { HdKeyring } from "@metamask/eth-hd-keyring";
|
|
14
|
+
import { EthAddressStrictStruct } from "@metamask/keyring-api";
|
|
15
|
+
import { assert, object, optional, string as stringStruct } from "@metamask/superstruct";
|
|
16
|
+
import { Mutex } from "async-mutex";
|
|
17
|
+
/**
|
|
18
|
+
* Based on the SLIP-10 coin type: https://github.com/satoshilabs/slips/pull/1983
|
|
19
|
+
*/
|
|
20
|
+
export const MONEY_DERIVATION_PATH = `m/44'/4392018'/0'/0`;
|
|
21
|
+
/**
|
|
22
|
+
* The unique type identifier for the {@link MoneyKeyring}.
|
|
23
|
+
*/
|
|
24
|
+
export const MONEY_KEYRING_TYPE = 'Money Keyring';
|
|
25
|
+
/**
|
|
26
|
+
* Struct for validating the serialized state of a {@link MoneyKeyring}.
|
|
27
|
+
*
|
|
28
|
+
* @property entropySource - The entropy source identifier.
|
|
29
|
+
* @property account - The account address, if one has been added.
|
|
30
|
+
*/
|
|
31
|
+
const MoneyKeyringSerializedStateStruct = object({
|
|
32
|
+
entropySource: stringStruct(),
|
|
33
|
+
account: optional(EthAddressStrictStruct),
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Keyring used to create and manage Money Accounts. It wraps an inner {@link HdKeyring}
|
|
37
|
+
* and enforces a specific HD path and a limit on the number of accounts. The mnemonic
|
|
38
|
+
* is never stored in the serialized state; instead, it is resolved at deserialization
|
|
39
|
+
* time via a callback.
|
|
40
|
+
*
|
|
41
|
+
* The inner {@link HdKeyring} is instantiated lazily on the first signing operation
|
|
42
|
+
* (via {@link MoneyKeyring.#getSigner}). A mutex ensures that concurrent callers never
|
|
43
|
+
* trigger more than one initialization.
|
|
44
|
+
*/
|
|
45
|
+
export class MoneyKeyring {
|
|
46
|
+
constructor({ getMnemonic, cryptographicFunctions }) {
|
|
47
|
+
_MoneyKeyring_instances.add(this);
|
|
48
|
+
this.type = MONEY_KEYRING_TYPE;
|
|
49
|
+
_MoneyKeyring_getMnemonic.set(this, void 0);
|
|
50
|
+
_MoneyKeyring_cryptographicFunctions.set(this, void 0);
|
|
51
|
+
_MoneyKeyring_entropySource.set(this, void 0);
|
|
52
|
+
_MoneyKeyring_account.set(this, void 0);
|
|
53
|
+
_MoneyKeyring_inner.set(this, void 0);
|
|
54
|
+
_MoneyKeyring_lock.set(this, new Mutex());
|
|
55
|
+
__classPrivateFieldSet(this, _MoneyKeyring_getMnemonic, getMnemonic, "f");
|
|
56
|
+
__classPrivateFieldSet(this, _MoneyKeyring_cryptographicFunctions, cryptographicFunctions, "f");
|
|
57
|
+
}
|
|
58
|
+
get entropySource() {
|
|
59
|
+
return __classPrivateFieldGet(this, _MoneyKeyring_entropySource, "f");
|
|
60
|
+
}
|
|
61
|
+
async serialize() {
|
|
62
|
+
const state = {
|
|
63
|
+
entropySource: __classPrivateFieldGet(this, _MoneyKeyring_entropySource, "f"),
|
|
64
|
+
account: __classPrivateFieldGet(this, _MoneyKeyring_account, "f"),
|
|
65
|
+
};
|
|
66
|
+
assert(state, MoneyKeyringSerializedStateStruct);
|
|
67
|
+
return state;
|
|
68
|
+
}
|
|
69
|
+
async deserialize(state) {
|
|
70
|
+
assert(state, MoneyKeyringSerializedStateStruct);
|
|
71
|
+
if (__classPrivateFieldGet(this, _MoneyKeyring_entropySource, "f") !== undefined) {
|
|
72
|
+
throw new Error('MoneyKeyring: cannot deserialize after initialization');
|
|
73
|
+
}
|
|
74
|
+
__classPrivateFieldSet(this, _MoneyKeyring_entropySource, state.entropySource, "f");
|
|
75
|
+
__classPrivateFieldSet(this, _MoneyKeyring_account, state.account, "f");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Add the single Money account to this keyring.
|
|
79
|
+
*
|
|
80
|
+
* @param numberOfAccounts - Must be 1 (the only supported value).
|
|
81
|
+
* @returns The address of the newly added account.
|
|
82
|
+
* @throws If `numberOfAccounts` is not 1 or an account already exists.
|
|
83
|
+
*/
|
|
84
|
+
async addAccounts(numberOfAccounts = 1) {
|
|
85
|
+
if (numberOfAccounts !== 1) {
|
|
86
|
+
throw new Error('MoneyKeyring: supports adding exactly one account');
|
|
87
|
+
}
|
|
88
|
+
return await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_withLockedInner).call(this, async (inner) => {
|
|
89
|
+
if (__classPrivateFieldGet(this, _MoneyKeyring_account, "f") !== undefined) {
|
|
90
|
+
throw new Error('MoneyKeyring: already has an account');
|
|
91
|
+
}
|
|
92
|
+
const [account] = await inner.addAccounts(1);
|
|
93
|
+
if (!account) {
|
|
94
|
+
throw new Error('MoneyKeyring: failed to add account');
|
|
95
|
+
}
|
|
96
|
+
__classPrivateFieldSet(this, _MoneyKeyring_account, account, "f");
|
|
97
|
+
return [account];
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// -----------------------------------------------------------------------------------
|
|
101
|
+
// The methods below are pass-throughs to the inner HdKeyring.
|
|
102
|
+
async getAccounts() {
|
|
103
|
+
return __classPrivateFieldGet(this, _MoneyKeyring_account, "f") ? [__classPrivateFieldGet(this, _MoneyKeyring_account, "f")] : [];
|
|
104
|
+
}
|
|
105
|
+
async signTransaction(...args) {
|
|
106
|
+
return (await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_getSigner).call(this)).signTransaction(...args);
|
|
107
|
+
}
|
|
108
|
+
async signPersonalMessage(...args) {
|
|
109
|
+
return (await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_getSigner).call(this)).signPersonalMessage(...args);
|
|
110
|
+
}
|
|
111
|
+
async signTypedData(...args) {
|
|
112
|
+
return (await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_getSigner).call(this)).signTypedData(...args);
|
|
113
|
+
}
|
|
114
|
+
async signEip7702Authorization(...args) {
|
|
115
|
+
return (await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_getSigner).call(this)).signEip7702Authorization(...args);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
_MoneyKeyring_getMnemonic = new WeakMap(), _MoneyKeyring_cryptographicFunctions = new WeakMap(), _MoneyKeyring_entropySource = new WeakMap(), _MoneyKeyring_account = new WeakMap(), _MoneyKeyring_inner = new WeakMap(), _MoneyKeyring_lock = new WeakMap(), _MoneyKeyring_instances = new WeakSet(), _MoneyKeyring_withLockedInner =
|
|
119
|
+
/**
|
|
120
|
+
* Runs a callback with the initialized inner {@link HdKeyring}, always within
|
|
121
|
+
* the mutex lock. Initializes {@link HdKeyring} on the first call.
|
|
122
|
+
*
|
|
123
|
+
* The initialization is deferred until first needed so that the potentially
|
|
124
|
+
* expensive {@link GetMnemonicCallback} is not invoked during
|
|
125
|
+
* {@link MoneyKeyring.deserialize} or {@link MoneyKeyring.getAccounts}.
|
|
126
|
+
*
|
|
127
|
+
* @param callback - Function to execute with the initialized inner keyring.
|
|
128
|
+
* @returns The result of the callback.
|
|
129
|
+
*/
|
|
130
|
+
async function _MoneyKeyring_withLockedInner(callback) {
|
|
131
|
+
const entropySource = __classPrivateFieldGet(this, _MoneyKeyring_entropySource, "f");
|
|
132
|
+
if (!entropySource) {
|
|
133
|
+
throw new Error('MoneyKeyring: not yet deserialized');
|
|
134
|
+
}
|
|
135
|
+
return __classPrivateFieldGet(this, _MoneyKeyring_lock, "f").runExclusive(async () => {
|
|
136
|
+
if (!__classPrivateFieldGet(this, _MoneyKeyring_inner, "f")) {
|
|
137
|
+
const mnemonic = await __classPrivateFieldGet(this, _MoneyKeyring_getMnemonic, "f").call(this, entropySource);
|
|
138
|
+
const inner = new HdKeyring({
|
|
139
|
+
cryptographicFunctions: __classPrivateFieldGet(this, _MoneyKeyring_cryptographicFunctions, "f"),
|
|
140
|
+
});
|
|
141
|
+
await inner.deserialize({
|
|
142
|
+
mnemonic,
|
|
143
|
+
numberOfAccounts: __classPrivateFieldGet(this, _MoneyKeyring_account, "f") ? 1 : 0,
|
|
144
|
+
hdPath: MONEY_DERIVATION_PATH,
|
|
145
|
+
});
|
|
146
|
+
if (__classPrivateFieldGet(this, _MoneyKeyring_account, "f")) {
|
|
147
|
+
const [derivedAccount] = await inner.getAccounts();
|
|
148
|
+
if (derivedAccount?.toLowerCase() !== __classPrivateFieldGet(this, _MoneyKeyring_account, "f").toLowerCase()) {
|
|
149
|
+
throw new Error('MoneyKeyring: signer account mismatch');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
__classPrivateFieldSet(this, _MoneyKeyring_inner, inner, "f");
|
|
153
|
+
}
|
|
154
|
+
return callback(__classPrivateFieldGet(this, _MoneyKeyring_inner, "f"));
|
|
155
|
+
});
|
|
156
|
+
}, _MoneyKeyring_getSigner =
|
|
157
|
+
/**
|
|
158
|
+
* Returns the initialized inner {@link HdKeyring}.
|
|
159
|
+
*
|
|
160
|
+
* Uses a fast path if already initialized, otherwise delegates to
|
|
161
|
+
* {@link MoneyKeyring.#withLockedInner}.
|
|
162
|
+
*
|
|
163
|
+
* @returns The EVM signer instance.
|
|
164
|
+
*/
|
|
165
|
+
async function _MoneyKeyring_getSigner() {
|
|
166
|
+
if (__classPrivateFieldGet(this, _MoneyKeyring_inner, "f")) {
|
|
167
|
+
return __classPrivateFieldGet(this, _MoneyKeyring_inner, "f");
|
|
168
|
+
}
|
|
169
|
+
// We use the mutex-protected method to initialize the inner keyring if needed, but once
|
|
170
|
+
// initialized, we can return it directly and use it as the signer instance.
|
|
171
|
+
return await __classPrivateFieldGet(this, _MoneyKeyring_instances, "m", _MoneyKeyring_withLockedInner).call(this, (inner) => inner);
|
|
172
|
+
};
|
|
173
|
+
MoneyKeyring.type = MONEY_KEYRING_TYPE;
|
|
174
|
+
//# sourceMappingURL=money-keyring.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money-keyring.mjs","sourceRoot":"","sources":["../src/money-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { HdKeyring } from '@metamask/eth-hd-keyring';\nimport { type CryptographicFunctions } from '@metamask/key-tree';\nimport { EthAddressStrictStruct } from '@metamask/keyring-api';\nimport type { Keyring } from '@metamask/keyring-utils';\nimport {\n assert,\n type Infer,\n object,\n optional,\n string as stringStruct,\n} from '@metamask/superstruct';\nimport { type Hex } from '@metamask/utils';\nimport { Mutex } from 'async-mutex';\n\n/**\n * Based on the SLIP-10 coin type: https://github.com/satoshilabs/slips/pull/1983\n */\nexport const MONEY_DERIVATION_PATH = `m/44'/4392018'/0'/0`;\n\n/**\n * The unique type identifier for the {@link MoneyKeyring}.\n */\nexport const MONEY_KEYRING_TYPE = 'Money Keyring';\n\n/**\n * Callback used to resolve a mnemonic from an entropy source ID.\n *\n * @param entropySource - The entropy source identifier.\n * @returns The mnemonic as an array of UTF-8 byte values.\n */\nexport type GetMnemonicCallback = (entropySource: string) => Promise<number[]>;\n\n/**\n * EVM signer interface, a subset of {@link HdKeyring} methods.\n */\nexport type EvmSigner = {\n signTransaction: HdKeyring['signTransaction'];\n signPersonalMessage: HdKeyring['signPersonalMessage'];\n signTypedData: HdKeyring['signTypedData'];\n signEip7702Authorization: HdKeyring['signEip7702Authorization'];\n};\n\n/**\n * Options for constructing a {@link MoneyKeyring}.\n *\n * @property getMnemonic - Callback to resolve the mnemonic from an entropy source ID.\n */\nexport type MoneyKeyringOptions = {\n getMnemonic: GetMnemonicCallback;\n cryptographicFunctions?: CryptographicFunctions;\n};\n\n/**\n * Struct for validating the serialized state of a {@link MoneyKeyring}.\n *\n * @property entropySource - The entropy source identifier.\n * @property account - The account address, if one has been added.\n */\nconst MoneyKeyringSerializedStateStruct = object({\n entropySource: stringStruct(),\n account: optional(EthAddressStrictStruct),\n});\n\n/**\n * The serialized state of a {@link MoneyKeyring} instance.\n */\nexport type MoneyKeyringSerializedState = Infer<\n typeof MoneyKeyringSerializedStateStruct\n>;\n\n/**\n * Keyring used to create and manage Money Accounts. It wraps an inner {@link HdKeyring}\n * and enforces a specific HD path and a limit on the number of accounts. The mnemonic\n * is never stored in the serialized state; instead, it is resolved at deserialization\n * time via a callback.\n *\n * The inner {@link HdKeyring} is instantiated lazily on the first signing operation\n * (via {@link MoneyKeyring.#getSigner}). A mutex ensures that concurrent callers never\n * trigger more than one initialization.\n */\nexport class MoneyKeyring implements Keyring {\n static type: string = MONEY_KEYRING_TYPE;\n\n readonly type: string = MONEY_KEYRING_TYPE;\n\n readonly #getMnemonic: GetMnemonicCallback;\n\n readonly #cryptographicFunctions: CryptographicFunctions | undefined;\n\n #entropySource: string | undefined;\n\n #account: Hex | undefined;\n\n #inner: HdKeyring | undefined;\n\n readonly #lock = new Mutex();\n\n constructor({ getMnemonic, cryptographicFunctions }: MoneyKeyringOptions) {\n this.#getMnemonic = getMnemonic;\n this.#cryptographicFunctions = cryptographicFunctions;\n }\n\n get entropySource(): string | undefined {\n return this.#entropySource;\n }\n\n async serialize(): Promise<MoneyKeyringSerializedState> {\n const state = {\n entropySource: this.#entropySource,\n account: this.#account,\n };\n assert(state, MoneyKeyringSerializedStateStruct);\n\n return state;\n }\n\n async deserialize(state: MoneyKeyringSerializedState): Promise<void> {\n assert(state, MoneyKeyringSerializedStateStruct);\n\n if (this.#entropySource !== undefined) {\n throw new Error('MoneyKeyring: cannot deserialize after initialization');\n }\n\n this.#entropySource = state.entropySource;\n this.#account = state.account;\n }\n\n /**\n * Runs a callback with the initialized inner {@link HdKeyring}, always within\n * the mutex lock. Initializes {@link HdKeyring} on the first call.\n *\n * The initialization is deferred until first needed so that the potentially\n * expensive {@link GetMnemonicCallback} is not invoked during\n * {@link MoneyKeyring.deserialize} or {@link MoneyKeyring.getAccounts}.\n *\n * @param callback - Function to execute with the initialized inner keyring.\n * @returns The result of the callback.\n */\n async #withLockedInner<ReturnType>(\n callback: (inner: HdKeyring) => Promise<ReturnType> | ReturnType,\n ): Promise<ReturnType> {\n const entropySource = this.#entropySource;\n if (!entropySource) {\n throw new Error('MoneyKeyring: not yet deserialized');\n }\n\n return this.#lock.runExclusive(async () => {\n if (!this.#inner) {\n const mnemonic = await this.#getMnemonic(entropySource);\n const inner = new HdKeyring({\n cryptographicFunctions: this.#cryptographicFunctions,\n });\n await inner.deserialize({\n mnemonic,\n numberOfAccounts: this.#account ? 1 : 0,\n hdPath: MONEY_DERIVATION_PATH,\n });\n\n if (this.#account) {\n const [derivedAccount] = await inner.getAccounts();\n if (derivedAccount?.toLowerCase() !== this.#account.toLowerCase()) {\n throw new Error('MoneyKeyring: signer account mismatch');\n }\n }\n\n this.#inner = inner;\n }\n\n return callback(this.#inner);\n });\n }\n\n /**\n * Returns the initialized inner {@link HdKeyring}.\n *\n * Uses a fast path if already initialized, otherwise delegates to\n * {@link MoneyKeyring.#withLockedInner}.\n *\n * @returns The EVM signer instance.\n */\n async #getSigner(): Promise<EvmSigner> {\n if (this.#inner) {\n return this.#inner;\n }\n\n // We use the mutex-protected method to initialize the inner keyring if needed, but once\n // initialized, we can return it directly and use it as the signer instance.\n return await this.#withLockedInner((inner) => inner);\n }\n\n /**\n * Add the single Money account to this keyring.\n *\n * @param numberOfAccounts - Must be 1 (the only supported value).\n * @returns The address of the newly added account.\n * @throws If `numberOfAccounts` is not 1 or an account already exists.\n */\n async addAccounts(numberOfAccounts = 1): Promise<Hex[]> {\n if (numberOfAccounts !== 1) {\n throw new Error('MoneyKeyring: supports adding exactly one account');\n }\n\n return await this.#withLockedInner(async (inner) => {\n if (this.#account !== undefined) {\n throw new Error('MoneyKeyring: already has an account');\n }\n\n const [account] = await inner.addAccounts(1);\n if (!account) {\n throw new Error('MoneyKeyring: failed to add account');\n }\n this.#account = account;\n\n return [account];\n });\n }\n\n // -----------------------------------------------------------------------------------\n // The methods below are pass-throughs to the inner HdKeyring.\n\n async getAccounts(): Promise<Hex[]> {\n return this.#account ? [this.#account] : [];\n }\n\n async signTransaction(\n ...args: Parameters<HdKeyring['signTransaction']>\n ): ReturnType<HdKeyring['signTransaction']> {\n return (await this.#getSigner()).signTransaction(...args);\n }\n\n async signPersonalMessage(\n ...args: Parameters<HdKeyring['signPersonalMessage']>\n ): ReturnType<HdKeyring['signPersonalMessage']> {\n return (await this.#getSigner()).signPersonalMessage(...args);\n }\n\n async signTypedData(\n ...args: Parameters<HdKeyring['signTypedData']>\n ): ReturnType<HdKeyring['signTypedData']> {\n return (await this.#getSigner()).signTypedData(...args);\n }\n\n async signEip7702Authorization(\n ...args: Parameters<HdKeyring['signEip7702Authorization']>\n ): ReturnType<HdKeyring['signEip7702Authorization']> {\n return (await this.#getSigner()).signEip7702Authorization(...args);\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metamask-previews/eth-money-keyring",
|
|
3
|
+
"version": "2.0.0-8ff7ba2",
|
|
4
|
+
"description": "A money account keyring that wraps the HD keyring with a different keyring type and derivation path.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ethereum",
|
|
7
|
+
"keyring"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://github.com/MetaMask/accounts#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/MetaMask/accounts/issues"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/MetaMask/accounts.git"
|
|
16
|
+
},
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "./dist/index.d.mts",
|
|
22
|
+
"default": "./dist/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/index.d.cts",
|
|
26
|
+
"default": "./dist/index.cjs"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.cjs",
|
|
31
|
+
"types": "./dist/index.d.cts",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist/"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "ts-bridge --project tsconfig.build.json --no-references",
|
|
37
|
+
"build:clean": "yarn build --clean",
|
|
38
|
+
"build:docs": "typedoc",
|
|
39
|
+
"changelog:update": "../../scripts/update-changelog.sh @metamask/eth-money-keyring",
|
|
40
|
+
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/eth-money-keyring",
|
|
41
|
+
"publish:preview": "yarn npm publish --tag preview",
|
|
42
|
+
"test": "jest",
|
|
43
|
+
"test:clean": "jest --clearCache"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@metamask/eth-hd-keyring": "13.1.0",
|
|
47
|
+
"@metamask/keyring-api": "22.0.0",
|
|
48
|
+
"@metamask/keyring-utils": "3.2.0",
|
|
49
|
+
"@metamask/superstruct": "^3.1.0",
|
|
50
|
+
"async-mutex": "^0.5.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@lavamoat/allow-scripts": "^3.2.1",
|
|
54
|
+
"@lavamoat/preinstall-always-fail": "^2.1.0",
|
|
55
|
+
"@metamask/auto-changelog": "^3.4.4",
|
|
56
|
+
"@metamask/eth-sig-util": "^8.2.0",
|
|
57
|
+
"@metamask/key-tree": "^10.0.2",
|
|
58
|
+
"@metamask/utils": "^11.10.0",
|
|
59
|
+
"@ts-bridge/cli": "^0.6.3",
|
|
60
|
+
"@types/jest": "^29.5.12",
|
|
61
|
+
"deepmerge": "^4.2.2",
|
|
62
|
+
"jest": "^29.5.0"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": "^18.18 || >=20"
|
|
66
|
+
},
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public",
|
|
69
|
+
"registry": "https://registry.npmjs.org/"
|
|
70
|
+
},
|
|
71
|
+
"lavamoat": {
|
|
72
|
+
"allowScripts": {
|
|
73
|
+
"@lavamoat/preinstall-always-fail": false
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|