-
Notifications
You must be signed in to change notification settings - Fork 8
/
RPCProviderFacade.ts
143 lines (137 loc) · 4.65 KB
/
RPCProviderFacade.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { EthersRequest, isIntercept } from "shared_types";
import { log } from "utils/logger";
import { v4 as uuidv4 } from "uuid";
import {
ExternalProvider,
isJsonRpcRequest,
JsonRpcCallback,
JsonRpcRequest,
JsonRpcResponse,
} from "./types";
const isMainnet = () => {
return window.ethereum.chainId === "0x1";
};
export class RPCProviderFacade {
constructor() {
if (window.ethereum) {
log("wrapping window.ethereum");
this.wrap(window.ethereum);
if (!!window.ethereum?.providers?.length) {
window.ethereum?.providers.forEach(this.wrap.bind(this));
}
} else {
log("adding ethereum#initialized event listener ");
window.addEventListener("ethereum#initialized", () => {
log("ethereum#initialized: wrapping window.ethereum");
this.wrap(window.ethereum);
});
}
}
wrap(provider: ExternalProvider) {
/*//////////////////////////////////////////////////////////////
WRAP WINDOW.ETHEREUM METHODS
//////////////////////////////////////////////////////////////*/
if (provider.send) {
log("wrapping deprecated send");
const deprecatedProviderSend = provider.send;
const deprecatedSend = (
methodOrPayload: string | JsonRpcRequest,
paramsOrCallback?: Array<unknown> | JsonRpcCallback
): Promise<JsonRpcResponse> | void => {
log("triggering deprecated send");
if (isJsonRpcRequest(methodOrPayload)) {
let request = methodOrPayload;
let callback = paramsOrCallback as JsonRpcCallback;
if (isIntercept(request.method) && isMainnet()) {
log("intercepted deprecated send");
this.waitForDecision(request).then(async () => {
return deprecatedProviderSend(request, callback);
});
} else {
return deprecatedProviderSend(request, callback);
}
} else {
let method = methodOrPayload;
let params = paramsOrCallback as any[];
if (isIntercept(method) && isMainnet()) {
log("intercepted deprecated send");
return this.waitForDecision({
method,
params,
}).then(async () => {
return deprecatedProviderSend(method, params);
}) as Promise<JsonRpcResponse>;
} else {
return deprecatedProviderSend(method, params);
}
}
};
provider.send = deprecatedSend.bind(this);
provider.stelo = true;
}
if (provider.sendAsync) {
log("wrapping deprecated sendAsync");
const deprecatedProviderSendAsync = provider.sendAsync;
const deprecatedSendAsync = (
request: JsonRpcRequest,
callback: (error: any, response: any) => void
): void => {
if (isIntercept(request.method) && isMainnet()) {
log("intercepted deprecated sendAsync");
this.waitForDecision(request)
.then(async () => {
deprecatedProviderSendAsync(request, callback);
})
.catch((err) => {
callback(err, {});
});
} else {
deprecatedProviderSendAsync(request, callback);
}
};
provider.sendAsync = deprecatedSendAsync.bind(this);
provider.stelo = true;
}
if (provider.request) {
log("wrapping request");
// We don't want metamask's ethereum.request externally accessible at runtime.
// We include it in the closure of _our_ request function so that we can forward approved requests.
const providerRequest = provider.request;
const request = async (request: JsonRpcRequest) => {
if (isIntercept(request.method) && isMainnet()) {
log("intercepted request");
return this.waitForDecision(request).then(async () => {
return await providerRequest(request);
});
} else {
return await providerRequest(request);
}
};
provider.request = request.bind(this);
provider.stelo = true;
}
}
async waitForDecision(request: EthersRequest) {
const rpcRequestId = uuidv4();
const event = new CustomEvent("STELO", {
detail: {
rpcRequestId,
...request,
userAddress: window.ethereum.selectedAddress,
},
});
window.dispatchEvent(event);
return new Promise((res, rej) => {
window.addEventListener("message", (event) => {
if (
event.data.origin === "stelo" &&
event.data.rpcRequestId === rpcRequestId
) {
event.data.approval
? res(undefined)
: rej({ message: "Rejected in Stelo" });
}
});
});
}
}