@portal-hq/web 0.0.1-beta-1

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.
@@ -0,0 +1,196 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import Mpc from './mpc';
11
+ import Provider from './provider';
12
+ import DefaultBackupStorage from './storage';
13
+ class Portal {
14
+ constructor({
15
+ // Required
16
+ apiKey, gatewayConfig,
17
+ // Optional
18
+ apiHost = 'api.portalhq.io', autoApprove = false, backup = { default: new DefaultBackupStorage() }, chainId = 1, mpcHost = 'mpc.portalhq.io', mpcVersion = 'v4', webHost = 'web.portalhq.io', }) {
19
+ this.ready = false;
20
+ this.readyCallbacks = [];
21
+ this.apiHost = apiHost;
22
+ this.apiKey = apiKey;
23
+ this.autoApprove = autoApprove;
24
+ this.backup = backup;
25
+ this.chainId = chainId;
26
+ this.gatewayConfig = gatewayConfig;
27
+ this.mpcHost = mpcHost;
28
+ this.mpcVersion = mpcVersion;
29
+ this.rpcUrl = this.getRpcUrl();
30
+ this.webHost = webHost;
31
+ console.log(`Backup:`, this.backup);
32
+ // Portal Instances
33
+ this.mpc = new Mpc({
34
+ apiHost: this.apiHost,
35
+ apiKey: this.apiKey,
36
+ autoApprove: this.autoApprove,
37
+ chainId: this.chainId,
38
+ mpcHost: this.mpcHost,
39
+ rpcUrl: this.rpcUrl,
40
+ webHost: this.webHost,
41
+ portal: this,
42
+ });
43
+ this.provider = new Provider({
44
+ autoApprove: this.autoApprove,
45
+ mpcHost: this.mpcHost,
46
+ mpcVersion: this.mpcVersion,
47
+ portal: this,
48
+ });
49
+ }
50
+ /*****************************
51
+ * Initialization Methods
52
+ *****************************/
53
+ onReady(callback) {
54
+ if (this.ready) {
55
+ callback();
56
+ }
57
+ else {
58
+ this.readyCallbacks.push(callback);
59
+ }
60
+ }
61
+ triggerReady() {
62
+ if (this.ready && this.readyCallbacks.length > 0) {
63
+ this.readyCallbacks.forEach((callback) => {
64
+ callback();
65
+ });
66
+ this.readyCallbacks = [];
67
+ }
68
+ }
69
+ /*****************************
70
+ * Wallet Methods
71
+ *****************************/
72
+ createWallet() {
73
+ return __awaiter(this, void 0, void 0, function* () {
74
+ const address = yield this.mpc.generate({
75
+ host: this.mpcHost,
76
+ mpcVersion: this.mpcVersion,
77
+ });
78
+ this.address = address;
79
+ return address;
80
+ });
81
+ }
82
+ backupWallet() {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ yield this.mpc.backup({
85
+ host: this.mpcHost,
86
+ mpcVersion: this.mpcVersion,
87
+ });
88
+ return '';
89
+ });
90
+ }
91
+ recoverWallet() {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ yield this.mpc.recover({
94
+ host: this.mpcHost,
95
+ mpcVersion: this.mpcVersion,
96
+ });
97
+ return true;
98
+ });
99
+ }
100
+ /****************************
101
+ * Provider Methods
102
+ ****************************/
103
+ ethSendTransaction(transaction) {
104
+ return __awaiter(this, void 0, void 0, function* () {
105
+ return this.provider.request({
106
+ method: 'eth_sendTransaction',
107
+ params: transaction,
108
+ });
109
+ });
110
+ }
111
+ ethSign(message) {
112
+ return __awaiter(this, void 0, void 0, function* () {
113
+ return this.provider.request({
114
+ method: 'eth_sign',
115
+ params: [this.address, this.stringToHex(message)],
116
+ });
117
+ });
118
+ }
119
+ ethSignTransaction(transaction) {
120
+ return __awaiter(this, void 0, void 0, function* () {
121
+ return this.provider.request({
122
+ method: 'eth_signTransaction',
123
+ params: transaction,
124
+ });
125
+ });
126
+ }
127
+ ethSignTypedData(data) {
128
+ return __awaiter(this, void 0, void 0, function* () {
129
+ return this.provider.request({
130
+ method: 'eth_signTypedData',
131
+ params: [this.address, data],
132
+ });
133
+ });
134
+ }
135
+ ethSignTypedDataV3(data) {
136
+ return __awaiter(this, void 0, void 0, function* () {
137
+ return this.provider.request({
138
+ method: 'eth_signTypedData_v3',
139
+ params: [this.address, data],
140
+ });
141
+ });
142
+ }
143
+ ethSignTypedDataV4(data) {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ return this.provider.request({
146
+ method: 'eth_signTypedData_v4',
147
+ params: [this.address, data],
148
+ });
149
+ });
150
+ }
151
+ personalSign(message) {
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ return this.provider.request({
154
+ method: 'personal_sign',
155
+ params: [this.stringToHex(message), this.address],
156
+ });
157
+ });
158
+ }
159
+ /****************************
160
+ * RPC Methods
161
+ ****************************/
162
+ getRpcUrl() {
163
+ if (typeof this.gatewayConfig === 'string') {
164
+ // If the gatewayConfig is just a static URL, return that
165
+ return this.gatewayConfig;
166
+ }
167
+ else if (typeof this.gatewayConfig === 'object' &&
168
+ !this.gatewayConfig.hasOwnProperty(this.chainId)) {
169
+ // If there's no explicit mapping for the current chainId, error out
170
+ throw new Error(`[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`);
171
+ }
172
+ // Get the entry for the current chainId from the gatewayConfig
173
+ const config = this.gatewayConfig[this.chainId];
174
+ if (typeof config === 'string') {
175
+ return config;
176
+ }
177
+ // If we got this far, there's no way to support the chain with the current config
178
+ throw new Error(`[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`);
179
+ }
180
+ /**************************
181
+ * RPC Encoding Methods
182
+ **************************/
183
+ stringToHex(str) {
184
+ if (str.startsWith('0x')) {
185
+ return str;
186
+ }
187
+ let hex = '0x';
188
+ for (let i = 0; i < str.length; i++) {
189
+ const charCode = str.charCodeAt(i);
190
+ const hexValue = charCode.toString(16);
191
+ hex += hexValue.padStart(2, '0'); // Ensure two-digit hex value
192
+ }
193
+ return hex;
194
+ }
195
+ }
196
+ export default Portal;
@@ -0,0 +1,351 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { PortalMpcError } from '@portal-hq/utils';
11
+ const WEB_SDK_VERSION = '0.0.1-beta-1';
12
+ class Mpc {
13
+ constructor({ apiKey, chainId, portal, rpcUrl,
14
+ // Optional
15
+ apiHost = 'api.portalhq.io', autoApprove = false, mpcHost = 'mpc.portalhq.io', webHost = 'web.portalhq.io', }) {
16
+ this.apiHost = apiHost;
17
+ this.apiKey = apiKey;
18
+ this.autoApprove = autoApprove;
19
+ this.chainId = chainId;
20
+ this.mpcHost = mpcHost;
21
+ this.portal = portal;
22
+ this.rpcUrl = rpcUrl;
23
+ this.webHost = webHost;
24
+ // Handle scoping of certain functions
25
+ this.configureIframe = this.configureIframe.bind(this);
26
+ // Create the iFrame for MPC operations
27
+ this.appendIframe();
28
+ }
29
+ // In Progress
30
+ backup(data) {
31
+ return __awaiter(this, void 0, void 0, function* () {
32
+ const message = {
33
+ type: 'portal:wasm:backup',
34
+ data,
35
+ };
36
+ return new Promise((resolve, reject) => {
37
+ // Create a function to be bound when backup is triggered
38
+ const handleBackup = (event) => {
39
+ const { type, data: result } = event;
40
+ // Check that the message is the one we're looking for
41
+ if (type === 'portal:wasm:backupResult') {
42
+ // Check if the result is an error
43
+ if (result.error && result.error.code > 0) {
44
+ // Remove the event listener
45
+ window.removeEventListener('message', handleBackup);
46
+ // Reject the promise with the error
47
+ return reject(new PortalMpcError(result.error));
48
+ }
49
+ // Remove the event listener
50
+ window.removeEventListener('message', handleBackup);
51
+ // Resolve the promise with the result
52
+ resolve(result);
53
+ }
54
+ };
55
+ // Bind the function to the message event
56
+ window.addEventListener('message', handleBackup);
57
+ // Send the request to the iframe
58
+ this.postMessage(message);
59
+ });
60
+ });
61
+ }
62
+ // In Progress
63
+ decrypt(data) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ const message = {
66
+ type: 'portal:wasm:decrypt',
67
+ data,
68
+ };
69
+ return new Promise((resolve, reject) => {
70
+ const handleDecrypt = (event) => {
71
+ const { type, data: result } = event;
72
+ // Check that the message is the one we're looking for
73
+ if (type === 'portal:wasm:decryptResult') {
74
+ // Check if the result is an error
75
+ if (result.error && result.error.code > 0) {
76
+ // Remove the event listener
77
+ window.removeEventListener('message', handleDecrypt);
78
+ // Reject the promise with the error
79
+ return reject(new PortalMpcError(result.error));
80
+ }
81
+ // Remove the event listener
82
+ window.removeEventListener('message', handleDecrypt);
83
+ // Resolve the promise with the result
84
+ resolve(result);
85
+ }
86
+ };
87
+ // Bind the function to the message event
88
+ window.addEventListener('message', handleDecrypt);
89
+ // Send the request to the iframe
90
+ this.postMessage(message);
91
+ });
92
+ });
93
+ }
94
+ // In Progress
95
+ encrypt(data) {
96
+ return __awaiter(this, void 0, void 0, function* () {
97
+ const message = {
98
+ type: 'portal:wasm:encrypt',
99
+ data,
100
+ };
101
+ return new Promise((resolve, reject) => {
102
+ const handleEncrypt = (event) => {
103
+ const { type, data: result } = event;
104
+ // Check that the message is the one we're looking for
105
+ if (type === 'portal:wasm:encryptResult') {
106
+ // Check if the result is an error
107
+ if (result.error && result.error.code > 0) {
108
+ // Remove the event listener
109
+ window.removeEventListener('message', handleEncrypt);
110
+ // Reject the promise with the error
111
+ return reject(new PortalMpcError(result.error));
112
+ }
113
+ // Remove the event listener
114
+ window.removeEventListener('message', handleEncrypt);
115
+ // Resolve the promise with the result
116
+ resolve(result);
117
+ }
118
+ };
119
+ // Bind the function to the message event
120
+ window.addEventListener('message', handleEncrypt);
121
+ // Send the request to the iframe
122
+ this.postMessage(message);
123
+ });
124
+ });
125
+ }
126
+ generate(data) {
127
+ return __awaiter(this, void 0, void 0, function* () {
128
+ const message = {
129
+ type: 'portal:wasm:generate',
130
+ data,
131
+ };
132
+ return new Promise((resolve, reject) => {
133
+ // Create a function to be bound when generate is triggered
134
+ const handleGenerate = (event) => {
135
+ console.log(`[MPC] Temporary listener received event: `, event);
136
+ const { type, data: result } = event.data;
137
+ // Handle errors
138
+ if (type === 'portal:wasm:generateError') {
139
+ // Remove the event listener
140
+ window.removeEventListener('message', handleGenerate);
141
+ // Reject the promise with the error
142
+ return reject(new PortalMpcError(result));
143
+ }
144
+ else if (type === 'portal:wasm:generateResult') {
145
+ // Remove the event listener
146
+ window.removeEventListener('message', handleGenerate);
147
+ // Resolve the promise with the result
148
+ resolve(result);
149
+ }
150
+ };
151
+ // Bind the function to the message event
152
+ window.addEventListener('message', handleGenerate);
153
+ // Send the request to the iframe
154
+ this.postMessage(message);
155
+ });
156
+ });
157
+ }
158
+ getAddress() {
159
+ return __awaiter(this, void 0, void 0, function* () {
160
+ const message = {
161
+ type: 'portal:address',
162
+ data: {},
163
+ };
164
+ return new Promise((resolve) => {
165
+ // Create a function to be bound when getAddress is triggered
166
+ const handleGetAddress = (event) => {
167
+ const { type, data: result } = event.data;
168
+ // Check that the message is the one we're looking for
169
+ if (type === 'portal:addressResult') {
170
+ // Remove the event listener
171
+ window.removeEventListener('message', handleGetAddress);
172
+ // Resolve the promise with the result
173
+ resolve(result);
174
+ }
175
+ };
176
+ // Bind the function to the message event
177
+ window.addEventListener('message', handleGetAddress);
178
+ // Send the request to the iframe
179
+ this.postMessage(message);
180
+ });
181
+ });
182
+ }
183
+ // In Progress
184
+ recover(data) {
185
+ return __awaiter(this, void 0, void 0, function* () {
186
+ yield this.recoverBackup(data);
187
+ // TODO: Write the DKG Result to local storage
188
+ const cipherText = yield this.recoverSigning(data);
189
+ return JSON.stringify(cipherText);
190
+ });
191
+ }
192
+ // In Progress
193
+ recoverBackup(data) {
194
+ return __awaiter(this, void 0, void 0, function* () {
195
+ const message = {
196
+ type: 'portal:wasm:recoverBackup',
197
+ data,
198
+ };
199
+ return new Promise((resolve, reject) => {
200
+ // Create a function to be bound when recoverSigning is triggered
201
+ const handleRecovery = (event) => {
202
+ const { type, data: result } = event;
203
+ // Check that the message is the one we're looking for
204
+ if (type === 'portal:wasm:recoverBackupResult') {
205
+ // Check if the result is an error
206
+ if (result.error && result.error.code > 0) {
207
+ // Remove the event listener
208
+ window.removeEventListener('message', handleRecovery);
209
+ // Reject the promise with the error
210
+ return reject(new PortalMpcError(result.error));
211
+ }
212
+ // Remove the event listener
213
+ window.removeEventListener('message', handleRecovery);
214
+ // Resolve the promise with the result
215
+ resolve(result);
216
+ }
217
+ };
218
+ // Bind the function to the message event
219
+ window.addEventListener('message', handleRecovery);
220
+ // Send the request to the iframe
221
+ this.postMessage(message);
222
+ });
223
+ });
224
+ }
225
+ // In Progress
226
+ recoverSigning(data) {
227
+ return __awaiter(this, void 0, void 0, function* () {
228
+ const message = {
229
+ type: 'portal:wasm:recoverSigning',
230
+ data,
231
+ };
232
+ return new Promise((resolve, reject) => {
233
+ // Create a function to be bound when recoverSigning is triggered
234
+ const handleRecovery = (event) => {
235
+ const { type, data: result } = event;
236
+ // Check that the message is the one we're looking for
237
+ if (type === 'portal:wasm:recoverSigningResult') {
238
+ // Check if the result is an error
239
+ if (result.error && result.error.code > 0) {
240
+ // Remove the event listener
241
+ window.removeEventListener('message', handleRecovery);
242
+ // Reject the promise with the error
243
+ return reject(new PortalMpcError(result.error));
244
+ }
245
+ // Remove the event listener
246
+ window.removeEventListener('message', handleRecovery);
247
+ // Resolve the promise with the result
248
+ resolve(result);
249
+ }
250
+ };
251
+ // Bind the function to the message event
252
+ window.addEventListener('message', handleRecovery);
253
+ // Send the request to the iframe
254
+ this.postMessage(message);
255
+ });
256
+ });
257
+ }
258
+ sign(data) {
259
+ return __awaiter(this, void 0, void 0, function* () {
260
+ const message = {
261
+ type: 'portal:wasm:sign',
262
+ data,
263
+ };
264
+ return new Promise((resolve, reject) => {
265
+ // Create a function to be bound when sign is triggered
266
+ const handleSign = (event) => {
267
+ const { type, data: result } = event.data;
268
+ if (type === 'portal:wasm:signError') {
269
+ // Remove the event listener
270
+ window.removeEventListener('message', handleSign);
271
+ // Reject the promise with the error
272
+ return reject(new PortalMpcError(result));
273
+ }
274
+ else if (type === 'portal:wasm:signResult') {
275
+ // Remove the event listener
276
+ window.removeEventListener('message', handleSign);
277
+ // Resolve the promise with the result
278
+ resolve(result);
279
+ }
280
+ };
281
+ // Bind the function to the message event
282
+ window.addEventListener('message', handleSign);
283
+ // Send the request to the iframe
284
+ this.postMessage(message);
285
+ });
286
+ });
287
+ }
288
+ /***************************
289
+ * Private Methods
290
+ ***************************/
291
+ /**
292
+ * Appends the iframe to the document body
293
+ */
294
+ appendIframe() {
295
+ const source = this.webHost.startsWith('locahost:')
296
+ ? `http://${this.webHost}/${WEB_SDK_VERSION}/iframe/index.html?t=${Date.now()}`
297
+ : `https://${this.webHost}/${WEB_SDK_VERSION}/iframe/index.html?t=${Date.now()}`;
298
+ const iframe = document.createElement('iframe');
299
+ iframe.height = '0';
300
+ iframe.width = '0';
301
+ iframe.src = source;
302
+ iframe.addEventListener('load', this.configureIframe);
303
+ document.body.appendChild(iframe);
304
+ this.iframe = iframe;
305
+ }
306
+ configureIframe() {
307
+ const config = {
308
+ apiHost: this.apiHost,
309
+ apiKey: this.apiKey,
310
+ autoApprove: this.autoApprove,
311
+ chainId: this.chainId,
312
+ mpcHost: this.mpcHost,
313
+ rpcUrl: this.rpcUrl,
314
+ };
315
+ const message = {
316
+ type: 'portal:configure',
317
+ data: config
318
+ };
319
+ this.postMessage(message);
320
+ this.waitForReadyMessage();
321
+ }
322
+ getOrigin() {
323
+ const origin = this.webHost.startsWith('localhost:')
324
+ ? `http://${this.webHost}`
325
+ : `https://${this.webHost}`;
326
+ return origin;
327
+ }
328
+ postMessage(event) {
329
+ var _a, _b, _c;
330
+ console.log(`[MpcProxy] Sending new message: `, event, (_a = this.iframe) === null || _a === void 0 ? void 0 : _a.contentWindow);
331
+ (_c = (_b = this.iframe) === null || _b === void 0 ? void 0 : _b.contentWindow) === null || _c === void 0 ? void 0 : _c.postMessage(event, this.getOrigin());
332
+ }
333
+ waitForReadyMessage() {
334
+ const handleReady = (message) => __awaiter(this, void 0, void 0, function* () {
335
+ const { type, data } = message.data;
336
+ if (type === 'portal:wasm:ready' && data === true) {
337
+ // Unbind the event listener
338
+ window.removeEventListener('message', handleReady);
339
+ // Update ready state
340
+ this.portal.ready = true;
341
+ // Update the address
342
+ const address = yield this.getAddress();
343
+ this.portal.address = address;
344
+ // Trigger the ready callback
345
+ this.portal.triggerReady();
346
+ }
347
+ });
348
+ window.addEventListener('message', handleReady);
349
+ }
350
+ }
351
+ export default Mpc;