@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.
- package/README.md +297 -0
- package/lib/commonjs/index.js +201 -0
- package/lib/commonjs/mpc/index.js +353 -0
- package/lib/commonjs/provider/index.js +259 -0
- package/lib/commonjs/storage/index.js +17 -0
- package/lib/esm/index.js +196 -0
- package/lib/esm/mpc/index.js +351 -0
- package/lib/esm/provider/index.js +257 -0
- package/lib/esm/storage/index.js +15 -0
- package/package.json +46 -0
- package/src/index.ts +232 -0
- package/src/mpc/index.ts +443 -0
- package/src/provider/index.ts +314 -0
- package/src/storage/index.ts +29 -0
- package/types.d.ts +341 -0
|
@@ -0,0 +1,257 @@
|
|
|
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 { ProviderRpcError, RpcErrorCodes } from '@portal-hq/utils';
|
|
11
|
+
const passiveSignerMethods = [
|
|
12
|
+
'eth_accounts',
|
|
13
|
+
'eth_chainId',
|
|
14
|
+
'eth_requestAccounts',
|
|
15
|
+
];
|
|
16
|
+
const signerMethods = [
|
|
17
|
+
'eth_accounts',
|
|
18
|
+
'eth_chainId',
|
|
19
|
+
'eth_requestAccounts',
|
|
20
|
+
'eth_sendTransaction',
|
|
21
|
+
'eth_sign',
|
|
22
|
+
'eth_signTransaction',
|
|
23
|
+
'eth_signTypedData_v3',
|
|
24
|
+
'eth_signTypedData_v4',
|
|
25
|
+
'personal_sign',
|
|
26
|
+
];
|
|
27
|
+
class Provider {
|
|
28
|
+
constructor({ autoApprove = false, portal, }) {
|
|
29
|
+
this.autoApprove = autoApprove;
|
|
30
|
+
this.events = {};
|
|
31
|
+
this.portal = portal;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Invokes all registered event handlers with the data provided
|
|
35
|
+
* - If any `once` handlers exist, they are removed after all handlers are invoked
|
|
36
|
+
*
|
|
37
|
+
* @param event The name of the event to be handled
|
|
38
|
+
* @param data The data to be passed to registered event handlers
|
|
39
|
+
* @returns BaseProvider
|
|
40
|
+
*/
|
|
41
|
+
emit(event, data) {
|
|
42
|
+
// Grab the registered event handlers if any are available
|
|
43
|
+
const handlers = this.events[event] || [];
|
|
44
|
+
// Execute every event handler
|
|
45
|
+
for (const registeredEventHandler of handlers) {
|
|
46
|
+
registeredEventHandler.handler(data);
|
|
47
|
+
}
|
|
48
|
+
// Remove any registered event handlers with the `once` flag
|
|
49
|
+
this.events[event] = handlers.filter((handler) => !handler.once);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Registers an event handler for the provided event
|
|
54
|
+
*
|
|
55
|
+
* @param event The event name to add a handler to
|
|
56
|
+
* @param callback The callback to be invoked when the event is emitted
|
|
57
|
+
* @returns BaseProvider
|
|
58
|
+
*/
|
|
59
|
+
on(event, callback) {
|
|
60
|
+
// If no handlers are registered for this event, create an entry for the event
|
|
61
|
+
if (!this.events[event]) {
|
|
62
|
+
this.events[event] = [];
|
|
63
|
+
}
|
|
64
|
+
// Register event handler with the rudimentary event bus
|
|
65
|
+
if (typeof callback !== 'undefined') {
|
|
66
|
+
this.events[event].push({
|
|
67
|
+
handler: callback,
|
|
68
|
+
once: false,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
removeEventListener(event, listenerToRemove) {
|
|
74
|
+
if (!this.events[event]) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!listenerToRemove) {
|
|
78
|
+
this.events[event] = [];
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const filterEventHandlers = (registeredEventHandler) => {
|
|
82
|
+
return registeredEventHandler.handler !== listenerToRemove;
|
|
83
|
+
};
|
|
84
|
+
this.events[event] = this.events[event].filter(filterEventHandlers);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Handles request routing in compliance with the EIP-1193 Ethereum Javascript Provider API
|
|
89
|
+
* - See here for more info: https://eips.ethereum.org/EIPS/eip-1193
|
|
90
|
+
*
|
|
91
|
+
* @param args The arguments of the request being made
|
|
92
|
+
* @returns Promise<any>
|
|
93
|
+
*/
|
|
94
|
+
request({ method, params }) {
|
|
95
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
if (method === 'eth_chainId') {
|
|
97
|
+
return this.portal.chainId;
|
|
98
|
+
}
|
|
99
|
+
const isSignerMethod = signerMethods.includes(method);
|
|
100
|
+
let result;
|
|
101
|
+
if (!isSignerMethod && !method.startsWith('wallet_')) {
|
|
102
|
+
// Send to Gateway for RPC calls
|
|
103
|
+
const response = yield this.handleGatewayRequest({ method, params });
|
|
104
|
+
this.emit('portal_signatureReceived', {
|
|
105
|
+
method,
|
|
106
|
+
params,
|
|
107
|
+
signature: response,
|
|
108
|
+
});
|
|
109
|
+
if (response.error) {
|
|
110
|
+
throw new ProviderRpcError(response.error);
|
|
111
|
+
}
|
|
112
|
+
result = response.result;
|
|
113
|
+
}
|
|
114
|
+
else if (isSignerMethod) {
|
|
115
|
+
// Handle signing
|
|
116
|
+
const transactionHash = yield this.handleSigningRequest({
|
|
117
|
+
method,
|
|
118
|
+
params,
|
|
119
|
+
});
|
|
120
|
+
if (transactionHash) {
|
|
121
|
+
this.emit('portal_signatureReceived', {
|
|
122
|
+
method,
|
|
123
|
+
params,
|
|
124
|
+
signature: transactionHash,
|
|
125
|
+
});
|
|
126
|
+
result = transactionHash;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Unsupported method
|
|
131
|
+
throw new ProviderRpcError({
|
|
132
|
+
code: RpcErrorCodes.UnsupportedMethod,
|
|
133
|
+
data: {
|
|
134
|
+
method,
|
|
135
|
+
params,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/************************
|
|
143
|
+
* Private Methods
|
|
144
|
+
************************/
|
|
145
|
+
/**
|
|
146
|
+
* Kicks off the approval flow for a given request
|
|
147
|
+
*
|
|
148
|
+
* @param args The arguments of the request being made
|
|
149
|
+
*/
|
|
150
|
+
getApproval({ method, params, }) {
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
// If autoApprove is enabled, just resolve to true
|
|
153
|
+
if (this.autoApprove) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
const signingHandlers = this.events['portal_signingRequested'];
|
|
157
|
+
if (!signingHandlers || signingHandlers.length === 0) {
|
|
158
|
+
throw new Error(`[PortalProvider] Auto-approve is disabled. Cannot perform signing requests without an event handler for the 'portal_signingRequested' event.`);
|
|
159
|
+
}
|
|
160
|
+
return new Promise((resolve) => {
|
|
161
|
+
// Remove already used listeners
|
|
162
|
+
this.removeEventListener('portal_signingApproved');
|
|
163
|
+
this.removeEventListener('portal_signingRejected');
|
|
164
|
+
const handleApproval = ({ method: approvedMethod, params: approvedParams }) => {
|
|
165
|
+
// Remove already used listeners
|
|
166
|
+
this.removeEventListener('portal_signingApproved');
|
|
167
|
+
this.removeEventListener('portal_signingRejected');
|
|
168
|
+
// First verify that this is the same signing request
|
|
169
|
+
if (method === approvedMethod &&
|
|
170
|
+
JSON.stringify(params) === JSON.stringify(approvedParams)) {
|
|
171
|
+
resolve(true);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const handleRejection = ({ method: rejectedMethod, params: rejectedParams }) => {
|
|
175
|
+
// Remove already used listeners
|
|
176
|
+
this.removeEventListener('portal_signingApproved');
|
|
177
|
+
this.removeEventListener('portal_signingRejected');
|
|
178
|
+
// First verify that this is the same signing request
|
|
179
|
+
if (method === rejectedMethod &&
|
|
180
|
+
JSON.stringify(params) === JSON.stringify(rejectedParams)) {
|
|
181
|
+
resolve(false);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
// If the signing has been approved, resolve to true
|
|
185
|
+
this.on('portal_signingApproved', handleApproval);
|
|
186
|
+
// If the signing request has been rejected, resolve to false
|
|
187
|
+
this.on('portal_signingRejected', handleRejection);
|
|
188
|
+
// Tell any listening clients that signing has been requested
|
|
189
|
+
this.emit('portal_signingRequested', {
|
|
190
|
+
method,
|
|
191
|
+
params,
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Sends the provided request payload along to the RPC HttpRequester
|
|
198
|
+
*
|
|
199
|
+
* @param args The arguments of the request being made
|
|
200
|
+
* @returns Promise<any>
|
|
201
|
+
*/
|
|
202
|
+
handleGatewayRequest({ method, params, }) {
|
|
203
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
204
|
+
// Pass request off to the gateway
|
|
205
|
+
const result = yield fetch(this.portal.getRpcUrl(), {
|
|
206
|
+
body: JSON.stringify({
|
|
207
|
+
jsonrpc: '2.0',
|
|
208
|
+
id: this.portal.chainId,
|
|
209
|
+
method,
|
|
210
|
+
params,
|
|
211
|
+
}),
|
|
212
|
+
method: 'POST',
|
|
213
|
+
});
|
|
214
|
+
return result.json();
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Sends the provided request payload along to the Signer
|
|
219
|
+
*
|
|
220
|
+
* @param args The arguments of the request being made
|
|
221
|
+
* @returns Promise<any>
|
|
222
|
+
*/
|
|
223
|
+
handleSigningRequest({ method, params, }) {
|
|
224
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
225
|
+
const isApproved = passiveSignerMethods.includes(method)
|
|
226
|
+
? true
|
|
227
|
+
: yield this.getApproval({ method, params });
|
|
228
|
+
if (!isApproved) {
|
|
229
|
+
console.info(`[PortalProvider] Request for signing method '${method}' could not be completed because it was not approved by the user.`);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
switch (method) {
|
|
233
|
+
case 'eth_chainId':
|
|
234
|
+
return `0x${this.portal.chainId.toString(16)}`;
|
|
235
|
+
case 'eth_accounts':
|
|
236
|
+
case 'eth_requestAccounts':
|
|
237
|
+
return [this.portal.address];
|
|
238
|
+
case 'eth_sendTransaction':
|
|
239
|
+
case 'eth_sign':
|
|
240
|
+
case 'eth_signTransaction':
|
|
241
|
+
case 'eth_signTypedData_v3':
|
|
242
|
+
case 'eth_signTypedData_v4':
|
|
243
|
+
case 'personal_sign':
|
|
244
|
+
const result = yield this.portal.mpc.sign({
|
|
245
|
+
host: this.portal.mpcHost,
|
|
246
|
+
method,
|
|
247
|
+
mpcVersion: this.portal.mpcVersion,
|
|
248
|
+
params,
|
|
249
|
+
});
|
|
250
|
+
return result;
|
|
251
|
+
default:
|
|
252
|
+
throw new Error('[PortalProvider] Method "' + method + '" not supported');
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
export default Provider;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class DefaultBackupStorage {
|
|
2
|
+
delete() {
|
|
3
|
+
throw new Error('[Portal] Storage method delete cannot be called directly. Please extend Storage.');
|
|
4
|
+
}
|
|
5
|
+
read() {
|
|
6
|
+
throw new Error('[Portal] Storage method read cannot be called directly. Please extend Storage.');
|
|
7
|
+
}
|
|
8
|
+
write(privateKey) {
|
|
9
|
+
throw new Error(`[Portal] Storage method write(${privateKey}) cannot be called directly. Please extend Storage.`);
|
|
10
|
+
}
|
|
11
|
+
validateOperations() {
|
|
12
|
+
throw new Error('[Portal] Storage method validateOperations cannot be called directly. Please extend Storage.');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export default DefaultBackupStorage;
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@portal-hq/web",
|
|
3
|
+
"description": "Portal MPC Support for Web",
|
|
4
|
+
"author": "Portal Labs, Inc.",
|
|
5
|
+
"homepage": "https://portalhq.io/",
|
|
6
|
+
"version": "0.0.1-beta-1",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "lib/commonjs/index",
|
|
9
|
+
"module": "lib/esm/index",
|
|
10
|
+
"source": "src/index",
|
|
11
|
+
"types": "src/index",
|
|
12
|
+
"files": [
|
|
13
|
+
"src",
|
|
14
|
+
"lib",
|
|
15
|
+
"types.d.ts",
|
|
16
|
+
"!src/iframe"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"bundle": "sh ./scripts/version-iframe.sh && webpack && sh ./scripts/copy-static-files.sh && git stash",
|
|
20
|
+
"coverage": "jest --collect-coverage",
|
|
21
|
+
"prepare": "sh ./scripts/version-mpc.sh && yarn prepare:cjs && yarn prepare:esm && rm -rf lib/esm/iframe && rm -rf lib/commonjs/iframe",
|
|
22
|
+
"prepare:cjs": "tsc --outDir lib/commonjs --module commonjs",
|
|
23
|
+
"prepare:esm": "tsc --outDir lib/esm --module es2015 --target es2015",
|
|
24
|
+
"test": "jest"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@portal-hq/utils": "^1.0.2"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@babel/core": "^7.22.5",
|
|
31
|
+
"@babel/preset-env": "^7.22.5",
|
|
32
|
+
"@babel/preset-typescript": "^7.18.6",
|
|
33
|
+
"@types/jest": "^29.2.0",
|
|
34
|
+
"@types/react": "*",
|
|
35
|
+
"@types/react-native": "^0.70.5",
|
|
36
|
+
"babel-loader": "^9.1.2",
|
|
37
|
+
"jest": "^29.2.1",
|
|
38
|
+
"jest-environment-jsdom": "^29.2.2",
|
|
39
|
+
"terser-webpack-plugin": "^5.3.9",
|
|
40
|
+
"ts-jest": "^29.0.3",
|
|
41
|
+
"ts-loader": "^9.4.3",
|
|
42
|
+
"typescript": "^4.8.4",
|
|
43
|
+
"webpack": "^5.87.0",
|
|
44
|
+
"webpack-cli": "^5.1.4"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import type { BackupOptions, EthereumTransaction, GatewayLike, PortalOptions, TypedData } from '../types'
|
|
2
|
+
import Mpc from './mpc'
|
|
3
|
+
import Provider from './provider'
|
|
4
|
+
import DefaultBackupStorage from './storage'
|
|
5
|
+
|
|
6
|
+
class Portal {
|
|
7
|
+
public address?: string
|
|
8
|
+
public chainId: number
|
|
9
|
+
public mpc: Mpc
|
|
10
|
+
public mpcHost: string
|
|
11
|
+
public mpcVersion: string
|
|
12
|
+
public provider: Provider
|
|
13
|
+
public ready: boolean = false
|
|
14
|
+
|
|
15
|
+
private apiHost: string
|
|
16
|
+
private apiKey: string
|
|
17
|
+
private autoApprove: boolean
|
|
18
|
+
private backup: BackupOptions
|
|
19
|
+
private gatewayConfig: GatewayLike
|
|
20
|
+
private readyCallbacks: (() => any | Promise<any>)[] = []
|
|
21
|
+
private rpcUrl: string
|
|
22
|
+
private webHost: string
|
|
23
|
+
|
|
24
|
+
constructor({
|
|
25
|
+
// Required
|
|
26
|
+
apiKey,
|
|
27
|
+
gatewayConfig,
|
|
28
|
+
|
|
29
|
+
// Optional
|
|
30
|
+
apiHost = 'api.portalhq.io',
|
|
31
|
+
autoApprove = false,
|
|
32
|
+
backup = { default: new DefaultBackupStorage() },
|
|
33
|
+
chainId = 1,
|
|
34
|
+
mpcHost = 'mpc.portalhq.io',
|
|
35
|
+
mpcVersion = 'v4',
|
|
36
|
+
webHost = 'web.portalhq.io',
|
|
37
|
+
}: PortalOptions) {
|
|
38
|
+
this.apiHost = apiHost
|
|
39
|
+
this.apiKey = apiKey
|
|
40
|
+
this.autoApprove = autoApprove
|
|
41
|
+
this.backup = backup
|
|
42
|
+
this.chainId = chainId
|
|
43
|
+
this.gatewayConfig = gatewayConfig
|
|
44
|
+
this.mpcHost = mpcHost
|
|
45
|
+
this.mpcVersion = mpcVersion
|
|
46
|
+
this.rpcUrl = this.getRpcUrl()
|
|
47
|
+
this.webHost = webHost
|
|
48
|
+
|
|
49
|
+
console.log(`Backup:`, this.backup)
|
|
50
|
+
|
|
51
|
+
// Portal Instances
|
|
52
|
+
this.mpc = new Mpc({
|
|
53
|
+
apiHost: this.apiHost,
|
|
54
|
+
apiKey: this.apiKey,
|
|
55
|
+
autoApprove: this.autoApprove,
|
|
56
|
+
chainId: this.chainId,
|
|
57
|
+
mpcHost: this.mpcHost,
|
|
58
|
+
rpcUrl: this.rpcUrl,
|
|
59
|
+
webHost: this.webHost,
|
|
60
|
+
portal: this,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
this.provider = new Provider({
|
|
64
|
+
autoApprove: this.autoApprove,
|
|
65
|
+
mpcHost: this.mpcHost,
|
|
66
|
+
mpcVersion: this.mpcVersion,
|
|
67
|
+
portal: this,
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/*****************************
|
|
72
|
+
* Initialization Methods
|
|
73
|
+
*****************************/
|
|
74
|
+
|
|
75
|
+
public onReady(callback: () => any | Promise<any>) {
|
|
76
|
+
if (this.ready) {
|
|
77
|
+
callback()
|
|
78
|
+
} else {
|
|
79
|
+
this.readyCallbacks.push(callback)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public triggerReady() {
|
|
84
|
+
if (this.ready && this.readyCallbacks.length > 0) {
|
|
85
|
+
this.readyCallbacks.forEach((callback) => {
|
|
86
|
+
callback()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
this.readyCallbacks = []
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*****************************
|
|
94
|
+
* Wallet Methods
|
|
95
|
+
*****************************/
|
|
96
|
+
|
|
97
|
+
public async createWallet(): Promise<string> {
|
|
98
|
+
const address = await this.mpc.generate({
|
|
99
|
+
host: this.mpcHost,
|
|
100
|
+
mpcVersion: this.mpcVersion,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
this.address = address
|
|
104
|
+
|
|
105
|
+
return address
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public async backupWallet(): Promise<string> {
|
|
109
|
+
await this.mpc.backup({
|
|
110
|
+
host: this.mpcHost,
|
|
111
|
+
mpcVersion: this.mpcVersion,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return ''
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public async recoverWallet(): Promise<boolean> {
|
|
118
|
+
await this.mpc.recover({
|
|
119
|
+
host: this.mpcHost,
|
|
120
|
+
mpcVersion: this.mpcVersion,
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
return true
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/****************************
|
|
127
|
+
* Provider Methods
|
|
128
|
+
****************************/
|
|
129
|
+
|
|
130
|
+
public async ethSendTransaction(transaction: EthereumTransaction): Promise<string> {
|
|
131
|
+
return this.provider.request({
|
|
132
|
+
method: 'eth_sendTransaction',
|
|
133
|
+
params: transaction,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public async ethSign(message: string): Promise<string> {
|
|
138
|
+
return this.provider.request({
|
|
139
|
+
method: 'eth_sign',
|
|
140
|
+
params: [this.address, this.stringToHex(message)],
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public async ethSignTransaction(transaction: EthereumTransaction): Promise<string> {
|
|
145
|
+
return this.provider.request({
|
|
146
|
+
method: 'eth_signTransaction',
|
|
147
|
+
params: transaction,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public async ethSignTypedData(data: TypedData): Promise<string> {
|
|
152
|
+
return this.provider.request({
|
|
153
|
+
method: 'eth_signTypedData',
|
|
154
|
+
params: [this.address, data],
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public async ethSignTypedDataV3(data: TypedData): Promise<string> {
|
|
159
|
+
return this.provider.request({
|
|
160
|
+
method: 'eth_signTypedData_v3',
|
|
161
|
+
params: [this.address, data],
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public async ethSignTypedDataV4(data: TypedData): Promise<string> {
|
|
167
|
+
return this.provider.request({
|
|
168
|
+
method: 'eth_signTypedData_v4',
|
|
169
|
+
params: [this.address, data],
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public async personalSign(message: string): Promise<string> {
|
|
174
|
+
return this.provider.request({
|
|
175
|
+
method: 'personal_sign',
|
|
176
|
+
params: [this.stringToHex(message), this.address],
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/****************************
|
|
181
|
+
* RPC Methods
|
|
182
|
+
****************************/
|
|
183
|
+
|
|
184
|
+
public getRpcUrl() {
|
|
185
|
+
if (typeof this.gatewayConfig === 'string') {
|
|
186
|
+
// If the gatewayConfig is just a static URL, return that
|
|
187
|
+
return this.gatewayConfig
|
|
188
|
+
} else if (
|
|
189
|
+
typeof this.gatewayConfig === 'object' &&
|
|
190
|
+
!this.gatewayConfig.hasOwnProperty(this.chainId)
|
|
191
|
+
) {
|
|
192
|
+
// If there's no explicit mapping for the current chainId, error out
|
|
193
|
+
throw new Error(
|
|
194
|
+
`[PortalProvider] No RPC endpoint configured for chainId: ${this.chainId}`,
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Get the entry for the current chainId from the gatewayConfig
|
|
199
|
+
const config = this.gatewayConfig[this.chainId]
|
|
200
|
+
|
|
201
|
+
if (typeof config === 'string') {
|
|
202
|
+
return config
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// If we got this far, there's no way to support the chain with the current config
|
|
206
|
+
throw new Error(
|
|
207
|
+
`[PortalProvider] Could not find a valid gatewayConfig entry for chainId: ${this.chainId}`,
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**************************
|
|
212
|
+
* RPC Encoding Methods
|
|
213
|
+
**************************/
|
|
214
|
+
|
|
215
|
+
private stringToHex(str: string): string {
|
|
216
|
+
if (str.startsWith('0x')) {
|
|
217
|
+
return str
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let hex = '0x';
|
|
221
|
+
|
|
222
|
+
for (let i = 0; i < str.length; i++) {
|
|
223
|
+
const charCode = str.charCodeAt(i);
|
|
224
|
+
const hexValue = charCode.toString(16);
|
|
225
|
+
hex += hexValue.padStart(2, '0'); // Ensure two-digit hex value
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return hex;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export default Portal
|