Skip to content

Commit 26005b4

Browse files
authored
feat: adding mobile wallet protocol (#1346)
* fix: remove duplicate code to create native envs * fix: improve SDK create options to allow extending with different storage and refactor node * fix: remove base64 utility * fix: improving ui components for mwp and multichain * fix: implementing mwp, connection and session management and ui modals * fix: reset providers, transport and dappclient per instance * fix: improve fixtures for multichain * fix: install modal fix * fix: refactor modals and improve architecture * fix: removing test.svg and adding timeout to MWP Transport requests * fix: improve install modal async promises * fix: add ws as external dependency and fix builds * fix: correct otp-modal code * fix: simplify code + improve modal callback and qrcode generation * fix: remove debugger * fix: linter issues * fix: test fix crypto.getRandomValue * fix: remove crypto usage and replace it with v4 from uuid
1 parent 7eb3e16 commit 26005b4

61 files changed

Lines changed: 2437 additions & 1290 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/sdk-install-modal-web/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = {
1818
'loader',
1919
'.stencil',
2020
'stencil.config.ts',
21+
'www'
2122
],
2223

2324
parserOptions: {

packages/sdk-multichain-ui/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = {
1818
'loader',
1919
'.stencil',
2020
'stencil.config.ts',
21+
'www'
2122
],
2223

2324
parserOptions: {

packages/sdk-multichain-ui/src/components.d.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,32 @@
77
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
88
export namespace Components {
99
interface MmInstallModal {
10+
"preferDesktop": boolean;
11+
"sdkVersion"?: string;
12+
"sessionRequest": any;
13+
}
14+
interface MmOtpModal {
1015
/**
1116
* The QR code link
1217
*/
13-
"link": string;
14-
"preferDesktop": boolean;
18+
"displayOTP"?: boolean;
19+
"otpCode": string;
1520
"sdkVersion"?: string;
1621
}
1722
}
1823
export interface MmInstallModalCustomEvent<T> extends CustomEvent<T> {
1924
detail: T;
2025
target: HTMLMmInstallModalElement;
2126
}
27+
export interface MmOtpModalCustomEvent<T> extends CustomEvent<T> {
28+
detail: T;
29+
target: HTMLMmOtpModalElement;
30+
}
2231
declare global {
2332
interface HTMLMmInstallModalElementEventMap {
2433
"close": { shouldTerminate?: boolean };
2534
"startDesktopOnboarding": any;
35+
"updateSessionRequest": any;
2636
}
2737
interface HTMLMmInstallModalElement extends Components.MmInstallModal, HTMLStencilElement {
2838
addEventListener<K extends keyof HTMLMmInstallModalElementEventMap>(type: K, listener: (this: HTMLMmInstallModalElement, ev: MmInstallModalCustomEvent<HTMLMmInstallModalElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
@@ -38,30 +48,61 @@ declare global {
3848
prototype: HTMLMmInstallModalElement;
3949
new (): HTMLMmInstallModalElement;
4050
};
51+
interface HTMLMmOtpModalElementEventMap {
52+
"close": any;
53+
"disconnect": any;
54+
"updateOTPCode": { otpCode: string };
55+
}
56+
interface HTMLMmOtpModalElement extends Components.MmOtpModal, HTMLStencilElement {
57+
addEventListener<K extends keyof HTMLMmOtpModalElementEventMap>(type: K, listener: (this: HTMLMmOtpModalElement, ev: MmOtpModalCustomEvent<HTMLMmOtpModalElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
58+
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
59+
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
60+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
61+
removeEventListener<K extends keyof HTMLMmOtpModalElementEventMap>(type: K, listener: (this: HTMLMmOtpModalElement, ev: MmOtpModalCustomEvent<HTMLMmOtpModalElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
62+
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
63+
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
64+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
65+
}
66+
var HTMLMmOtpModalElement: {
67+
prototype: HTMLMmOtpModalElement;
68+
new (): HTMLMmOtpModalElement;
69+
};
4170
interface HTMLElementTagNameMap {
4271
"mm-install-modal": HTMLMmInstallModalElement;
72+
"mm-otp-modal": HTMLMmOtpModalElement;
4373
}
4474
}
4575
declare namespace LocalJSX {
4676
interface MmInstallModal {
47-
/**
48-
* The QR code link
49-
*/
50-
"link"?: string;
5177
"onClose"?: (event: MmInstallModalCustomEvent<{ shouldTerminate?: boolean }>) => void;
5278
"onStartDesktopOnboarding"?: (event: MmInstallModalCustomEvent<any>) => void;
79+
"onUpdateSessionRequest"?: (event: MmInstallModalCustomEvent<any>) => void;
5380
"preferDesktop"?: boolean;
5481
"sdkVersion"?: string;
82+
"sessionRequest"?: any;
83+
}
84+
interface MmOtpModal {
85+
/**
86+
* The QR code link
87+
*/
88+
"displayOTP"?: boolean;
89+
"onClose"?: (event: MmOtpModalCustomEvent<any>) => void;
90+
"onDisconnect"?: (event: MmOtpModalCustomEvent<any>) => void;
91+
"onUpdateOTPCode"?: (event: MmOtpModalCustomEvent<{ otpCode: string }>) => void;
92+
"otpCode"?: string;
93+
"sdkVersion"?: string;
5594
}
5695
interface IntrinsicElements {
5796
"mm-install-modal": MmInstallModal;
97+
"mm-otp-modal": MmOtpModal;
5898
}
5999
}
60100
export { LocalJSX as JSX };
61101
declare module "@stencil/core" {
62102
export namespace JSX {
63103
interface IntrinsicElements {
64104
"mm-install-modal": LocalJSX.MmInstallModal & JSXBase.HTMLAttributes<HTMLMmInstallModalElement>;
105+
"mm-otp-modal": LocalJSX.MmOtpModal & JSXBase.HTMLAttributes<HTMLMmOtpModalElement>;
65106
}
66107
}
67108
}

packages/sdk-multichain-ui/src/components/mm-install-modal/mm-install-modal.tsx

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Prop, h, Event, EventEmitter, State, Element } from '@stencil/core';
1+
import { Component, Prop, h, Event, EventEmitter, State, Element, Watch } from '@stencil/core';
22
import { WidgetWrapper } from '../widget-wrapper/widget-wrapper';
33
import InstallIcon from '../misc/InstallIcon';
44
import SDKVersion from '../misc/SDKVersion';
@@ -23,10 +23,7 @@ import SVG from '../../assets/fox.svg'
2323
shadow: true,
2424
})
2525
export class InstallModal {
26-
/**
27-
* The QR code link
28-
*/
29-
@Prop() link: string;
26+
@Prop() sessionRequest: any
3027

3128
@Prop() sdkVersion?: string;
3229

@@ -39,6 +36,8 @@ export class InstallModal {
3936

4037
@Event() startDesktopOnboarding: EventEmitter;
4138

39+
@Event() updateSessionRequest: EventEmitter;
40+
4241
@Element() el: HTMLElement;
4342

4443
@State() private translationsLoaded: boolean = false;
@@ -52,7 +51,7 @@ export class InstallModal {
5251
}
5352

5453
componentDidLoad() {
55-
this.generateQRCode();
54+
this.generateQRCode(this.sessionRequest);
5655
}
5756

5857
async connectedCallback() {
@@ -62,22 +61,18 @@ export class InstallModal {
6261
this.translationsLoaded = true;
6362
}
6463

65-
private generateQRCode() {
66-
if (!this.qrCodeContainer || !this.link) {
64+
private generateQRCode(sessionRequest: {id: string, expiresAt: number}) {
65+
if (!this.qrCodeContainer) {
6766
return;
6867
}
69-
7068
const options: Options = {
71-
width: 165,
72-
height: 165,
7369
type: 'svg' as DrawType,
74-
data: this.link,
70+
data: JSON.stringify(sessionRequest),
7571
image: SVG,
7672
imageOptions: {
7773
hideBackgroundDots: true,
78-
imageSize:0.7,
7974
crossOrigin: 'anonymous',
80-
margin:5
75+
imageSize:0.3,
8176
},
8277
dotsOptions: {
8378
color: '#222222',
@@ -102,12 +97,8 @@ export class InstallModal {
10297
errorCorrectionLevel: 'Q' as ErrorCorrectionLevel
10398
},
10499
};
105-
106100
const qrCode = new QRCodeStyling(options);
107-
108101
this.qrCodeContainer.innerHTML = '';
109-
110-
// Append the QR code to the container
111102
qrCode.append(this.qrCodeContainer);
112103
}
113104

@@ -119,6 +110,11 @@ export class InstallModal {
119110
this.startDesktopOnboarding.emit();
120111
}
121112

113+
@Watch('sessionRequest')
114+
updateSessionRequestHandler(sessionRequest:any) {
115+
this.generateQRCode(sessionRequest);
116+
}
117+
122118
render() {
123119
if (!this.translationsLoaded) {
124120
return null; // or a loading state
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { Component, Prop, h, Event, EventEmitter, State, Element } from '@stencil/core';
2+
import { WidgetWrapper } from '../widget-wrapper/widget-wrapper';
3+
import SDKVersion from '../misc/SDKVersion';
4+
import CloseButton from '../misc/CloseButton';
5+
import { SimpleI18n } from '../misc/simple-i18n';
6+
import InstallIcon from '../misc/InstallIcon';
7+
8+
@Component({
9+
tag: 'mm-otp-modal',
10+
styleUrl: '../style.css',
11+
shadow: true,
12+
})
13+
export class OtpModal {
14+
/**
15+
* The QR code link
16+
*/
17+
@Prop() displayOTP?: boolean;
18+
19+
@Prop() sdkVersion?: string;
20+
21+
private i18nInstance: SimpleI18n;
22+
23+
@Prop() otpCode: string;
24+
25+
@Event() close: EventEmitter;
26+
27+
@Event() disconnect: EventEmitter;
28+
29+
@Event() updateOTPCode: EventEmitter<{ otpCode: string }>;
30+
31+
@Element() el: HTMLElement;
32+
33+
@State() private translationsLoaded: boolean = false;
34+
35+
constructor() {
36+
this.i18nInstance = new SimpleI18n();
37+
}
38+
39+
async connectedCallback() {
40+
await this.i18nInstance.init({
41+
fallbackLng: 'en'
42+
});
43+
this.translationsLoaded = true;
44+
}
45+
46+
onClose() {
47+
this.close.emit();
48+
}
49+
50+
onDisconnect() {
51+
this.disconnect.emit();
52+
}
53+
54+
onUpdateOTPValueHandler(otpCode: string) {
55+
this.updateOTPCode.emit({
56+
otpCode,
57+
})
58+
}
59+
60+
disconnectedCallback() {
61+
this.onClose();
62+
}
63+
64+
render() {
65+
if (!this.translationsLoaded) {
66+
return null;
67+
}
68+
69+
const displayOTP = this.displayOTP ?? true;
70+
const sdkVersion = this.sdkVersion
71+
const t = (key: string) => this.i18nInstance.t(key);
72+
73+
return (
74+
<WidgetWrapper className="pending-modal">
75+
<div class='backdrop' onClick={() => this.onClose()}></div>
76+
<div class='modal'>
77+
<div class='closeButtonContainer'>
78+
<div class='right'>
79+
<span class='closeButton' onClick={() => this.onClose()}>
80+
<CloseButton />
81+
</span>
82+
</div>
83+
</div>
84+
<div class='logoContainer'>
85+
<InstallIcon />
86+
</div>
87+
<div>
88+
<div
89+
class='flexContainer'
90+
style={{
91+
flexDirection: 'column',
92+
color: 'black',
93+
}}
94+
>
95+
<div
96+
class='flexItem'
97+
style={{
98+
textAlign: 'center',
99+
marginTop: '30px',
100+
marginBottom: '30px',
101+
fontSize: '16px',
102+
}}
103+
>
104+
{displayOTP
105+
? t('PENDING_MODAL.OPEN_META_MASK_SELECT_CODE')
106+
: t('PENDING_MODAL.OPEN_META_MASK_CONTINUE')}
107+
</div>
108+
<div
109+
id="sdk-mm-otp-value"
110+
style={{ padding: '10px', fontSize: '32px', display: this.otpCode ? 'block' : 'none' }}
111+
>{this.otpCode}</div>
112+
{displayOTP && (
113+
<div class='notice'>
114+
* {t('PENDING_MODAL.NUMBER_AFTER_OPEN_NOTICE')}
115+
</div>
116+
)}
117+
</div>
118+
<div style={{ marginTop: '20px' }}>
119+
<button
120+
class='button blue'
121+
style={{
122+
marginTop: '5px',
123+
color: '#0376C9',
124+
borderColor: '#0376C9',
125+
borderWidth: '1px',
126+
borderStyle: 'solid',
127+
backgroundColor: 'white',
128+
}}
129+
onClick={() => this.onDisconnect()}
130+
>
131+
{t('PENDING_MODAL.DISCONNECT')}
132+
</button>
133+
</div>
134+
</div>
135+
<SDKVersion version={sdkVersion} />
136+
</div>
137+
</WidgetWrapper>
138+
)
139+
}
140+
}

packages/sdk-multichain-ui/src/index.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,24 @@
3232
</style>
3333
</head>
3434
<body>
35+
<div class="nav">
36+
<a href="#install">Install Modal</a>
37+
<a href="#otp">OTP Modal</a>
38+
</div>
3539

3640
<div class="component-container">
3741
<div id="install" style="display: none">
42+
<h2>Install Modal</h2>
3843
<mm-install-modal
3944
link="https://example.com/install"
4045
sdk-version="1.0.0"
41-
prefer-desktop="true" />
46+
prefer-desktop="true"
47+
></mm-install-modal>
48+
</div>
49+
50+
<div id="otp" style="display: none">
51+
<h2>OTP Modal</h2>
52+
<mm-otp-modal></mm-otp-modal>
4253
</div>
4354
</div>
4455

@@ -53,6 +64,7 @@
5364
componentDiv.style.display = 'block';
5465
}
5566
}
67+
5668
window.addEventListener('hashchange', showComponent);
5769
showComponent();
5870
</script>

packages/sdk-multichain-ui/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
* to consume components of this package as outlined in the `README.md`.
99
*/
1010

11-
export type { Components, MmInstallModalCustomEvent, JSX } from './components'
11+
export type { Components, MmInstallModalCustomEvent,MmOtpModalCustomEvent, JSX } from './components'
1212

0 commit comments

Comments
 (0)