-
-
Notifications
You must be signed in to change notification settings - Fork 277
feat(wallet): Add split persistence backed by better-sqlite3 #8480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/wallet-library
Are you sure you want to change the base?
Changes from all commits
8c1e9e5
0b122f7
331ef42
283acca
18b57d1
b6b0596
428d427
c7ca356
c6a88f5
62ff2e3
6df4a99
cfad387
b36d336
366b265
f4e8dfa
86a69b0
ce80922
bd9c504
4c16ea9
58ab0c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| set -e | ||
| set -o pipefail | ||
|
|
||
| # Pin cwd to the package root so all paths are predictable regardless of how | ||
| # this script is invoked. Also derive the monorepo root (two levels up). | ||
| PACKAGE_ROOT="$(cd "$(dirname "$0")/.." && pwd)" | ||
| MONOREPO_ROOT="$(cd "${PACKAGE_ROOT}/../.." && pwd)" | ||
| cd "${PACKAGE_ROOT}" | ||
|
|
||
| # Run foundryup's TypeScript entry point directly via tsx. This avoids having | ||
| # to build @metamask/foundryup first, which matters in CI where workspace deps | ||
| # aren't built before tests run. | ||
| if ! output=$(yarn tsx ../foundryup/src/cli.ts --binaries anvil 2>&1); then | ||
| echo "$output" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Install the better-sqlite3 native addon if missing. Yarn has | ||
| # `enableScripts: false` globally, so install scripts never run during | ||
| # `yarn install` and the addon may be absent from the filesystem. Invoke the | ||
| # prebuild-install binary directly to fetch a matching prebuild for the active | ||
| # Node version and platform. | ||
| BETTER_SQLITE3_DIR="${MONOREPO_ROOT}/node_modules/better-sqlite3" | ||
| if [ ! -f "${BETTER_SQLITE3_DIR}/build/Release/better_sqlite3.node" ]; then | ||
| ( | ||
| cd "${BETTER_SQLITE3_DIR}" | ||
| "${MONOREPO_ROOT}/node_modules/.bin/prebuild-install" | ||
| ) | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import type { StateMetadataConstraint } from '@metamask/base-controller'; | ||
| import { Messenger } from '@metamask/messenger'; | ||
| import type { Json } from '@metamask/utils'; | ||
| import { hasProperty } from '@metamask/utils'; | ||
|
|
||
| import type { | ||
| DefaultActions, | ||
|
|
@@ -11,23 +12,34 @@ import type { | |
| import { initialize } from './initialization'; | ||
| import type { WalletOptions } from './types'; | ||
|
|
||
| export type WalletConstructorArgs = { | ||
| state?: Record<string, Json>; | ||
| options: WalletOptions; | ||
| }; | ||
|
|
||
| export class Wallet { | ||
| // TODO: Expand types when passing additionalConfigurations. | ||
| public readonly messenger: RootMessenger<DefaultActions, DefaultEvents>; | ||
|
|
||
| readonly #instances: DefaultInstances; | ||
|
|
||
| constructor({ state = {}, options }: WalletConstructorArgs) { | ||
| readonly #controllerMetadata: Readonly< | ||
| Record<string, Readonly<StateMetadataConstraint>> | ||
| >; | ||
|
|
||
| #destroyed = false; | ||
|
|
||
| constructor({ state, ...options }: WalletOptions) { | ||
| this.messenger = new Messenger({ | ||
| namespace: 'Root', | ||
| }); | ||
|
|
||
| this.#instances = initialize({ state, messenger: this.messenger, options }); | ||
| this.#instances = initialize({ | ||
| state: state ?? {}, | ||
| messenger: this.messenger, | ||
| options, | ||
| }); | ||
|
|
||
| this.#controllerMetadata = Object.fromEntries( | ||
| Object.entries(this.#instances) | ||
| .filter(([_, instance]) => hasProperty(instance, 'metadata')) | ||
| .map(([name, instance]) => [name, instance.metadata]), | ||
| ); | ||
| } | ||
|
|
||
| get state(): DefaultState { | ||
|
|
@@ -40,17 +52,30 @@ export class Wallet { | |
| ) as DefaultState; | ||
| } | ||
|
|
||
| get controllerMetadata(): Readonly< | ||
| Record<string, Readonly<StateMetadataConstraint>> | ||
| > { | ||
| return this.#controllerMetadata; | ||
| } | ||
|
|
||
| async destroy(): Promise<void> { | ||
| await Promise.all( | ||
| if (this.#destroyed) { | ||
| return; | ||
| } | ||
| this.#destroyed = true; | ||
|
|
||
| await Promise.allSettled( | ||
| Object.values(this.#instances).map((instance) => { | ||
| // @ts-expect-error Accessing protected property. | ||
| if (typeof instance.destroy === 'function') { | ||
| // @ts-expect-error Accessing protected property. | ||
| return instance.destroy(); | ||
| return (async (): Promise<void> => await instance.destroy())(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe simpler to make the map function async? |
||
| } | ||
| /* istanbul ignore next */ | ||
| return undefined; | ||
| return Promise.resolve(); | ||
| }), | ||
| ); | ||
|
|
||
| this.messenger.publish('Root:walletDestroyed'); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should reconsider the root messenger namespace, perhaps it should be |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,8 @@ | ||
| export { Wallet } from './Wallet'; | ||
| export type { WalletOptions } from './types'; | ||
| export type { | ||
| DefaultActions, | ||
| DefaultEvents, | ||
| RootMessenger, | ||
| WalletDestroyedEvent, | ||
| } from './initialization'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.