-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathshim.js
More file actions
161 lines (149 loc) · 7.18 KB
/
Copy pathshim.js
File metadata and controls
161 lines (149 loc) · 7.18 KB
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/* eslint-disable */
// Boot profiling: this is the earliest JS the app runs (index.js imports './shim'
// first, and the web build resolves that to shim.web.js instead), so importing the
// profiler here stamps the true start of the RN realm. Imported directly rather
// than through the folder's index so the report module — which pulls in
// react-native and expo-file-system — does not load ahead of the shims below.
import './src/mobile/services/bootProfiler/bootProfiler'
import { getRandomValues, install } from 'react-native-quick-crypto'
install()
// Every secret the wallet generates - the keystore main key, seed phrases, scrypt salts, AES IVs -
// traces back to this one function, and two of the polyfills below compete for it: whichever runs last
// owns `global.crypto.getRandomValues`. react-native-get-random-values only claims the slot if it is
// still empty, but when it does own it and its native module is unreachable it answers from a
// Math.random() loop, with a console warning and nothing more.
//
// `install()` wins because every import in this file is hoisted above it, so all of them have already
// run by the time it assigns. That ordering is invisible in the source and one bare import moved
// around would silently flip it, so it is asserted rather than trusted. Failing to boot is the right
// outcome here: a build that could seed keys from a non-cryptographic source must never ship.
if (global.crypto.getRandomValues !== getRandomValues)
throw new Error(
'shim.js: global.crypto.getRandomValues is not the one from react-native-quick-crypto, so key generation could fall back to a non-cryptographic random source. Refusing to start.'
)
// 3. Ethers/Legacy shims
// Keep these for ethers v5/v6 compatibility until you fully migrate
import 'react-native-get-random-values'
import '@ethersproject/shims'
// 4. Basic Node process polyfills
if (typeof process === 'undefined') {
global.process = require('process')
} else {
const bProcess = require('process')
for (const p in bProcess) {
if (!(p in process)) process[p] = bProcess[p]
}
}
process.browser = false
// ─────────────────────────────────────────────────────────────────────────────
// Location shim
// ─────────────────────────────────────────────────────────────────────────────
if (typeof location === 'undefined') {
global.location = {
href: '',
pathname: '/',
search: '',
hash: '',
origin: '',
protocol: 'https:',
hostname: 'localhost'
}
}
// ─────────────────────────────────────────────────────────────────────────────
// Document shim
//
// Provides a minimal document object for common code shared between web and
// mobile (e.g. useSyncedState, EthereumProvider, Dropdown, Select components).
// Bare `document.X` references resolve here via global/globalThis lookup.
// ─────────────────────────────────────────────────────────────────────────────
const documentShim = {
location: global.location,
addEventListener: () => {},
removeEventListener: () => {},
querySelector: () => null,
querySelectorAll: () => [],
visibilityState: 'visible',
readyState: 'complete',
hidden: false,
body: {
classList: { add: () => {}, remove: () => {} },
appendChild: () => {},
removeChild: () => {}
},
documentElement: {
classList: { add: () => {}, remove: () => {} }
},
head: null,
title: '',
createElement: () => ({
setAttribute() {},
appendChild() {},
removeChild() {},
click() {},
remove() {},
style: {},
classList: { add() {}, remove() {} }
})
}
global.document = documentShim
// ─────────────────────────────────────────────────────────────────────────────
// Window Proxy — hides `document` from `window.X` access
//
// WHY: @walletconnect/window-getters reads `window.document` via
// getFromWindow('document'). If it's truthy, getDocumentOrThrow() returns it,
// and getWindowMetadata() proceeds to call doc.getElementsByTagName('link')
// which crashes because our shim doesn't implement full DOM.
//
// By making `window.document` return `undefined`, getDocumentOrThrow() throws,
// getWindowMetadata() catches and returns null, and populateAppMetadata falls
// back to user-provided metadata (which WalletKit.init({ metadata }) supplies).
//
// Bare `document` (no `window.` prefix) still resolves to global.document
// (the shim above) because JS identifier resolution goes through the scope
// chain / globalThis, NOT through this Proxy.
//
// navigator, location, localStorage, addEventListener, etc. pass through
// unchanged so RN-specific and benign reads keep working.
// ─────────────────────────────────────────────────────────────────────────────
global.window = new Proxy(global, {
get(target, prop, receiver) {
if (prop === 'document') return undefined
return Reflect.get(target, prop, receiver)
},
has(target, prop) {
if (prop === 'document') return false
return Reflect.has(target, prop)
}
// default set/defineProperty/deleteProperty pass through so WC SDK can do
// window.X = ... assignments without breaking.
})
// ─────────────────────────────────────────────────────────────────────────────
// Additional global shims
// ─────────────────────────────────────────────────────────────────────────────
if (typeof MutationObserver === 'undefined') {
global.MutationObserver = class {
constructor() {}
disconnect() {}
observe() {}
takeRecords() {
return []
}
}
}
if (typeof CustomEvent === 'undefined') {
global.CustomEvent = class {
constructor(type, options) {
this.type = type
this.detail = options?.detail || {}
}
}
}
// Typo handling because of certain library errors
if (typeof CustomeEvent === 'undefined') {
global.CustomeEvent = global.CustomEvent
}
// Window-level event listener shims for mobile app compatibility.
// These are set on global (which the Proxy forwards for non-document props).
global.addEventListener = global.addEventListener || (() => {})
global.removeEventListener = global.removeEventListener || (() => {})
global.close = global.close || (() => {})