Skip to content

Commit 94e09da

Browse files
authored
fix: create core package with domain protocols and unit test coverage (#1313)
* fix: create initial package with domain protocols and unit test coverage * fix: remove duplicate key * fix: lint changelog command * fix: code improvement * fix: improve tests + code * fix: env var uppercase * fix: add unrelease header to changelog * fix: lint invalid changelog * fix: changelog error * fix: missing slash in initial changelog file * fix: wrong constants on method to redirect, typo in switch ethereum chain * fix: remove disable
1 parent 7f61c77 commit 94e09da

File tree

27 files changed

+2064
-6
lines changed

27 files changed

+2064
-6
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"packages/devreactnative",
2222
"packages/devnext",
2323
"packages/deve2e",
24-
"packages/playground-next"
24+
"packages/playground-next",
25+
"packages/sdk-multichain"
2526
],
2627
"scripts": {
2728
"build": "yarn install && cd packages/sdk-socket-server-next && yarn install && cd ../.. && yarn workspaces foreach --verbose run build:pre-tsc && yarn workspaces foreach --verbose --topological --parallel --no-private run build && yarn workspaces foreach --verbose run build:post-tsc ",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Changelog
2+
All notable changes to this project will be documented in this file.
3+
4+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## [Unreleased]
8+
9+
[Unreleased]: https://github.com/MetaMask/metamask-sdk/

packages/sdk-multichain/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# MetaMask SDK Multichain
2+
3+
The MetaMask SDK Multichain is a protocol-based, domain-driven SDK that enables seamless integration with MetaMask wallets across multiple blockchain networks and platforms.
4+
5+
## Overview
6+
7+
The SDK provides a unified interface for dapps to connect with MetaMask Extension or Mobile wallets, regardless of the platform (browser, mobile, or Node.js) or blockchain protocol. It automatically handles connection flows, deeplinks, and QR codes based on the user's environment.
8+
9+
## Architecture
10+
11+
### Domain-Driven Design
12+
13+
The SDK follows a clean domain-driven architecture with clear separation of concerns:
14+
15+
```
16+
src/
17+
├── domain/ # Core business logic and abstractions
18+
│ ├── multichain/ # Multichain protocol abstractions
19+
│ ├── events/ # Event-driven communication
20+
│ ├── platform/ # Platform detection utilities
21+
│ └── store/ # Storage abstractions
22+
```

packages/sdk-multichain/biome.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
3+
"vcs": {
4+
"enabled": false,
5+
"clientKind": "git",
6+
"useIgnoreFile": false
7+
},
8+
"files": {
9+
"includes": ["**", "!**/node_modules/**", "!**/dist/**"],
10+
"ignoreUnknown": false
11+
},
12+
"formatter": {
13+
"enabled": true,
14+
"indentStyle": "tab",
15+
"lineWidth": 180
16+
},
17+
"linter": {
18+
"enabled": true,
19+
"rules": {
20+
"recommended": true
21+
}
22+
},
23+
"javascript": {
24+
"formatter": {
25+
"quoteStyle": "double"
26+
}
27+
},
28+
"assist": {
29+
"enabled": true,
30+
"actions": {
31+
"source": {
32+
"organizeImports": "on"
33+
}
34+
}
35+
}
36+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{
2+
"name": "@metamask/multichain-sdk",
3+
"version": "0.0.0",
4+
"description": "Multichain package for MetaMask SDK",
5+
"main": "dist/node/cjs/multichain-sdk.js",
6+
"module": "dist/browser/es/multichain-sdk.mjs",
7+
"browser": "dist/browser/es/multichain-sdk.mjs",
8+
"unpkg": "dist/browser/umd/multichain-sdk.js",
9+
"react-native": "dist/react-native/es/multichain-sdk.mjs",
10+
"types": "dist/types/multichain-sdk.d.ts",
11+
"sideEffects": false,
12+
"files": [
13+
"/dist"
14+
],
15+
"scripts": {
16+
"clean": "npx rimraf ./dist",
17+
"build:types": "tsc --project tsconfig.json --emitDeclarationOnly --declaration --outFile",
18+
"build": "yarn clean && npx tsup",
19+
"build:post-tsc": "echo 'N/A'",
20+
"build:pre-tsc": "echo 'N/A'",
21+
"lint": "yarn biome lint ./src",
22+
"lint:ci": "yarn biome ci ./src",
23+
"lint:fix": "yarn biome format --write ./src",
24+
"lint:changelog": "../../scripts/validate-changelog.sh @metamask/multichain-sdk",
25+
"test": "vitest run",
26+
"test:unit": "vitest run",
27+
"test:watch": "vitest watch",
28+
"test:ci": "vitest run --coverage --coverage.reporter=text --silent",
29+
"allow-scripts": ""
30+
},
31+
"devDependencies": {
32+
"@biomejs/biome": "2.0.0",
33+
"@metamask/auto-changelog": "^3.4.3",
34+
"@react-native-async-storage/async-storage": "^1.19.6",
35+
"@vitest/coverage-v8": "^3.2.4",
36+
"esbuild-plugin-umd-wrapper": "^3.0.0",
37+
"nock": "^14.0.4",
38+
"prettier": "^3.3.3",
39+
"tsup": "^8.5.0",
40+
"typescript": "^5.8.3",
41+
"typescript-eslint": "^8.6.0",
42+
"vitest": "^3.1.2"
43+
},
44+
"publishConfig": {
45+
"access": "public",
46+
"registry": "https://registry.npmjs.org/"
47+
},
48+
"license": "MIT",
49+
"dependencies": {
50+
"@metamask/multichain-api-client": "^0.6.4",
51+
"@metamask/sdk-analytics": "workspace:^",
52+
"@metamask/utils": "^11.4.0",
53+
"@paulmillr/qr": "^0.2.1",
54+
"bowser": "^2.11.0",
55+
"cross-fetch": "^4.1.0",
56+
"eventemitter2": "^6.4.9",
57+
"uuid": "^11.1.0"
58+
}
59+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { EventEmitter2 } from 'eventemitter2';
2+
3+
import type { EventTypes } from './types';
4+
5+
6+
/**
7+
* A type-safe event emitter that provides a strongly-typed wrapper around EventEmitter2.
8+
*
9+
* This class ensures type safety for event names and their corresponding argument types,
10+
* making it easier to work with events in a type-safe manner.
11+
*
12+
* @template TEvents - A record type mapping event names to their argument types.
13+
* Each key represents an event name, and the value is a tuple of argument types.
14+
*/
15+
export class EventEmitter<
16+
TEvents extends Record<string, unknown[]> = EventTypes,
17+
> {
18+
readonly #emitter = new EventEmitter2();
19+
20+
/**
21+
* Emits an event with the specified name and arguments.
22+
*
23+
* @template TEventName - The name of the event to emit (must be a key of TEvents)
24+
* @param eventName - The name of the event to emit
25+
* @param eventArg - The arguments to pass to the event handlers
26+
*/
27+
emit<TEventName extends keyof TEvents & string>(
28+
eventName: TEventName,
29+
...eventArg: TEvents[TEventName]
30+
) {
31+
this.#emitter.emit(eventName, ...eventArg);
32+
}
33+
34+
/**
35+
* Registers an event handler for the specified event.
36+
*
37+
* @template TEventName - The name of the event to listen for (must be a key of TEvents)
38+
* @param eventName - The name of the event to listen for
39+
* @param handler - The function to call when the event is emitted
40+
*/
41+
on<TEventName extends keyof TEvents & string>(
42+
eventName: TEventName,
43+
handler: (...eventArg: TEvents[TEventName]) => void,
44+
) {
45+
this.#emitter.on(eventName, handler);
46+
}
47+
48+
/**
49+
* Sets the maximum number of listeners that can be registered for any single event.
50+
*
51+
* This is useful for preventing memory leaks when many listeners are registered.
52+
* By default, EventEmitter2 will warn if more than 10 listeners are registered
53+
* for a single event.
54+
*
55+
* @param maxListeners - The maximum number of listeners per event (0 means unlimited)
56+
*/
57+
setMaxListeners(maxListeners: number) {
58+
this.#emitter.setMaxListeners(maxListeners);
59+
}
60+
61+
/**
62+
* Removes a specific event handler for the specified event.
63+
*
64+
* @template TEventName - The name of the event to remove the handler from (must be a key of TEvents)
65+
* @param eventName - The name of the event to remove the handler from
66+
* @param handler - The specific handler function to remove
67+
*/
68+
off<TEventName extends keyof TEvents & string>(
69+
eventName: TEventName,
70+
handler: (...eventArg: TEvents[TEventName]) => void,
71+
) {
72+
this.#emitter.off(eventName, handler);
73+
}
74+
}
75+
76+
77+
export type * from './types';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Extension native Events
3+
*/
4+
export type ExtensionEvents = {
5+
chainChanged: [evt: unknown];
6+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { ExtensionEvents } from './extension';
2+
import type { SDKEvents } from './sdk';
3+
4+
export type EventTypes = SDKEvents | ExtensionEvents;
5+
export type { SDKEvents, ExtensionEvents };
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type SDKEvents = {
2+
display_uri: [evt: string];
3+
};

0 commit comments

Comments
 (0)