- Preliminaires
- Setup steps
- Look at the bundle
- Add
SES
library file- Find out the current version
- Download the lockdown.umd.js file at the directory root
- Add the reference to this file at index.js
- Add the file to the .prettierignore file
- Prevent lockdown.umd.js to be transformed - with .babelignore
- Everything OK
- Add
lockdown()
call- New Error - extends2.default
- Issues with
@babel/runtime
- Causes of Error
- Debugging Pro-tip
- Blacklisting @babel/plugin-transform-regenerator
- Setting up custom plugin arrangement at
babel.config.js
- New Error - intrinsics.Promise.resolve.prototype
- Removing the polyfill of
Promise
- Comment one line at React Native
- New Error - intrinsics.%FunctionPrototype%.toString.prototype
- Who is reassigning
Function.prototype.toString
?- Disable default integrations in sentry
- New Error - Attempted to assign to readonly property.
- Where is this
readonly property
?- Patching some
eth
libraries - New Error - Exception eventually appears
- Patching some
- The next
readonly property
- The
Object.defineProperty
workaround - It's working!
- The
# Stop!
# Make sure that you installed node, sentry, react native, xcode, etc etc.
git clone [email protected]:bentobox19/metamask-mobile.git mobile
cd mobile
# It's good to keep up with the original repo
git remote add upstream https://github.com/MetaMask/metamask-mobile.git
git fetch upstream
git merge upstream/main
git checkout -b your-experimental-branch
# Do
npx browserslist@latest --update-db
# And we do
yarn setup
# Other things
cp .ios.env.example .ios.env && \
cp .android.env.example .android.env && \
cp .js.env.example .js.env
# Make sure you setup your .js.env file. (without the #)
# export MM_OPENSEA_KEY="123-API-TOKEN"
# export MM_INFURA_PROJECT_ID="123-API-TOKEN"
# Run each command on a separate terminal window
yarn watch
yarn start:ios
# Alternatives to `yarn watch`
## `react-native start`
## `yarn watch:clean ` equivalent to `react-native start --reset-cache`
Everything should be working here, provided you followed the above steps to the letter.
http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false
At https://www.npmjs.com/package/ses
curl -O https://npmfs.com/download/ses/0.15.9/dist/lockdown.umd.js
0.15.9
is 2 days old as of 2022.02.21
.
import './shim.js';
import './lockdown.umd.js';
import 'react-native-gesture-handler';
import 'react-native-url-polyfill/auto';
.prettierignore
metro.config.js
jest.preprocessor.js
scripts/metamask-bot-build-announce.js
CHANGELOG.md
lockdown.umd.js
Create the file .babelignore
with the line
lockdown.umd.js
👌
At index.js
// eslint-disable-next-line
lockdown({consoleTaming: 'unsafe'});
/**
* Application entry point responsible for registering root component
*/
AppRegistry.registerComponent(name, () => Root);
If you don't use the option {consoleTaming: "unsafe"}
, your app will fail silently.
TypeError: An error was thrown when attempting to render log messages via LogBox.
(0, _extends2.default) is not a function. (In '(0, _extends2.default)({}, parseInterpolation(argsWithoutComponentStack), {
componentStack: componentStack
})', '(0, _extends2.default)' is undefined)
parseLogBoxLog
index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=io.metamask.MetaMask:34632:33
registerWarning
index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=io.metamask.MetaMask:32199:46
_extends
polyfill atnode_modules/@babel/runtime/helpers/extends.js
._getPrototypeOf
polyfill atnode_modules/@babel/runtime/helpers/getPrototypeOf.js
.
If you make changes in babel, make sure you re-run react-native with the --reset-cache
flag.
react-native start --reset-cache
Plugins cannot be individually disabled from a preset.
From babel/babel#14168 (reply in thread)
Note that the reason that we don't allow it is because plugins are an implementation detail of presets: what matters is the overall result of the preset, and not how it's internally accomplished.
We need either to set up our own babel.config.js
, or patch-package
the preset. Let's try the former.
- We derive this configuration from
metro-react-native-babel-preset
// Borrowing from metro-react-native-babel-preset
const lazyImports = require('metro-react-native-babel-preset/src/configs/lazy-imports');
function isTypeScriptSource(fileName) {
return !!fileName && fileName.endsWith(".ts");
}
function isTSXSource(fileName) {
return !!fileName && fileName.endsWith(".tsx");
}
module.exports = {
comments: false,
compact: true,
overrides: [
{
plugins: [
// TODO
// Check whether we need all this plugins
// within this block
'@babel/plugin-transform-flow-strip-types',
'@babel/plugin-syntax-flow',
'@babel/plugin-transform-block-scoping',
['@babel/plugin-proposal-class-properties', {
loose: true,
}],
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-syntax-export-default-from',
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-transform-unicode-regex',
]
},
{
test: isTypeScriptSource,
plugins: [
[
'@babel/plugin-transform-typescript',
{
isTSX: false,
allowNamespaces: true
}
]
]
},
{
test: isTSXSource,
plugins: [
[
'@babel/plugin-transform-typescript',
{
isTSX: true,
allowNamespaces: true
}
]
]
},
{
plugins: [
'@babel/plugin-transform-react-jsx',
'@babel/plugin-proposal-export-default-from',
[
'@babel/plugin-transform-modules-commonjs',
{
strict: false,
strictMode: false,
lazy: importSpecifier => lazyImports.has(importSpecifier),
allowTopLevelThis: true
}
],
'@babel/plugin-transform-classes',
'transform-inline-environment-variables',
'react-native-reanimated/plugin'
]
}
]
};
failed to delete intrinsics.Promise.resolve.prototype
- node_modules/react-native/Libraries/Core/InitializeCore.js
require('./setUpSystrace');
require('./setUpErrorHandling');
// require('./polyfillPromise');
require('./setUpRegeneratorRuntime');
require('./setUpTimers');
Updating this patch is tricky:
rm -rf node_modules
# 1. Get the dependencies
yarn
# 2. Apply the changes at /patches
npx patch-package
# 3. Here you do the change to the file, then
# 4. Record your changes
npx patch-package react-native
# 5. commit, push, etc here
# 6. Complete the setup
yarn setup
ERROR failed to delete intrinsics.%FunctionPrototype%.toString.prototype [TypeError: Unable to delete property.]
sentry
is the one assigning to Function.prototype.toString
. This is a System Integration.
FunctionToString.prototype.setupOnce = function () {
// eslint-disable-next-line @typescript-eslint/unbound-method
originalFunctionToString = Function.prototype.toString;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Function.prototype.toString = function() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var context = this.__sentry_original__ || this;
return originalFunctionToString.apply(context, args);
};
};
From https://docs.sentry.io/platforms/javascript/configuration/integrations/default/
System integrations are enabled by default to integrate into the standard library or the interpreter itself. They are documented so you can be both aware of what they do and disable them if they cause issues.
From https://docs.sentry.io/platforms/react-native/configuration/options/
integrations In some SDKs, the integrations are configured through this parameter on library initialization. For more information, please see our documentation for a specific integration.
defaultIntegrations This can be used to disable integrations that are added by default. When set to false, no default integrations are added.
diff --git a/app/util/setupSentry.js b/app/util/setupSentry.js
index 7b633ec0..a98c1b5a 100644
--- a/app/util/setupSentry.js
+++ b/app/util/setupSentry.js
@@ -18,6 +18,7 @@ export default function setupSentry() {
dsn,
debug: __DEV__,
environment,
+ defaultIntegrations: false,
integrations: [
new Dedupe(),
new ExtraErrorData(),
TypeError: Attempted to assign to readonly property.
ERROR Error: Requiring module "node_modules/ethjs/node_modules/ethjs-query/lib/index.js", which threw an exception: TypeError: Attempted to assign to readonly property.
ERROR TypeError: undefined is not a constructor (evaluating 'new (_$$_REQUIRE(_dependencyMap[0], "ethjs-query"))(cprovider, self.options.query)'
node_modules/ethjs/lib/index.js
var query = new (_$$_REQUIRE(_dependencyMap[0], "ethjs-query"))(cprovider, self.options.query);
which calls
node_modules/ethjs/node_modules/ethjs-query/lib/index.js
var _regenerator2 = _interopRequireDefault(_$$_REQUIRE(_dependencyMap[0], "babel-runtime/regenerator"));
This is the first line of the file within the bundle. Let's call the _$$REQUIRE()
function from the console
_$$_REQUIRE(_dependencyMap[0], "babel-runtime/regenerator")
Throws
Error: Requiring module "node_modules/babel-runtime/regenerator/index.js", which threw an exception: TypeError: Attempted to assign to readonly property.
We have already removed the regenerator plugin. Investigating into the library, we found that it's referenced in its package.json
. Further grepping show us more libraries with the same problem.
To the following files...
node_modules/ethjs/node_modules/ethjs-query/package.json
node_modules/ethjs-contract/package.json
node_modules/ethjs/node_modules/ethjs-contract/package.json
...Do the following change:
"main": "lib/index.js",
- > "main": "src/index.js",
And invoke npx patch-package --exclude "nothing" <LIB_NAME>
.
This flag --exclude
is needed to be able to include the package.json
file into the patch.
TypeError: Attempted to assign to readonly property.
There are also some other exceptions that will appear at the debugger
Exception with thrown value: TypeError: undefined is not an object (evaluating 'ethQuery[method]')
Exception with thrown value: TypeError: undefined is not an object (evaluating 'new this.web3.eth')
- At
node_modules/web3-core-methods/src/index.js
var Method = function Method(options) {
if (!options.call || !options.name) {
throw new Error('When creating a method you need to provide at least the "name" and "call" property.');
}
this.name = options.name;
this.call = options.call;
this.params = options.params || 0;
As Function.protoype.call
is frozen, this.call = options.call
throws
TypeError: Attempted to assign to readonly property.
Replace this
this.call = options.call;
By this
Object.defineProperty(this, 'call', {
value: option.call
});
- Is it?
- Now we need to do extensive QA. I.e. Check every single functionality of the mobile app. But this is a good start!