From 72f0d30b526e3ee7798d8d21fc3211a7ea3c3dbe Mon Sep 17 00:00:00 2001 From: Kirill Fedoseev Date: Tue, 8 Feb 2022 00:32:41 +0400 Subject: [PATCH] CollectedSignatures AMB watcher for MEV bundling (#634) --- oracle/config/foreign-mev-sender.config.js | 37 ++ ...mev-collected-signatures-watcher.config.js | 30 ++ oracle/docker-compose-helpers.yml | 46 ++ oracle/package.json | 4 + oracle/scripts/erc20_to_native/sendForeign.js | 2 +- oracle/scripts/erc20_to_native/sendHome.js | 2 +- oracle/scripts/interestFetcher.js | 2 +- oracle/src/confirmRelay.js | 2 +- .../estimateGas.js | 9 +- .../processAMBCollectedSignaturesMEV/index.js | 183 ++++++++ .../processAffirmationRequests/estimateGas.js | 4 +- .../processCollectedSignatures/estimateGas.js | 3 +- .../processSignatureRequests/estimateGas.js | 3 +- oracle/src/mevSender.js | 159 +++++++ oracle/src/mevWatcher.js | 251 ++++++++++ oracle/src/sender.js | 2 +- oracle/src/services/web3.js | 16 +- oracle/src/tx/sendTx.js | 30 +- oracle/src/utils/mev.js | 43 ++ oracle/src/watcher.js | 13 +- yarn.lock | 444 +++++++++++++++++- 21 files changed, 1243 insertions(+), 42 deletions(-) create mode 100644 oracle/config/foreign-mev-sender.config.js create mode 100644 oracle/config/mev-collected-signatures-watcher.config.js create mode 100644 oracle/src/events/processAMBCollectedSignaturesMEV/index.js create mode 100644 oracle/src/mevSender.js create mode 100644 oracle/src/mevWatcher.js create mode 100644 oracle/src/utils/mev.js diff --git a/oracle/config/foreign-mev-sender.config.js b/oracle/config/foreign-mev-sender.config.js new file mode 100644 index 00000000..530d012d --- /dev/null +++ b/oracle/config/foreign-mev-sender.config.js @@ -0,0 +1,37 @@ +const baseConfig = require('./base.config') + +const { DEFAULT_TRANSACTION_RESEND_INTERVAL } = require('../src/utils/constants') +const { MEV_HELPER_ABI } = require('../src/utils/mev') +const { web3Foreign, getFlashbotsProvider } = require('../src/services/web3') + +const { + ORACLE_FOREIGN_TX_RESEND_INTERVAL, + ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS, + ORACLE_MEV_FOREIGN_MIN_GAS_PRICE, + ORACLE_MEV_FOREIGN_FLAT_MINER_FEE, + ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS, + ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS, + ORACLE_MEV_FOREIGN_BUNDLES_BLOCK_RANGE +} = process.env + +const contract = new baseConfig.foreign.web3.eth.Contract(MEV_HELPER_ABI, ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS) + +module.exports = { + ...baseConfig, + pollingInterval: baseConfig.foreign.pollingInterval, + mevForeign: { + contractAddress: ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS, + contract, + minGasPrice: ORACLE_MEV_FOREIGN_MIN_GAS_PRICE, + flatMinerFee: ORACLE_MEV_FOREIGN_FLAT_MINER_FEE, + maxPriorityFeePerGas: ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS, + maxFeePerGas: ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS, + bundlesPerIteration: Math.max(parseInt(ORACLE_MEV_FOREIGN_BUNDLES_BLOCK_RANGE, 10) || 5, 1), + getFlashbotsProvider + }, + mevJobsRedisKey: `${baseConfig.id}-collected-signatures-mev:mevJobs`, + id: 'mev-sender-foreign', + name: 'mev-sender-foreign', + web3: web3Foreign, + resendInterval: parseInt(ORACLE_FOREIGN_TX_RESEND_INTERVAL, 10) || DEFAULT_TRANSACTION_RESEND_INTERVAL +} diff --git a/oracle/config/mev-collected-signatures-watcher.config.js b/oracle/config/mev-collected-signatures-watcher.config.js new file mode 100644 index 00000000..1093ffb4 --- /dev/null +++ b/oracle/config/mev-collected-signatures-watcher.config.js @@ -0,0 +1,30 @@ +const baseConfig = require('./base.config') +const { MEV_HELPER_ABI } = require('../src/utils/mev') + +const { + ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS, + ORACLE_MEV_FOREIGN_MIN_GAS_PRICE, + ORACLE_MEV_FOREIGN_FLAT_MINER_FEE, + ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS, + ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS +} = process.env + +const id = `${baseConfig.id}-collected-signatures-mev` + +const contract = new baseConfig.foreign.web3.eth.Contract(MEV_HELPER_ABI, ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS) + +module.exports = { + ...baseConfig, + mevForeign: { + contractAddress: ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS, + contract, + minGasPrice: ORACLE_MEV_FOREIGN_MIN_GAS_PRICE, + flatMinerFee: ORACLE_MEV_FOREIGN_FLAT_MINER_FEE, + maxPriorityFeePerGas: ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS, + maxFeePerGas: ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS + }, + main: baseConfig.home, + event: 'CollectedSignatures', + name: `watcher-${id}`, + id +} diff --git a/oracle/docker-compose-helpers.yml b/oracle/docker-compose-helpers.yml index 716a8a13..8ea5f126 100644 --- a/oracle/docker-compose-helpers.yml +++ b/oracle/docker-compose-helpers.yml @@ -1,6 +1,14 @@ --- version: '2.4' services: + redis: + cpus: 0.1 + mem_limit: 500m + command: [ redis-server, --appendonly, 'yes' ] + hostname: redis + image: redis:4 + restart: unless-stopped + volumes: [ '~/bridge_data/helpers/redis:/data' ] interestFetcher: cpus: 0.1 mem_limit: 500m @@ -13,3 +21,41 @@ services: INTERVAL: 300000 restart: unless-stopped entrypoint: yarn helper:interestFether + mevWatcher: + cpus: 0.1 + mem_limit: 500m + image: poanetwork/tokenbridge-oracle:latest + env_file: ./.env + environment: + NODE_ENV: production + ORACLE_VALIDATOR_ADDRESS: ${ORACLE_VALIDATOR_ADDRESS} + ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS: 'TBD' + ORACLE_MEV_FOREIGN_MIN_GAS_PRICE: '50000000000' # 50 gwei + ORACLE_MEV_FOREIGN_FLAT_MINER_FEE: '1500000000000000' # 0.0015 eth = 300k gas * 5 gwei + ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS: '0' # 0 gwei + ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS: '1000000000000' # 1000 gwei + ORACLE_FOREIGN_RPC_POLLING_INTERVAL: '15000' # CollectedSignatures event polling interval + ORACLE_HOME_START_BLOCK: 'TBD' + ORACLE_HOME_SKIP_MANUAL_LANE: 'true' + restart: unless-stopped + entrypoint: yarn mev:watcher:collected-signatures + mevSender: + cpus: 0.1 + mem_limit: 500m + image: poanetwork/tokenbridge-oracle:latest + env_file: ./.env + environment: + NODE_ENV: production + ORACLE_VALIDATOR_ADDRESS: ${ORACLE_VALIDATOR_ADDRESS} + ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: ${ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY} + ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS: 'TBD' + ORACLE_MEV_FOREIGN_MIN_GAS_PRICE: '50000000000' # 50 gwei + ORACLE_MEV_FOREIGN_FLAT_MINER_FEE: '1500000000000000' # 0.0015 eth = 300k gas * 5 gwei + ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS: '0' # 0 gwei + ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS: '1000000000000' # 1000 gwei + ORACLE_MEV_FOREIGN_FLASHBOTS_RPC_URL: 'https://relay-goerli.flashbots.net' + ORACLE_MEV_FOREIGN_FLASHBOTS_AUTH_SIGNING_KEY: 82db7175932f4e6c8e45283b78b54fd5f195149378ec90d95b8fd0ec8bdadf1d + ORACLE_MEV_FOREIGN_BUNDLES_BLOCK_RANGE: '5' + ORACLE_FOREIGN_RPC_POLLING_INTERVAL: '70000' # time between sending different batches of MEV bundles (~= 5 blocks * 14 seconds) + restart: unless-stopped + entrypoint: yarn mev:sender:foreign diff --git a/oracle/package.json b/oracle/package.json index d1f05b65..4bc3cd7b 100644 --- a/oracle/package.json +++ b/oracle/package.json @@ -19,6 +19,8 @@ "confirm:information-request": "./scripts/start-worker.sh confirmRelay information-request-watcher", "manager:shutdown": "./scripts/start-worker.sh shutdownManager shutdown-manager", "helper:interestFether": "node ./scripts/interestFetcher.js", + "mev:watcher:collected-signatures": "./scripts/start-worker.sh mevWatcher mev-collected-signatures-watcher", + "mev:sender:foreign": "./scripts/start-worker.sh mevSender foreign-mev-sender", "dev": "concurrently -n 'watcher:signature-request,watcher:collected-signatures,watcher:affirmation-request,watcher:transfer, sender:home,sender:foreign' -c 'red,green,yellow,blue,magenta,cyan' 'yarn watcher:signature-request' 'yarn watcher:collected-signatures' 'yarn watcher:affirmation-request' 'yarn watcher:transfer' 'yarn sender:home' 'yarn sender:foreign'", "test": "NODE_ENV=test mocha", "test:watch": "NODE_ENV=test mocha --watch --reporter=min", @@ -28,10 +30,12 @@ "author": "", "license": "ISC", "dependencies": { + "@flashbots/ethers-provider-bundle": "^0.4.3", "amqp-connection-manager": "^2.0.0", "amqplib": "^0.5.2", "bignumber.js": "^7.2.1", "dotenv": "^5.0.1", + "ethers": "^5.5.3", "ioredis": "^3.2.2", "node-fetch": "^2.1.2", "pino": "^4.17.3", diff --git a/oracle/scripts/erc20_to_native/sendForeign.js b/oracle/scripts/erc20_to_native/sendForeign.js index d6922908..3875b196 100644 --- a/oracle/scripts/erc20_to_native/sendForeign.js +++ b/oracle/scripts/erc20_to_native/sendForeign.js @@ -36,7 +36,7 @@ async function main() { data, nonce, gasPrice: FOREIGN_TEST_TX_GAS_PRICE, - amount: '0', + value: '0', gasLimit, to: bridgeableTokenAddress, web3: web3Foreign, diff --git a/oracle/scripts/erc20_to_native/sendHome.js b/oracle/scripts/erc20_to_native/sendHome.js index 327c695f..1741ca2e 100644 --- a/oracle/scripts/erc20_to_native/sendHome.js +++ b/oracle/scripts/erc20_to_native/sendHome.js @@ -29,7 +29,7 @@ async function main() { data: '0x', nonce, gasPrice: HOME_TEST_TX_GAS_PRICE, - amount: HOME_MIN_AMOUNT_PER_TX, + value: web3Home.utils.toWei(HOME_MIN_AMOUNT_PER_TX), gasLimit: 100000, to: COMMON_HOME_BRIDGE_ADDRESS, web3: web3Home, diff --git a/oracle/scripts/interestFetcher.js b/oracle/scripts/interestFetcher.js index 1e30773f..7f90e2dd 100644 --- a/oracle/scripts/interestFetcher.js +++ b/oracle/scripts/interestFetcher.js @@ -54,7 +54,7 @@ async function main() { nonce, gasPrice, gasLimit: Math.round(gasLimit * 1.5), - amount: '0', + value: '0', chainId, web3: web3Home }) diff --git a/oracle/src/confirmRelay.js b/oracle/src/confirmRelay.js index 577b92ab..48f4d8a0 100644 --- a/oracle/src/confirmRelay.js +++ b/oracle/src/confirmRelay.js @@ -174,7 +174,7 @@ async function sendJobTx(jobs) { const txHash = await sendTx({ data: job.data, nonce, - amount: '0', + value: '0', gasLimit, privateKey: config.validatorPrivateKey, to: job.to, diff --git a/oracle/src/events/processAMBCollectedSignatures/estimateGas.js b/oracle/src/events/processAMBCollectedSignatures/estimateGas.js index 89732676..65ac5c17 100644 --- a/oracle/src/events/processAMBCollectedSignatures/estimateGas.js +++ b/oracle/src/events/processAMBCollectedSignatures/estimateGas.js @@ -4,7 +4,6 @@ const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError const logger = require('../../services/logger').child({ module: 'processCollectedSignatures:estimateGas' }) -const { parseAMBHeader } = require('../../utils/message') const web3 = new Web3() const { toBN } = Web3.utils @@ -22,15 +21,9 @@ async function estimateGas({ address }) { try { - const gasEstimate = await foreignBridge.methods.executeSignatures(message, signatures).estimateGas({ + return await foreignBridge.methods.executeSignatures(message, signatures).estimateGas({ from: address }) - const msgGasLimit = parseAMBHeader(message).gasLimit - - // + estimateExtraGas(len) - // is not needed here, since estimateGas will already take into account gas - // needed for memory expansion, message processing, etc. - return gasEstimate + msgGasLimit } catch (e) { if (e instanceof HttpListProviderError) { throw e diff --git a/oracle/src/events/processAMBCollectedSignaturesMEV/index.js b/oracle/src/events/processAMBCollectedSignaturesMEV/index.js new file mode 100644 index 00000000..9415f6eb --- /dev/null +++ b/oracle/src/events/processAMBCollectedSignaturesMEV/index.js @@ -0,0 +1,183 @@ +require('dotenv').config() +const promiseLimit = require('promise-limit') +const { HttpListProviderError } = require('../../services/HttpListProvider') +const { getValidatorContract } = require('../../tx/web3') +const rootLogger = require('../../services/logger') +const { signatureToVRS, packSignatures } = require('../../utils/message') +const { readAccessListFile, isRevertError } = require('../../utils/utils') +const { parseAMBMessage } = require('../../../../commons') +const estimateGas = require('../processAMBCollectedSignatures/estimateGas') +const { AlreadyProcessedError, IncompatibleContractError, InvalidValidatorError } = require('../../utils/errors') +const { MAX_CONCURRENT_EVENTS, EXTRA_GAS_ABSOLUTE } = require('../../utils/constants') + +const limit = promiseLimit(MAX_CONCURRENT_EVENTS) + +const { ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST, ORACLE_HOME_TO_FOREIGN_BLOCK_LIST } = process.env +const ORACLE_HOME_SKIP_MANUAL_LANE = process.env.ORACLE_HOME_SKIP_MANUAL_LANE === 'true' + +function processCollectedSignaturesBuilder(config) { + const { home, foreign, mevForeign } = config + + let validatorContract = null + + return async function processCollectedSignatures(signatures) { + const txToSend = [] + + if (validatorContract === null) { + validatorContract = await getValidatorContract(foreign.bridgeContract, foreign.web3) + } + + rootLogger.debug(`Processing ${signatures.length} CollectedSignatures events`) + const callbacks = signatures + .map(colSignature => async () => { + const { messageHash, NumberOfCollectedSignatures } = colSignature.returnValues + + const logger = rootLogger.child({ + eventTransactionHash: colSignature.transactionHash + }) + + logger.info(`Processing CollectedSignatures ${colSignature.transactionHash}`) + const message = await home.bridgeContract.methods.message(messageHash).call() + const parsedMessage = parseAMBMessage(message) + + if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST || ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) { + const sender = parsedMessage.sender.toLowerCase() + const executor = parsedMessage.executor.toLowerCase() + + if (ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST) { + const allowanceList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_ALLOWANCE_LIST, logger) + if (!allowanceList.includes(executor) && !allowanceList.includes(sender)) { + logger.info( + { sender, executor }, + 'Validator skips a message. Neither sender nor executor addresses are in the allowance list.' + ) + return + } + } else if (ORACLE_HOME_TO_FOREIGN_BLOCK_LIST) { + const blockList = await readAccessListFile(ORACLE_HOME_TO_FOREIGN_BLOCK_LIST, logger) + if (blockList.includes(executor)) { + logger.info({ executor }, 'Validator skips a message. Executor address is in the block list.') + return + } + if (blockList.includes(sender)) { + logger.info({ sender }, 'Validator skips a message. Sender address is in the block list.') + return + } + } + } + + if (ORACLE_HOME_SKIP_MANUAL_LANE && parsedMessage.decodedDataType.manualLane) { + logger.info( + { dataType: parsedMessage.dataType }, + 'Validator skips a message. Message was forwarded to the manual lane by the extension' + ) + return + } + + logger.debug({ NumberOfCollectedSignatures }, 'Number of signatures to get') + + const requiredSignatures = [] + requiredSignatures.length = NumberOfCollectedSignatures + requiredSignatures.fill(0) + + const signaturesArray = [] + const [v, r, s] = [[], [], []] + logger.debug('Getting message signatures') + const signaturePromises = requiredSignatures.map(async (el, index) => { + logger.debug({ index }, 'Getting message signature') + const signature = await home.bridgeContract.methods.signature(messageHash, index).call() + const vrs = signatureToVRS(signature) + v.push(vrs.v) + r.push(vrs.r) + s.push(vrs.s) + signaturesArray.push(vrs) + }) + + await Promise.all(signaturePromises) + const signatures = packSignatures(signaturesArray) + logger.info(`Processing messageId: ${parsedMessage.messageId}`) + + let gasEstimate + try { + logger.debug('Estimate gas') + gasEstimate = await estimateGas({ + foreignBridge: foreign.bridgeContract, + validatorContract, + v, + r, + s, + signatures, + message, + numberOfCollectedSignatures: NumberOfCollectedSignatures, + messageId: parsedMessage.messageId, + address: config.validatorAddress + }) + logger.debug({ gasEstimate }, 'Gas estimated') + } catch (e) { + if (e instanceof HttpListProviderError) { + throw new Error('RPC Connection Error: submitSignature Gas Estimate cannot be obtained.') + } else if (e instanceof AlreadyProcessedError) { + logger.info(`Already processed CollectedSignatures ${colSignature.transactionHash}`) + return + } else if (e instanceof IncompatibleContractError || e instanceof InvalidValidatorError) { + logger.error(`The message couldn't be processed; skipping: ${e.message}`) + return + } else { + logger.error(e, 'Unknown error while processing transaction') + throw e + } + } + + const executeData = foreign.bridgeContract.methods.executeSignatures(message, signatures).encodeABI() + const profit = await estimateProfit( + mevForeign.contract, + mevForeign.minGasPrice, + executeData, + mevForeign.flatMinerFee + ) + if (profit === '0') { + logger.error('No MEV opportunity found when testing with min gas price, skipping job') + return + } + logger.info(`Estimated profit of ${profit} when simulating with ${mevForeign.minGasPrice} gas price`) + + txToSend.push({ + profit, + executeData, + data: mevForeign.contract.methods.execute(executeData).encodeABI(), + gasEstimate, + extraGas: EXTRA_GAS_ABSOLUTE, + maxFeePerGas: mevForeign.maxFeePerGas, + maxPriorityFeePerGas: mevForeign.maxPriorityFeePerGas, + transactionReference: colSignature.transactionHash, + to: mevForeign.contractAddress, + value: mevForeign.flatMinerFee + }) + }) + .map(promise => limit(promise)) + + await Promise.all(callbacks) + + return txToSend + } +} + +async function estimateProfit(contract, gasPrice, data, minerFee) { + return contract.methods + .estimateProfit(gasPrice, data) + .call({ value: minerFee }) + .then( + res => res.toString(), + e => { + if (isRevertError(e)) { + return '0' + } + throw e + } + ) +} + +module.exports = { + processCollectedSignaturesBuilder, + estimateProfit +} diff --git a/oracle/src/events/processAffirmationRequests/estimateGas.js b/oracle/src/events/processAffirmationRequests/estimateGas.js index f49ae4e5..6ab9de04 100644 --- a/oracle/src/events/processAffirmationRequests/estimateGas.js +++ b/oracle/src/events/processAffirmationRequests/estimateGas.js @@ -6,11 +6,9 @@ const logger = require('../../services/logger').child({ async function estimateGas({ web3, homeBridge, validatorContract, recipient, value, txHash, address }) { try { - const gasEstimate = await homeBridge.methods.executeAffirmation(recipient, value, txHash).estimateGas({ + return await homeBridge.methods.executeAffirmation(recipient, value, txHash).estimateGas({ from: address }) - - return gasEstimate } catch (e) { if (e instanceof HttpListProviderError) { throw e diff --git a/oracle/src/events/processCollectedSignatures/estimateGas.js b/oracle/src/events/processCollectedSignatures/estimateGas.js index dae22325..08c690de 100644 --- a/oracle/src/events/processCollectedSignatures/estimateGas.js +++ b/oracle/src/events/processCollectedSignatures/estimateGas.js @@ -20,8 +20,7 @@ async function estimateGas({ signatures }) { try { - const gasEstimate = await foreignBridge.methods.executeSignatures(message, signatures).estimateGas() - return gasEstimate + return await foreignBridge.methods.executeSignatures(message, signatures).estimateGas() } catch (e) { if (e instanceof HttpListProviderError) { throw e diff --git a/oracle/src/events/processSignatureRequests/estimateGas.js b/oracle/src/events/processSignatureRequests/estimateGas.js index 374e0817..238f2402 100644 --- a/oracle/src/events/processSignatureRequests/estimateGas.js +++ b/oracle/src/events/processSignatureRequests/estimateGas.js @@ -6,10 +6,9 @@ const logger = require('../../services/logger').child({ async function estimateGas({ web3, homeBridge, validatorContract, signature, message, address }) { try { - const gasEstimate = await homeBridge.methods.submitSignature(signature, message).estimateGas({ + return await homeBridge.methods.submitSignature(signature, message).estimateGas({ from: address }) - return gasEstimate } catch (e) { if (e instanceof HttpListProviderError) { throw e diff --git a/oracle/src/mevSender.js b/oracle/src/mevSender.js new file mode 100644 index 00000000..03dbc09e --- /dev/null +++ b/oracle/src/mevSender.js @@ -0,0 +1,159 @@ +require('../env') +const path = require('path') +const BigNumber = require('bignumber.js') +const { redis } = require('./services/redisClient') +const logger = require('./services/logger') +const { sendTx } = require('./tx/sendTx') +const { getNonce, getChainId, getBlock } = require('./tx/web3') +const { addExtraGas, checkHTTPS, watchdog } = require('./utils/utils') +const { EXIT_CODES, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT } = require('./utils/constants') +const { estimateProfit } = require('./events/processAMBCollectedSignaturesMEV') + +if (process.argv.length < 3) { + logger.error('Please check the number of arguments, config file was not provided') + process.exit(EXIT_CODES.GENERAL_ERROR) +} + +const config = require(path.join('../config/', process.argv[2])) + +const { web3, mevForeign, validatorAddress } = config + +let chainId = 0 +let flashbotsProvider + +async function initialize() { + try { + const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger) + + web3.currentProvider.urls.forEach(checkHttps(config.id)) + + chainId = await getChainId(web3) + flashbotsProvider = await mevForeign.getFlashbotsProvider(chainId) + return runMain() + } catch (e) { + logger.error(e.message) + process.exit(EXIT_CODES.GENERAL_ERROR) + } +} + +async function runMain() { + try { + if (redis.status === 'ready') { + if (config.maxProcessingTime) { + await watchdog(main, config.maxProcessingTime, () => { + logger.fatal('Max processing time reached') + process.exit(EXIT_CODES.MAX_TIME_REACHED) + }) + } else { + await main() + } + } + } catch (e) { + logger.error(e) + } + + setTimeout(runMain, config.pollingInterval) +} + +async function main() { + try { + const jobs = Object.values(await redis.hgetall(config.mevJobsRedisKey)).map(JSON.parse) + const totalJobs = jobs.length + + if (totalJobs === 0) { + logger.debug('Nothing to process') + return + } + + const { baseFeePerGas: pendingBaseFee, number: pendingBlockNumber } = await getBlock(web3, 'pending') + const bestJob = pickBestJob(jobs, pendingBaseFee) + + if (!bestJob) { + logger.info({ totalJobs, pendingBaseFee }, 'No suitable job was found, waiting for a lower gas price') + return + } + + const jobLogger = logger.child({ eventTransactionHash: bestJob.transactionReference }) + + const maxProfit = await estimateProfit( + mevForeign.contract, + mevForeign.minGasPrice, + bestJob.executeData, + bestJob.value + ) + + if (maxProfit === '0') { + jobLogger.info(`No MEV opportunity found when testing with min gas price ${mevForeign.minGasPrice}, removing job`) + await redis.hdel(config.mevJobsRedisKey, bestJob.transactionReference) + return + } + jobLogger.info(`Estimated profit of ${maxProfit} when simulating with ${mevForeign.minGasPrice} gas price`) + bestJob.profit = maxProfit + + if (new BigNumber(pendingBaseFee).gt(mevForeign.minGasPrice)) { + const profit = await estimateProfit(mevForeign.contract, pendingBaseFee, bestJob.executeData, bestJob.value) + if (profit === '0') { + jobLogger.info( + `No MEV opportunity found when testing with current gas price ${pendingBaseFee}, waiting for lower gas price` + ) + bestJob.maxFeePerGas = pendingBaseFee + await redis.hset(config.mevJobsRedisKey, bestJob.transactionReference, JSON.stringify(bestJob)) + return + } + jobLogger.info(`Estimated profit of ${profit} when simulating with ${pendingBaseFee} gas price`) + } + + let gasLimit + if (typeof bestJob.extraGas === 'number') { + gasLimit = addExtraGas(bestJob.gasEstimate + bestJob.extraGas, 0, MAX_GAS_LIMIT) + } else { + gasLimit = addExtraGas(bestJob.gasEstimate, EXTRA_GAS_PERCENTAGE, MAX_GAS_LIMIT) + } + + const nonce = await getNonce(web3, validatorAddress) + jobLogger.info( + { nonce, fromBlock: pendingBlockNumber, toBlock: pendingBlockNumber + mevForeign.bundlesPerIteration - 1 }, + 'Sending MEV bundles' + ) + const txHash = await sendTx({ + data: bestJob.data, + nonce, + value: bestJob.value, + gasLimit, + privateKey: config.validatorPrivateKey, + to: bestJob.to, + chainId, + web3, + gasPriceOptions: { + maxFeePerGas: bestJob.maxFeePerGas, + maxPriorityFeePerGas: bestJob.maxPriorityFeePerGas + }, + mevOptions: { + provider: flashbotsProvider, + fromBlock: pendingBlockNumber, + toBlock: pendingBlockNumber + mevForeign.bundlesPerIteration - 1, + logger + } + }) + + jobLogger.info({ txHash }, `Tx generated ${txHash} for event Tx ${bestJob.transactionReference}`) + + await redis.hset(config.mevJobsRedisKey, bestJob.transactionReference, JSON.stringify(bestJob)) + jobLogger.debug(`Finished processing msg`) + } catch (e) { + logger.error(e) + } +} + +function pickBestJob(jobs, feePerGas) { + const feePerGasBN = new BigNumber(feePerGas) + let best = null + jobs.forEach(job => { + if (feePerGasBN.lt(job.maxFeePerGas) && (!best || new BigNumber(best.profit).lt(job.profit))) { + best = job + } + }) + return best +} + +initialize() diff --git a/oracle/src/mevWatcher.js b/oracle/src/mevWatcher.js new file mode 100644 index 00000000..2df086e7 --- /dev/null +++ b/oracle/src/mevWatcher.js @@ -0,0 +1,251 @@ +require('../env') +const path = require('path') +const { redis } = require('./services/redisClient') +const logger = require('./services/logger') +const { getBlockNumber, getRequiredBlockConfirmations, getEvents } = require('./tx/web3') +const { checkHTTPS, watchdog, syncForEach } = require('./utils/utils') +const { processCollectedSignaturesBuilder } = require('./events/processAMBCollectedSignaturesMEV') +const { + EXIT_CODES, + BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT, + MAX_HISTORY_BLOCK_TO_REPROCESS +} = require('./utils/constants') + +if (process.argv.length < 3) { + logger.error('Please check the number of arguments, config file was not provided') + process.exit(EXIT_CODES.GENERAL_ERROR) +} + +const config = require(path.join('../config/', process.argv[2])) + +const processAMBCollectedSignaturesMEV = processCollectedSignaturesBuilder(config) + +const { + web3, + bridgeContract, + eventContract, + startBlock, + pollingInterval, + chain, + reprocessingOptions, + blockPollingLimit +} = config.main +const lastBlockRedisKey = `${config.id}:lastProcessedBlock` +const lastReprocessedBlockRedisKey = `${config.id}:lastReprocessedBlock` +const seenEventsRedisKey = `${config.id}:seenEvents` +const mevJobsRedisKey = `${config.id}:mevJobs` +let lastProcessedBlock = Math.max(startBlock - 1, 0) +let lastReprocessedBlock +let lastSeenBlockNumber = 0 +let sameBlockNumberCounter = 0 + +async function initialize() { + try { + const checkHttps = checkHTTPS(process.env.ORACLE_ALLOW_HTTP_FOR_RPC, logger) + + web3.currentProvider.urls.forEach(checkHttps(chain)) + + await getLastProcessedBlock() + await getLastReprocessedBlock() + runMain({ sendToQueue: saveJobsToRedis }) + } catch (e) { + logger.error(e) + process.exit(EXIT_CODES.GENERAL_ERROR) + } +} + +async function runMain({ sendToQueue }) { + try { + if (redis.status === 'ready') { + if (config.maxProcessingTime) { + await watchdog(() => main({ sendToQueue }), config.maxProcessingTime, () => { + logger.fatal('Max processing time reached') + process.exit(EXIT_CODES.MAX_TIME_REACHED) + }) + } else { + await main({ sendToQueue }) + } + } + } catch (e) { + logger.error(e) + } + + setTimeout(() => { + runMain({ sendToQueue }) + }, pollingInterval) +} + +async function saveJobsToRedis(jobs) { + return syncForEach(jobs, job => redis.hset(mevJobsRedisKey, job.transactionReference, JSON.stringify(job))) +} + +async function getLastProcessedBlock() { + const result = await redis.get(lastBlockRedisKey) + logger.debug({ fromRedis: result, fromConfig: lastProcessedBlock }, 'Last Processed block obtained') + lastProcessedBlock = result ? parseInt(result, 10) : lastProcessedBlock +} + +async function getLastReprocessedBlock() { + if (reprocessingOptions.enabled) { + const result = await redis.get(lastReprocessedBlockRedisKey) + if (result) { + lastReprocessedBlock = Math.max(parseInt(result, 10), lastProcessedBlock - MAX_HISTORY_BLOCK_TO_REPROCESS) + } else { + lastReprocessedBlock = lastProcessedBlock + } + logger.debug({ block: lastReprocessedBlock }, 'Last reprocessed block obtained') + } else { + // when reprocessing is being enabled not for the first time, + // we do not want to process blocks for which we didn't recorded seen events, + // instead, we want to start from the current block. + // Thus we should delete this reprocessing pointer once it is disabled. + await redis.del(lastReprocessedBlockRedisKey) + } +} + +function updateLastProcessedBlock(lastBlockNumber) { + lastProcessedBlock = lastBlockNumber + return redis.set(lastBlockRedisKey, lastProcessedBlock) +} + +function updateLastReprocessedBlock(lastBlockNumber) { + lastReprocessedBlock = lastBlockNumber + return redis.set(lastReprocessedBlockRedisKey, lastReprocessedBlock) +} + +function processEvents(events) { + switch (config.id) { + case 'amb-collected-signatures-mev': + return processAMBCollectedSignaturesMEV(events) + default: + return [] + } +} + +const eventKey = e => `${e.transactionHash}-${e.logIndex}` + +async function reprocessOldLogs(sendToQueue) { + const fromBlock = lastReprocessedBlock + 1 + const toBlock = lastReprocessedBlock + reprocessingOptions.batchSize + const events = await getEvents({ + contract: eventContract, + event: config.event, + fromBlock, + toBlock, + filter: config.eventFilter + }) + const alreadySeenEvents = await getSeenEvents(fromBlock, toBlock) + const missingEvents = events.filter(e => !alreadySeenEvents[eventKey(e)]) + if (missingEvents.length === 0) { + logger.debug('No missed events were found') + } else { + logger.info(`Found ${missingEvents.length} ${config.event} missed events`) + const job = await processEvents(missingEvents) + logger.info('Missed events transactions to send:', job.length) + if (job.length) { + await sendToQueue(job) + } + } + + await updateLastReprocessedBlock(toBlock) + await deleteSeenEvents(0, toBlock) +} + +async function getSeenEvents(fromBlock, toBlock) { + const keys = await redis.zrangebyscore(seenEventsRedisKey, fromBlock, toBlock) + const res = {} + keys.forEach(k => { + res[k] = true + }) + return res +} + +function deleteSeenEvents(fromBlock, toBlock) { + return redis.zremrangebyscore(seenEventsRedisKey, fromBlock, toBlock) +} + +function addSeenEvents(events) { + return redis.zadd(seenEventsRedisKey, ...events.flatMap(e => [e.blockNumber, eventKey(e)])) +} + +async function getLastBlockToProcess(web3, bridgeContract) { + const [lastBlockNumber, requiredBlockConfirmations] = await Promise.all([ + getBlockNumber(web3), + getRequiredBlockConfirmations(bridgeContract) + ]) + + if (lastBlockNumber < lastSeenBlockNumber) { + sameBlockNumberCounter = 0 + logger.warn({ lastBlockNumber, lastSeenBlockNumber }, 'Received block number less than already seen block') + web3.currentProvider.switchToFallbackRPC() + } else if (lastBlockNumber === lastSeenBlockNumber) { + sameBlockNumberCounter++ + if (sameBlockNumberCounter > 1) { + logger.info({ lastBlockNumber, sameBlockNumberCounter }, 'Received the same block number more than twice') + if (sameBlockNumberCounter >= BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT) { + sameBlockNumberCounter = 0 + logger.warn( + { lastBlockNumber, n: BLOCK_NUMBER_PROGRESS_ITERATIONS_LIMIT }, + 'Received the same block number for too many times. Probably node is not synced anymore' + ) + web3.currentProvider.switchToFallbackRPC() + } + } + } else { + sameBlockNumberCounter = 0 + lastSeenBlockNumber = lastBlockNumber + } + return lastBlockNumber - requiredBlockConfirmations +} + +async function main({ sendToQueue }) { + try { + const lastBlockToProcess = await getLastBlockToProcess(web3, bridgeContract) + + if (reprocessingOptions.enabled) { + if (lastReprocessedBlock + reprocessingOptions.batchSize + reprocessingOptions.blockDelay < lastBlockToProcess) { + await reprocessOldLogs(sendToQueue) + return + } + } + + if (lastBlockToProcess <= lastProcessedBlock) { + logger.debug('All blocks already processed') + return + } + + const fromBlock = lastProcessedBlock + 1 + const rangeEndBlock = blockPollingLimit ? fromBlock + blockPollingLimit : lastBlockToProcess + const toBlock = Math.min(lastBlockToProcess, rangeEndBlock) + + const events = await getEvents({ + contract: eventContract, + event: config.event, + fromBlock, + toBlock, + filter: config.eventFilter + }) + logger.info(`Found ${events.length} ${config.event} events`) + + if (events.length) { + const job = await processEvents(events) + logger.info('Transactions to send:', job.length) + + if (job.length) { + await sendToQueue(job) + } + if (reprocessingOptions.enabled) { + await addSeenEvents(events) + } + } + + logger.debug({ lastProcessedBlock: toBlock.toString() }, 'Updating last processed block') + await updateLastProcessedBlock(toBlock) + } catch (e) { + logger.error(e) + } + + logger.debug('Finished') +} + +initialize() diff --git a/oracle/src/sender.js b/oracle/src/sender.js index 529e95d7..46aa607b 100644 --- a/oracle/src/sender.js +++ b/oracle/src/sender.js @@ -169,7 +169,7 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT const txHash = await sendTx({ data: job.data, nonce, - amount: '0', + value: '0', gasLimit, privateKey: config.validatorPrivateKey, to: job.to, diff --git a/oracle/src/services/web3.js b/oracle/src/services/web3.js index 0d0c49df..0f060de4 100644 --- a/oracle/src/services/web3.js +++ b/oracle/src/services/web3.js @@ -1,4 +1,6 @@ const Web3 = require('web3') +const ethers = require('ethers') +const flashbots = require('@flashbots/ethers-provider-bundle') const { HttpListProvider } = require('./HttpListProvider') const { SafeEthLogsProvider } = require('./SafeEthLogsProvider') const { RedundantHttpListProvider } = require('./RedundantHttpListProvider') @@ -9,6 +11,8 @@ const { COMMON_FOREIGN_RPC_URL, ORACLE_SIDE_RPC_URL, ORACLE_FOREIGN_ARCHIVE_RPC_URL, + ORACLE_MEV_FOREIGN_FLASHBOTS_RPC_URL, + ORACLE_MEV_FOREIGN_FLASHBOTS_AUTH_SIGNING_KEY, ORACLE_RPC_REQUEST_TIMEOUT, ORACLE_HOME_RPC_POLLING_INTERVAL, ORACLE_FOREIGN_RPC_POLLING_INTERVAL @@ -94,6 +98,15 @@ if (foreignUrls.length > 1) { web3ForeignRedundant = new Web3(redundantProvider) } +let getFlashbotsProvider +if (ORACLE_MEV_FOREIGN_FLASHBOTS_RPC_URL) { + const provider = new ethers.providers.JsonRpcProvider(foreignUrls[0]) + const authSigner = new ethers.Wallet(ORACLE_MEV_FOREIGN_FLASHBOTS_AUTH_SIGNING_KEY, provider) + + getFlashbotsProvider = chainId => + flashbots.FlashbotsBundleProvider.create(provider, authSigner, ORACLE_MEV_FOREIGN_FLASHBOTS_RPC_URL, chainId) +} + module.exports = { web3Home, web3Foreign, @@ -102,5 +115,6 @@ module.exports = { web3HomeRedundant, web3ForeignRedundant, web3HomeFallback, - web3ForeignFallback + web3ForeignFallback, + getFlashbotsProvider } diff --git a/oracle/src/tx/sendTx.js b/oracle/src/tx/sendTx.js index ab660ad4..88d95ccd 100644 --- a/oracle/src/tx/sendTx.js +++ b/oracle/src/tx/sendTx.js @@ -1,6 +1,5 @@ -const { toWei } = require('web3').utils - -async function sendTx({ privateKey, data, nonce, gasPrice, gasPriceOptions, amount, gasLimit, to, chainId, web3 }) { +async function sendTx(opts) { + const { privateKey, data, nonce, gasPrice, gasPriceOptions, value, gasLimit, to, chainId, web3, mevOptions } = opts const gasOpts = gasPriceOptions || { gasPrice } const serializedTx = await web3.eth.accounts.signTransaction( { @@ -8,19 +7,32 @@ async function sendTx({ privateKey, data, nonce, gasPrice, gasPriceOptions, amou chainId, to, data, - value: toWei(amount), + value, gas: gasLimit, ...gasOpts }, privateKey ) - return new Promise((res, rej) => - web3.eth - .sendSignedTransaction(serializedTx.rawTransaction) - .once('transactionHash', res) - .once('error', rej) + if (!mevOptions) { + return new Promise((res, rej) => + web3.eth + .sendSignedTransaction(serializedTx.rawTransaction) + .once('transactionHash', res) + .once('error', rej) + ) + } + + mevOptions.logger.debug( + { rawTx: serializedTx.rawTransaction, txHash: serializedTx.transactionHash }, + 'Signed MEV helper transaction' ) + + for (let blockNumber = mevOptions.fromBlock; blockNumber <= mevOptions.toBlock; blockNumber++) { + mevOptions.logger.debug({ txHash: serializedTx.transactionHash, blockNumber }, 'Sending MEV bundle transaction') + await mevOptions.provider.sendRawBundle([serializedTx.rawTransaction], blockNumber) + } + return Promise.resolve(serializedTx.transactionHash) } module.exports = { diff --git a/oracle/src/utils/mev.js b/oracle/src/utils/mev.js new file mode 100644 index 00000000..32f3c0d2 --- /dev/null +++ b/oracle/src/utils/mev.js @@ -0,0 +1,43 @@ +const MEV_HELPER_ABI = [ + { + constant: false, + inputs: [ + { + name: '_data', + type: 'bytes' + } + ], + name: 'execute', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + }, + { + constant: false, + inputs: [ + { + name: '_gasPrice', + type: 'uint256' + }, + { + name: '_data', + type: 'bytes' + } + ], + name: 'estimateProfit', + outputs: [ + { + name: '', + type: 'uint256' + } + ], + payable: true, + stateMutability: 'nonpayable', + type: 'function' + } +] + +module.exports = { + MEV_HELPER_ABI +} diff --git a/oracle/src/watcher.js b/oracle/src/watcher.js index 22bb91ad..e7ef51ac 100644 --- a/oracle/src/watcher.js +++ b/oracle/src/watcher.js @@ -30,7 +30,16 @@ const processAMBInformationRequests = require('./events/processAMBInformationReq const { getTokensState } = require('./utils/tokenState') -const { web3, bridgeContract, eventContract, startBlock, pollingInterval, chain, reprocessingOptions } = config.main +const { + web3, + bridgeContract, + eventContract, + startBlock, + pollingInterval, + chain, + reprocessingOptions, + blockPollingLimit +} = config.main const lastBlockRedisKey = `${config.id}:lastProcessedBlock` const lastReprocessedBlockRedisKey = `${config.id}:lastReprocessedBlock` const seenEventsRedisKey = `${config.id}:seenEvents` @@ -268,7 +277,7 @@ async function main({ sendToQueue }) { } const fromBlock = lastProcessedBlock + 1 - const rangeEndBlock = config.blockPollingLimit ? fromBlock + config.blockPollingLimit : lastBlockToProcess + const rangeEndBlock = blockPollingLimit ? fromBlock + blockPollingLimit : lastBlockToProcess let toBlock = Math.min(lastBlockToProcess, rangeEndBlock) let events = await getEvents({ diff --git a/yarn.lock b/yarn.lock index f55ac82c..2b52489e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1337,6 +1337,34 @@ "@ethersproject/properties" "^5.0.3" "@ethersproject/strings" "^5.0.4" +"@ethersproject/abi@5.5.0", "@ethersproject/abi@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" + integrity sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/abstract-provider@5.5.1", "@ethersproject/abstract-provider@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" + integrity sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + "@ethersproject/abstract-provider@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.1.0.tgz#1f24c56cda5524ef4ed3cfc562a01d6b6f8eeb0b" @@ -1350,6 +1378,17 @@ "@ethersproject/transactions" "^5.1.0" "@ethersproject/web" "^5.1.0" +"@ethersproject/abstract-signer@5.5.0", "@ethersproject/abstract-signer@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" + integrity sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/abstract-signer@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.1.0.tgz#744c7a2d0ebe3cc0bc38294d0f53d5ca3f4e49e3" @@ -1361,6 +1400,17 @@ "@ethersproject/logger" "^5.1.0" "@ethersproject/properties" "^5.1.0" +"@ethersproject/address@5.5.0", "@ethersproject/address@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" + integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.1.0.tgz#3854fd7ebcb6af7597de66f847c3345dae735b58" @@ -1372,6 +1422,13 @@ "@ethersproject/logger" "^5.1.0" "@ethersproject/rlp" "^5.1.0" +"@ethersproject/base64@5.5.0", "@ethersproject/base64@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" + integrity sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/base64@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.1.0.tgz#27240c174d0a4e13f6eae87416fd876caf7f42b6" @@ -1379,6 +1436,23 @@ dependencies: "@ethersproject/bytes" "^5.1.0" +"@ethersproject/basex@5.5.0", "@ethersproject/basex@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" + integrity sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + +"@ethersproject/bignumber@5.5.0", "@ethersproject/bignumber@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" + integrity sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + bn.js "^4.11.9" + "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.1.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.1.1.tgz#84812695253ccbc639117f7ac49ee1529b68e637" @@ -1388,6 +1462,13 @@ "@ethersproject/logger" "^5.1.0" bn.js "^4.4.0" +"@ethersproject/bytes@5.5.0", "@ethersproject/bytes@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" + integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== + dependencies: + "@ethersproject/logger" "^5.5.0" + "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.1.0.tgz#55dfa9c4c21df1b1b538be3accb50fb76d5facfd" @@ -1395,6 +1476,13 @@ dependencies: "@ethersproject/logger" "^5.1.0" +"@ethersproject/constants@5.5.0", "@ethersproject/constants@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" + integrity sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.1.0.tgz#4e7da6367ea0e9be87585d8b09f3fccf384b1452" @@ -1402,6 +1490,36 @@ dependencies: "@ethersproject/bignumber" "^5.1.0" +"@ethersproject/contracts@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" + integrity sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg== + dependencies: + "@ethersproject/abi" "^5.5.0" + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + +"@ethersproject/hash@5.5.0", "@ethersproject/hash@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" + integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.4": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.1.0.tgz#40961d64837d57f580b7b055e0d74174876d891e" @@ -1416,6 +1534,51 @@ "@ethersproject/properties" "^5.1.0" "@ethersproject/strings" "^5.1.0" +"@ethersproject/hdnode@5.5.0", "@ethersproject/hdnode@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" + integrity sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/wordlists" "^5.5.0" + +"@ethersproject/json-wallets@5.5.0", "@ethersproject/json-wallets@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz#dd522d4297e15bccc8e1427d247ec8376b60e325" + integrity sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hdnode" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.5.0", "@ethersproject/keccak256@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" + integrity sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + js-sha3 "0.8.0" + "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0-beta.130", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.1.0.tgz#fdcd88fb13bfef4271b225cdd8dec4d315c8e60e" @@ -1424,11 +1587,23 @@ "@ethersproject/bytes" "^5.1.0" js-sha3 "0.5.7" +"@ethersproject/logger@5.5.0", "@ethersproject/logger@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" + integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== + "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.1.0.tgz#4cdeeefac029373349d5818f39c31b82cc6d9bbf" integrity sha512-wtUaD1lBX10HBXjjKV9VHCBnTdUaKQnQ2XSET1ezglqLdPdllNOIlLfhyCRqXm5xwcjExVI5ETokOYfjPtaAlw== +"@ethersproject/networks@5.5.2", "@ethersproject/networks@^5.5.0": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.2.tgz#784c8b1283cd2a931114ab428dae1bd00c07630b" + integrity sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ== + dependencies: + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.1.0.tgz#f537290cb05aa6dc5e81e910926c04cfd5814bca" @@ -1436,6 +1611,21 @@ dependencies: "@ethersproject/logger" "^5.1.0" +"@ethersproject/pbkdf2@5.5.0", "@ethersproject/pbkdf2@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" + integrity sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + +"@ethersproject/properties@5.5.0", "@ethersproject/properties@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" + integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== + dependencies: + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.1.0.tgz#9484bd6def16595fc6e4bdc26f29dff4d3f6ac42" @@ -1443,6 +1633,47 @@ dependencies: "@ethersproject/logger" "^5.1.0" +"@ethersproject/providers@5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.2.tgz#131ccf52dc17afd0ab69ed444b8c0e3a27297d99" + integrity sha512-hkbx7x/MKcRjyrO4StKXCzCpWer6s97xnm34xkfPiarhtEUVAN4TBBpamM+z66WcTt7H5B53YwbRj1n7i8pZoQ== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@5.5.1", "@ethersproject/random@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.5.1.tgz#7cdf38ea93dc0b1ed1d8e480ccdaf3535c555415" + integrity sha512-YaU2dQ7DuhL5Au7KbcQLHxcRHfgyNgvFV4sQOo0HrtW3Zkrc9ctWNz8wXQ4uCSfSDsqX2vcjhroxU5RQRV0nqA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/rlp@5.5.0", "@ethersproject/rlp@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" + integrity sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.1.0.tgz#700f4f071c27fa298d3c1d637485fefe919dd084" @@ -1451,6 +1682,27 @@ "@ethersproject/bytes" "^5.1.0" "@ethersproject/logger" "^5.1.0" +"@ethersproject/sha2@5.5.0", "@ethersproject/sha2@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" + integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.5.0", "@ethersproject/signing-key@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" + integrity sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + "@ethersproject/signing-key@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.1.0.tgz#6eddfbddb6826b597b9650e01acf817bf8991b9c" @@ -1462,6 +1714,27 @@ bn.js "^4.4.0" elliptic "6.5.4" +"@ethersproject/solidity@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.5.0.tgz#2662eb3e5da471b85a20531e420054278362f93f" + integrity sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/strings@5.5.0", "@ethersproject/strings@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" + integrity sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.1.0.tgz#0f95a56c3c8c9d5510a06c241d818779750e2da5" @@ -1471,6 +1744,21 @@ "@ethersproject/constants" "^5.1.0" "@ethersproject/logger" "^5.1.0" +"@ethersproject/transactions@5.5.0", "@ethersproject/transactions@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" + integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.1.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.1.1.tgz#5a6bbb25fb062c3cc75eb0db12faefcdd3870813" @@ -1486,6 +1774,47 @@ "@ethersproject/rlp" "^5.1.0" "@ethersproject/signing-key" "^5.1.0" +"@ethersproject/units@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.5.0.tgz#104d02db5b5dc42cc672cc4587bafb87a95ee45e" + integrity sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/wallet@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" + integrity sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/hdnode" "^5.5.0" + "@ethersproject/json-wallets" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/wordlists" "^5.5.0" + +"@ethersproject/web@5.5.1", "@ethersproject/web@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.1.tgz#cfcc4a074a6936c657878ac58917a61341681316" + integrity sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg== + dependencies: + "@ethersproject/base64" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/web@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.1.0.tgz#ed56bbe4e3d9a8ffe3b2ed882da5c62d3551381b" @@ -1497,6 +1826,17 @@ "@ethersproject/properties" "^5.1.0" "@ethersproject/strings" "^5.1.0" +"@ethersproject/wordlists@5.5.0", "@ethersproject/wordlists@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" + integrity sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@evocateur/libnpmaccess@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845" @@ -1576,6 +1916,14 @@ resolved "https://registry.yarnpkg.com/@findeth/abi/-/abi-0.7.1.tgz#60d0801cb252e587dc3228f00c00581bb748aebc" integrity sha512-9uNu+/UxeuIibxIB7slf7BGG2PWjgBZr+rKzohhLb7VuoZjmlCcKZkenqwErROxkPdsap7OGO/o1DuYMvObMvw== +"@flashbots/ethers-provider-bundle@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@flashbots/ethers-provider-bundle/-/ethers-provider-bundle-0.4.3.tgz#d8d23684eb02829181672176b110bc262195ec48" + integrity sha512-vH5XhdNjFDG3+m2rHa4TBf7ljv+LDdtWldZCEmwmCggZN70cb2J1x7DlNROT/tE4keTMUjydMs4CfAOOyKc6UQ== + dependencies: + ts-node "^9.1.0" + typescript "^4.1.2" + "@graphql-tools/batch-delegate@^6.2.4", "@graphql-tools/batch-delegate@^6.2.6": version "6.2.6" resolved "https://registry.yarnpkg.com/@graphql-tools/batch-delegate/-/batch-delegate-6.2.6.tgz#fbea98dc825f87ef29ea5f3f371912c2a2aa2f2c" @@ -6665,6 +7013,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + bech32@=1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.3.tgz#bd47a8986bbb3eec34a56a097a84b8d3e9a2dfcd" @@ -8482,6 +8835,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-fetch@3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" @@ -10831,6 +11189,42 @@ ethers@^4.0.32, ethers@^4.0.40: uuid "2.0.1" xmlhttprequest "1.8.0" +ethers@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.5.3.tgz#1e361516711c0c3244b6210e7e3ecabf0c75fca0" + integrity sha512-fTT4WT8/hTe/BLwRUtl7I5zlpF3XC3P/Xwqxc5AIP2HGlH15qpmjs0Ou78az93b1rLITzXLFxoNX63B8ZbUd7g== + dependencies: + "@ethersproject/abi" "5.5.0" + "@ethersproject/abstract-provider" "5.5.1" + "@ethersproject/abstract-signer" "5.5.0" + "@ethersproject/address" "5.5.0" + "@ethersproject/base64" "5.5.0" + "@ethersproject/basex" "5.5.0" + "@ethersproject/bignumber" "5.5.0" + "@ethersproject/bytes" "5.5.0" + "@ethersproject/constants" "5.5.0" + "@ethersproject/contracts" "5.5.0" + "@ethersproject/hash" "5.5.0" + "@ethersproject/hdnode" "5.5.0" + "@ethersproject/json-wallets" "5.5.0" + "@ethersproject/keccak256" "5.5.0" + "@ethersproject/logger" "5.5.0" + "@ethersproject/networks" "5.5.2" + "@ethersproject/pbkdf2" "5.5.0" + "@ethersproject/properties" "5.5.0" + "@ethersproject/providers" "5.5.2" + "@ethersproject/random" "5.5.1" + "@ethersproject/rlp" "5.5.0" + "@ethersproject/sha2" "5.5.0" + "@ethersproject/signing-key" "5.5.0" + "@ethersproject/solidity" "5.5.0" + "@ethersproject/strings" "5.5.0" + "@ethersproject/transactions" "5.5.0" + "@ethersproject/units" "5.5.0" + "@ethersproject/wallet" "5.5.0" + "@ethersproject/web" "5.5.1" + "@ethersproject/wordlists" "5.5.0" + ethjs-unit@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" @@ -12601,7 +12995,7 @@ hash.js@1.1.3: inherits "^2.0.3" minimalistic-assert "^1.0.0" -hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -14709,16 +15103,16 @@ js-sha3@0.5.7, js-sha3@^0.5.7: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= +js-sha3@0.8.0, js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + js-sha3@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.6.1.tgz#5b89f77a7477679877f58c4a075240934b1f95c0" integrity sha1-W4n3enR3Z5h39YxKB1JAk0sflcA= -js-sha3@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -16720,9 +17114,9 @@ nanoid@^3.1.12, nanoid@^3.1.3: integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== nanoid@^3.1.28: - version "3.1.30" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" - integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== + version "3.2.0" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" + integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== nanomatch@^1.2.9: version "1.2.13" @@ -20950,7 +21344,7 @@ scrypt-js@2.0.4: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== -scrypt-js@^3.0.0, scrypt-js@^3.0.1: +scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== @@ -21580,6 +21974,14 @@ source-map-support@^0.5.16, source-map-support@^0.5.3, source-map-support@^0.5.6 buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@^0.5.17: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -22891,6 +23293,18 @@ ts-node@^8.0.2: source-map-support "^0.5.6" yn "3.1.1" +ts-node@^9.1.0: + version "9.1.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== + dependencies: + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + ts-pnp@1.1.2, ts-pnp@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552" @@ -23051,6 +23465,11 @@ typescript@^3.5.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a" integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== +typescript@^4.1.2: + version "4.5.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" + integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== + ua-parser-js@^0.7.18: version "0.7.28" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" @@ -25772,6 +26191,11 @@ ws@7.4.5, ws@^7.2.1, ws@^7.2.3, ws@^7.3.1, ws@^7.4.3: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.5.tgz#a484dd851e9beb6fdb420027e3885e8ce48986c1" integrity sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g== +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"