diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ad890741574..33d991cd5b3 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -36,7 +36,7 @@ - diff --git a/android/app/src/main/java/io/metamask/MainActivity.java b/android/app/src/main/java/io/metamask/MainActivity.java index 8c14d80028b..56b86058cb1 100644 --- a/android/app/src/main/java/io/metamask/MainActivity.java +++ b/android/app/src/main/java/io/metamask/MainActivity.java @@ -52,14 +52,22 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onNewIntent(Intent intent) { - super.onNewIntent(intent); - if (intent != null && - intent.hasExtra("branch_force_new_session") && - intent.getBooleanExtra("branch_force_new_session",false)) { - RNBranchModule.onNewIntent(intent); - } + super.onNewIntent(intent); + /* + if activity is in foreground (or in backstack but partially visible) launch the same + activity will skip onStart, handle this case with reInit + if reInit() is called without this flag, you will see the following message: + BRANCH_SDK: Warning. Session initialization already happened. + To force a new session, + set intent extra, "branch_force_new_session", to true. + */ + if (intent != null && + intent.hasExtra("branch_force_new_session") && + intent.getBooleanExtra("branch_force_new_session", false)) { + RNBranchModule.onNewIntent(intent); + } } - + @Override protected ReactActivityDelegate createReactActivityDelegate() { return new ReactActivityDelegate(this, getMainComponentName()) { diff --git a/app/components/UI/FiatOrders/PaymentMethodSelector/moonpay.js b/app/components/UI/FiatOrders/PaymentMethodSelector/moonpay.js index 9a10bdb8552..16e1e1e5169 100644 --- a/app/components/UI/FiatOrders/PaymentMethodSelector/moonpay.js +++ b/app/components/UI/FiatOrders/PaymentMethodSelector/moonpay.js @@ -7,7 +7,7 @@ import PaymentMethod from '../components/PaymentMethod'; import Title from '../../../Base/Title'; import Text from '../../../Base/Text'; import { useAssetFromTheme } from '../../../../util/theme'; -import { NETWORKS_CHAIN_ID } from '../../../../constants/on-ramp'; +import { NETWORKS_CHAIN_ID } from '../../../../constants/network'; const styles = StyleSheet.create({ title: { diff --git a/app/components/UI/FiatOrders/PaymentMethodSelector/transak.js b/app/components/UI/FiatOrders/PaymentMethodSelector/transak.js index 25b6c97e771..c592335a69d 100644 --- a/app/components/UI/FiatOrders/PaymentMethodSelector/transak.js +++ b/app/components/UI/FiatOrders/PaymentMethodSelector/transak.js @@ -8,7 +8,7 @@ import Title from '../../../Base/Title'; import Text from '../../../Base/Text'; import Device from '../../../../util/device'; -import { NETWORKS_CHAIN_ID } from '../../../../constants/on-ramp'; +import { NETWORKS_CHAIN_ID } from '../../../../constants/network'; const styles = StyleSheet.create({ title: { diff --git a/app/components/UI/FiatOrders/index.js b/app/components/UI/FiatOrders/index.js index a4e399a18b9..ed00b87fc54 100644 --- a/app/components/UI/FiatOrders/index.js +++ b/app/components/UI/FiatOrders/index.js @@ -9,8 +9,8 @@ import { renderNumber } from '../../../util/number'; import { FIAT_ORDER_PROVIDERS, FIAT_ORDER_STATES, - NETWORKS_CHAIN_ID, } from '../../../constants/on-ramp'; +import { NETWORKS_CHAIN_ID } from '../../../constants/network'; import { getPendingOrders, updateFiatOrder, diff --git a/app/components/UI/FiatOrders/orderProcessor/moonpay.js b/app/components/UI/FiatOrders/orderProcessor/moonpay.js index 0f995845642..51e46741a7a 100644 --- a/app/components/UI/FiatOrders/orderProcessor/moonpay.js +++ b/app/components/UI/FiatOrders/orderProcessor/moonpay.js @@ -5,8 +5,8 @@ import { FIAT_ORDER_PROVIDERS, FIAT_ORDER_STATES, MOONPAY_NETWORK_PARAMETERS, - NETWORKS_CHAIN_ID, } from '../../../../constants/on-ramp'; +import { NETWORKS_CHAIN_ID } from '../../../../constants/network'; import AppConstants from '../../../../core/AppConstants'; import Logger from '../../../../util/Logger'; diff --git a/app/components/UI/FiatOrders/orderProcessor/transak.js b/app/components/UI/FiatOrders/orderProcessor/transak.js index 9cab87f49ac..e62f11d0213 100644 --- a/app/components/UI/FiatOrders/orderProcessor/transak.js +++ b/app/components/UI/FiatOrders/orderProcessor/transak.js @@ -6,9 +6,9 @@ import Logger from '../../../../util/Logger'; import { FIAT_ORDER_PROVIDERS, FIAT_ORDER_STATES, - NETWORKS_CHAIN_ID, TRANSAK_NETWORK_PARAMETERS, } from '../../../../constants/on-ramp'; +import { NETWORKS_CHAIN_ID } from '../../../../constants/network'; //* env vars diff --git a/app/components/UI/FiatOrders/orderProcessor/wyreApplePay.js b/app/components/UI/FiatOrders/orderProcessor/wyreApplePay.js index 4ebc67a97fa..613df1fc63c 100644 --- a/app/components/UI/FiatOrders/orderProcessor/wyreApplePay.js +++ b/app/components/UI/FiatOrders/orderProcessor/wyreApplePay.js @@ -10,8 +10,8 @@ import useCurrency from '../../../Base/Keypad/useCurrency'; import { FIAT_ORDER_PROVIDERS, FIAT_ORDER_STATES, - NETWORKS_CHAIN_ID, } from '../../../../constants/on-ramp'; +import { NETWORKS_CHAIN_ID } from '../../../../constants/network'; //* env vars diff --git a/app/components/UI/Swaps/QuotesView.js b/app/components/UI/Swaps/QuotesView.js index 65292c914b9..5d0ab4699fd 100644 --- a/app/components/UI/Swaps/QuotesView.js +++ b/app/components/UI/Swaps/QuotesView.js @@ -1,5 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; +import Eth from 'ethjs-query'; import { View, StyleSheet, @@ -30,8 +31,13 @@ import { renderFromWei, toWei, weiToFiat, + calculateEthFeeForMultiLayer, } from '../../../util/number'; -import { isMainnetByChainId } from '../../../util/networks'; +import { + isMainnetByChainId, + isMultiLayerFeeNetwork, + fetchEstimatedL1FeeOptimism, +} from '../../../util/networks'; import { getErrorMessage, getFetchParams, @@ -379,6 +385,7 @@ function SwapsQuotesView({ /* State */ const isMainnet = isMainnetByChainId(chainId); + const multiLayerFeeNetwork = isMultiLayerFeeNetwork(chainId); const [firstLoadTime, setFirstLoadTime] = useState(Date.now()); const [isFirstLoad, setIsFirstLoad] = useState(true); const [shouldFinishFirstLoad, setShouldFinishFirstLoad] = useState(false); @@ -390,6 +397,7 @@ function SwapsQuotesView({ const [trackedError, setTrackedError] = useState(false); const [animateOnGasChange, setAnimateOnGasChange] = useState(false); const [isAnimating, setIsAnimating] = useState(false); + const [multiLayerL1FeeTotal, setMultiLayerL1FeeTotal] = useState(null); /* Selected quote, initially topAggId (see effects) */ const [selectedQuoteId, setSelectedQuoteId] = useState(null); @@ -464,15 +472,31 @@ function SwapsQuotesView({ () => allQuotes.find((quote) => quote?.aggregator === selectedQuoteId), [allQuotes, selectedQuoteId], ); - const selectedQuoteValue = useMemo( - () => quoteValues[selectedQuoteId], - [ - // eslint-disable-next-line react-hooks/exhaustive-deps - quoteValues[selectedQuoteId], - quoteValues, - selectedQuoteId, - ], - ); + const selectedQuoteValue = useMemo(() => { + if (!quoteValues[selectedQuoteId] || multiLayerL1FeeTotal === null) { + return quoteValues[selectedQuoteId]; + } + const fees = { + ethFee: calculateEthFeeForMultiLayer({ + multiLayerL1FeeTotal, + ethFee: quoteValues[selectedQuoteId].ethFee, + }), + maxEthFee: calculateEthFeeForMultiLayer({ + multiLayerL1FeeTotal, + ethFee: quoteValues[selectedQuoteId].maxEthFee, + }), + }; + return { + ...quoteValues[selectedQuoteId], + ...fees, + }; + }, [ + // eslint-disable-next-line react-hooks/exhaustive-deps + quoteValues[selectedQuoteId], + multiLayerL1FeeTotal, + quoteValues, + selectedQuoteId, + ]); const gasEstimates = useMemo( () => customGasEstimate || usedGasEstimate, @@ -1565,6 +1589,26 @@ function SwapsQuotesView({ handleQuotesErrorMetric(error); }, [error, handleQuotesErrorMetric, trackedError]); + useEffect(() => { + if (!multiLayerFeeNetwork || !selectedQuote?.trade) { + return; + } + const getEstimatedL1Fee = async () => { + try { + const eth = new Eth(Engine.context.NetworkController.provider); + const result = await fetchEstimatedL1FeeOptimism(eth, { + txParams: selectedQuote.trade, + chainId, + }); + setMultiLayerL1FeeTotal(result); + } catch (e) { + Logger.error(e, 'fetchEstimatedL1FeeOptimism call failed'); + setMultiLayerL1FeeTotal(null); + } + }; + getEstimatedL1Fee(); + }, [selectedQuote?.trade, multiLayerFeeNetwork, chainId]); + const openLinkAboutGas = () => Linking.openURL( 'https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172', @@ -1985,19 +2029,12 @@ function SwapsQuotesView({ - - - {renderFromWei( - toWei(selectedQuoteValue?.maxEthFee || '0x0'), - )}{' '} - {getTicker(ticker)} - - + + {renderFromWei( + toWei(selectedQuoteValue?.maxEthFee || '0x0'), + )}{' '} + {getTicker(ticker)} + {` ${ weiToFiat( @@ -2187,6 +2224,7 @@ function SwapsQuotesView({ selectedQuote={selectedQuoteId} showOverallValue={hasConversionRate} ticker={getTicker(ticker)} + multiLayerL1FeeTotal={multiLayerL1FeeTotal} /> {strings('swaps.estimated_network_fees')} - {renderFromWei(toWei(selectedDetailsQuoteValues.ethFee))}{' '} + {renderFromWei(toWei(selectedDetailsQuoteValuesEthFee))}{' '} {ticker} {' '} (~ {weiToFiat( - toWei(selectedDetailsQuoteValues.ethFee), + toWei(selectedDetailsQuoteValuesEthFee), conversionRate, currentCurrency, )} @@ -386,6 +393,10 @@ function QuotesModal({ const { aggregator } = quote; const isSelected = aggregator === selectedQuote; const quoteValue = quoteValues[aggregator]; + const quoteEthFee = calculateEthFeeForMultiLayer({ + multiLayerL1FeeTotal, + ethFee: quoteValue?.ethFee, + }); return ( {weiToFiat( - toWei(quoteValue?.ethFee ?? 0), + toWei(quoteEthFee), conversionRate, currentCurrency, )} @@ -504,6 +515,7 @@ QuotesModal.propTypes = { ticker: PropTypes.string, quoteValues: PropTypes.object, showOverallValue: PropTypes.bool, + multiLayerL1FeeTotal: PropTypes.string, }; const mapStateToProps = (state) => ({ diff --git a/app/components/UI/Swaps/utils/index.js b/app/components/UI/Swaps/utils/index.js index 264e21b1265..83a466b74df 100644 --- a/app/components/UI/Swaps/utils/index.js +++ b/app/components/UI/Swaps/utils/index.js @@ -10,6 +10,8 @@ const { SWAPS_TESTNET_CHAIN_ID, POLYGON_CHAIN_ID, AVALANCHE_CHAIN_ID, + ARBITRUM_CHAIN_ID, + OPTIMISM_CHAIN_ID, } = swapsUtils; const allowedChainIds = [ @@ -17,6 +19,8 @@ const allowedChainIds = [ BSC_CHAIN_ID, POLYGON_CHAIN_ID, AVALANCHE_CHAIN_ID, + ARBITRUM_CHAIN_ID, + OPTIMISM_CHAIN_ID, SWAPS_TESTNET_CHAIN_ID, ]; diff --git a/app/components/UI/TypedSign/index.js b/app/components/UI/TypedSign/index.js index 2ce5aa4c9eb..6678033fb33 100644 --- a/app/components/UI/TypedSign/index.js +++ b/app/components/UI/TypedSign/index.js @@ -114,9 +114,17 @@ class TypedSign extends PureComponent { ); }; + walletConnectNotificationTitle = (confirmation, isError) => { + if (isError) return strings('notifications.wc_signed_failed_title'); + return confirmation + ? strings('notifications.wc_signed_title') + : strings('notifications.wc_signed_rejected_title'); + }; + showWalletConnectNotification = ( messageParams = {}, confirmation = false, + isError = false, ) => { InteractionManager.runAfterInteractions(() => { messageParams.origin && @@ -125,9 +133,7 @@ class TypedSign extends PureComponent { NotificationManager.showSimpleNotification({ status: `simple_notification${!confirmation ? '_rejected' : ''}`, duration: 5000, - title: confirmation - ? strings('notifications.wc_signed_title') - : strings('notifications.wc_signed_rejected_title'), + title: this.walletConnectNotificationTitle(confirmation, isError), description: strings('notifications.wc_description'), }); }); @@ -138,15 +144,22 @@ class TypedSign extends PureComponent { const { KeyringController, TypedMessageManager } = Engine.context; const messageId = messageParams.metamaskId; const version = messageParams.version; - const cleanMessageParams = await TypedMessageManager.approveMessage( - messageParams, - ); - const rawSig = await KeyringController.signTypedMessage( - cleanMessageParams, - version, - ); - TypedMessageManager.setMessageStatusSigned(messageId, rawSig); - this.showWalletConnectNotification(messageParams, true); + let rawSig; + let cleanMessageParams; + try { + cleanMessageParams = await TypedMessageManager.approveMessage( + messageParams, + ); + rawSig = await KeyringController.signTypedMessage( + cleanMessageParams, + version, + ); + TypedMessageManager.setMessageStatusSigned(messageId, rawSig); + this.showWalletConnectNotification(messageParams, true); + } catch (error) { + TypedMessageManager.setMessageStatusSigned(messageId, error.message); + this.showWalletConnectNotification(messageParams, false, true); + } }; rejectMessage = () => { diff --git a/app/constants/network.js b/app/constants/network.js index c10eee256e8..766d30305e7 100644 --- a/app/constants/network.js +++ b/app/constants/network.js @@ -7,3 +7,19 @@ export const GOERLI = 'goerli'; export const RPC = 'rpc'; export const NO_RPC_BLOCK_EXPLORER = 'NO_BLOCK_EXPLORER'; export const PRIVATENETWORK = 'PRIVATENETWORK'; + +/** + * @enum {string} + */ +export const NETWORKS_CHAIN_ID = { + MAINNET: '1', + OPTIMISM: '10', + KOVAN: '42', + BSC: '56', + POLYGON: '137', + FANTOM: '250', + ARBITRUM: '42161', + AVAXCCHAIN: '43114', + CELO: '42220', + HARMONY: '1666600000', +}; diff --git a/app/constants/on-ramp.js b/app/constants/on-ramp.js index 19162504dbf..86228110a44 100644 --- a/app/constants/on-ramp.js +++ b/app/constants/on-ramp.js @@ -1,3 +1,5 @@ +import { NETWORKS_CHAIN_ID } from './network'; + /** * @enum {string} */ @@ -37,23 +39,6 @@ export const PAYMENT_CATEGORY = { MULTIPLE: 'Multiple Options', }; -/** - * @enum {string} - */ - -export const NETWORKS_CHAIN_ID = { - MAINNET: '1', - OPTIMISM: '10', - KOVAN: '42', - BSC: '56', - POLYGON: '137', - FANTOM: '250', - ARBITRUM: '42161', - AVAXCCHAIN: '43114', - CELO: '42220', - HARMONY: '1666600000', -}; - export const NETWORKS_NAMES = { [NETWORKS_CHAIN_ID.MAINNET]: 'Ethereum', [NETWORKS_CHAIN_ID.OPTIMISM]: 'Optimism', diff --git a/app/core/AppConstants.js b/app/core/AppConstants.js index 02c92e486e0..9e68ffed882 100644 --- a/app/core/AppConstants.js +++ b/app/core/AppConstants.js @@ -143,4 +143,5 @@ export default { }, CANCEL_RATE: 'Transactions (Cancel)', SPEED_UP_RATE: 'Transactions (Speed Up)', + ETH_SIGN_ERROR: 'eth_sign requires 32 byte message hash', }; diff --git a/app/core/Engine.js b/app/core/Engine.js index 96c29b1e610..c80a839a99a 100644 --- a/app/core/Engine.js +++ b/app/core/Engine.js @@ -385,6 +385,8 @@ class Engine { swapsUtils.SWAPS_TESTNET_CHAIN_ID, swapsUtils.POLYGON_CHAIN_ID, swapsUtils.AVALANCHE_CHAIN_ID, + swapsUtils.ARBITRUM_CHAIN_ID, + swapsUtils.OPTIMISM_CHAIN_ID, ], }, ), diff --git a/app/core/RPCMethods/RPCMethodMiddleware.ts b/app/core/RPCMethods/RPCMethodMiddleware.ts index 6afa2f1cb05..29d3eaf5c75 100644 --- a/app/core/RPCMethods/RPCMethodMiddleware.ts +++ b/app/core/RPCMethods/RPCMethodMiddleware.ts @@ -329,9 +329,8 @@ export const getRpcMethodMiddleware = ({ res.result = rawSig; } else { - throw ethErrors.rpc.invalidParams( - 'eth_sign requires 32 byte message hash', - ); + res.result = AppConstants.ETH_SIGN_ERROR; + throw ethErrors.rpc.invalidParams(AppConstants.ETH_SIGN_ERROR); } }, diff --git a/app/core/RemoteConnection/ECIES.ts b/app/core/RemoteConnection/ECIES.ts new file mode 100644 index 00000000000..95e45d77120 --- /dev/null +++ b/app/core/RemoteConnection/ECIES.ts @@ -0,0 +1,71 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +// eslint-disable-next-line import/no-nodejs-modules +import { Buffer } from 'buffer'; +import { encrypt, decrypt, PrivateKey } from 'eciesjs'; + +/** + * Class that exposes methods to generate and compute + * Elliptic Curve Integrated Encryption Scheme (ECIES) for key exchange and symmetric encryption/decryption + * + * It also exposes encryption/decryption methods that are used + * by the communication layer to encrypt/decrypt in/out data + * The encryption/decryption is made using a symmetric key generated from the ECIES key exchange + */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export default class ECIES { + ecies: any; + + constructor() { + this.ecies = null; // ECIES instance + } + + /** + * Creates ECIES instance + * + * @returns - Generates ECIES instance + */ + generateECIES(): void { + this.ecies = new PrivateKey(); + } + + /** + * Returns ECIES instance public key + * + * @returns - public key in base64 format + */ + getPublicKey(): string { + return this.ecies.publicKey.toHex(); + } + + /** + * Encrypts a data message using the public key of the side to encrypt data for + * + * @param {string} data - data string to be encrypted + * @param {string} otherPublicKey - public key of the side to encrypt data for + * @returns - encrypted string in base64 + */ + encrypt(data: string, otherPublicKey: string): string { + const encryptedData = encrypt(otherPublicKey, Buffer.from(data)); + + return Buffer.from(encryptedData).toString('base64'); + } + + /** + * Decrypts a data message using the instance private key + * + * @param {string} encryptedData - base64 data string to be decrypted + * @returns - decrypted data || error message + */ + decrypt(encryptedData: string): string { + const payload = Buffer.from(encryptedData, 'base64'); + + try { + const decrypted = decrypt(this.ecies.toHex(), payload); + + return decrypted.toString(); + } catch (error: any) { + return error.message; + } + } +} diff --git a/app/core/RemoteConnection/KeyExchange.ts b/app/core/RemoteConnection/KeyExchange.ts new file mode 100644 index 00000000000..50016e19ad7 --- /dev/null +++ b/app/core/RemoteConnection/KeyExchange.ts @@ -0,0 +1,115 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +import { EventEmitter2 } from 'eventemitter2'; +import ECIES from './ECIES'; + +enum KeySteps { + NONE = 'none', + SYN = 'key_handshake_SYN', + SYNACK = 'key_handshake_SYNACK', + ACK = 'key_handshake_ACK', +} + +export default class KeyExchange extends EventEmitter2 { + keysExchanged = false; + + myECIES = null; + + otherPublicKey = ''; + + CommLayer: any; + + myPublicKey: any; + + sendPublicKey: any; + + step: string = KeySteps.NONE; + + constructor({ CommLayer, otherPublicKey, sendPublicKey }) { + super(); + + this.myECIES = new ECIES(); + this.myECIES.generateECIES(); + this.CommLayer = CommLayer; + this.myPublicKey = this.myECIES.getPublicKey(); + + if (otherPublicKey) { + this.onOtherPublicKey(otherPublicKey); + } + this.sendPublicKey = sendPublicKey; + + this.CommLayer.on('key_exchange', ({ message }) => { + if (this.keysExchanged) { + return; + } + + if (message.type === KeySteps.SYN) { + this.checkStep(KeySteps.NONE); + this.step = KeySteps.ACK; + + if (this.sendPublicKey && message.pubkey && !this.otherPublicKey) { + this.onOtherPublicKey(message.pubkey); + } + + this.CommLayer.sendMessage({ + type: KeySteps.SYNACK, + pubkey: this.myPublicKey, + }); + } else if (message.type === KeySteps.SYNACK) { + this.checkStep(KeySteps.SYNACK); + + this.onOtherPublicKey(message.pubkey); + + this.CommLayer.sendMessage({ type: KeySteps.ACK }); + this.keysExchanged = true; + this.emit('keys_exchanged'); + } else if (message.type === KeySteps.ACK) { + this.checkStep(KeySteps.ACK); + this.keysExchanged = true; + this.emit('keys_exchanged'); + } + }); + } + + clean(): void { + this.step = KeySteps.NONE; + this.keysExchanged = false; + this.otherPublicKey = ''; + } + + start(isOriginator: boolean): void { + if (isOriginator) { + this.clean(); + } + this.checkStep(KeySteps.NONE); + this.step = KeySteps.SYNACK; + this.CommLayer.sendMessage({ + type: KeySteps.SYN, + pubkey: this.sendPublicKey ? this.myPublicKey : undefined, + }); + } + + checkStep(step: string): void { + if (this.step !== step) { + throw new Error(`Wrong Step ${this.step} ${step}`); + } + } + + onOtherPublicKey(pubkey: string): void { + this.otherPublicKey = pubkey; + } + + encryptMessage(message: string): string { + if (!this.otherPublicKey) { + throw new Error('Keys not exchanged'); + } + return this.myECIES.encrypt(message, this.otherPublicKey); + } + + decryptMessage(message: string): string { + if (!this.otherPublicKey) { + throw new Error('Keys not exchanged'); + } + return this.myECIES.decrypt(message); + } +} diff --git a/app/core/RemoteConnection/Socket.ts b/app/core/RemoteConnection/Socket.ts new file mode 100644 index 00000000000..bc89eaa6f43 --- /dev/null +++ b/app/core/RemoteConnection/Socket.ts @@ -0,0 +1,200 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +import { EventEmitter2 } from 'eventemitter2'; +import io from 'socket.io-client'; +import { v4 as uuidv4 } from 'uuid'; +import KeyExchange from './KeyExchange'; +import { CommunicationLayerPreference } from '.'; + +export default class Socket extends EventEmitter2 { + socket = null; + + clientsConnected = false; + + clientsReady = false; + + isOriginator; + + channelId = null; + + keyExchange: KeyExchange; + + manualDisconnect = false; + + reconnect: boolean; + + commLayer: CommunicationLayerPreference; + + constructor({ otherPublicKey, reconnect, commLayer, transports }) { + super(); + + this.reconnect = reconnect; + this.commLayer = commLayer; + + const options: { transports?: any; autoConnect: boolean } = { + autoConnect: false, + }; + + if (transports) { + options.transports = transports; + } + + this.socket = io( + 'https://metamask-sdk-socket.metafi.codefi.network/', + options, + ); + + const keyExchangeInitParameter = { + CommLayer: this, + otherPublicKey, + sendPublicKey: false, + }; + + this.keyExchange = new KeyExchange(keyExchangeInitParameter); + + this.keyExchange.on('keys_exchanged', () => { + this.emit('clients_ready', { + isOriginator: this.isOriginator, + }); + }); + } + + receiveMessages(channelId) { + this.socket.on(`clients_connected-${channelId}`, (id) => { + this.channelId = id; + this.clientsConnected = true; + if (this.isOriginator) { + if (!this.keyExchange.keysExchanged) { + this.keyExchange.start(this.isOriginator); + } + } + + if (this.reconnect) { + if (this.keyExchange.keysExchanged) { + this.sendMessage({ type: 'ready' }); + if (this.commLayer === CommunicationLayerPreference.WEBRTC) { + this.emit('clients_ready', { + isOriginator: this.isOriginator, + }); + } + } else if (!this.isOriginator) { + this.sendMessage({ type: 'key_handshake_start' }); + } + this.reconnect = false; + } + }); + + this.socket.on(`channel_created-${channelId}`, (id) => { + this.emit('channel_created', id); + }); + + this.socket.on(`clients_disconnected-${channelId}`, () => { + this.clientsConnected = false; + this.emit('clients_disconnected'); + }); + + this.socket.on(`message-${channelId}`, ({ id, message, error }) => { + if (error) { + throw new Error(error); + } + + this.checkSameId(id); + + if ( + this.isOriginator && + this.keyExchange.keysExchanged && + message?.type === 'key_handshake_start' + ) { + return this.keyExchange.start(this.isOriginator); + } + + if (!this.keyExchange.keysExchanged) { + const messageReceived = message; + if (messageReceived?.type.startsWith('key_handshake')) { + return this.emit('key_exchange', { message: messageReceived }); + } + throw new Error('Keys not exchanged'); + } + + const decryptedMessage = this.keyExchange.decryptMessage(message); + const messageReceived = JSON.parse(decryptedMessage); + return this.emit('message', { message: messageReceived }); + }); + + this.socket.on( + `clients_waiting_to_join-${channelId}`, + (numberUsers: number) => { + this.emit('clients_waiting_to_join', numberUsers); + }, + ); + } + + checkSameId(id) { + if (id !== this.channelId) { + throw new Error('Wrong id'); + } + } + + send(type, message) { + this.socket.emit(type, message); + } + + sendMessage(message) { + if (!this.channelId) { + throw new Error('Create a channel first'); + } + + if (!this.keyExchange.keysExchanged) { + if (message?.type.startsWith('key_handshake')) { + return this.socket.emit('message', { id: this.channelId, message }); + } + throw new Error('Keys not exchanged'); + } + + const encryptedMessage = this.keyExchange.encryptMessage( + JSON.stringify(message), + ); + + return this.socket.emit('message', { + id: this.channelId, + message: encryptedMessage, + }); + } + + connectToChannel(id) { + this.socket.connect(); + this.channelId = id; + this.receiveMessages(this.channelId); + this.socket.emit('join_channel', id); + } + + createChannel() { + this.socket.connect(); + this.isOriginator = true; + const channelId = uuidv4(); + this.receiveMessages(channelId); + this.socket.emit('join_channel', channelId); + return { channelId, pubKey: this.keyExchange.myPublicKey }; + } + + pause() { + this.manualDisconnect = true; + if (this.keyExchange.keysExchanged) { + this.sendMessage({ type: 'pause' }); + } + this.socket.disconnect(); + } + + resume() { + this.manualDisconnect = false; + if (this.keyExchange.keysExchanged) { + this.reconnect = true; + this.socket.connect(); + this.socket.emit('join_channel', this.channelId); + } + } + + disconnect() { + this.socket.disconnect(); + } +} diff --git a/app/core/RemoteConnection/WebRTC.ts b/app/core/RemoteConnection/WebRTC.ts new file mode 100644 index 00000000000..dc3fba7eb2b --- /dev/null +++ b/app/core/RemoteConnection/WebRTC.ts @@ -0,0 +1,254 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +/* eslint-disable no-console */ +import { EventEmitter2 } from 'eventemitter2'; +import Socket from './Socket'; +import KeyExchange from './KeyExchange'; + +export default class WebRTC extends EventEmitter2 { + handshakeDone = false; + + isOriginator = false; + + clientsConnected = false; + + clientsReady = false; + + socket = null; + + webrtc = null; + + dataChannel = null; + + keyExchange: KeyExchange; + + RTCPeerConnection: any; + + RTCSessionDescription: any; + + RTCIceCandidate: any; + + reconnect: boolean; + + constructor({ otherPublicKey, webRTCLib, commLayer, reconnect, transports }) { + super(); + this.reconnect = reconnect; + if (webRTCLib) { + this.RTCPeerConnection = webRTCLib.RTCPeerConnection; + this.RTCSessionDescription = webRTCLib.RTCSessionDescription; + this.RTCIceCandidate = webRTCLib.RTCIceCandidate; + } else { + this.RTCPeerConnection = RTCPeerConnection; + this.RTCSessionDescription = RTCSessionDescription; + this.RTCIceCandidate = RTCIceCandidate; + } + + this.socket = new Socket({ + otherPublicKey, + commLayer, + reconnect, + transports, + }); + + const keyExchangeInitParameter = { + CommLayer: this, + otherPublicKey: null, + sendPublicKey: true, + }; + + this.keyExchange = new KeyExchange(keyExchangeInitParameter); + + this.keyExchange.on('keys_exchanged', () => { + this.clientsReady = true; + this.emit('clients_ready', { + isOriginator: this.isOriginator, + }); + }); + + this.socket.on('clients_disconnected', () => { + if (!this.clientsConnected) { + this.socket.removeAllListeners(); + return this.emit('clients_disconnected'); + } + return this.clientsConnected; + }); + + this.socket.on('message', async ({ message }) => { + const { offer, answer, candidate, type } = message; + if (type === 'offer') { + await this.webrtc.setRemoteDescription( + new this.RTCSessionDescription(offer), + ); + + const answerLocal = await this.webrtc.createAnswer(); + await this.webrtc.setLocalDescription(answerLocal); + + this.socket.sendMessage({ type: 'answer', answer: answerLocal }); + } else if (type === 'answer') { + await this.webrtc.setRemoteDescription( + new this.RTCSessionDescription(answer), + ); + + this.handshakeDone = true; + } else if (type === 'candidate') { + this.webrtc.addIceCandidate(new this.RTCIceCandidate(candidate)); + } + }); + + this.socket.on('clients_ready', async ({ isOriginator }) => { + this.setupWebrtc(); + if (!isOriginator) { + return; + } + const offer = await this.webrtc.createOffer(); + + await this.webrtc.setLocalDescription(offer); + + this.isOriginator = isOriginator; + this.socket.sendMessage({ type: 'offer', offer }); + }); + + this.socket.on('channel_created', (id) => { + this.emit('channel_created', id); + }); + + this.socket.on('clients_waiting_to_join', (numberUsers) => { + this.emit('clients_waiting_to_join', numberUsers); + }); + } + + setupWebrtc() { + const configuration = { + iceServers: [{ urls: 'stun:15.237.115.65' }], + }; + + this.webrtc = new this.RTCPeerConnection(configuration); + + this.webrtc.ondatachannel = (evt) => { + console.log('Data channel is created!'); + const receiveChannel = evt.channel; + receiveChannel.onopen = () => { + console.log('Data channel is open and ready to be used.'); + this.clientsConnected = true; + + if (this.isOriginator) { + if (!this.keyExchange.keysExchanged) { + this.keyExchange.start(this.isOriginator); + } + } + + if (this.reconnect) { + if (this.keyExchange.keysExchanged) { + this.sendMessage({ type: 'ready' }); + this.emit('clients_ready', { + isOriginator: this.isOriginator, + }); + } else if (!this.isOriginator) { + this.sendMessage({ type: 'key_handshake_start' }); + } + this.reconnect = false; + } + }; + + this.onMessage = this.onMessage.bind(this); + + receiveChannel.onmessage = this.onMessage; + }; + + this.webrtc.onconnectionstatechange = () => { + const connectionStatus = this.webrtc.connectionState; + console.log('connectionStatus', connectionStatus); + if (['disconnected', 'failed', 'closed'].includes(connectionStatus)) { + return this.emit('clients_disconnected'); + } + + return connectionStatus; + }; + + this.webrtc.onicecandidate = ({ candidate }) => { + if (candidate) { + this.socket.sendMessage({ + type: 'candidate', + candidate, + }); + } + }; + + this.webrtc.onicecandidateerror = (error) => + console.log('ICE ERROR', error); + + this.dataChannel = this.webrtc.createDataChannel('messenger'); + + this.dataChannel.onerror = (error) => { + if (error.error.code === 0) { + return this.emit('clients_disconnected'); + } + console.log('ERROR: datachannel', error); + return error; + }; + } + + connectToChannel(id) { + this.socket.connectToChannel(id); + } + + onMessage(message) { + /* if (!message.isTrusted) { + throw new Error('Message not trusted'); + }*/ + + if (!this.keyExchange.keysExchanged) { + const messageReceived = JSON.parse(message.data); + if (messageReceived?.type.startsWith('key_handshake')) { + return this.emit('key_exchange', { message: messageReceived }); + } + throw new Error('Keys not exchanged'); + } + + const decryptedMessage = this.keyExchange.decryptMessage(message.data); + const messageReceived = JSON.parse(decryptedMessage); + return this.emit('message', { message: messageReceived }); + } + + sendMessage(message) { + if (!this.clientsConnected) { + throw new Error('Clients not connected'); + } + + if (!this.keyExchange.keysExchanged) { + if (message?.type.startsWith('key_handshake')) { + return this.dataChannel.send(JSON.stringify(message)); + } + throw new Error('Keys not exchanged'); + } + const encryptedMessage = this.keyExchange.encryptMessage( + JSON.stringify(message), + ); + + return this.dataChannel.send(encryptedMessage); + } + + createChannel() { + return this.socket.createChannel(); + } + + pause() { + if (this.keyExchange.keysExchanged) { + this.sendMessage({ type: 'pause' }); + } + this.webrtc?.close(); + // this.removeAllListeners(); + this.socket.pause(); + // this.socket.removeAllListeners(); + } + + resume() { + this.reconnect = true; + this.socket.resume(); + } + + disconnect() { + this.socket.disconnect(); + this.webrtc?.close(); + } +} diff --git a/app/core/RemoteConnection/index.ts b/app/core/RemoteConnection/index.ts new file mode 100644 index 00000000000..fe45d355116 --- /dev/null +++ b/app/core/RemoteConnection/index.ts @@ -0,0 +1,247 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck +import { EventEmitter2 } from 'eventemitter2'; +import { validate } from 'uuid'; +import Socket from './Socket'; +import WebRTC from './WebRTC'; + +export interface DappMetadata { + url: string; + name: string; +} + +interface RemoteCommunicationOptions { + platform: string; + commLayer: string; + otherPublicKey?: string; + webRTCLib?: any; + reconnect?: any; + dappMetadata?: DappMetadata; + transports?: string[]; +} + +export enum CommunicationLayerPreference { + WEBRTC = 'webrtc', + SOCKET = 'socket', + WALLETCONNECT = 'wc', +} + +export default class RemoteCommunication extends EventEmitter2 { + commLayer = null; + + channelId = null; + + connected = false; + + isOriginator: boolean; + + originatorInfo: any; + + walletInfo: any; + + paused: boolean; + + CommLayer: typeof WebRTC | typeof Socket; + + otherPublicKey: string; + + webRTCLib: any; + + dappMetadata: DappMetadata; + + transports: string[]; + + platform: string; + + constructor({ + platform, + commLayer = 'socket', + otherPublicKey, + webRTCLib, + reconnect, + dappMetadata, + transports, + }: RemoteCommunicationOptions) { + super(); + + const CommLayer = + commLayer === CommunicationLayerPreference.WEBRTC ? WebRTC : Socket; + + this.CommLayer = CommLayer; + this.otherPublicKey = otherPublicKey; + this.webRTCLib = webRTCLib; + this.dappMetadata = dappMetadata; + this.transports = transports; + this.platform = platform; + + this.setupCommLayer({ + CommLayer, + otherPublicKey, + webRTCLib, + commLayer, + reconnect, + }); + } + + setupCommLayer({ + CommLayer, + otherPublicKey, + webRTCLib, + commLayer, + reconnect, + }) { + this.commLayer = new CommLayer({ + otherPublicKey, + webRTCLib, + commLayer, + reconnect, + transports: this.transports, + }); + + this.commLayer.on('message', ({ message }) => { + this.onMessageCommLayer(message); + }); + + this.commLayer.on('clients_ready', ({ isOriginator }) => { + this.isOriginator = isOriginator; + + if (!isOriginator) { + return; + } + + let url = + (typeof document !== 'undefined' && document.URL) || 'url undefined'; + let title = + (typeof document !== 'undefined' && document.title) || + 'title undefined'; + + if (this.dappMetadata?.url) { + url = this.dappMetadata.url; + } + + if (this.dappMetadata?.name) { + title = this.dappMetadata.name; + } + + this.commLayer.sendMessage({ + type: 'originator_info', + originatorInfo: { url, title, platform: this.platform }, + }); + }); + + this.commLayer.on('clients_disconnected', () => { + if (this.paused) { + return; + } + + if (!this.isOriginator) { + this.paused = true; + return; + } + + this.clean(); + this.commLayer.removeAllListeners(); + this.setupCommLayer({ + CommLayer, + otherPublicKey, + webRTCLib, + commLayer: this.commLayer, + reconnect: false, + }); + this.emit('clients_disconnected'); + }); + + this.commLayer.on('channel_created', (id) => { + this.emit('channel_created', id); + }); + + this.commLayer.on('clients_waiting_to_join', (numberUsers) => { + this.emit('clients_waiting_to_join', numberUsers); + }); + } + + clean() { + this.channelId = null; + this.connected = false; + } + + connectToChannel(id) { + if (!validate(id)) { + throw new Error('Invalid channel'); + } + this.commLayer.connectToChannel(id); + } + + sendMessage(message) { + if (this.paused) { + this.once('clients_ready', () => { + this.commLayer.sendMessage(message); + }); + } else { + this.commLayer.sendMessage(message); + } + } + + onMessageCommLayer(message) { + if (message.type === 'originator_info') { + this.commLayer.sendMessage({ + type: 'wallet_info', + walletInfo: { + type: 'MetaMask', + version: 'MetaMask/Mobile', + }, + }); + this.originatorInfo = message.originatorInfo; + this.connected = true; + this.emit('clients_ready', { + isOriginator: this.isOriginator, + originatorInfo: message.originatorInfo, + }); + this.paused = false; + return; + } else if (message.type === 'wallet_info') { + this.walletInfo = message.walletInfo; + this.connected = true; + this.emit('clients_ready', { + isOriginator: this.isOriginator, + walletInfo: message.walletInfo, + }); + this.paused = false; + return; + } else if (message.type === 'pause') { + this.paused = true; + } else if (message.type === 'ready') { + this.paused = false; + this.emit('clients_ready', { + isOriginator: this.isOriginator, + walletInfo: this.walletInfo, + }); + } + + this.emit('message', { message }); + } + + generateChannelId() { + if (this.connected) { + throw new Error('Channel already created'); + } + + this.clean(); + + const { channelId, pubKey } = this.commLayer.createChannel(); + this.channelId = channelId; + return { channelId, pubKey }; + } + + pause() { + this.commLayer.pause(); + } + + resume() { + this.commLayer.resume(); + } + + disconnect() { + this.commLayer.disconnect(); + } +} diff --git a/app/core/SDKConnect.ts b/app/core/SDKConnect.ts index 9068c385563..354aa506f44 100644 --- a/app/core/SDKConnect.ts +++ b/app/core/SDKConnect.ts @@ -1,7 +1,7 @@ import BackgroundBridge from './BackgroundBridge/BackgroundBridge'; import RemoteCommunication, { CommunicationLayerPreference, -} from '@metamask/sdk-communication-layer'; +} from './RemoteConnection'; import getRpcMethodMiddleware from './RPCMethods/RPCMethodMiddleware'; import AppConstants from './AppConstants'; import Minimizer from 'react-native-minimizer'; diff --git a/app/util/address/index.js b/app/util/address/index.js index a515aa0a008..8d324424a3e 100644 --- a/app/util/address/index.js +++ b/app/util/address/index.js @@ -4,6 +4,7 @@ import { isHexString, addHexPrefix, isValidChecksumAddress, + isHexPrefixed, } from 'ethereumjs-util'; import URL from 'url-parse'; import punycode from 'punycode/punycode'; @@ -424,3 +425,15 @@ export function isValidAddressInputViaQRCode(input) { } return isValidHexAddress(input); } + +/** Removes hex prefix from a string if it's there. + * + * @param {string} str + * @returns {string} + */ +export const stripHexPrefix = (str) => { + if (typeof str !== 'string') { + return str; + } + return isHexPrefixed(str) ? str.slice(2) : str; +}; diff --git a/app/util/address/index.test.ts b/app/util/address/index.test.ts index 977d115ec71..dbf31fa0222 100644 --- a/app/util/address/index.test.ts +++ b/app/util/address/index.test.ts @@ -4,6 +4,7 @@ import { formatAddress, isValidHexAddress, isValidAddressInputViaQRCode, + stripHexPrefix, } from '.'; describe('isENS', () => { @@ -122,3 +123,18 @@ describe('isValidAddressInputViaQRCode', () => { expect(isValidAddressInputViaQRCode(mockInput)).toBe(false); }); }); + +describe('stripHexPrefix', () => { + const str = + '0x4cfd3e90fc78b0f86bf7524722150bb8da9c60cd532564d7ff43f5716514f553'; + const stripped = + '4cfd3e90fc78b0f86bf7524722150bb8da9c60cd532564d7ff43f5716514f553'; + + it('returns a string without a hex prefix', () => { + expect(stripHexPrefix(str)).toBe(stripped); + }); + + it('returns the same string since there is no hex prefix', () => { + expect(stripHexPrefix(stripped)).toBe(stripped); + }); +}); diff --git a/app/util/networks/customNetworks.tsx b/app/util/networks/customNetworks.tsx index ac853d8b1d7..5a81f9f90a9 100644 --- a/app/util/networks/customNetworks.tsx +++ b/app/util/networks/customNetworks.tsx @@ -19,7 +19,7 @@ const PopularList = [ rpcUrl: `https://arbitrum-mainnet.infura.io/v3/${infuraProjectId}`, ticker: 'ETH', rpcPrefs: { - blockExplorerUrl: 'https://explorer.arbitrum.io', + blockExplorerUrl: 'https://arbiscan.io', imageUrl: 'AETH', }, }, diff --git a/app/util/networks/index.js b/app/util/networks/index.js index 2b871c47e7b..30a7c5d8c4c 100644 --- a/app/util/networks/index.js +++ b/app/util/networks/index.js @@ -1,4 +1,10 @@ import URL from 'url-parse'; +import { utils } from 'ethers'; +import EthContract from 'ethjs-contract'; +import { getContractFactory } from '@eth-optimism/contracts/dist/contract-defs'; +import { predeploys } from '@eth-optimism/contracts/dist/predeploys'; +import { util } from '@metamask/controllers'; + import AppConstants from '../../core/AppConstants'; import { MAINNET, @@ -7,13 +13,13 @@ import { RINKEBY, GOERLI, RPC, + NETWORKS_CHAIN_ID, } from '../../../app/constants/network'; import { NetworkSwitchErrorType } from '../../../app/constants/error'; -import { util } from '@metamask/controllers'; import Engine from '../../core/Engine'; import { toLowerCaseEquals } from './../general'; import { fastSplit } from '../../util/number'; - +import { buildUnserializedTransaction } from '../../util/transactions/optimismTransaction'; import handleNetworkSwitch from './handleNetworkSwitch'; export { handleNetworkSwitch }; @@ -99,6 +105,9 @@ export const getDecimalChainId = (chainId) => { export const isMainnetByChainId = (chainId) => getDecimalChainId(String(chainId)) === String(1); +export const isMultiLayerFeeNetwork = (chainId) => + chainId === NETWORKS_CHAIN_ID.OPTIMISM; + export const getNetworkName = (id) => NetworkListKeys.find((key) => NetworkList[key].networkId === Number(id)); @@ -271,3 +280,30 @@ export function blockTagParamIndex(payload) { return undefined; } } + +// The code in this file is largely drawn from https://community.optimism.io/docs/developers/l2/new-fees.html#for-frontend-and-wallet-developers +const buildOVMGasPriceOracleContract = (eth) => { + const OVMGasPriceOracle = getContractFactory('OVM_GasPriceOracle').attach( + predeploys.OVM_GasPriceOracle, + ); + const abi = JSON.parse( + OVMGasPriceOracle.interface.format(utils.FormatTypes.json), + ); + const contract = new EthContract(eth); + return contract(abi).at(OVMGasPriceOracle.address); +}; + +/** + * It returns an estimated L1 fee for the Optimism network. + * + * @param {Object} eth + * @param {Object} txMeta + * @returns {String} + */ +export const fetchEstimatedL1FeeOptimism = async (eth, txMeta) => { + const contract = buildOVMGasPriceOracleContract(eth); + const serializedTransaction = + buildUnserializedTransaction(txMeta).serialize(); + const result = await contract.getL1Fee(serializedTransaction); + return result?.[0]?.toString(16); +}; diff --git a/app/util/number/index.js b/app/util/number/index.js index 878656d9304..c4023257b10 100644 --- a/app/util/number/index.js +++ b/app/util/number/index.js @@ -6,9 +6,10 @@ import { utils as ethersUtils } from 'ethers'; import convert from 'ethjs-unit'; import { util } from '@metamask/controllers'; import numberToBN from 'number-to-bn'; -import currencySymbols from '../currency-symbols.json'; import BigNumber from 'bignumber.js'; +import currencySymbols from '../currency-symbols.json'; + // Big Number Constants const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber('1000000000000000000'); const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber('1000000000'); @@ -741,3 +742,21 @@ export const toHexadecimal = (decimal) => { if (decimal.startsWith('0x')) return decimal; return toBigNumber.dec(decimal).toString(16); }; + +export const calculateEthFeeForMultiLayer = ({ + multiLayerL1FeeTotal, + ethFee = 0, +}) => { + if (!multiLayerL1FeeTotal) { + return ethFee; + } + const multiLayerL1FeeTotalDecEth = conversionUtil(multiLayerL1FeeTotal, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + fromDenomination: 'WEI', + toDenomination: 'ETH', + }); + return new BigNumber(multiLayerL1FeeTotalDecEth) + .plus(new BigNumber(ethFee ?? 0)) + .toString(10); +}; diff --git a/app/util/number/index.test.ts b/app/util/number/index.test.ts index fdf07bc3d81..95bcc29e700 100644 --- a/app/util/number/index.test.ts +++ b/app/util/number/index.test.ts @@ -24,6 +24,7 @@ import { safeNumberToBN, fastSplit, isNumber, + calculateEthFeeForMultiLayer, } from '.'; describe('Number utils :: BNToHex', () => { @@ -664,3 +665,23 @@ describe('Number utils :: isNumber', () => { expect(isNumber(null)).toBe(false); }); }); + +describe('Number utils :: calculateEthFeeForMultiLayer', () => { + it('returns ethFee if multiLayerL1FeeTotal is falsy', () => { + expect( + calculateEthFeeForMultiLayer({ + multiLayerL1FeeTotal: undefined, + ethFee: 0.000001, + }), + ).toBe(0.000001); + }); + + it('returns a new ETH fee which includes a multiLayerL1FeeTotal fee', () => { + expect( + calculateEthFeeForMultiLayer({ + multiLayerL1FeeTotal: 'ce37bdd0b8b8', + ethFee: 0.000001, + }), + ).toBe('0.000227739'); + }); +}); diff --git a/app/util/test/testSetup.js b/app/util/test/testSetup.js index 5fc7aa22b3c..3ab92086595 100644 --- a/app/util/test/testSetup.js +++ b/app/util/test/testSetup.js @@ -126,6 +126,19 @@ jest.mock( ); jest.mock('@react-native-cookies/cookies', () => 'RNCookies'); +const mockReactNativeWebRTC = { + RTCPeerConnection: () => null, + RTCIceCandidate: () => null, + RTCSessionDescription: () => null, + RTCView: () => null, + MediaStream: () => null, + MediaStreamTrack: () => null, + mediaDevices: () => null, + registerGlobals: () => null, +}; + +jest.mock('react-native-webrtc', () => mockReactNativeWebRTC); + NativeModules.RNGestureHandlerModule = { attachGestureHandler: jest.fn(), createGestureHandler: jest.fn(), diff --git a/app/util/transactions/index.test.ts b/app/util/transactions/index.test.ts index fcf1f6ab29e..3b0ebeba8d6 100644 --- a/app/util/transactions/index.test.ts +++ b/app/util/transactions/index.test.ts @@ -1,9 +1,10 @@ import { swapsUtils } from '@metamask/swaps-controller'; import { util } from '@metamask/controllers'; +import { BN } from 'ethereumjs-util'; + import { BNToHex } from '../number'; import { UINT256_BN_MAX_VALUE } from '../../constants/transaction'; import { NEGATIVE_TOKEN_DECIMALS } from '../../constants/error'; - import { generateTransferData, decodeApproveData, @@ -16,6 +17,7 @@ import { CONTRACT_METHOD_DEPLOY, TOKEN_METHOD_TRANSFER_FROM, } from '.'; +import { buildUnserializedTransaction } from './optimismTransaction'; import Engine from '../../core/Engine'; import { strings } from '../../../locales/i18n'; @@ -379,3 +381,28 @@ describe('Transactions utils :: minimumTokenAllowance', () => { }).toThrow(NEGATIVE_TOKEN_DECIMALS); }); }); + +describe('Transactions utils :: buildUnserializedTransaction', () => { + it('returns a transaction that can be serialized and fed to an Optimism smart contract', () => { + const unserializedTransaction = buildUnserializedTransaction({ + txParams: { + nonce: '0x0', + gasPrice: `0x${new BN('100').toString(16)}`, + gas: `0x${new BN('21000').toString(16)}`, + to: '0x0000000000000000000000000000000000000000', + value: `0x${new BN('10000000000000').toString(16)}`, + data: '0x0', + }, + chainId: '10', + metamaskNetworkId: '10', + }); + expect(unserializedTransaction.toJSON()).toMatchObject({ + nonce: '0x0', + gasPrice: '0x64', + gasLimit: '0x5208', + to: '0x0000000000000000000000000000000000000000', + value: '0x9184e72a000', + data: '0x00', + }); + }); +}); diff --git a/app/util/transactions/optimismTransaction.ts b/app/util/transactions/optimismTransaction.ts new file mode 100644 index 00000000000..54c1ce4d79f --- /dev/null +++ b/app/util/transactions/optimismTransaction.ts @@ -0,0 +1,49 @@ +import { omit } from 'lodash'; +import { BN } from 'ethereumjs-util'; +import Common, { Chain, Hardfork } from '@ethereumjs/common'; +import { TransactionFactory, TxData } from '@ethereumjs/tx'; + +import { stripHexPrefix } from '../../util/address'; + +interface TxDataWithGas extends TxData { + gas: string; +} + +interface TxMeta { + chainId: string; + metamaskNetworkId: string; + txParams: TxDataWithGas; +} + +const buildTxParams = (txMeta: TxMeta) => ({ + ...omit(txMeta.txParams, 'gas'), + gasLimit: txMeta.txParams.gas, +}); + +const buildTransactionCommon = (txMeta: TxMeta) => + // This produces a transaction whose information does not completely match an + // Optimism transaction — for instance, DEFAULT_CHAIN is still 'mainnet' and + // genesis points to the mainnet genesis, not the Optimism genesis — but + // considering that all we want to do is serialize a transaction, this works + // fine for our use case. + Common.forCustomChain(Chain.Mainnet, { + chainId: new BN(stripHexPrefix(txMeta.chainId), 16), + networkId: new BN(txMeta.metamaskNetworkId, 10), + // Optimism only supports type-0 transactions; it does not support any of + // the newer EIPs since EIP-155. Source: + // + defaultHardfork: Hardfork.SpuriousDragon, + }); + +/** + * Returns a transaction that can be serialized and fed to an Optimism smart contract. + * + * @param {Object} txMeta + * @returns {Object} + */ +// eslint-disable-next-line import/prefer-default-export +export const buildUnserializedTransaction = (txMeta: TxMeta) => { + const txParams = buildTxParams(txMeta); + const common = buildTransactionCommon(txMeta); + return TransactionFactory.fromTxData(txParams, { common }); +}; diff --git a/bitrise.yml b/bitrise.yml index c64a5777a11..9c9c1080851 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -22,11 +22,6 @@ workflows: - cache_local_deps: 'yes' - command: setup title: Yarn Setup - - yarn@0: - inputs: - - cache_local_deps: 'yes' - - command: setup:react-native-webrtc - title: Yarn Download WebRTC Bitcode - yarn@0: inputs: - cache_local_deps: 'yes' @@ -48,11 +43,6 @@ workflows: - cache_local_deps: 'yes' - command: setup title: Yarn Setup - - yarn@0: - inputs: - - cache_local_deps: 'yes' - - command: setup:react-native-webrtc - title: Yarn Download WebRTC Bitcode - yarn@0: inputs: - cache_local_deps: 'yes' diff --git a/ios/MetaMask.xcodeproj/project.pbxproj b/ios/MetaMask.xcodeproj/project.pbxproj index 916d94e2122..3e0b6a6a5ea 100644 --- a/ios/MetaMask.xcodeproj/project.pbxproj +++ b/ios/MetaMask.xcodeproj/project.pbxproj @@ -1142,7 +1142,7 @@ "$(SRCROOT)/../node_modules/react-native-tcp/ios/**", ); INFOPLIST_FILE = MetaMask/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1186,6 +1186,7 @@ CURRENT_PROJECT_VERSION = 1018; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", @@ -1208,7 +1209,7 @@ "$(SRCROOT)/../node_modules/react-native-tcp/ios/**", ); INFOPLIST_FILE = MetaMask/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1258,6 +1259,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1300,6 +1302,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; + ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1396,6 +1399,7 @@ CURRENT_PROJECT_VERSION = 1018; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 48XVW22RCG; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)", diff --git a/ios/MetaMask/Info.plist b/ios/MetaMask/Info.plist index 3b534004143..6907770e02e 100644 --- a/ios/MetaMask/Info.plist +++ b/ios/MetaMask/Info.plist @@ -20,6 +20,13 @@ $(MARKETING_VERSION) CFBundleSignature ???? + branch_universal_link_domains + + metamask.app.link + metamask-alternate.app.link + metamask.test.app.link + metamask-alternate.test.app.link + CFBundleURLTypes @@ -115,7 +122,12 @@ UIViewControllerBasedStatusBarAppearance branch_key - $(MM_BRANCH_KEY_LIVE) + + live + $(MM_BRANCH_KEY_LIVE) + test + $(MM_BRANCH_KEY_TEST) + fox_code $(MM_FOX_CODE) mixpanel_token diff --git a/ios/Podfile b/ios/Podfile index fb377e1bfea..fd768f67f67 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,13 +1,13 @@ require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' -platform :ios, "11.0" +platform :ios, "12.0" def react_native_post_install(installer) installer.pods_project.targets.each do |target| target.build_configurations.each do |config| if Gem::Version.new('9.0') > Gem::Version.new(config.build_settings['IPHONEOS_DEPLOYMENT_TARGET']) - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = "11.0" + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = "12.0" end config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES' end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 29172c0180a..ad424d03565 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,6 +1,6 @@ PODS: - boost (1.76.0) - - Branch (1.41.0) + - Branch (1.43.2) - BVLinearGradient (2.5.6): - React - CocoaAsyncSocket (7.6.5) @@ -75,6 +75,7 @@ PODS: - FlipperKit/FlipperKitNetworkPlugin - fmt (6.2.1) - glog (0.3.5) + - JitsiWebRTC (106.0.0) - libevent (2.1.12) - lottie-ios (2.5.3) - lottie-react-native (3.1.0): @@ -297,8 +298,8 @@ PODS: - React - react-native-blur (0.8.0): - React - - react-native-branch (5.4.0): - - Branch (= 1.41.0) + - react-native-branch (5.6.2): + - Branch (= 1.43.2) - React-Core - react-native-camera (3.44.3): - React-Core @@ -333,7 +334,8 @@ PODS: - React - react-native-viewpager (3.3.1): - React - - react-native-webrtc (1.100.0): + - react-native-webrtc (106.0.0-beta.7): + - JitsiWebRTC (~> 106.0.0) - React-Core - react-native-webview (11.13.0): - React-Core @@ -610,6 +612,7 @@ SPEC REPOS: - Flipper-RSocket - FlipperKit - fmt + - JitsiWebRTC - libevent - lottie-ios - OpenSSL-Universal @@ -774,7 +777,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: a7c83b31436843459a1961bfd74b96033dc77234 - Branch: bd6c472d6cab03206877ac86c1241327a3f217d1 + Branch: 74cc856025984f691833c8fa332834ac38a0cf4e BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 @@ -791,6 +794,7 @@ SPEC CHECKSUMS: FlipperKit: aec2d931adeee48a07bab1ea8bcc8a6bb87dfce4 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 5337263514dd6f09803962437687240c5dc39aa4 + JitsiWebRTC: f441eb0e2d67f0588bf24e21c5162e97342714fb libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 lottie-ios: a50d5c0160425cd4b01b852bb9578963e6d92d31 lottie-react-native: 7ca15c46249b61e3f9ffcf114cb4123e907a2156 @@ -811,7 +815,7 @@ SPEC CHECKSUMS: react-native-aes: ff31f0dd4c791eb423a631ee04570fcf3c618924 react-native-background-timer: 1b6e6b4e10f1b74c367a1fdc3c72b67c619b222b react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c - react-native-branch: 5b20bf032b3a1a20d2b76e57215bd3254a137dd5 + react-native-branch: 4e42fda662d96893afbbd02839806931398e3d2e react-native-camera: b8cc03e2feec0c04403d0998e37cf519d8fd4c6f react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a @@ -824,7 +828,7 @@ SPEC CHECKSUMS: react-native-video: 0bb76b6d6b77da3009611586c7dbf817b947f30e react-native-view-shot: 4475fde003fe8a210053d1f98fb9e06c1d834e1c react-native-viewpager: f730c1d175a2c1ae789464855d4a2c14247d3109 - react-native-webrtc: b8f2769386d51a6a8c89778478618fe311226bc3 + react-native-webrtc: f73459325441d15812f86071bdbd0bf3376235d1 react-native-webview: 133a6a5149f963259646e710b4545c67ef35d7c9 React-perflogger: 169fb34f60c5fd793b370002ee9c916eba9bc4ae React-RCTActionSheet: 2355539e02ad5cd4b1328682ab046487e1e1e920 @@ -868,6 +872,6 @@ SPEC CHECKSUMS: Yoga: b316a990bb6d115afa1b436b5626ac7c61717d17 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 7f7f2a91148820d72060690efb985d8d04e01f4b +PODFILE CHECKSUM: 74e503a275b4ed097cb01c171027882e9cdc699d COCOAPODS: 1.11.3 diff --git a/locales/languages/en.json b/locales/languages/en.json index d36d2327571..750689fed64 100644 --- a/locales/languages/en.json +++ b/locales/languages/en.json @@ -1480,6 +1480,7 @@ "wc_sent_tx_title": "Sent transaction", "wc_connected_rejected_title": "You've rejected the connect request", "wc_signed_rejected_title": "You've rejected the sign request", + "wc_signed_failed_title": "This sign request failed", "wc_sent_tx_rejected_title": "You've rejected the transaction request", "wc_description": "Please check the application" }, diff --git a/package.json b/package.json index be2c3cfb963..3be4fa82f47 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,10 @@ "clean:node": "rm -rf node_modules && yarn --frozen-lockfile", "clean": "yarn clean:ios && yarn clean:android && yarn clean:node", "clean-android": "yarn clean:android && yarn clean:node", - "setup:react-native-webrtc": "cd node_modules/react-native-webrtc/tools && ./downloadBitcode.sh && cd ../../../", "lint": "eslint '**/*.{js,ts,tsx}' --ignore-path=.prettierignore", "lint:fix": "eslint '**/*.{js,ts,tsx}' --fix --ignore-path=.prettierignore", "format": "prettier '**/*.{js,ts,tsx,json}' --write", - "setup": "yarn clean && yarn setup:react-native-webrtc && yarn pod:install && yarn allow-scripts && ./scripts/postinstall.sh", + "setup": "yarn clean && yarn pod:install && yarn allow-scripts && ./scripts/postinstall.sh", "setup:e2e": "cd wdio && yarn install", "start:ios": "./scripts/build.sh ios debug", "start:ios:qa": "./scripts/build.sh ios qaDebug", @@ -127,6 +126,9 @@ }, "dependencies": { "@consensys/on-ramp-sdk": "^1.3.1-beta.10", + "@eth-optimism/contracts": "0.0.0-2021919175625", + "@ethereumjs/common": "^2.3.1", + "@ethereumjs/tx": "^3.2.1", "@exodus/react-native-payments": "git+https://github.com/MetaMask/react-native-payments.git#dbc8cbbed570892d2fea5e3d183bf243e062c1e5", "@keystonehq/bc-ur-registry-eth": "^0.7.7", "@keystonehq/metamask-airgapped-keyring": "^0.3.0", @@ -138,7 +140,7 @@ "@metamask/post-message-stream": "6.0.0", "@metamask/sdk-communication-layer": "^0.0.1-beta.3", "@metamask/snap-controllers": "^0.23.0", - "@metamask/swaps-controller": "^6.6.0", + "@metamask/swaps-controller": "^6.7.0", "@ngraveio/bc-ur": "^1.1.6", "@react-native-async-storage/async-storage": "1.17.10", "@react-native-clipboard/clipboard": "^1.8.4", @@ -200,6 +202,7 @@ "is-url": "^1.2.4", "json-rpc-engine": "^6.1.0", "json-rpc-middleware-stream": "3.0.0", + "lodash": "^4.17.21", "lottie-react-native": "git+https://github.com/MetaMask/lottie-react-native.git#7ce6a78ac4ac7b9891bc513cb3f12f8b9c9d9106", "metro-config": "^0.71.1", "multihashes": "0.4.14", @@ -224,7 +227,7 @@ "react-native-animatable": "^1.3.3", "react-native-animated-fox": "git+https://github.com/MetaMask/react-native-animated-fox.git#16e38d54d829709e497f196e31fa8ff00cdf2aa9", "react-native-background-timer": "2.1.1", - "react-native-branch": "5.4.0", + "react-native-branch": "^5.6.2", "react-native-browser-polyfill": "0.1.2", "react-native-button": "git+https://github.com/MetaMask/react-native-button.git#fd79f4d6c8ced4086ba6e3021b6ed6d4a3e19cf7", "react-native-camera": "^3.36.0", @@ -275,7 +278,7 @@ "react-native-vector-icons": "6.4.2", "react-native-video": "^5.1.1", "react-native-view-shot": "^3.1.2", - "react-native-webrtc": "^1.100.0", + "react-native-webrtc": "^106.0.0-beta.7", "react-native-webview": "11.13.0", "react-redux": "7.2.4", "readable-stream": "1.0.33", @@ -285,7 +288,7 @@ "redux-persist-filesystem-storage": "^3.0.0", "reselect": "^4.0.0", "rn-fetch-blob": "^0.12.0", - "socket.io-client": "^4.5.1", + "socket.io-client": "^4.5.3", "stream-browserify": "1.0.0", "swappable-obj-proxy": "^1.1.0", "through2": "3.0.1", @@ -491,7 +494,9 @@ "appium-windows-driver": false, "chromedriver": false, "canvas": false, - "react-native-webrtc": true + "react-native-webrtc": true, + "node-hid": false, + "usb": false } } } diff --git a/yarn.lock b/yarn.lock index 5e6013014cb..4f2bb5fc350 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1936,6 +1936,39 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@eth-optimism/contracts@0.0.0-2021919175625": + version "0.0.0-2021919175625" + resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.0.0-2021919175625.tgz#551ed5d98ac4405596e97f723c28ee6376e65546" + integrity sha512-vf/rH0lUk5TRvY7/Rq8jAYXfJvAixcgnYyHVPPBdPUdWlOPfnUx1bG5N1xfaBI/zKJ1zen5gsJW4zaM+rUVKsQ== + dependencies: + "@eth-optimism/core-utils" "^0.0.0-2021919175625" + "@ethersproject/abstract-provider" "^5.4.1" + "@ethersproject/abstract-signer" "^5.4.1" + "@ethersproject/contracts" "^5.4.1" + "@ethersproject/hardware-wallets" "^5.4.0" + +"@eth-optimism/core-utils@^0.0.0-2021919175625": + version "0.0.0-2022819193354" + resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.0.0-2022819193354.tgz#37e799e050ed303369d842f4a14714240e58bd44" + integrity sha512-5fgc5QRVk+MINgDCU8N5TxtlR1te4jrjZrFaXa8hQ/XRZ1WfkE9RmYSA5jvv57eCHreK5HxJW1sjF940LzarJw== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/contracts" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/providers" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bufio "^1.0.7" + chai "^4.3.4" + "@ethereumjs/common@^2.0.0": version "2.6.0" resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.0.tgz#feb96fb154da41ee2cc2c5df667621a440f36348" @@ -2056,7 +2089,7 @@ "@ethersproject/transactions" "^5.4.0" "@ethersproject/web" "^5.4.0" -"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.4.1", "@ethersproject/abstract-provider@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== @@ -2093,7 +2126,7 @@ "@ethersproject/logger" "^5.4.0" "@ethersproject/properties" "^5.4.0" -"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.4.1", "@ethersproject/abstract-signer@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== @@ -2238,7 +2271,7 @@ "@ethersproject/properties" "^5.4.0" "@ethersproject/transactions" "^5.4.0" -"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.7.0": +"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.4.1", "@ethersproject/contracts@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== @@ -2254,6 +2287,18 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/transactions" "^5.7.0" +"@ethersproject/hardware-wallets@^5.4.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hardware-wallets/-/hardware-wallets-5.7.0.tgz#1c902fc255e2f108af44d4c1dc46ec2c34cb669c" + integrity sha512-DjMMXIisRc8xFvEoLoYz1w7JDOYmaz/a0X9sp7Zu668RR8U1zCAyj5ow25HLRW+TCzEC5XiFetTXqS5kXonFCQ== + dependencies: + "@ledgerhq/hw-app-eth" "5.27.2" + "@ledgerhq/hw-transport" "5.26.0" + "@ledgerhq/hw-transport-u2f" "5.26.0" + ethers "^5.7.0" + optionalDependencies: + "@ledgerhq/hw-transport-node-hid" "5.26.0" + "@ethersproject/hash@5.4.0", "@ethersproject/hash@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.4.0.tgz#d18a8e927e828e22860a011f39e429d388344ae0" @@ -2402,6 +2447,13 @@ dependencies: "@ethersproject/logger" "^5.7.0" +"@ethersproject/networks@5.7.1": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks@^5.4.0": version "5.4.1" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.4.1.tgz#2ce83b8e42aa85216e5d277a7952d97b6ce8d852" @@ -2497,7 +2549,7 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/providers@^5.7.0": +"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.0": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== @@ -2750,6 +2802,17 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@ethersproject/web@5.7.1": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/wordlists@5.4.0", "@ethersproject/wordlists@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.4.0.tgz#f34205ec3bbc9e2c49cadaee774cf0b07e7573d7" @@ -3817,6 +3880,97 @@ resolved "https://registry.yarnpkg.com/@lavamoat/preinstall-always-fail/-/preinstall-always-fail-1.0.0.tgz#e78a6e3d9e212a4fef869ec37d4f5fb498dea373" integrity sha512-vD2DcC0ffJj1w2y1Lu0OU39wHmlPEd2tCDW04Bm6Kf4LyRnCHCezTsS8yzeSJ+4so7XP+TITuR5FGJRWxPb+GA== +"@ledgerhq/cryptoassets@^5.27.2": + version "5.53.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/cryptoassets/-/cryptoassets-5.53.0.tgz#11dcc93211960c6fd6620392e4dd91896aaabe58" + integrity sha512-M3ibc3LRuHid5UtL7FI3IC6nMEppvly98QHFoSa7lJU0HDzQxY6zHec/SPM4uuJUC8sXoGVAiRJDkgny54damw== + dependencies: + invariant "2" + +"@ledgerhq/devices@^5.26.0", "@ledgerhq/devices@^5.51.1": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-5.51.1.tgz#d741a4a5d8f17c2f9d282fd27147e6fe1999edb7" + integrity sha512-4w+P0VkbjzEXC7kv8T1GJ/9AVaP9I6uasMZ/JcdwZBS3qwvKo5A5z9uGhP5c7TvItzcmPb44b5Mw2kT+WjUuAA== + dependencies: + "@ledgerhq/errors" "^5.50.0" + "@ledgerhq/logs" "^5.50.0" + rxjs "6" + semver "^7.3.5" + +"@ledgerhq/errors@^5.26.0", "@ledgerhq/errors@^5.50.0": + version "5.50.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-5.50.0.tgz#e3a6834cb8c19346efca214c1af84ed28e69dad9" + integrity sha512-gu6aJ/BHuRlpU7kgVpy2vcYk6atjB4iauP2ymF7Gk0ez0Y/6VSMVSJvubeEQN+IV60+OBK0JgeIZG7OiHaw8ow== + +"@ledgerhq/hw-app-eth@5.27.2": + version "5.27.2" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-5.27.2.tgz#65a2ed613a69340e0cd69c942147455ec513d006" + integrity sha512-llNdrE894cCN8j6yxJEUniciyLVcLmu5N0UmIJLOObztG+5rOF4bX54h4SreTWK+E10Z0CzHSeyE5Lz/tVcqqQ== + dependencies: + "@ledgerhq/cryptoassets" "^5.27.2" + "@ledgerhq/errors" "^5.26.0" + "@ledgerhq/hw-transport" "^5.26.0" + bignumber.js "^9.0.1" + rlp "^2.2.6" + +"@ledgerhq/hw-transport-node-hid-noevents@^5.26.0": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-5.51.1.tgz#71f37f812e448178ad0bcc2258982150d211c1ab" + integrity sha512-9wFf1L8ZQplF7XOY2sQGEeOhpmBRzrn+4X43kghZ7FBDoltrcK+s/D7S+7ffg3j2OySyP6vIIIgloXylao5Scg== + dependencies: + "@ledgerhq/devices" "^5.51.1" + "@ledgerhq/errors" "^5.50.0" + "@ledgerhq/hw-transport" "^5.51.1" + "@ledgerhq/logs" "^5.50.0" + node-hid "2.1.1" + +"@ledgerhq/hw-transport-node-hid@5.26.0": + version "5.26.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-5.26.0.tgz#69bc4f8067cdd9c09ef4aed0e0b3c58328936e4b" + integrity sha512-qhaefZVZatJ6UuK8Wb6WSFNOLWc2mxcv/xgsfKi5HJCIr4bPF/ecIeN+7fRcEaycxj4XykY6Z4A7zDVulfFH4w== + dependencies: + "@ledgerhq/devices" "^5.26.0" + "@ledgerhq/errors" "^5.26.0" + "@ledgerhq/hw-transport" "^5.26.0" + "@ledgerhq/hw-transport-node-hid-noevents" "^5.26.0" + "@ledgerhq/logs" "^5.26.0" + lodash "^4.17.20" + node-hid "1.3.0" + usb "^1.6.3" + +"@ledgerhq/hw-transport-u2f@5.26.0": + version "5.26.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-u2f/-/hw-transport-u2f-5.26.0.tgz#b7d9d13193eb82b051fd7a838cd652372f907ec5" + integrity sha512-QTxP1Rsh+WZ184LUOelYVLeaQl3++V3I2jFik+l9JZtakwEHjD0XqOT750xpYNL/vfHsy31Wlz+oicdxGzFk+w== + dependencies: + "@ledgerhq/errors" "^5.26.0" + "@ledgerhq/hw-transport" "^5.26.0" + "@ledgerhq/logs" "^5.26.0" + u2f-api "0.2.7" + +"@ledgerhq/hw-transport@5.26.0": + version "5.26.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.26.0.tgz#bfedc3d48400ad2fe48278d9444344b72aa9d0fe" + integrity sha512-NFeJOJmyEfAX8uuIBTpocWHcz630sqPcXbu864Q+OCBm4EK5UOKV1h/pX7e0xgNIKY8zhJ/O4p4cIZp9tnXLHQ== + dependencies: + "@ledgerhq/devices" "^5.26.0" + "@ledgerhq/errors" "^5.26.0" + events "^3.2.0" + +"@ledgerhq/hw-transport@^5.26.0", "@ledgerhq/hw-transport@^5.51.1": + version "5.51.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.51.1.tgz#8dd14a8e58cbee4df0c29eaeef983a79f5f22578" + integrity sha512-6wDYdbWrw9VwHIcoDnqWBaDFyviyjZWv6H9vz9Vyhe4Qd7TIFmbTl/eWs6hZvtZBza9K8y7zD8ChHwRI4s9tSw== + dependencies: + "@ledgerhq/devices" "^5.51.1" + "@ledgerhq/errors" "^5.50.0" + events "^3.3.0" + +"@ledgerhq/logs@^5.26.0", "@ledgerhq/logs@^5.50.0": + version "5.50.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-5.50.0.tgz#29c6419e8379d496ab6d0426eadf3c4d100cd186" + integrity sha512-swKHYCOZUGyVt4ge0u8a7AwNcA//h4nx5wIi0sruGye1IJ5Cva0GyK9L2/WdX+kWVTKp92ZiEo1df31lrWGPgA== + "@log4js-node/log4js-api@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@log4js-node/log4js-api/-/log4js-api-1.0.2.tgz#7a8143fb33f077df3e579dca7f18fea74a02ec8b" @@ -4189,10 +4343,10 @@ ses "^0.17.0" superstruct "^0.16.7" -"@metamask/swaps-controller@^6.6.0": - version "6.6.0" - resolved "https://registry.yarnpkg.com/@metamask/swaps-controller/-/swaps-controller-6.6.0.tgz#328fa32bee32d07521cf0a0fb1cd0b7f0e878e20" - integrity sha512-vMQpSexx1ONea2q1oYEOC7yUnd6glkkirFdgpXUXp2iILp1euAa9YukljhWRXQ3Juii33TwVhT7sCcE+Y+yEoA== +"@metamask/swaps-controller@^6.7.0": + version "6.7.0" + resolved "https://registry.yarnpkg.com/@metamask/swaps-controller/-/swaps-controller-6.7.0.tgz#bcd231e034a30a95c9c89aff4f24c8c1cae1e5f6" + integrity sha512-ZgdueQn8fcD5pYl8UuNSWdnXqWMhPsLGC3VobwsRSF1cj3ACSZNwS4Q9lbY46B2lhkvy7Tu6dOt4k0BjgrPDEw== dependencies: "@metamask/controllers" "^26.0.0" abort-controller "^3.0.0" @@ -6585,6 +6739,11 @@ adbkit-apkreader@^3.1.2: debug "~4.1.1" yauzl "^2.7.0" +adm-zip@0.5.9: + version "0.5.9" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.9.tgz#b33691028333821c0cf95c31374c5462f2905a83" + integrity sha512-s+3fXLkeeLjZ2kLjCBwQufpI5fuN+kIGBxu6530nVQZGVol0d7Y/M88/xw9HGGUcJjKf8LutN3VPRUBq6N7Ajg== + aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" @@ -7661,6 +7820,11 @@ assertion-error-formatter@^3.0.0: pad-right "^0.2.2" repeat-string "^1.6.1" +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -8645,6 +8809,11 @@ bufferutil@^4.0.1: dependencies: node-gyp-build "^4.3.0" +bufio@^1.0.7: + version "1.1.3" + resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.1.3.tgz#7f8e524fd719ced2caa563a09d50550f283f745f" + integrity sha512-W0ydG8t+ST+drUpEwl1N+dU9Ije06g8+43CLtvEIzfKo9nPFLXbKqDYE2XSg4w6RugsBcCj7pEU7jOpBC6BqrA== + bunyan-debug-stream@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/bunyan-debug-stream/-/bunyan-debug-stream-2.0.1.tgz#9bd7c7e30c7b2cf711317e9d37529b0464c3b164" @@ -8859,6 +9028,19 @@ cbor-sync@^1.0.4: resolved "https://registry.yarnpkg.com/cbor-sync/-/cbor-sync-1.0.4.tgz#5a11a1ab75c2a14d1af1b237fd84aa8c1593662f" integrity sha512-GWlXN4wiz0vdWWXBU71Dvc1q3aBo0HytqwAZnXF1wOwjqNnDWA1vZ1gDMFLlqohak31VQzmhiYfiCX5QSSfagA== +chai@^4.3.4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" @@ -8951,6 +9133,11 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== + checkpoint-store@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" @@ -10139,6 +10326,13 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-eql@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.2.tgz#270ceb902f87724077e6f6449aed81463f42fc1c" + integrity sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w== + dependencies: + type-detect "^4.0.0" + deep-equal@*: version "2.0.5" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.5.tgz#55cd2fe326d83f9cbf7261ef0e060b3f724c5cb9" @@ -10349,6 +10543,11 @@ detect-browser@^5.2.0: resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-5.3.0.tgz#9705ef2bddf46072d0f7265a1fe300e36fe7ceca" integrity sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w== +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + detect-libc@^2.0.0, detect-libc@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" @@ -12155,6 +12354,42 @@ ethers@^5.4.1: "@ethersproject/web" "5.4.0" "@ethersproject/wordlists" "5.4.0" +ethers@^5.7.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" + integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== + dependencies: + "@ethersproject/abi" "5.7.0" + "@ethersproject/abstract-provider" "5.7.0" + "@ethersproject/abstract-signer" "5.7.0" + "@ethersproject/address" "5.7.0" + "@ethersproject/base64" "5.7.0" + "@ethersproject/basex" "5.7.0" + "@ethersproject/bignumber" "5.7.0" + "@ethersproject/bytes" "5.7.0" + "@ethersproject/constants" "5.7.0" + "@ethersproject/contracts" "5.7.0" + "@ethersproject/hash" "5.7.0" + "@ethersproject/hdnode" "5.7.0" + "@ethersproject/json-wallets" "5.7.0" + "@ethersproject/keccak256" "5.7.0" + "@ethersproject/logger" "5.7.0" + "@ethersproject/networks" "5.7.1" + "@ethersproject/pbkdf2" "5.7.0" + "@ethersproject/properties" "5.7.0" + "@ethersproject/providers" "5.7.2" + "@ethersproject/random" "5.7.0" + "@ethersproject/rlp" "5.7.0" + "@ethersproject/sha2" "5.7.0" + "@ethersproject/signing-key" "5.7.0" + "@ethersproject/solidity" "5.7.0" + "@ethersproject/strings" "5.7.0" + "@ethersproject/transactions" "5.7.0" + "@ethersproject/units" "5.7.0" + "@ethersproject/wallet" "5.7.0" + "@ethersproject/web" "5.7.1" + "@ethersproject/wordlists" "5.7.0" + ethjs-abi@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/ethjs-abi/-/ethjs-abi-0.2.0.tgz#d3e2c221011520fc499b71682036c14fcc2f5b25" @@ -12387,7 +12622,7 @@ events@^1.1.1: resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= -events@^3.0.0: +events@^3.0.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -13364,6 +13599,11 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== + get-intrinsic@^1.0.1, get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" @@ -14246,7 +14486,7 @@ intersect@~0.0.3: resolved "https://registry.yarnpkg.com/intersect/-/intersect-0.0.3.tgz#c1a4a5e5eac6ede4af7504cc07e0ada7bc9f4920" integrity sha1-waSl5erG7eSvdQTMB+Ctp7yfSSA= -invariant@2.2.4, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: +invariant@2, invariant@2.2.4, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -16407,6 +16647,13 @@ lottie-ios@2.5.0: prop-types "^15.5.10" react-native-safe-module "^1.1.0" +loupe@^2.3.1: + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + dependencies: + get-func-name "^2.0.0" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -17865,6 +18112,13 @@ nocache@^2.1.0: resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" integrity sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q== +node-abi@^2.18.0, node-abi@^2.21.0, node-abi@^2.7.0: + version "2.30.1" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.30.1.tgz#c437d4b1fe0e285aaf290d45b45d4d7afedac4cf" + integrity sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w== + dependencies: + semver "^5.4.1" + node-abi@^3.3.0: version "3.22.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.22.0.tgz#00b8250e86a0816576258227edbce7bbe0039362" @@ -17877,6 +18131,16 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-addon-api@^3.0.2: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + +node-addon-api@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" + integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== + node-addon-api@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501" @@ -17929,6 +18193,25 @@ node-gyp@^7.1.0: tar "^6.0.2" which "^2.0.2" +node-hid@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-1.3.0.tgz#346a468505cee13d69ccd760052cbaf749f66a41" + integrity sha512-BA6G4V84kiNd1uAChub/Z/5s/xS3EHBCxotQ0nyYrUG65mXewUDHE1tWOSqA2dp3N+mV0Ffq9wo2AW9t4p/G7g== + dependencies: + bindings "^1.5.0" + nan "^2.14.0" + node-abi "^2.18.0" + prebuild-install "^5.3.4" + +node-hid@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-2.1.1.tgz#f83c8aa0bb4e6758b5f7383542477da93f67359d" + integrity sha512-Skzhqow7hyLZU93eIPthM9yjot9lszg9xrKxESleEs05V2NcbUptZc5HFqzjOkSmL0sFlZFr3kmvaYebx06wrw== + dependencies: + bindings "^1.5.0" + node-addon-api "^3.0.2" + prebuild-install "^6.0.0" + node-idevice@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/node-idevice/-/node-idevice-0.1.6.tgz#9411aa768b44bfb7cd25ece5c8a1c8b4b6f1fa44" @@ -18011,6 +18294,11 @@ noms@0.0.0: inherits "^2.0.1" readable-stream "~1.0.31" +noop-logger@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + integrity sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ== + nopt-usage@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/nopt-usage/-/nopt-usage-0.1.0.tgz#b18b8c183e181047ca9e63b7cde7cfc702cca579" @@ -18109,7 +18397,7 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npmlog@4.x, npmlog@^4.1.2: +npmlog@4.x, npmlog@^4.0.1, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -18907,6 +19195,11 @@ path@0.12.7: process "^0.11.1" util "^0.10.3" +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -19156,6 +19449,46 @@ postcss-value-parser@^3.3.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== +prebuild-install@^5.3.4: + version "5.3.6" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.6.tgz#7c225568d864c71d89d07f8796042733a3f54291" + integrity sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" + noop-logger "^0.1.1" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + which-pm-runs "^1.0.0" + +prebuild-install@^6.0.0: + version "6.1.4" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-6.1.4.tgz#ae3c0142ad611d58570b89af4986088a4937e00f" + integrity sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^2.21.0" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + prebuild-install@^7.1.0: version "7.1.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" @@ -19900,10 +20233,10 @@ react-native-background-timer@2.1.1: resolved "https://registry.yarnpkg.com/react-native-background-timer/-/react-native-background-timer-2.1.1.tgz#9a2489681ab2f8033c213c73272e9d4c47572cd5" integrity sha512-cuXIIv+dcG8a8qkTD8pMzeqOEZCO+UGKglZWIe1osve+yJslmCowYQff+bI9xa7NOt2w+Vtd4L3d9JonlSqODg== -react-native-branch@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/react-native-branch/-/react-native-branch-5.4.0.tgz#ba71422e38047b744ae5d88a834e7a7cb5185d68" - integrity sha512-KuYpFPm6cKzUSTMOpI216w4BYP/5E4g8MH5a/4/xaWPqVqZmeoQS89/GqDxg7YwOG70YMtwdSobulh/wrv2tMw== +react-native-branch@^5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/react-native-branch/-/react-native-branch-5.6.2.tgz#c3ec137a5427a21fcd6559b83f02e6162ea627f6" + integrity sha512-plYpedNI1SSquD7bDOJ4UptkPIrGtz4O1NXfFA5tBshGb0AhuUSYS13dNZuHa+Ug+7YaqBcc0SF02vIB0ktZKw== react-native-browser-polyfill@0.1.2: version "0.1.2" @@ -20341,12 +20674,14 @@ react-native-view-shot@^3.1.2: resolved "https://registry.yarnpkg.com/react-native-view-shot/-/react-native-view-shot-3.1.2.tgz#8c8e84c67a4bc8b603e697dbbd59dbc9b4f84825" integrity sha512-9u9fPtp6a52UMoZ/UCPrCjKZk8tnkI9To0Eh6yYnLKFEGkRZ7Chm6DqwDJbYJHeZrheCCopaD5oEOnRqhF4L2Q== -react-native-webrtc@^1.100.0: - version "1.100.0" - resolved "https://registry.yarnpkg.com/react-native-webrtc/-/react-native-webrtc-1.100.0.tgz#f81562dc46467fe75f9f96e98dbe311222e83abd" - integrity sha512-sBLl8Ihj3xfYn0NZJdDxelB/dZ422FWg/kcLdSw6Wk4eM3MeNW3iFwVsqg2dLzeDnuoQ06i7hOdtP7pGAiKsug== +react-native-webrtc@^106.0.0-beta.7: + version "106.0.0-beta.7" + resolved "https://registry.yarnpkg.com/react-native-webrtc/-/react-native-webrtc-106.0.0-beta.7.tgz#c333c2a57a5fd2c1ca35f4b401912a95502d850b" + integrity sha512-GoIl/99BgVouFCb0gc9BfboPm6tOAQ1skHmpZR6CDtPxCbRxt8pEZXRiRxfvW9LbOm6yTpbSe2DK/kQrffZN3w== dependencies: + adm-zip "0.5.9" base64-js "1.5.1" + debug "4.3.4" event-target-shim "6.0.2" tar "6.1.11" @@ -21316,6 +21651,13 @@ rustbn.js@~0.2.0: resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== +rxjs@6, rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.7: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + "rxjs@>= 6": version "7.2.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.2.0.tgz#5cd12409639e9514a71c9f5f9192b2c4ae94de31" @@ -21323,13 +21665,6 @@ rustbn.js@~0.2.0: dependencies: tslib "~2.1.0" -rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.7: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - rxjs@^7.5.5: version "7.5.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" @@ -21522,7 +21857,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -21960,7 +22295,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socket.io-client@^4.5.1: +socket.io-client@^4.5.1, socket.io-client@^4.5.3: version "4.5.3" resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.3.tgz#bed69209d001465b2fea650d2e95c1e82768ab5e" integrity sha512-I/hqDYpQ6JKwtJOf5ikM+Qz+YujZPMEl6qBLhxiP0nX+TfXKhW4KZZG8lamrD6Y5ngjmYHreESVasVCgi5Kl3A== @@ -23190,7 +23525,7 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@4.0.8: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -23260,6 +23595,11 @@ typescript@^4.4.2, typescript@^4.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== +u2f-api@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/u2f-api/-/u2f-api-0.2.7.tgz#17bf196b242f6bf72353d9858e6a7566cc192720" + integrity sha512-fqLNg8vpvLOD5J/z4B6wpPg4Lvowz1nJ9xdHcCzdUPKcFE/qNCceV2gNZxSJd5vhAZemHr/K/hbzVA0zxB5mkg== + ua-parser-js@^0.7.18, ua-parser-js@^0.7.21, ua-parser-js@^0.7.24, ua-parser-js@^1.0.1: version "0.7.31" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" @@ -23469,6 +23809,14 @@ url@0.11.0: punycode "1.3.2" querystring "0.2.0" +usb@^1.6.3: + version "1.9.2" + resolved "https://registry.yarnpkg.com/usb/-/usb-1.9.2.tgz#fb6b36f744ecc707a196c45a6ec72442cb6f2b73" + integrity sha512-dryNz030LWBPAf6gj8vyq0Iev3vPbCLHCT8dBw3gQRXRzVNsIdeuU+VjPp3ksmSPkeMAl1k+kQ14Ij0QHyeiAg== + dependencies: + node-addon-api "^4.2.0" + node-gyp-build "^4.3.0" + use-callback-ref@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.5.tgz#6115ed242cfbaed5915499c0a9842ca2912f38a5" @@ -24077,6 +24425,11 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-pm-runs@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35" + integrity sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA== + which-typed-array@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff"