From 176541c6cfccdbe83f9f7c0ecc6c9a74ec63789e Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 23 Mar 2022 18:36:27 -0300 Subject: [PATCH 001/100] feat: add Arbitrum GRT bridge Deployment commands in the CLI are also updated to include an L2 deployment. Configuration and address book entries for Arbitrum are added as well. --- .solcover.js | 2 +- arbitrum-addresses.json | 42 + cli/address-book.ts | 2 +- cli/cli.ts | 3 + cli/commands/bridge/index.ts | 22 + cli/commands/bridge/to-l1.ts | 214 ++ cli/commands/bridge/to-l2.ts | 151 ++ cli/commands/migrate.ts | 27 +- cli/commands/protocol/configure-bridge.ts | 89 + cli/commands/protocol/get.ts | 12 + cli/commands/protocol/index.ts | 8 +- cli/commands/protocol/list.ts | 9 +- cli/commands/protocol/set.ts | 25 + cli/contracts.ts | 34 +- cli/defaults.ts | 16 + cli/env.ts | 2 +- cli/network.ts | 3 +- cli/utils.ts | 16 + config/graph.arbitrum-one.yml | 121 + config/graph.mainnet.yml | 11 + contracts/arbitrum/AddressAliasHelper.sol | 46 + contracts/arbitrum/IArbToken.sol | 47 + contracts/arbitrum/IBridge.sol | 77 + contracts/arbitrum/IInbox.sol | 88 + contracts/arbitrum/IMessageProvider.sol | 32 + contracts/arbitrum/IOutbox.sol | 58 + contracts/arbitrum/ITokenGateway.sol | 74 + contracts/arbitrum/L1ArbitrumMessenger.sol | 103 + contracts/arbitrum/L2ArbitrumMessenger.sol | 47 + contracts/arbitrum/README.md | 5 + contracts/gateway/BridgeEscrow.sol | 40 + contracts/gateway/GraphTokenGateway.sol | 57 + contracts/gateway/L1GraphTokenGateway.sol | 357 +++ contracts/governance/Managed.sol | 12 +- contracts/l2/gateway/L2GraphTokenGateway.sol | 302 +++ contracts/l2/token/GraphTokenUpgradeable.sol | 204 ++ contracts/l2/token/L2GraphToken.sol | 94 + contracts/tests/arbitrum/BridgeMock.sol | 125 ++ contracts/tests/arbitrum/InboxMock.sol | 198 ++ contracts/tests/arbitrum/OutboxMock.sol | 157 ++ contracts/token/IGraphToken.sol | 8 + gre/gre.ts | 2 +- hardhat.config.ts | 7 + package.json | 9 +- test/epochs.test.ts | 2 + test/gateway/bridgeEscrow.test.ts | 77 + test/gateway/l1GraphTokenGateway.test.ts | 630 ++++++ test/graphToken.test.ts | 274 +-- test/l2/l2GraphToken.test.ts | 107 + test/l2/l2GraphTokenGateway.test.ts | 409 ++++ test/lib/deployment.ts | 56 + test/lib/fixtures.ts | 232 +- test/lib/graphTokenTests.ts | 287 +++ test/lib/testHelpers.ts | 51 +- test/staking/rebate.test.ts | 3 +- yarn.lock | 2104 +++++++++--------- 56 files changed, 5798 insertions(+), 1392 deletions(-) create mode 100644 arbitrum-addresses.json create mode 100644 cli/commands/bridge/index.ts create mode 100644 cli/commands/bridge/to-l1.ts create mode 100644 cli/commands/bridge/to-l2.ts create mode 100644 cli/commands/protocol/configure-bridge.ts create mode 100644 config/graph.arbitrum-one.yml create mode 100644 contracts/arbitrum/AddressAliasHelper.sol create mode 100644 contracts/arbitrum/IArbToken.sol create mode 100644 contracts/arbitrum/IBridge.sol create mode 100644 contracts/arbitrum/IInbox.sol create mode 100644 contracts/arbitrum/IMessageProvider.sol create mode 100644 contracts/arbitrum/IOutbox.sol create mode 100644 contracts/arbitrum/ITokenGateway.sol create mode 100644 contracts/arbitrum/L1ArbitrumMessenger.sol create mode 100644 contracts/arbitrum/L2ArbitrumMessenger.sol create mode 100644 contracts/arbitrum/README.md create mode 100644 contracts/gateway/BridgeEscrow.sol create mode 100644 contracts/gateway/GraphTokenGateway.sol create mode 100644 contracts/gateway/L1GraphTokenGateway.sol create mode 100644 contracts/l2/gateway/L2GraphTokenGateway.sol create mode 100644 contracts/l2/token/GraphTokenUpgradeable.sol create mode 100644 contracts/l2/token/L2GraphToken.sol create mode 100644 contracts/tests/arbitrum/BridgeMock.sol create mode 100644 contracts/tests/arbitrum/InboxMock.sol create mode 100644 contracts/tests/arbitrum/OutboxMock.sol create mode 100644 test/gateway/bridgeEscrow.test.ts create mode 100644 test/gateway/l1GraphTokenGateway.test.ts create mode 100644 test/l2/l2GraphToken.test.ts create mode 100644 test/l2/l2GraphTokenGateway.test.ts create mode 100644 test/lib/graphTokenTests.ts diff --git a/.solcover.js b/.solcover.js index 747f078e5..b10738c1f 100644 --- a/.solcover.js +++ b/.solcover.js @@ -1,4 +1,4 @@ -const skipFiles = ['bancor', 'ens', 'erc1056'] +const skipFiles = ['bancor', 'ens', 'erc1056', 'arbitrum', 'tests/arbitrum'] module.exports = { providerOptions: { diff --git a/arbitrum-addresses.json b/arbitrum-addresses.json new file mode 100644 index 000000000..4249270ad --- /dev/null +++ b/arbitrum-addresses.json @@ -0,0 +1,42 @@ +{ + "source": "https://developer.offchainlabs.com/docs/useful_addresses", + "1": { + "L1GatewayRouter": { + "address": "0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef" + }, + "IInbox": { + "address": "0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f" + } + }, + "4": { + "L1GatewayRouter": { + "address": "0x70C143928eCfFaf9F5b406f7f4fC28Dc43d68380" + }, + "IInbox": { + "address": "0x578BAde599406A8fE3d24Fd7f7211c0911F5B29e" + } + }, + "5": { + "L1GatewayRouter": { + "address": "0x4c7708168395aEa569453Fc36862D2ffcDaC588c" + }, + "IInbox": { + "address": "0x6BEbC4925716945D46F0Ec336D5C2564F419682C" + } + }, + "42161": { + "L2GatewayRouter": { + "address": "0x5288c571Fd7aD117beA99bF60FE0846C4E84F933" + } + }, + "421611": { + "L2GatewayRouter": { + "address": "0x9413AD42910c1eA60c737dB5f58d1C504498a3cD" + } + }, + "421613": { + "L2GatewayRouter": { + "address": "0xE5B9d8d42d656d1DcB8065A6c012FE3780246041" + } + } +} diff --git a/cli/address-book.ts b/cli/address-book.ts index 39818c82a..5c938fe2a 100644 --- a/cli/address-book.ts +++ b/cli/address-book.ts @@ -28,7 +28,7 @@ export interface AddressBook { } export const getAddressBook = (path: string, chainId: string): AddressBook => { - if (!path) throw new Error(`A path the the address book file is required.`) + if (!path) throw new Error(`A path to the address book file is required.`) if (!chainId) throw new Error(`A chainId is required.`) const addressBook = JSON.parse(fs.readFileSync(path, 'utf8') || '{}') as AddressBookJson diff --git a/cli/cli.ts b/cli/cli.ts index a9e754536..ab2449679 100755 --- a/cli/cli.ts +++ b/cli/cli.ts @@ -8,6 +8,7 @@ import { proxyCommand } from './commands/proxy' import { protocolCommand } from './commands/protocol' import { contractsCommand } from './commands/contracts' import { airdropCommand } from './commands/airdrop' +import { bridgeCommand } from './commands/bridge' import { cliOpts } from './defaults' @@ -28,11 +29,13 @@ yargs .option('p', cliOpts.providerUrl) .option('n', cliOpts.accountNumber) .option('s', cliOpts.skipConfirmation) + .option('r', cliOpts.arbitrumAddressBook) .command(deployCommand) .command(migrateCommand) .command(proxyCommand) .command(protocolCommand) .command(contractsCommand) .command(airdropCommand) + .command(bridgeCommand) .demandCommand(1, 'Choose a command from the above list') .help().argv diff --git a/cli/commands/bridge/index.ts b/cli/commands/bridge/index.ts new file mode 100644 index 000000000..fcdf1bcc1 --- /dev/null +++ b/cli/commands/bridge/index.ts @@ -0,0 +1,22 @@ +import yargs, { Argv } from 'yargs' + +import { redeemSendToL2Command, sendToL2Command } from './to-l2' +import { startSendToL1Command, finishSendToL1Command, waitFinishSendToL1Command } from './to-l1' +import { cliOpts } from '../../defaults' + +export const bridgeCommand = { + command: 'bridge', + describe: 'Graph token bridge actions.', + builder: (yargs: Argv): yargs.Argv => { + return yargs + .option('-l', cliOpts.l2ProviderUrl) + .command(sendToL2Command) + .command(redeemSendToL2Command) + .command(startSendToL1Command) + .command(finishSendToL1Command) + .command(waitFinishSendToL1Command) + }, + handler: (): void => { + yargs.showHelp() + }, +} diff --git a/cli/commands/bridge/to-l1.ts b/cli/commands/bridge/to-l1.ts new file mode 100644 index 000000000..f3673a53a --- /dev/null +++ b/cli/commands/bridge/to-l1.ts @@ -0,0 +1,214 @@ +import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' +import { logger } from '../../logging' +import { getAddressBook } from '../../address-book' +import { getProvider, sendTransaction, toGRT } from '../../network' +import { chainIdIsL2 } from '../../utils' +import { loadAddressBookContract } from '../../contracts' +import { + L2TransactionReceipt, + getL2Network, + L2ToL1MessageStatus, + L2ToL1MessageWriter, +} from '@arbitrum/sdk' +import { L2GraphTokenGateway } from '../../../build/types/L2GraphTokenGateway' +import { BigNumber } from 'ethers' +import { JsonRpcProvider } from '@ethersproject/providers' +import { providers } from 'ethers' + +const FOURTEEN_DAYS_IN_SECONDS = 24 * 3600 * 14 + +const BLOCK_SEARCH_THRESHOLD = 6 * 3600 +const searchForArbBlockByTimestamp = async ( + l2Provider: JsonRpcProvider, + timestamp: number, +): Promise => { + let step = 131072 + let block = await l2Provider.getBlock('latest') + while (block.timestamp > timestamp) { + while (block.number - step < 0) { + step = Math.round(step / 2) + } + block = await l2Provider.getBlock(block.number - step) + } + while (step > 1 && Math.abs(block.timestamp - timestamp) > BLOCK_SEARCH_THRESHOLD) { + step = Math.round(step / 2) + if (block.timestamp - timestamp > 0) { + block = await l2Provider.getBlock(block.number - step) + } else { + block = await l2Provider.getBlock(block.number + step) + } + } + return block.number +} + +const wait = (ms: number): Promise => { + return new Promise((res) => setTimeout(res, ms)) +} + +const waitUntilOutboxEntryCreatedWithCb = async ( + msg: L2ToL1MessageWriter, + provider: providers.Provider, + retryDelay: number, + callback: () => void, +) => { + let done = false + while (!done) { + const status = await msg.status(provider) + if (status == L2ToL1MessageStatus.CONFIRMED || status == L2ToL1MessageStatus.EXECUTED) { + done = true + } else { + callback() + await wait(retryDelay) + } + } +} + +export const startSendToL1 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Sending tokens to L1 <<<\n`) + const l2Provider = getProvider(cliArgs.l2ProviderUrl) + const l2ChainId = (await l2Provider.getNetwork()).chainId + + if (chainIdIsL2(cli.chainId) || !chainIdIsL2(l2ChainId)) { + throw new Error( + 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', + ) + } + + const l1GRT = cli.contracts['GraphToken'] + const l1GRTAddress = l1GRT.address + const amount = toGRT(cliArgs.amount) + const recipient = cliArgs.recipient ? cliArgs.recipient : cli.wallet.address + const l2Wallet = cli.wallet.connect(l2Provider) + const l2AddressBook = getAddressBook(cliArgs.addressBook, l2ChainId.toString()) + + const gateway = loadAddressBookContract('L2GraphTokenGateway', l2AddressBook, l2Wallet) + const l2GRT = loadAddressBookContract('L2GraphToken', l2AddressBook, l2Wallet) + + const l1Gateway = cli.contracts['L1GraphTokenGateway'] + logger.info(`Will send ${cliArgs.amount} GRT to ${recipient}`) + logger.info(`Using L2 gateway ${gateway.address} and L1 gateway ${l1Gateway.address}`) + + const params = [l1GRTAddress, recipient, amount, '0x'] + logger.info('Approving token transfer') + await sendTransaction(l2Wallet, l2GRT, 'approve', [gateway.address, amount]) + logger.info('Sending outbound transfer transaction') + const receipt = await sendTransaction( + l2Wallet, + gateway, + 'outboundTransfer(address,address,uint256,bytes)', + params, + ) + const l2Receipt = new L2TransactionReceipt(receipt) + const l2ToL1Message = ( + await l2Receipt.getL2ToL1Messages(cli.wallet, await getL2Network(l2Provider)) + )[0] + + logger.info(`The transaction generated an L2 to L1 message in outbox with eth block number:`) + logger.info(l2ToL1Message.event.ethBlockNum.toString()) + logger.info( + `After the dispute period is finalized (in ~1 week), you can finalize this by calling`, + ) + logger.info(`finish-send-to-l1 with the following txhash:`) + logger.info(l2Receipt.transactionHash) +} + +export const finishSendToL1 = async ( + cli: CLIEnvironment, + cliArgs: CLIArgs, + wait: boolean, +): Promise => { + logger.info(`>>> Finishing transaction sending tokens to L1 <<<\n`) + const l2Provider = getProvider(cliArgs.l2ProviderUrl) + const l2ChainId = (await l2Provider.getNetwork()).chainId + + if (chainIdIsL2(cli.chainId) || !chainIdIsL2(l2ChainId)) { + throw new Error( + 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', + ) + } + + const l2AddressBook = getAddressBook(cliArgs.addressBook, l2ChainId.toString()) + + const gateway = loadAddressBookContract( + 'L2GraphTokenGateway', + l2AddressBook, + l2Provider, + ) as L2GraphTokenGateway + let txHash: string + if (cliArgs.txHash) { + txHash = cliArgs.txHash + } else { + logger.info( + `Looking for withdrawals initiated by ${cli.wallet.address} in roughly the last 14 days`, + ) + const fromBlock = await searchForArbBlockByTimestamp( + l2Provider, + Math.round(Date.now() / 1000) - FOURTEEN_DAYS_IN_SECONDS, + ) + const filt = gateway.filters.WithdrawalInitiated(null, cli.wallet.address) + const allEvents = await gateway.queryFilter(filt, BigNumber.from(fromBlock).toHexString()) + if (allEvents.length == 0) { + throw new Error('No withdrawals found') + } + txHash = allEvents[allEvents.length - 1].transactionHash + } + logger.info(`Getting receipt from transaction ${txHash}`) + const receipt = await l2Provider.getTransactionReceipt(txHash) + + const l2Receipt = new L2TransactionReceipt(receipt) + logger.info(`Getting L2 to L1 message...`) + const l2ToL1Message = ( + await l2Receipt.getL2ToL1Messages(cli.wallet, await getL2Network(l2Provider)) + )[0] + + if (wait) { + const retryDelayMs = cliArgs.retryDelaySeconds ? cliArgs.retryDelaySeconds * 1000 : 60000 + logger.info('Waiting for outbox entry to be created, this can take a full week...') + await waitUntilOutboxEntryCreatedWithCb(l2ToL1Message, l2Provider, retryDelayMs, () => { + logger.info('Still waiting...') + }) + } else { + const status = await l2ToL1Message.status(l2Provider) + if (status == L2ToL1MessageStatus.EXECUTED) { + throw new Error('Message already executed!') + } else if (status != L2ToL1MessageStatus.CONFIRMED) { + throw new Error( + `Transaction is not confirmed, status is ${status} when it should be ${L2ToL1MessageStatus.CONFIRMED}. Has the dispute period passed?`, + ) + } + } + + logger.info('Executing outbox transaction') + const tx = await l2ToL1Message.execute(l2Provider) + const outboxExecuteReceipt = await tx.wait() + logger.info('Transaction succeeded! tx hash:') + logger.info(outboxExecuteReceipt.transactionHash) +} + +export const startSendToL1Command = { + command: 'start-send-to-l1 [recipient]', + describe: 'Start an L2-to-L1 Graph Token transaction', + handler: async (argv: CLIArgs): Promise => { + return startSendToL1(await loadEnv(argv), argv) + }, +} + +export const finishSendToL1Command = { + command: 'finish-send-to-l1 [txHash]', + describe: + 'Finish an L2-to-L1 Graph Token transaction. L2 dispute period must have completed. ' + + 'If txHash is not specified, the last withdrawal from the main account in the past 14 days will be redeemed.', + handler: async (argv: CLIArgs): Promise => { + return finishSendToL1(await loadEnv(argv), argv, false) + }, +} + +export const waitFinishSendToL1Command = { + command: 'wait-finish-send-to-l1 [txHash] [retryDelaySeconds]', + describe: + "Wait for an L2-to-L1 Graph Token transaction's dispute period to complete (which takes about a week), and then finalize it. " + + 'If txHash is not specified, the last withdrawal from the main account in the past 14 days will be redeemed.', + handler: async (argv: CLIArgs): Promise => { + return finishSendToL1(await loadEnv(argv), argv, true) + }, +} diff --git a/cli/commands/bridge/to-l2.ts b/cli/commands/bridge/to-l2.ts new file mode 100644 index 000000000..f176bc482 --- /dev/null +++ b/cli/commands/bridge/to-l2.ts @@ -0,0 +1,151 @@ +import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' +import { logger } from '../../logging' +import { getProvider, sendTransaction, toGRT } from '../../network' +import { utils } from 'ethers' +import { parseEther } from '@ethersproject/units' +import { + L1TransactionReceipt, + L1ToL2MessageStatus, + L1ToL2MessageWriter, + L1ToL2MessageGasEstimator, +} from '@arbitrum/sdk' +import { chainIdIsL2 } from '../../utils' + +const logAutoRedeemReason = (autoRedeemRec) => { + if (autoRedeemRec == null) { + logger.info(`Auto redeem was not attempted.`) + return + } + logger.info(`Auto redeem reverted.`) +} + +const checkAndRedeemMessage = async (l1ToL2Message: L1ToL2MessageWriter) => { + logger.info(`Waiting for status of ${l1ToL2Message.retryableCreationId}`) + const res = await l1ToL2Message.waitForStatus() + logger.info('Getting auto redeem attempt') + const autoRedeemRec = await l1ToL2Message.getAutoRedeemAttempt() + const l2TxReceipt = res.status === L1ToL2MessageStatus.REDEEMED ? res.l2TxReceipt : autoRedeemRec + let l2TxHash = l2TxReceipt ? l2TxReceipt.transactionHash : 'null' + if (res.status === L1ToL2MessageStatus.FUNDS_DEPOSITED_ON_L2) { + /** Message wasn't auto-redeemed! */ + logger.warn('Funds were deposited on L2 but the retryable ticket was not redeemed') + logAutoRedeemReason(autoRedeemRec) + logger.info('Attempting to redeem...') + await l1ToL2Message.redeem() + l2TxHash = (await l1ToL2Message.getSuccessfulRedeem()).transactionHash + } else if (res.status != L1ToL2MessageStatus.REDEEMED) { + throw new Error(`Unexpected L1ToL2MessageStatus ${res.status}`) + } + logger.info(`Transfer successful: ${l2TxHash}`) +} + +export const sendToL2 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Sending tokens to L2 <<<\n`) + const l2Provider = getProvider(cliArgs.l2ProviderUrl) + const l2ChainId = (await l2Provider.getNetwork()).chainId + + if (chainIdIsL2(cli.chainId) || !chainIdIsL2(l2ChainId)) { + throw new Error( + 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', + ) + } + const gateway = cli.contracts['L1GraphTokenGateway'] + const l1GRT = cli.contracts['GraphToken'] + const l1GRTAddress = l1GRT.address + const amount = toGRT(cliArgs.amount) + const recipient = cliArgs.recipient ? cliArgs.recipient : cli.wallet.address + const l2Dest = await gateway.l2Counterpart() + + logger.info(`Will send ${cliArgs.amount} GRT to ${recipient}`) + logger.info(`Using L1 gateway ${gateway.address} and L2 gateway ${l2Dest}`) + // See https://github.com/OffchainLabs/arbitrum/blob/master/packages/arb-ts/src/lib/bridge.ts + const depositCalldata = await gateway.getOutboundCalldata( + l1GRTAddress, + cli.wallet.address, + recipient, + amount, + '0x', + ) + + // Comment from Offchain Labs' implementation: + // we add a 0.05 ether "deposit" buffer to pay for execution in the gas estimation + logger.info('Estimating retryable ticket gas:') + const baseFee = (await cli.wallet.provider.getBlock('latest')).baseFeePerGas + const gasEstimator = new L1ToL2MessageGasEstimator(l2Provider) + const gasParams = await gasEstimator.estimateMessage( + gateway.address, + l2Dest, + depositCalldata, + parseEther('0'), + baseFee, + gateway.address, + gateway.address, + ) + const maxGas = gasParams.maxGasBid + const gasPriceBid = gasParams.maxGasPriceBid + const maxSubmissionPrice = gasParams.maxSubmissionPriceBid + logger.info( + `Using max gas: ${maxGas}, gas price bid: ${gasPriceBid}, max submission price: ${maxSubmissionPrice}`, + ) + + const ethValue = maxSubmissionPrice.add(gasPriceBid.mul(maxGas)) + logger.info(`tx value: ${ethValue}`) + const data = utils.defaultAbiCoder.encode(['uint256', 'bytes'], [maxSubmissionPrice, '0x']) + + const params = [l1GRTAddress, recipient, amount, maxGas, gasPriceBid, data] + logger.info('Approving token transfer') + await sendTransaction(cli.wallet, l1GRT, 'approve', [gateway.address, amount]) + logger.info('Sending outbound transfer transaction') + const receipt = await sendTransaction(cli.wallet, gateway, 'outboundTransfer', params, { + value: ethValue, + }) + const l1Receipt = new L1TransactionReceipt(receipt) + const l1ToL2Message = await l1Receipt.getL1ToL2Message(cli.wallet.connect(l2Provider)) + + logger.info('Waiting for message to propagate to L2...') + try { + await checkAndRedeemMessage(l1ToL2Message) + } catch (e) { + logger.error('Auto redeem failed') + logger.error(e) + logger.error('You can re-attempt using redeem-send-to-l2 with the following txHash:') + logger.error(receipt.transactionHash) + } +} + +export const redeemSendToL2 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Redeeming pending tokens on L2 <<<\n`) + const l2Provider = getProvider(cliArgs.l2ProviderUrl) + const l2ChainId = (await l2Provider.getNetwork()).chainId + + if (chainIdIsL2(cli.chainId) || !chainIdIsL2(l2ChainId)) { + throw new Error( + 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', + ) + } + const l1Provider = cli.wallet.provider + + const receipt = await l1Provider.getTransactionReceipt(cliArgs.txHash) + const l1Receipt = new L1TransactionReceipt(receipt) + const l1ToL2Messages = await l1Receipt.getL1ToL2Messages(cli.wallet.connect(l2Provider)) + const l1ToL2Message = l1ToL2Messages[0] + + logger.info('Checking message status in L2...') + await checkAndRedeemMessage(l1ToL2Message) +} + +export const sendToL2Command = { + command: 'send-to-l2 [recipient]', + describe: 'Perform an L1-to-L2 Graph Token transaction', + handler: async (argv: CLIArgs): Promise => { + return sendToL2(await loadEnv(argv), argv) + }, +} + +export const redeemSendToL2Command = { + command: 'redeem-send-to-l2 ', + describe: 'Finish an L1-to-L2 Graph Token transaction if it failed to auto-redeem', + handler: async (argv: CLIArgs): Promise => { + return redeemSendToL2(await loadEnv(argv), argv) + }, +} diff --git a/cli/commands/migrate.ts b/cli/commands/migrate.ts index b893c6e63..374f28b16 100644 --- a/cli/commands/migrate.ts +++ b/cli/commands/migrate.ts @@ -12,12 +12,13 @@ import { } from '../network' import { loadEnv, CLIArgs, CLIEnvironment } from '../env' import { confirm } from '../helpers' +import { chainIdIsL2 } from '../utils' const { EtherSymbol } = constants const { formatEther } = utils // Contracts are deployed in the order defined in this list -const allContracts = [ +let allContracts = [ 'GraphProxyAdmin', 'BancorFormula', 'Controller', @@ -33,6 +34,27 @@ const allContracts = [ 'RewardsManager', 'DisputeManager', 'AllocationExchange', + 'L1GraphTokenGateway', + 'BridgeEscrow', +] + +const l2Contracts = [ + 'GraphProxyAdmin', + 'BancorFormula', + 'Controller', + 'EpochManager', + 'L2GraphToken', + 'GraphCurationToken', + 'ServiceRegistry', + 'Curation', + 'SubgraphNFTDescriptor', + 'SubgraphNFT', + 'GNS', + 'Staking', + 'RewardsManager', + 'DisputeManager', + 'AllocationExchange', + 'L2GraphTokenGateway', ] export const migrate = async ( @@ -52,6 +74,9 @@ export const migrate = async ( if (chainId == 1337) { await (cli.wallet.provider as providers.JsonRpcProvider).send('evm_setAutomine', [true]) + allContracts = ['EthereumDIDRegistry', ...allContracts] + } else if (chainIdIsL2(chainId)) { + allContracts = l2Contracts } logger.info(`>>> Migrating contracts <<<\n`) diff --git a/cli/commands/protocol/configure-bridge.ts b/cli/commands/protocol/configure-bridge.ts new file mode 100644 index 000000000..d96d462a5 --- /dev/null +++ b/cli/commands/protocol/configure-bridge.ts @@ -0,0 +1,89 @@ +import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' +import { logger } from '../../logging' +import { getAddressBook } from '../../address-book' +import { sendTransaction } from '../../network' +import { chainIdIsL2, l1ToL2ChainIdMap, l2ToL1ChainIdMap } from '../../utils' + +export const configureL1Bridge = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Setting L1 Bridge Configuration <<<\n`) + + if (chainIdIsL2(cli.chainId)) { + throw new Error('Cannot set L1 configuration on an L2 network!') + } + const l2ChainId = cliArgs.l2ChainId ? cliArgs.l2ChainId : l1ToL2ChainIdMap[cli.chainId] + logger.info('Connecting with the contracts on L2 chainId ' + l2ChainId) + const l2AddressBook = getAddressBook(cliArgs.addressBook, l2ChainId) + const arbAddressBook = getAddressBook(cliArgs.arbAddressBook, cli.chainId.toString()) + + const gateway = cli.contracts['L1GraphTokenGateway'] + + const l2GRT = l2AddressBook.getEntry('L2GraphToken') + logger.info('L2 GRT address: ' + l2GRT.address) + await sendTransaction(cli.wallet, gateway, 'setL2TokenAddress', [l2GRT.address]) + + const l2Counterpart = l2AddressBook.getEntry('L2GraphTokenGateway') + logger.info('L2 Gateway address: ' + l2Counterpart.address) + await sendTransaction(cli.wallet, gateway, 'setL2CounterpartAddress', [l2Counterpart.address]) + + const bridgeEscrow = cli.contracts.BridgeEscrow + logger.info('Escrow address: ' + bridgeEscrow.address) + await sendTransaction(cli.wallet, gateway, 'setEscrowAddress', [bridgeEscrow.address]) + await sendTransaction(cli.wallet, bridgeEscrow, 'approveAll', [gateway.address]) + + const l1Inbox = arbAddressBook.getEntry('IInbox') + const l1Router = arbAddressBook.getEntry('L1GatewayRouter') + logger.info( + 'L1 Inbox address: ' + l1Inbox.address + ' and L1 Router address: ' + l1Router.address, + ) + await sendTransaction(cli.wallet, gateway, 'setArbitrumAddresses', [ + l1Inbox.address, + l1Router.address, + ]) +} + +export const configureL2Bridge = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Setting L2 Bridge Configuration <<<\n`) + + if (!chainIdIsL2(cli.chainId)) { + throw new Error('Cannot set L2 configuration on an L1 network!') + } + const l1ChainId = cliArgs.l1ChainId ? cliArgs.l1ChainId : l2ToL1ChainIdMap[cli.chainId] + logger.info('Connecting with the contracts on L1 chainId ' + l1ChainId) + const l1AddressBook = getAddressBook(cliArgs.addressBook, l1ChainId) + const arbAddressBook = getAddressBook(cliArgs.arbAddressBook, cli.chainId.toString()) + + const gateway = cli.contracts['L2GraphTokenGateway'] + const token = cli.contracts['L2GraphToken'] + + const l1GRT = l1AddressBook.getEntry('GraphToken') + logger.info('L1 GRT address: ' + l1GRT.address) + await sendTransaction(cli.wallet, gateway, 'setL1TokenAddress', [l1GRT.address]) + await sendTransaction(cli.wallet, token, 'setL1Address', [l1GRT.address]) + + const l1Counterpart = l1AddressBook.getEntry('L1GraphTokenGateway') + logger.info('L1 Gateway address: ' + l1Counterpart.address) + await sendTransaction(cli.wallet, gateway, 'setL1CounterpartAddress', [l1Counterpart.address]) + + const l2Router = arbAddressBook.getEntry('L2GatewayRouter') + logger.info('L2 Router address: ' + l2Router.address) + await sendTransaction(cli.wallet, gateway, 'setL2Router', [l2Router.address]) + + logger.info('L2 Gateway address: ' + gateway.address) + await sendTransaction(cli.wallet, token, 'setGateway', [gateway.address]) +} + +export const configureL1BridgeCommand = { + command: 'configure-l1-bridge [l2ChainId]', + describe: 'Configure L1/L2 bridge parameters (L1 side) using the address book', + handler: async (argv: CLIArgs): Promise => { + return configureL1Bridge(await loadEnv(argv), argv) + }, +} + +export const configureL2BridgeCommand = { + command: 'configure-l2-bridge [l1ChainId]', + describe: 'Configure L1/L2 bridge parameters (L2 side) using the address book', + handler: async (argv: CLIArgs): Promise => { + return configureL2Bridge(await loadEnv(argv), argv) + }, +} diff --git a/cli/commands/protocol/get.ts b/cli/commands/protocol/get.ts index 27932d209..439b539b4 100644 --- a/cli/commands/protocol/get.ts +++ b/cli/commands/protocol/get.ts @@ -51,6 +51,18 @@ export const gettersList = { 'controller-get-paused': { contract: 'Controller', name: 'paused' }, 'controller-get-partial-paused': { contract: 'Controller', name: 'partialPaused' }, 'controller-get-pause-guardian': { contract: 'Controller', name: 'pauseGuardian' }, + 'l1-gateway-l2-grt': { contract: 'L1GraphTokenGateway', name: 'l2GRT' }, + 'l1-gateway-inbox': { contract: 'L1GraphTokenGateway', name: 'inbox' }, + 'l1-gateway-escrow': { contract: 'L1GraphTokenGateway', name: 'escrow' }, + 'l1-gateway-l1-router': { contract: 'L1GraphTokenGateway', name: 'l1Router' }, + 'l1-gateway-l2-counterpart': { contract: 'L1GraphTokenGateway', name: 'l2Counterpart' }, + 'l1-gateway-paused': { contract: 'L1GraphTokenGateway', name: 'paused' }, + 'l2-gateway-l1-grt': { contract: 'L2GraphTokenGateway', name: 'l1GRT' }, + 'l2-gateway-l2-router': { contract: 'L2GraphTokenGateway', name: 'l2Router' }, + 'l2-gateway-l1-counterpart': { contract: 'L2GraphTokenGateway', name: 'l1Counterpart' }, + 'l2-gateway-paused': { contract: 'L2GraphTokenGateway', name: 'paused' }, + 'l2-token-gateway': { contract: 'L2GraphToken', name: 'gateway' }, + 'l2-token-l1-address': { contract: 'L2GraphToken', name: 'l1Address' }, } const buildHelp = () => { diff --git a/cli/commands/protocol/index.ts b/cli/commands/protocol/index.ts index 69ca4e4c2..acab2be90 100644 --- a/cli/commands/protocol/index.ts +++ b/cli/commands/protocol/index.ts @@ -3,6 +3,7 @@ import yargs, { Argv } from 'yargs' import { listCommand } from './list' import { getCommand } from './get' import { setCommand } from './set' +import { configureL1BridgeCommand, configureL2BridgeCommand } from './configure-bridge' export interface ProtocolFunction { contract: string @@ -17,7 +18,12 @@ export const protocolCommand = { command: 'protocol', describe: 'Graph protocol configuration', builder: (yargs: Argv): yargs.Argv => { - return yargs.command(getCommand).command(setCommand).command(listCommand) + return yargs + .command(getCommand) + .command(setCommand) + .command(listCommand) + .command(configureL1BridgeCommand) + .command(configureL2BridgeCommand) }, handler: (): void => { yargs.showHelp() diff --git a/cli/commands/protocol/list.ts b/cli/commands/protocol/list.ts index 228151e04..c82ca1d81 100644 --- a/cli/commands/protocol/list.ts +++ b/cli/commands/protocol/list.ts @@ -15,6 +15,9 @@ const contractNames = [ 'DisputeManager', 'RewardsManager', 'GNS', + 'L1GraphTokenGateway', + 'L2GraphToken', + 'L2GraphTokenGateway', ] export const listProtocolParams = async (cli: CLIEnvironment): Promise => { @@ -26,13 +29,15 @@ export const listProtocolParams = async (cli: CLIEnvironment): Promise => colWidths: [30, 50], }) + if (!(contractName in cli.contracts)) { + continue + } const contract = cli.contracts[contractName] table.push(['* address', contract.address]) const req = [] for (const fn of Object.values(gettersList)) { if (fn.contract != contractName) continue - const contract = cli.contracts[fn.contract] if (contract.interface.getFunction(fn.name).inputs.length == 0) { const contractFn: ContractFunction = contract.functions[fn.name] @@ -56,7 +61,7 @@ export const listProtocolParams = async (cli: CLIEnvironment): Promise => const controller = cli.contracts['Controller'] for (const contractName of contractNames) { - if (contractName === 'Controller') continue + if (contractName === 'Controller' || !(contractName in cli.contracts)) continue const contract = cli.contracts[contractName] const contractFn = contract.functions['controller'] diff --git a/cli/commands/protocol/set.ts b/cli/commands/protocol/set.ts index 4fc05f877..966d07897 100644 --- a/cli/commands/protocol/set.ts +++ b/cli/commands/protocol/set.ts @@ -59,6 +59,31 @@ export const settersList = { 'controller-set-paused': { contract: 'Controller', name: 'setPaused' }, 'controller-set-partial-paused': { contract: 'Controller', name: 'setPartialPaused' }, 'controller-set-pause-guardian': { contract: 'Controller', name: 'setPauseGuardian' }, + 'l1-gateway-set-l2-grt': { contract: 'L1GraphTokenGateway', name: 'setL2TokenAddress' }, + 'l1-gateway-set-arbitrum-addresses': { + contract: 'L1GraphTokenGateway', + name: 'setArbitrumAddresses', + }, + 'l1-gateway-set-l2-counterpart': { + contract: 'L1GraphTokenGateway', + name: 'setL2CounterpartAddress', + }, + 'l1-gateway-set-escrow-address': { + contract: 'L1GraphTokenGateway', + name: 'setEscrowAddress', + }, + 'l1-gateway-set-paused': { contract: 'L1GraphTokenGateway', name: 'setPaused' }, + 'bridge-escrow-approve-all': { contract: 'BridgeEscrow', name: 'approveAll' }, + 'bridge-escrow-revoke-all': { contract: 'BridgeEscrow', name: 'revokeAll' }, + 'l2-gateway-set-l1-grt': { contract: 'L2GraphTokenGateway', name: 'setL1TokenAddress' }, + 'l2-gateway-set-l2-router': { contract: 'L2GraphTokenGateway', name: 'setL2Router' }, + 'l2-gateway-set-l1-counterpart': { + contract: 'L2GraphTokenGateway', + name: 'setL1CounterpartAddress', + }, + 'l2-gateway-set-paused': { contract: 'L2GraphTokenGateway', name: 'setPaused' }, + 'l2-token-set-gateway': { contract: 'L2GraphToken', name: 'setGateway' }, + 'l2-token-set-l1-address': { contract: 'L2GraphToken', name: 'setL1Address' }, } const buildHelp = () => { diff --git a/cli/contracts.ts b/cli/contracts.ts index 152d5a261..9af5c126d 100644 --- a/cli/contracts.ts +++ b/cli/contracts.ts @@ -1,4 +1,4 @@ -import { providers, Signer } from 'ethers' +import { BaseContract, providers, Signer } from 'ethers' import { AddressBook } from './address-book' import { logger } from './logging' @@ -21,6 +21,11 @@ import { AllocationExchange } from '../build/types/AllocationExchange' import { SubgraphNFT } from '../build/types/SubgraphNFT' import { GraphCurationToken } from '../build/types/GraphCurationToken' import { SubgraphNFTDescriptor } from '../build/types/SubgraphNFTDescriptor' +import { L1GraphTokenGateway } from '../build/types/L1GraphTokenGateway' +import { L2GraphToken } from '../build/types/L2GraphToken' +import { L2GraphTokenGateway } from '../build/types/L2GraphTokenGateway' +import { BridgeEscrow } from '../build/types/BridgeEscrow' +import { chainIdIsL2 } from './utils' export interface NetworkContracts { EpochManager: EpochManager @@ -40,20 +45,37 @@ export interface NetworkContracts { SubgraphNFT: SubgraphNFT SubgraphNFTDescriptor: SubgraphNFTDescriptor GraphCurationToken: GraphCurationToken + L1GraphTokenGateway: L1GraphTokenGateway + BridgeEscrow: BridgeEscrow + L2GraphToken: L2GraphToken + L2GraphTokenGateway: L2GraphTokenGateway +} + +export const loadAddressBookContract = ( + contractName: string, + addressBook: AddressBook, + signerOrProvider?: Signer | providers.Provider, +): BaseContract => { + const contractEntry = addressBook.getEntry(contractName) + let contract = getContractAt(contractName, contractEntry.address) + if (signerOrProvider) { + contract = contract.connect(signerOrProvider) + } + return contract } export const loadContracts = ( addressBook: AddressBook, + chainId: number | string, signerOrProvider?: Signer | providers.Provider, ): NetworkContracts => { const contracts = {} for (const contractName of addressBook.listEntries()) { - const contractEntry = addressBook.getEntry(contractName) try { - const contract = getContractAt(contractName, contractEntry.address) - contracts[contractName] = contract - if (signerOrProvider) { - contracts[contractName] = contracts[contractName].connect(signerOrProvider) + contracts[contractName] = loadAddressBookContract(contractName, addressBook, signerOrProvider) + // On L2 networks, we alias L2GraphToken as GraphToken + if (signerOrProvider && chainIdIsL2(chainId) && contractName == 'L2GraphToken') { + contracts['GraphToken'] = contracts[contractName] } } catch (err) { logger.warn(`Could not load contract ${contractName} - ${err.message}`) diff --git a/cli/defaults.ts b/cli/defaults.ts index 5c0a1dd6a..ed7ecb0af 100644 --- a/cli/defaults.ts +++ b/cli/defaults.ts @@ -7,6 +7,8 @@ export const local = { addressBookPath: './addresses.json', graphConfigPath: './config/graph.mainnet.yml', accountNumber: '0', + arbitrumAddressBookPath: './arbitrum-addresses.json', + arbProviderUrl: 'https://rinkeby.arbitrum.io/rpc', } export const defaultOverrides: Overrides = { @@ -62,4 +64,18 @@ export const cliOpts = { type: 'boolean', default: false, }, + arbitrumAddressBook: { + alias: 'arb-address-book', + description: 'The path to the address book file for Arbitrum deployments', + type: 'string', + group: 'Config', + default: local.arbitrumAddressBookPath, + }, + l2ProviderUrl: { + alias: 'l2-provider-url', + description: 'The URL of an Arbitrum provider (only for bridge commands)', + type: 'string', + group: 'Arbitrum', + default: local.arbProviderUrl, + }, } as { [key: string]: Options } diff --git a/cli/env.ts b/cli/env.ts index d889fcdc6..ab89c9d85 100644 --- a/cli/env.ts +++ b/cli/env.ts @@ -43,7 +43,7 @@ export const loadEnv = async (argv: CLIArgs, wallet?: Wallet): Promise, - overrides?: Overrides, + overrides?: PayableOverrides, ): Promise => { // Setup overrides if (overrides) { diff --git a/cli/utils.ts b/cli/utils.ts index a11e40f4e..846c5ce9d 100644 --- a/cli/utils.ts +++ b/cli/utils.ts @@ -1,7 +1,19 @@ +import { addCustomNetwork } from '@arbitrum/sdk' import { Contract, Wallet, providers } from 'ethers' import { loadArtifact } from './artifacts' +export const l1ToL2ChainIdMap = { + '1': '42161', + '4': '421611', + '5': '421613', +} + +export const l2ChainIds = Object.values(l1ToL2ChainIdMap).map(Number) +export const l2ToL1ChainIdMap = Object.fromEntries( + Object.entries(l1ToL2ChainIdMap).map(([k, v]) => [v, k]), +) + export const contractAt = ( contractName: string, contractAddress: string, @@ -12,3 +24,7 @@ export const contractAt = ( export const getProvider = (providerUrl: string, network?: number): providers.JsonRpcProvider => new providers.JsonRpcProvider(providerUrl, network) + +export const chainIdIsL2 = (chainId: number | string): boolean => { + return l2ChainIds.includes(Number(chainId)) +} diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml new file mode 100644 index 000000000..53117372b --- /dev/null +++ b/config/graph.arbitrum-one.yml @@ -0,0 +1,121 @@ +general: + arbitrator: &arbitrator "0x113DC95e796836b8F0Fa71eE7fB42f221740c3B0" # Arbitration Council + governor: &governor "0x3e43EF77fAAd296F65eF172E8eF06F8231c9DeAd" # Graph Council + authority: &authority "0x79fd74da4c906509862c8fe93e87a9602e370bc4" # Authority that signs payment vouchers + +contracts: + Controller: + calls: + - fn: "setContractProxy" + id: "0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f" # keccak256('Curation') + contractAddress: "${{Curation.address}}" + - fn: "setContractProxy" + id: "0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3" # keccak256('GNS') + contractAddress: "${{GNS.address}}" + - fn: "setContractProxy" + id: "0xf942813d07d17b56de9a9afc8de0ced6e8c053bbfdcc87b7badea4ddcf27c307" # keccak256('DisputeManager') + contractAddress: "${{DisputeManager.address}}" + - fn: "setContractProxy" + id: "0xc713c3df6d14cdf946460395d09af88993ee2b948b1a808161494e32c5f67063" # keccak256('EpochManager') + contractAddress: "${{EpochManager.address}}" + - fn: "setContractProxy" + id: "0x966f1e8d8d8014e05f6ec4a57138da9be1f7c5a7f802928a18072f7c53180761" # keccak256('RewardsManager') + contractAddress: "${{RewardsManager.address}}" + - fn: "setContractProxy" + id: "0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034" # keccak256('Staking') + contractAddress: "${{Staking.address}}" + - fn: "setContractProxy" + id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') + contractAddress: "${{L2GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L2GraphTokenGateway.address}}" + ServiceRegistry: + proxy: true + init: + controller: "${{Controller.address}}" + EpochManager: + proxy: true + init: + controller: "${{Controller.address}}" + lengthInBlocks: 1108 # 4 hours (in 13 second blocks) + L2GraphToken: + proxy: true + init: + owner: *governor + calls: + - fn: "addMinter" + minter: "${{RewardsManager.address}}" + Curation: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + curationTokenMaster: "${{GraphCurationToken.address}}" + reserveRatio: 500000 # 50% (parts per million) + curationTaxPercentage: 10000 # 1% (parts per million) + minimumCurationDeposit: "1000000000000000000" # 1 GRT + DisputeManager: + proxy: true + init: + controller: "${{Controller.address}}" + arbitrator: *arbitrator + minimumDeposit: "10000000000000000000000" # 10,000 GRT (in wei) + fishermanRewardPercentage: 500000 # 50% (parts per million) + idxSlashingPercentage: 25000 # 2.5% (parts per million) + qrySlashingPercentage: 5000 # 0.5% (parts per million) + GNS: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + subgraphNFT: "${{SubgraphNFT.address}}" + calls: + - fn: "approveAll" + SubgraphNFT: + init: + governor: "${{Env.deployer}}" + calls: + - fn: "setTokenDescriptor" + tokenDescriptor: "${{SubgraphNFTDescriptor.address}}" + - fn: "setMinter" + minter: "${{GNS.address}}" + Staking: + proxy: true + init: + controller: "${{Controller.address}}" + minimumIndexerStake: "100000000000000000000000" # 100,000 GRT (in wei) + thawingPeriod: 6646 # 10 days (in blocks) + protocolPercentage: 10000 # 1% (parts per million) + curationPercentage: 100000 # 10% (parts per million) + channelDisputeEpochs: 2 # (in epochs) + maxAllocationEpochs: 6 # Based on epoch length this is 28 days (in epochs) + delegationUnbondingPeriod: 6 # Based on epoch length this is 28 days (in epochs) + delegationRatio: 16 # 16x (delegated stake to indexer stake multiplier) + rebateAlphaNumerator: 77 # rebateAlphaNumerator / rebateAlphaDenominator + rebateAlphaDenominator: 100 # rebateAlphaNumerator / rebateAlphaDenominator + calls: + - fn: "setDelegationTaxPercentage" + delegationTaxPercentage: 5000 # 0.5% (parts per million) + - fn: "setSlasher" + slasher: "${{DisputeManager.address}}" + allowed: true + - fn: "setAssetHolder" + assetHolder: "${{AllocationExchange.address}}" + allowed: true + RewardsManager: + proxy: true + init: + controller: "${{Controller.address}}" + AllocationExchange: + init: + graphToken: "${{GraphToken.address}}" + staking: "${{Staking.address}}" + governor: *governor + authority: *authority + calls: + - fn: "approveAll" + L2GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" diff --git a/config/graph.mainnet.yml b/config/graph.mainnet.yml index df24ef7d6..88e32d38b 100644 --- a/config/graph.mainnet.yml +++ b/config/graph.mainnet.yml @@ -30,6 +30,9 @@ contracts: - fn: "setContractProxy" id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') contractAddress: "${{GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L1GraphTokenGateway.address}}" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian - fn: "transferOwnership" @@ -132,3 +135,11 @@ contracts: authority: *authority calls: - fn: "approveAll" + L1GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + BridgeEscrow: + proxy: true + init: + controller: "${{Controller.address}}" diff --git a/contracts/arbitrum/AddressAliasHelper.sol b/contracts/arbitrum/AddressAliasHelper.sol new file mode 100644 index 000000000..740b70361 --- /dev/null +++ b/contracts/arbitrum/AddressAliasHelper.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2019-2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/84e64dee6ee82adbf8ec34fd4b86c207a61d9007/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +library AddressAliasHelper { + uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); + + /// @notice Utility function that converts the address in the L1 that submitted a tx to + /// the inbox to the msg.sender viewed in the L2 + /// @param l1Address the address in the L1 that triggered the tx to L2 + /// @return l2Address L2 address as viewed in msg.sender + function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { + l2Address = address(uint160(l1Address) + offset); + } + + /// @notice Utility function that converts the msg.sender viewed in the L2 to the + /// address in the L1 that submitted a tx to the inbox + /// @param l2Address L2 address as viewed in msg.sender + /// @return l1Address the address in the L1 that triggered the tx to L2 + function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { + l1Address = address(uint160(l2Address) - offset); + } +} diff --git a/contracts/arbitrum/IArbToken.sol b/contracts/arbitrum/IArbToken.sol new file mode 100644 index 000000000..d7d5a2d8c --- /dev/null +++ b/contracts/arbitrum/IArbToken.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2020, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-peripherals + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +/** + * @title Minimum expected interface for L2 token that interacts with the L2 token bridge (this is the interface necessary + * for a custom token that interacts with the bridge, see TestArbCustomToken.sol for an example implementation). + */ +pragma solidity ^0.7.6; + +interface IArbToken { + /** + * @notice should increase token supply by amount, and should (probably) only be callable by the L1 bridge. + */ + function bridgeMint(address account, uint256 amount) external; + + /** + * @notice should decrease token supply by amount, and should (probably) only be callable by the L1 bridge. + */ + function bridgeBurn(address account, uint256 amount) external; + + /** + * @return address of layer 1 token + */ + function l1Address() external view returns (address); +} diff --git a/contracts/arbitrum/IBridge.sol b/contracts/arbitrum/IBridge.sol new file mode 100644 index 000000000..ff78253fc --- /dev/null +++ b/contracts/arbitrum/IBridge.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +interface IBridge { + event MessageDelivered( + uint256 indexed messageIndex, + bytes32 indexed beforeInboxAcc, + address inbox, + uint8 kind, + address sender, + bytes32 messageDataHash + ); + + event BridgeCallTriggered( + address indexed outbox, + address indexed destAddr, + uint256 amount, + bytes data + ); + + event InboxToggle(address indexed inbox, bool enabled); + + event OutboxToggle(address indexed outbox, bool enabled); + + function deliverMessageToInbox( + uint8 kind, + address sender, + bytes32 messageDataHash + ) external payable returns (uint256); + + function executeCall( + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (bool success, bytes memory returnData); + + // These are only callable by the admin + function setInbox(address inbox, bool enabled) external; + + function setOutbox(address inbox, bool enabled) external; + + // View functions + + function activeOutbox() external view returns (address); + + function allowedInboxes(address inbox) external view returns (bool); + + function allowedOutboxes(address outbox) external view returns (bool); + + function inboxAccs(uint256 index) external view returns (bytes32); + + function messageCount() external view returns (uint256); +} diff --git a/contracts/arbitrum/IInbox.sol b/contracts/arbitrum/IInbox.sol new file mode 100644 index 000000000..a9315bbf8 --- /dev/null +++ b/contracts/arbitrum/IInbox.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +import "./IBridge.sol"; +import "./IMessageProvider.sol"; + +interface IInbox is IMessageProvider { + function sendL2Message(bytes calldata messageData) external returns (uint256); + + function sendUnsignedTransaction( + uint256 maxGas, + uint256 gasPriceBid, + uint256 nonce, + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (uint256); + + function sendContractTransaction( + uint256 maxGas, + uint256 gasPriceBid, + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (uint256); + + function sendL1FundedUnsignedTransaction( + uint256 maxGas, + uint256 gasPriceBid, + uint256 nonce, + address destAddr, + bytes calldata data + ) external payable returns (uint256); + + function sendL1FundedContractTransaction( + uint256 maxGas, + uint256 gasPriceBid, + address destAddr, + bytes calldata data + ) external payable returns (uint256); + + function createRetryableTicket( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes calldata data + ) external payable returns (uint256); + + function depositEth(uint256 maxSubmissionCost) external payable returns (uint256); + + function bridge() external view returns (IBridge); + + function pauseCreateRetryables() external; + + function unpauseCreateRetryables() external; + + function startRewriteAddress() external; + + function stopRewriteAddress() external; +} diff --git a/contracts/arbitrum/IMessageProvider.sol b/contracts/arbitrum/IMessageProvider.sol new file mode 100644 index 000000000..8fbfdb171 --- /dev/null +++ b/contracts/arbitrum/IMessageProvider.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +interface IMessageProvider { + event InboxMessageDelivered(uint256 indexed messageNum, bytes data); + + event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); +} diff --git a/contracts/arbitrum/IOutbox.sol b/contracts/arbitrum/IOutbox.sol new file mode 100644 index 000000000..687c86abf --- /dev/null +++ b/contracts/arbitrum/IOutbox.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +interface IOutbox { + event OutboxEntryCreated( + uint256 indexed batchNum, + uint256 outboxEntryIndex, + bytes32 outputRoot, + uint256 numInBatch + ); + event OutBoxTransactionExecuted( + address indexed destAddr, + address indexed l2Sender, + uint256 indexed outboxEntryIndex, + uint256 transactionIndex + ); + + function l2ToL1Sender() external view returns (address); + + function l2ToL1Block() external view returns (uint256); + + function l2ToL1EthBlock() external view returns (uint256); + + function l2ToL1Timestamp() external view returns (uint256); + + function l2ToL1BatchNum() external view returns (uint256); + + function l2ToL1OutputId() external view returns (bytes32); + + function processOutgoingMessages(bytes calldata sendsData, uint256[] calldata sendLengths) + external; + + function outboxEntryExists(uint256 batchNum) external view returns (bool); +} diff --git a/contracts/arbitrum/ITokenGateway.sol b/contracts/arbitrum/ITokenGateway.sol new file mode 100644 index 000000000..977fe07f2 --- /dev/null +++ b/contracts/arbitrum/ITokenGateway.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2020, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-peripherals + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +interface ITokenGateway { + /// @notice event deprecated in favor of DepositInitiated and WithdrawalInitiated + // event OutboundTransferInitiated( + // address token, + // address indexed _from, + // address indexed _to, + // uint256 indexed _transferId, + // uint256 _amount, + // bytes _data + // ); + + /// @notice event deprecated in favor of DepositFinalized and WithdrawalFinalized + // event InboundTransferFinalized( + // address token, + // address indexed _from, + // address indexed _to, + // uint256 indexed _transferId, + // uint256 _amount, + // bytes _data + // ); + + function outboundTransfer( + address _token, + address _to, + uint256 _amount, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes calldata _data + ) external payable returns (bytes memory); + + function finalizeInboundTransfer( + address _token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable; + + /** + * @notice Calculate the address used when bridging an ERC20 token + * @dev the L1 and L2 address oracles may not always be in sync. + * For example, a custom token may have been registered but not deployed or the contract self destructed. + * @param l1ERC20 address of L1 token + * @return L2 address of a bridged ERC20 token + */ + function calculateL2TokenAddress(address l1ERC20) external view returns (address); +} diff --git a/contracts/arbitrum/L1ArbitrumMessenger.sol b/contracts/arbitrum/L1ArbitrumMessenger.sol new file mode 100644 index 000000000..b893fa262 --- /dev/null +++ b/contracts/arbitrum/L1ArbitrumMessenger.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2020, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-peripherals + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +import "./IInbox.sol"; +import "./IOutbox.sol"; + +/// @notice L1 utility contract to assist with L1 <=> L2 interactions +/// @dev this is an abstract contract instead of library so the functions can be easily overriden when testing +abstract contract L1ArbitrumMessenger { + event TxToL2(address indexed _from, address indexed _to, uint256 indexed _seqNum, bytes _data); + + struct L2GasParams { + uint256 _maxSubmissionCost; + uint256 _maxGas; + uint256 _gasPriceBid; + } + + function sendTxToL2( + address _inbox, + address _to, + address _user, + uint256 _l1CallValue, + uint256 _l2CallValue, + L2GasParams memory _l2GasParams, + bytes memory _data + ) internal virtual returns (uint256) { + // alternative function entry point when struggling with the stack size + return + sendTxToL2( + _inbox, + _to, + _user, + _l1CallValue, + _l2CallValue, + _l2GasParams._maxSubmissionCost, + _l2GasParams._maxGas, + _l2GasParams._gasPriceBid, + _data + ); + } + + function sendTxToL2( + address _inbox, + address _to, + address _user, + uint256 _l1CallValue, + uint256 _l2CallValue, + uint256 _maxSubmissionCost, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes memory _data + ) internal virtual returns (uint256) { + uint256 seqNum = IInbox(_inbox).createRetryableTicket{ value: _l1CallValue }( + _to, + _l2CallValue, + _maxSubmissionCost, + _user, + _user, + _maxGas, + _gasPriceBid, + _data + ); + emit TxToL2(_user, _to, seqNum, _data); + return seqNum; + } + + function getBridge(address _inbox) internal view virtual returns (IBridge) { + return IInbox(_inbox).bridge(); + } + + /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies + function getL2ToL1Sender(address _inbox) internal view virtual returns (address) { + IOutbox outbox = IOutbox(getBridge(_inbox).activeOutbox()); + address l2ToL1Sender = outbox.l2ToL1Sender(); + + require(l2ToL1Sender != address(0), "NO_SENDER"); + return l2ToL1Sender; + } +} diff --git a/contracts/arbitrum/L2ArbitrumMessenger.sol b/contracts/arbitrum/L2ArbitrumMessenger.sol new file mode 100644 index 000000000..e03985bef --- /dev/null +++ b/contracts/arbitrum/L2ArbitrumMessenger.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2020, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-peripherals + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +import "arbos-precompiles/arbos/builtin/ArbSys.sol"; + +/// @notice L2 utility contract to assist with L1 <=> L2 interactions +/// @dev this is an abstract contract instead of library so the functions can be easily overriden when testing +abstract contract L2ArbitrumMessenger { + address internal constant ARB_SYS_ADDRESS = address(100); + + event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data); + + function sendTxToL1( + uint256 _l1CallValue, + address _from, + address _to, + bytes memory _data + ) internal virtual returns (uint256) { + uint256 _id = ArbSys(ARB_SYS_ADDRESS).sendTxToL1{ value: _l1CallValue }(_to, _data); + emit TxToL1(_from, _to, _id, _data); + return _id; + } +} diff --git a/contracts/arbitrum/README.md b/contracts/arbitrum/README.md new file mode 100644 index 000000000..abc87553e --- /dev/null +++ b/contracts/arbitrum/README.md @@ -0,0 +1,5 @@ +# Arbitrum contracts + +These contracts have been copied from the [Arbitrum repo](https://github.com/OffchainLabs/arbitrum). + +They are also available as part of the npm packages [arb-bridge-eth](https://www.npmjs.com/package/arb-bridge-eth) and [arb-bridge-peripherals](https://www.npmjs.com/package/arb-bridge-peripherals). The reason for copying them rather than installing those packages is the contracts only support Solidity `^0.6.11`, so we had to change the version to `^0.7.6` for it to be compatible with our other contracts. diff --git a/contracts/gateway/BridgeEscrow.sol b/contracts/gateway/BridgeEscrow.sol new file mode 100644 index 000000000..605f13a50 --- /dev/null +++ b/contracts/gateway/BridgeEscrow.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../upgrades/GraphUpgradeable.sol"; +import "../governance/Managed.sol"; +import "../token/IGraphToken.sol"; + +/** + * @title Bridge Escrow + * @dev This contracts acts as a gateway for an L2 bridge (or several). It simply holds GRT and has + * a set of spenders that can transfer the tokens; the L1 side of each L2 bridge has to be + * approved as a spender. + */ +contract BridgeEscrow is GraphUpgradeable, Managed { + /** + * @dev Initialize this contract. + * @param _controller Address of the Controller that manages this contract + */ + function initialize(address _controller) external onlyImpl { + Managed._initialize(_controller); + } + + /** + * @dev Approve a spender (i.e. a bridge that manages the GRT funds held by the escrow) + * @param _spender Address of the spender that will be approved + */ + function approveAll(address _spender) external onlyGovernor { + graphToken().approve(_spender, type(uint256).max); + } + + /** + * @dev Revoke a spender (i.e. a bridge that will no longer manage the GRT funds held by the escrow) + * @param _spender Address of the spender that will be revoked + */ + function revokeAll(address _spender) external onlyGovernor { + IGraphToken grt = graphToken(); + grt.decreaseAllowance(_spender, grt.allowance(address(this), _spender)); + } +} diff --git a/contracts/gateway/GraphTokenGateway.sol b/contracts/gateway/GraphTokenGateway.sol new file mode 100644 index 000000000..00e8441f5 --- /dev/null +++ b/contracts/gateway/GraphTokenGateway.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../upgrades/GraphUpgradeable.sol"; +import "../arbitrum/ITokenGateway.sol"; +import "../governance/Pausable.sol"; +import "../governance/Managed.sol"; + +/** + * @title L1/L2 Graph Token Gateway + * @dev This includes everything that's shared between the L1 and L2 sides of the bridge. + */ +abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITokenGateway { + /** + * @dev Check if the caller is the Controller's governor or this contract's pause guardian. + */ + modifier onlyGovernorOrGuardian() { + require( + msg.sender == controller.getGovernor() || msg.sender == pauseGuardian, + "Only Governor or Guardian can call" + ); + _; + } + + /** + * @notice Change the Pause Guardian for this contract + * @param _newPauseGuardian The address of the new Pause Guardian + */ + function setPauseGuardian(address _newPauseGuardian) external onlyGovernor { + require(_newPauseGuardian != address(0), "PauseGuardian must be set"); + _setPauseGuardian(_newPauseGuardian); + } + + /** + * @dev Override the default pausing from Managed to allow pausing this + * particular contract instead of pausing from the Controller. + */ + function _notPaused() internal view override { + require(!_paused, "Paused (contract)"); + } + + /** + * @notice Change the paused state of the contract + * @param _newPaused New value for the pause state (true means the transfers will be paused) + */ + function setPaused(bool _newPaused) external onlyGovernorOrGuardian { + _setPaused(_newPaused); + } + + /** + * @notice Getter to access paused state of this contract + */ + function paused() external view returns (bool) { + return _paused; + } +} diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol new file mode 100644 index 000000000..19fd73be5 --- /dev/null +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts/math/SafeMath.sol"; + +import "../arbitrum/L1ArbitrumMessenger.sol"; +import "./GraphTokenGateway.sol"; + +/** + * @title L1 Graph Token Gateway Contract + * @dev Provides the L1 side of the Ethereum-Arbitrum GRT bridge. Sends GRT to the L2 chain + * by escrowing them and sending a message to the L2 gateway, and receives tokens from L2 by + * releasing them from escrow. + * Based on Offchain Labs' reference implementation and Livepeer's arbitrum-lpt-bridge + * (See: https://github.com/OffchainLabs/arbitrum/tree/master/packages/arb-bridge-peripherals/contracts/tokenbridge + * and https://github.com/livepeer/arbitrum-lpt-bridge) + */ +contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { + using SafeMath for uint256; + + // Address of the Graph Token contract on L2 + address public l2GRT; + // Address of the Arbitrum Inbox + address public inbox; + // Address of the Arbitrum Gateway Router on L1 + address public l1Router; + // Address of the L2GraphTokenGateway on L2 that is the counterpart of this gateway + address public l2Counterpart; + // Address of the BridgeEscrow contract that holds the GRT in the bridge + address public escrow; + // Addresses for which this mapping is true are allowed to send callhooks in outbound transfers + mapping(address => bool) public callhookWhitelist; + + // Emitted when an outbound transfer is initiated, i.e. tokens are deposited from L1 to L2 + event DepositInitiated( + address l1Token, + address indexed from, + address indexed to, + uint256 indexed sequenceNumber, + uint256 amount + ); + + // Emitted when an incoming transfer is finalized, i.e tokens are withdrawn from L2 to L1 + event WithdrawalFinalized( + address l1Token, + address indexed from, + address indexed to, + uint256 indexed exitNum, + uint256 amount + ); + + // Emitted when the Arbitrum Inbox and Gateway Router addresses have been updated + event ArbitrumAddressesSet(address inbox, address l1Router); + // Emitted when the L2 GRT address has been updated + event L2TokenAddressSet(address l2GRT); + // Emitted when the counterpart L2GraphTokenGateway address has been updated + event L2CounterpartAddressSet(address l2Counterpart); + // Emitted when the escrow address has been updated + event EscrowAddressSet(address escrow); + // Emitted when an address is added to the callhook whitelist + event AddedToCallhookWhitelist(address newWhitelisted); + // Emitted when an address is removed from the callhook whitelist + event RemovedFromCallhookWhitelist(address notWhitelisted); + + /** + * @dev Allows a function to be called only by the gateway's L2 counterpart. + * The message will actually come from the Arbitrum Bridge, but the Outbox + * can tell us who the sender from L2 is. + */ + modifier onlyL2Counterpart() { + // a message coming from the counterpart gateway was executed by the bridge + IBridge bridge = IInbox(inbox).bridge(); + require(msg.sender == address(bridge), "NOT_FROM_BRIDGE"); + + // and the outbox reports that the L2 address of the sender is the counterpart gateway + address l2ToL1Sender = IOutbox(bridge.activeOutbox()).l2ToL1Sender(); + require(l2ToL1Sender == l2Counterpart, "ONLY_COUNTERPART_GATEWAY"); + _; + } + + /** + * @dev Initialize this contract. + * The contract will be paused. + * Note some parameters have to be set separately as they are generally + * not expected to be available at initialization time: + * - inbox and l1Router using setArbitrumAddresses + * - l2GRT using setL2TokenAddress + * - l2Counterpart using setL2CounterpartAddress + * - escrow using setEscrowAddress + * - whitelisted callhook callers using addToCallhookWhitelist + * - pauseGuardian using setPauseGuardian + * @param _controller Address of the Controller that manages this contract + */ + function initialize(address _controller) external onlyImpl { + Managed._initialize(_controller); + _paused = true; + } + + /** + * @dev Sets the addresses for L1 contracts provided by Arbitrum + * @param _inbox Address of the Inbox that is part of the Arbitrum Bridge + * @param _l1Router Address of the Gateway Router + */ + function setArbitrumAddresses(address _inbox, address _l1Router) external onlyGovernor { + require(_inbox != address(0), "INVALID_INBOX"); + require(_l1Router != address(0), "INVALID_L1_ROUTER"); + inbox = _inbox; + l1Router = _l1Router; + emit ArbitrumAddressesSet(_inbox, _l1Router); + } + + /** + * @dev Sets the address of the L2 Graph Token + * @param _l2GRT Address of the GRT contract on L2 + */ + function setL2TokenAddress(address _l2GRT) external onlyGovernor { + require(_l2GRT != address(0), "INVALID_L2_GRT"); + l2GRT = _l2GRT; + emit L2TokenAddressSet(_l2GRT); + } + + /** + * @dev Sets the address of the counterpart gateway on L2 + * @param _l2Counterpart Address of the corresponding L2GraphTokenGateway on Arbitrum + */ + function setL2CounterpartAddress(address _l2Counterpart) external onlyGovernor { + require(_l2Counterpart != address(0), "INVALID_L2_COUNTERPART"); + l2Counterpart = _l2Counterpart; + emit L2CounterpartAddressSet(_l2Counterpart); + } + + /** + * @dev Sets the address of the escrow contract on L1 + * @param _escrow Address of the BridgeEscrow + */ + function setEscrowAddress(address _escrow) external onlyGovernor { + require(_escrow != address(0) && Address.isContract(_escrow), "INVALID_ESCROW"); + escrow = _escrow; + emit EscrowAddressSet(_escrow); + } + + /** + * @dev Adds an address to the callhook whitelist. + * This address will be allowed to include callhooks when transferring tokens. + * @param _newWhitelisted Address to add to the whitelist + */ + function addToCallhookWhitelist(address _newWhitelisted) external onlyGovernor { + require(_newWhitelisted != address(0), "INVALID_ADDRESS"); + require(!callhookWhitelist[_newWhitelisted], "ALREADY_WHITELISTED"); + callhookWhitelist[_newWhitelisted] = true; + emit AddedToCallhookWhitelist(_newWhitelisted); + } + + /** + * @dev Removes an address from the callhook whitelist. + * This address will no longer be allowed to include callhooks when transferring tokens. + * @param _notWhitelisted Address to remove from the whitelist + */ + function removeFromCallhookWhitelist(address _notWhitelisted) external onlyGovernor { + require(_notWhitelisted != address(0), "INVALID_ADDRESS"); + require(callhookWhitelist[_notWhitelisted], "NOT_WHITELISTED"); + callhookWhitelist[_notWhitelisted] = false; + emit RemovedFromCallhookWhitelist(_notWhitelisted); + } + + /** + * @notice Creates and sends a retryable ticket to transfer GRT to L2 using the Arbitrum Inbox. + * The tokens are escrowed by the gateway until they are withdrawn back to L1. + * The ticket must be redeemed on L2 to receive tokens at the specified address. + * Note that the caller must previously allow the gateway to spend the specified amount of GRT. + * @dev maxGas and gasPriceBid must be set using Arbitrum's NodeInterface.estimateRetryableTicket method. + * Also note that whitelisted senders (some protocol contracts) can include additional calldata + * for a callhook to be executed on the L2 side when the tokens are received. In this case, the L2 transaction + * can revert if the callhook reverts, potentially locking the tokens on the bridge if the callhook + * never succeeds. This requires extra care when adding contracts to the whitelist, but is necessary to ensure that + * the tickets can be retried in the case of a temporary failure, and to ensure the atomicity of callhooks + * with token transfers. + * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router) + * @param _to Recipient address on L2 + * @param _amount Amount of tokens to tranfer + * @param _maxGas Gas limit for L2 execution of the ticket + * @param _gasPriceBid Price per gas on L2 + * @param _data Encoded maxSubmissionCost and sender address along with additional calldata + * @return Sequence number of the retryable ticket created by Inbox + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes calldata _data + ) external payable override notPaused returns (bytes memory) { + IGraphToken token = graphToken(); + require(_l1Token == address(token), "TOKEN_NOT_GRT"); + require(_amount > 0, "INVALID_ZERO_AMOUNT"); + require(_to != address(0), "INVALID_DESTINATION"); + + // nested scopes to avoid stack too deep errors + address from; + uint256 seqNum; + { + uint256 maxSubmissionCost; + bytes memory outboundCalldata; + { + bytes memory extraData; + (from, maxSubmissionCost, extraData) = parseOutboundData(_data); + require( + extraData.length == 0 || callhookWhitelist[msg.sender] == true, + "CALL_HOOK_DATA_NOT_ALLOWED" + ); + require(maxSubmissionCost > 0, "NO_SUBMISSION_COST"); + + { + // makes sure only sufficient ETH is supplied as required for successful redemption on L2 + // if a user does not desire immediate redemption they should provide + // a msg.value of AT LEAST maxSubmissionCost + uint256 expectedEth = maxSubmissionCost.add(_maxGas.mul(_gasPriceBid)); + require(msg.value >= expectedEth, "WRONG_ETH_VALUE"); + } + outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData); + } + { + L2GasParams memory gasParams = L2GasParams( + maxSubmissionCost, + _maxGas, + _gasPriceBid + ); + // transfer tokens to escrow + token.transferFrom(from, escrow, _amount); + seqNum = sendTxToL2( + inbox, + l2Counterpart, + from, + msg.value, + 0, + gasParams, + outboundCalldata + ); + } + } + emit DepositInitiated(_l1Token, from, _to, seqNum, _amount); + + return abi.encode(seqNum); + } + + /** + * @notice Receives withdrawn tokens from L2 + * The equivalent tokens are released from escrow and sent to the destination. + * @dev can only accept transactions coming from the L2 GRT Gateway. + * The last parameter is unused but kept for compatibility with Arbitrum gateways, + * and the encoded exitNum is assumed to be 0. + * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router) + * @param _from Address of the sender + * @param _to Recepient address on L1 + * @param _amount Amount of tokens transferred + */ + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata // _data, contains exitNum, unused by this contract + ) external payable override notPaused onlyL2Counterpart { + IGraphToken token = graphToken(); + require(_l1Token == address(token), "TOKEN_NOT_GRT"); + + uint256 escrowBalance = token.balanceOf(escrow); + // If the bridge doesn't have enough tokens, something's very wrong! + require(_amount <= escrowBalance, "BRIDGE_OUT_OF_FUNDS"); + token.transferFrom(escrow, _to, _amount); + + emit WithdrawalFinalized(_l1Token, _from, _to, 0, _amount); + } + + /** + * @notice Decodes calldata required for migration of tokens + * @dev Data must include maxSubmissionCost, extraData can be left empty. When the router + * sends an outbound message, data also contains the from address. + * @param _data encoded callhook data + * @return Sender of the tx + * @return Base ether value required to keep retryable ticket alive + * @return Additional data sent to L2 + */ + function parseOutboundData(bytes memory _data) + private + view + returns ( + address, + uint256, + bytes memory + ) + { + address from; + uint256 maxSubmissionCost; + bytes memory extraData; + if (msg.sender == l1Router) { + // Data encoded by the Gateway Router includes the sender address + (from, extraData) = abi.decode(_data, (address, bytes)); + } else { + from = msg.sender; + extraData = _data; + } + // User-encoded data contains the max retryable ticket submission cost + // and additional L2 calldata + (maxSubmissionCost, extraData) = abi.decode(extraData, (uint256, bytes)); + return (from, maxSubmissionCost, extraData); + } + + /** + * @notice Creates calldata required to create a retryable ticket + * @dev encodes the target function with its params which + * will be called on L2 when the retryable ticket is redeemed + * @param _l1Token Address of the Graph token contract on L1 + * @param _from Address on L1 from which we're transferring tokens + * @param _to Address on L2 to which we're transferring tokens + * @param _amount Amount of GRT to transfer + * @param _data Additional call data for the L2 transaction, which must be empty unless the caller is whitelisted + * @return Encoded calldata (including function selector) for the L2 transaction + */ + function getOutboundCalldata( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes memory _data + ) public pure returns (bytes memory) { + bytes memory emptyBytes; + + return + abi.encodeWithSelector( + ITokenGateway.finalizeInboundTransfer.selector, + _l1Token, + _from, + _to, + _amount, + abi.encode(emptyBytes, _data) + ); + } + + /** + * @notice Calculate the L2 address of a bridged token + * @dev In our case, this would only work for GRT. + * @param _l1ERC20 address of L1 GRT contract + * @return L2 address of the bridged GRT token + */ + function calculateL2TokenAddress(address _l1ERC20) external view override returns (address) { + IGraphToken token = graphToken(); + if (_l1ERC20 != address(token)) { + return address(0); + } + return l2GRT; + } +} diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index 716403111..ce18fb009 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -9,6 +9,7 @@ import "../epochs/IEpochManager.sol"; import "../rewards/IRewardsManager.sol"; import "../staking/IStaking.sol"; import "../token/IGraphToken.sol"; +import "../arbitrum/ITokenGateway.sol"; /** * @title Graph Managed contract @@ -44,7 +45,7 @@ contract Managed { require(!controller.partialPaused(), "Partial-paused"); } - function _notPaused() internal view { + function _notPaused() internal view virtual { require(!controller.paused(), "Paused"); } @@ -145,6 +146,14 @@ contract Managed { return IGraphToken(_resolveContract(keccak256("GraphToken"))); } + /** + * @dev Return GraphTokenGateway (L1 or L2) interface. + * @return Graph token gateway contract registered with Controller + */ + function graphTokenGateway() internal view returns (ITokenGateway) { + return ITokenGateway(_resolveContract(keccak256("GraphTokenGateway"))); + } + /** * @dev Resolve a contract address from the cache or the Controller if not found. * @return Address of the contract @@ -182,5 +191,6 @@ contract Managed { _syncContract("RewardsManager"); _syncContract("Staking"); _syncContract("GraphToken"); + _syncContract("GraphTokenGateway"); } } diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol new file mode 100644 index 000000000..593e0e228 --- /dev/null +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import "@openzeppelin/contracts/math/SafeMath.sol"; + +import "../../arbitrum/L2ArbitrumMessenger.sol"; +import "../../arbitrum/AddressAliasHelper.sol"; +import "../../gateway/GraphTokenGateway.sol"; +import "../token/L2GraphToken.sol"; + +/** + * @title L2 Graph Token Gateway Contract + * @dev Provides the L2 side of the Ethereum-Arbitrum GRT bridge. Receives GRT from the L1 chain + * and mints them on the L2 side. Sends GRT back to L1 by burning them on the L2 side. + * Based on Offchain Labs' reference implementation and Livepeer's arbitrum-lpt-bridge + * (See: https://github.com/OffchainLabs/arbitrum/tree/master/packages/arb-bridge-peripherals/contracts/tokenbridge + * and https://github.com/livepeer/arbitrum-lpt-bridge) + */ +contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger { + using SafeMath for uint256; + + // Address of the Graph Token contract on L1 + address public l1GRT; + // Address of the L1GraphTokenGateway that is the counterpart of this gateway on L1 + address public l1Counterpart; + // Address of the Arbitrum Gateway Router on L2 + address public l2Router; + + // Calldata included in an outbound transfer, stored as a structure for convenience and stack depth + struct OutboundCalldata { + address from; + bytes extraData; + } + + // Emitted when an incoming transfer is finalized, i.e. tokens were deposited from L1 to L2 + event DepositFinalized( + address indexed l1Token, + address indexed from, + address indexed to, + uint256 amount + ); + + // Emitted when an outbound transfer is initiated, i.e. tokens are being withdrawn from L2 back to L1 + event WithdrawalInitiated( + address l1Token, + address indexed from, + address indexed to, + uint256 indexed l2ToL1Id, + uint256 exitNum, + uint256 amount + ); + + // Emitted when the Arbitrum Gateway Router address on L2 has been updated + event L2RouterSet(address l2Router); + // Emitted when the L1 Graph Token address has been updated + event L1TokenAddressSet(address l1GRT); + // Emitted when the address of the counterpart gateway on L1 has been updated + event L1CounterpartAddressSet(address l1Counterpart); + + /** + * @dev Checks that the sender is the L2 alias of the counterpart + * gateway on L1. + */ + modifier onlyL1Counterpart() { + require( + msg.sender == AddressAliasHelper.applyL1ToL2Alias(l1Counterpart), + "ONLY_COUNTERPART_GATEWAY" + ); + _; + } + + /** + * @dev Initialize this contract. + * The contract will be paused. + * Note some parameters have to be set separately as they are generally + * not expected to be available at initialization time: + * - l2Router using setL2Router + * - l1GRT using setL1TokenAddress + * - l1Counterpart using setL1CounterpartAddress + * - pauseGuardian using setPauseGuardian + * @param _controller Address of the Controller that manages this contract + */ + function initialize(address _controller) external onlyImpl { + Managed._initialize(_controller); + _paused = true; + } + + /** + * @dev Sets the address of the Arbitrum Gateway Router on L2 + * @param _l2Router Address of the L2 Router (provided by Arbitrum) + */ + function setL2Router(address _l2Router) external onlyGovernor { + require(_l2Router != address(0), "INVALID_L2_ROUTER"); + l2Router = _l2Router; + emit L2RouterSet(_l2Router); + } + + /** + * @dev Sets the address of the Graph Token on L1 + * @param _l1GRT L1 address of the Graph Token contract + */ + function setL1TokenAddress(address _l1GRT) external onlyGovernor { + require(_l1GRT != address(0), "INVALID_L1_GRT"); + l1GRT = _l1GRT; + emit L1TokenAddressSet(_l1GRT); + } + + /** + * @dev Sets the address of the counterpart gateway on L1 + * @param _l1Counterpart Address of the L1GraphTokenGateway on L1 + */ + function setL1CounterpartAddress(address _l1Counterpart) external onlyGovernor { + require(_l1Counterpart != address(0), "INVALID_L1_COUNTERPART"); + l1Counterpart = _l1Counterpart; + emit L1CounterpartAddressSet(_l1Counterpart); + } + + /** + * @notice Burns L2 tokens and initiates a transfer to L1. + * The tokens will be available on L1 only after the wait period (7 days) is over, + * and will require an Outbox.executeTransaction to finalize. + * Note that the caller must previously allow the gateway to spend the specified amount of GRT. + * @dev no additional callhook data is allowed. The two unused params are needed + * for compatibility with Arbitrum's gateway router. + * The function is payable for ITokenGateway compatibility, but msg.value must be zero. + * @param _l1Token L1 Address of GRT (needed for compatibility with Arbitrum Gateway Router) + * @param _to Recipient address on L1 + * @param _amount Amount of tokens to burn + * @param _data Contains sender and additional data (always empty) to send to L1 + * @return ID of the withdraw transaction + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + uint256, // unused on L2 + uint256, // unused on L2 + bytes calldata _data + ) public payable override notPaused returns (bytes memory) { + require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); + require(_amount > 0, "INVALID_ZERO_AMOUNT"); + require(msg.value == 0, "INVALID_NONZERO_VALUE"); + require(_to != address(0), "INVALID_DESTINATION"); + + OutboundCalldata memory outboundCalldata; + + (outboundCalldata.from, outboundCalldata.extraData) = parseOutboundData(_data); + require(outboundCalldata.extraData.length == 0, "CALL_HOOK_DATA_NOT_ALLOWED"); + + // from needs to approve this contract to burn the amount first + L2GraphToken(calculateL2TokenAddress(l1GRT)).bridgeBurn(outboundCalldata.from, _amount); + + uint256 id = sendTxToL1( + 0, + outboundCalldata.from, + l1Counterpart, + getOutboundCalldata( + _l1Token, + outboundCalldata.from, + _to, + _amount, + outboundCalldata.extraData + ) + ); + + // we don't need to track exitNums (b/c we have no fast exits) so we always use 0 + emit WithdrawalInitiated(_l1Token, outboundCalldata.from, _to, id, 0, _amount); + + return abi.encode(id); + } + + /** + * @notice Burns L2 tokens and initiates a transfer to L1. + * The tokens will be received on L1 only after the wait period (7 days) is over, + * and will require an Outbox.executeTransaction to finalize. + * @dev no additional callhook data is allowed + * @param _l1Token L1 Address of GRT (needed for compatibility with Arbitrum Gateway Router) + * @param _to Recipient address on L1 + * @param _amount Amount of tokens to burn + * @param _data Contains sender and additional data to send to L1 + * @return ID of the withdraw tx + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + bytes calldata _data + ) external returns (bytes memory) { + return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data); + } + + /** + * @notice Calculate the L2 address of a bridged token + * @dev In our case, this would only work for GRT. + * @param l1ERC20 address of L1 GRT contract + * @return L2 address of the bridged GRT token + */ + function calculateL2TokenAddress(address l1ERC20) public view override returns (address) { + if (l1ERC20 != l1GRT) { + return address(0); + } + return address(graphToken()); + } + + /** + * @notice Receives token amount from L1 and mints the equivalent tokens to the receiving address + * @dev Only accepts transactions from the L1 GRT Gateway. + * The function is payable for ITokenGateway compatibility, but msg.value must be zero. + * Note that whitelisted senders (some protocol contracts) can include additional calldata + * for a callhook to be executed on the L2 side when the tokens are received. In this case, the L2 transaction + * can revert if the callhook reverts, potentially locking the tokens on the bridge if the callhook + * never succeeds. This requires extra care when adding contracts to the whitelist, but is necessary to ensure that + * the tickets can be retried in the case of a temporary failure, and to ensure the atomicity of callhooks + * with token transfers. + * @param _l1Token L1 Address of GRT + * @param _from Address of the sender on L1 + * @param _to Recipient address on L2 + * @param _amount Amount of tokens transferred + * @param _data Extra callhook data, only used when the sender is whitelisted + */ + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable override notPaused onlyL1Counterpart { + require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); + require(msg.value == 0, "INVALID_NONZERO_VALUE"); + + L2GraphToken(calculateL2TokenAddress(l1GRT)).bridgeMint(_to, _amount); + + if (_data.length > 0) { + bytes memory callhookData; + { + bytes memory gatewayData; + (gatewayData, callhookData) = abi.decode(_data, (bytes, bytes)); + } + bool success; + // solhint-disable-next-line avoid-low-level-calls + (success, ) = _to.call(callhookData); + // Callhooks shouldn't revert, but if they do: + // we revert, so that the retryable ticket can be re-attempted + // later. + if (!success) { + revert("CALLHOOK_FAILED"); + } + } + + emit DepositFinalized(_l1Token, _from, _to, _amount); + } + + /** + * @notice Creates calldata required to send tx to L1 + * @dev encodes the target function with its params which + * will be called on L1 when the message is received on L1 + * @param _token Address of the token on L1 + * @param _from Address of the token sender on L2 + * @param _to Address to which we're sending tokens on L1 + * @param _amount Amount of GRT to transfer + * @param _data Additional calldata for the transaction + * @return Calldata for a transaction sent to L1 + */ + function getOutboundCalldata( + address _token, + address _from, + address _to, + uint256 _amount, + bytes memory _data + ) public pure returns (bytes memory) { + return + abi.encodeWithSelector( + ITokenGateway.finalizeInboundTransfer.selector, + _token, + _from, + _to, + _amount, + abi.encode(0, _data) // we don't need to track exitNums (b/c we have no fast exits) so we always use 0 + ); + } + + /** + * @notice Decodes calldata required for migration of tokens + * @dev extraData can be left empty + * @param _data Encoded callhook data + * @return Sender of the tx + * @return Any other data sent to L1 + */ + function parseOutboundData(bytes memory _data) private view returns (address, bytes memory) { + address from; + bytes memory extraData; + if (msg.sender == l2Router) { + (from, extraData) = abi.decode(_data, (address, bytes)); + } else { + from = msg.sender; + extraData = _data; + } + return (from, extraData); + } +} diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol new file mode 100644 index 000000000..0df9b8e06 --- /dev/null +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol"; +import "@openzeppelin/contracts/cryptography/ECDSA.sol"; +import "@openzeppelin/contracts/math/SafeMath.sol"; + +import "../../upgrades/GraphUpgradeable.sol"; +import "../../token/GraphToken.sol"; +import "../../governance/Governed.sol"; + +/** + * @title GraphTokenUpgradeable contract + * @dev This is the implementation of the ERC20 Graph Token. + * The implementation exposes a permit() function to allow for a spender to send a signed message + * and approve funds to a spender following EIP2612 to make integration with other contracts easier. + * + * The token is initially owned by the deployer address that can mint tokens to create the initial + * distribution. For convenience, an initial supply can be passed in the constructor that will be + * assigned to the deployer. + * + * The governor can add contracts allowed to mint indexing rewards. + * + * Note this is an exact copy of the original GraphToken contract, but using + * initializer functions and upgradeable OpenZeppelin contracts instead of + * the original's constructor + non-upgradeable approach. + */ +contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable { + using SafeMath for uint256; + + // -- EIP712 -- + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator + + bytes32 private constant DOMAIN_TYPE_HASH = + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" + ); + bytes32 private constant DOMAIN_NAME_HASH = keccak256("Graph Token"); + bytes32 private constant DOMAIN_VERSION_HASH = keccak256("0"); + bytes32 private constant DOMAIN_SALT = + 0xe33842a7acd1d5a1d28f25a931703e5605152dc48d64dc4716efdae1f5659591; // Randomly generated salt + bytes32 private constant PERMIT_TYPEHASH = + keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ); + + // -- State -- + + // solhint-disable-next-line var-name-mixedcase + bytes32 private DOMAIN_SEPARATOR; + mapping(address => bool) private _minters; + mapping(address => uint256) public nonces; + + // -- Events -- + + event MinterAdded(address indexed account); + event MinterRemoved(address indexed account); + + modifier onlyMinter() { + require(isMinter(msg.sender), "Only minter can call"); + _; + } + + /** + * @dev Approve token allowance by validating a message signed by the holder. + * @param _owner Address of the token holder + * @param _spender Address of the approved spender + * @param _value Amount of tokens to approve the spender + * @param _deadline Expiration time of the signed permit (if zero, the permit will never expire, so use with caution) + * @param _v Signature recovery id + * @param _r Signature r value + * @param _s Signature s value + */ + function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s + ) external { + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + keccak256( + abi.encode(PERMIT_TYPEHASH, _owner, _spender, _value, nonces[_owner], _deadline) + ) + ) + ); + + address recoveredAddress = ECDSA.recover(digest, _v, _r, _s); + require(_owner == recoveredAddress, "GRT: invalid permit"); + require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit"); + + nonces[_owner] = nonces[_owner].add(1); + _approve(_owner, _spender, _value); + } + + /** + * @dev Add a new minter. + * @param _account Address of the minter + */ + function addMinter(address _account) external onlyGovernor { + require(_account != address(0), "INVALID_MINTER"); + _addMinter(_account); + } + + /** + * @dev Remove a minter. + * @param _account Address of the minter + */ + function removeMinter(address _account) external onlyGovernor { + require(_minters[_account], "NOT_A_MINTER"); + _removeMinter(_account); + } + + /** + * @dev Renounce to be a minter. + */ + function renounceMinter() external { + require(_minters[msg.sender], "NOT_A_MINTER"); + _removeMinter(msg.sender); + } + + /** + * @dev Mint new tokens. + * @param _to Address to send the newly minted tokens + * @param _amount Amount of tokens to mint + */ + function mint(address _to, uint256 _amount) external onlyMinter { + _mint(_to, _amount); + } + + /** + * @dev Return if the `_account` is a minter or not. + * @param _account Address to check + * @return True if the `_account` is minter + */ + function isMinter(address _account) public view returns (bool) { + return _minters[_account]; + } + + /** + * @dev Graph Token Contract initializer. + * @param _owner Owner of this contract, who will hold the initial supply and will be a minter + * @param _initialSupply Initial supply of GRT + */ + function _initialize(address _owner, uint256 _initialSupply) internal { + __ERC20_init("Graph Token", "GRT"); + Governed._initialize(_owner); + + // The Governor has the initial supply of tokens + _mint(_owner, _initialSupply); + + // The Governor is the default minter + _addMinter(_owner); + + // EIP-712 domain separator + DOMAIN_SEPARATOR = keccak256( + abi.encode( + DOMAIN_TYPE_HASH, + DOMAIN_NAME_HASH, + DOMAIN_VERSION_HASH, + _getChainID(), + address(this), + DOMAIN_SALT + ) + ); + } + + /** + * @dev Add a new minter. + * @param _account Address of the minter + */ + function _addMinter(address _account) private { + _minters[_account] = true; + emit MinterAdded(_account); + } + + /** + * @dev Remove a minter. + * @param _account Address of the minter + */ + function _removeMinter(address _account) private { + _minters[_account] = false; + emit MinterRemoved(_account); + } + + /** + * @dev Get the running network chain ID. + * @return The chain ID + */ + function _getChainID() private pure returns (uint256) { + uint256 id; + // solhint-disable-next-line no-inline-assembly + assembly { + id := chainid() + } + return id; + } +} diff --git a/contracts/l2/token/L2GraphToken.sol b/contracts/l2/token/L2GraphToken.sol new file mode 100644 index 000000000..ec6ca4eb8 --- /dev/null +++ b/contracts/l2/token/L2GraphToken.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "@openzeppelin/contracts/math/SafeMath.sol"; + +import "./GraphTokenUpgradeable.sol"; +import "../../arbitrum/IArbToken.sol"; + +/** + * @title L2 Graph Token Contract + * @dev Provides the L2 version of the GRT token, meant to be minted/burned + * through the L2GraphTokenGateway. + */ +contract L2GraphToken is GraphTokenUpgradeable, IArbToken { + using SafeMath for uint256; + + // Address of the gateway (on L2) that is allowed to mint tokens + address public gateway; + // Address of the corresponding Graph Token contract on L1 + address public override l1Address; + + // Emitted when the bridge / gateway has minted new tokens, i.e. tokens were transferred to L2 + event BridgeMinted(address indexed account, uint256 amount); + // Emitted when the bridge / gateway has burned tokens, i.e. tokens were transferred back to L1 + event BridgeBurned(address indexed account, uint256 amount); + // Emitted when the address of the gateway has been updated + event GatewaySet(address gateway); + // Emitted when the address of the Graph Token contract on L1 has been updated + event L1AddressSet(address l1Address); + + /** + * @dev Checks that the sender is the L2 gateway from the L1/L2 token bridge + */ + modifier onlyGateway() { + require(msg.sender == gateway, "NOT_GATEWAY"); + _; + } + + /** + * @dev L2 Graph Token Contract initializer. + * Note some parameters have to be set separately as they are generally + * not expected to be available at initialization time: + * - gateway using setGateway + * - l1Address using setL1Address + * @param _owner Governance address that owns this contract + */ + function initialize(address _owner) external onlyImpl { + require(_owner != address(0), "Owner must be set"); + // Initial supply hard coded to 0 as tokens are only supposed + // to be minted through the bridge. + GraphTokenUpgradeable._initialize(_owner, 0); + } + + /** + * @dev Sets the address of the L2 gateway allowed to mint tokens + * @param _gw Address for the L2GraphTokenGateway that will be allowed to mint tokens + */ + function setGateway(address _gw) external onlyGovernor { + require(_gw != address(0), "INVALID_GATEWAY"); + gateway = _gw; + emit GatewaySet(gateway); + } + + /** + * @dev Sets the address of the counterpart token on L1 + * @param _addr Address for the GraphToken contract on L1 + */ + function setL1Address(address _addr) external onlyGovernor { + require(_addr != address(0), "INVALID_L1_ADDRESS"); + l1Address = _addr; + emit L1AddressSet(_addr); + } + + /** + * @dev Increases token supply, only callable by the L1/L2 bridge (when tokens are transferred to L2) + * @param _account Address to credit with the new tokens + * @param _amount Number of tokens to mint + */ + function bridgeMint(address _account, uint256 _amount) external override onlyGateway { + _mint(_account, _amount); + emit BridgeMinted(_account, _amount); + } + + /** + * @dev Decreases token supply, only callable by the L1/L2 bridge (when tokens are transferred to L1). + * @param _account Address from which to extract the tokens + * @param _amount Number of tokens to burn + */ + function bridgeBurn(address _account, uint256 _amount) external override onlyGateway { + burnFrom(_account, _amount); + emit BridgeBurned(_account, _amount); + } +} diff --git a/contracts/tests/arbitrum/BridgeMock.sol b/contracts/tests/arbitrum/BridgeMock.sol new file mode 100644 index 000000000..4f2848288 --- /dev/null +++ b/contracts/tests/arbitrum/BridgeMock.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../../arbitrum/IBridge.sol"; + +/** + * @title Arbitrum Bridge mock contract + * @dev This contract implements Arbitrum's IBridge interface for testing purposes + */ +contract BridgeMock is IBridge { + // Address of the (mock) Arbitrum Inbox + address public inbox; + // Address of the (mock) Arbitrum Outbox + address public outbox; + // Index of the next message on the inbox messages array + uint256 public messageIndex; + // Inbox messages array + bytes32[] public override inboxAccs; + + /** + * @dev Deliver a message to the inbox. The encoded message will be + * added to the inbox array, and messageIndex will be incremented. + * @param _kind Type of the message + * @param _sender Address that is sending the message + * @param _messageDataHash keccak256 hash of the message data + * @return The next index for the inbox array + */ + function deliverMessageToInbox( + uint8 _kind, + address _sender, + bytes32 _messageDataHash + ) external payable override returns (uint256) { + messageIndex = messageIndex + 1; + inboxAccs.push(keccak256(abi.encodePacked(inbox, _kind, _sender, _messageDataHash))); + emit MessageDelivered( + messageIndex, + inboxAccs[messageIndex - 1], + msg.sender, + _kind, + _sender, + _messageDataHash + ); + return messageIndex; + } + + /** + * @dev Executes an L1 function call incoing from L2. This can only be called + * by the Outbox. + * @param _destAddr Contract to call + * @param _amount ETH value to send + * @param _data Calldata for the function call + * @return True if the call was successful, false otherwise + * @return Return data from the call + */ + function executeCall( + address _destAddr, + uint256 _amount, + bytes calldata _data + ) external override returns (bool, bytes memory) { + require(outbox == msg.sender, "NOT_FROM_OUTBOX"); + bool success; + bytes memory returnData; + + // solhint-disable-next-line avoid-low-level-calls + (success, returnData) = _destAddr.call{ value: _amount }(_data); + emit BridgeCallTriggered(msg.sender, _destAddr, _amount, _data); + return (success, returnData); + } + + /** + * @dev Set the address of the inbox. Anyone can call this, because it's a mock. + * @param _inbox Address of the inbox + * @param _enabled Enable the inbox (ignored) + */ + function setInbox(address _inbox, bool _enabled) external override { + inbox = _inbox; + emit InboxToggle(inbox, _enabled); + } + + /** + * @dev Set the address of the outbox. Anyone can call this, because it's a mock. + * @param _outbox Address of the outbox + * @param _enabled Enable the outbox (ignored) + */ + function setOutbox(address _outbox, bool _enabled) external override { + outbox = _outbox; + emit OutboxToggle(outbox, _enabled); + } + + // View functions + + /** + * @dev Getter for the active outbox (in this case there's only one) + */ + function activeOutbox() external view override returns (address) { + return outbox; + } + + /** + * @dev Getter for whether an address is an allowed inbox (in this case there's only one) + * @param _inbox Address to check + * @return True if the address is the allowed inbox, false otherwise + */ + function allowedInboxes(address _inbox) external view override returns (bool) { + return _inbox == inbox; + } + + /** + * @dev Getter for whether an address is an allowed outbox (in this case there's only one) + * @param _outbox Address to check + * @return True if the address is the allowed outbox, false otherwise + */ + function allowedOutboxes(address _outbox) external view override returns (bool) { + return _outbox == outbox; + } + + /** + * @dev Getter for the count of messages in the inboxAccs + * @return Number of messages in inboxAccs + */ + function messageCount() external view override returns (uint256) { + return inboxAccs.length; + } +} diff --git a/contracts/tests/arbitrum/InboxMock.sol b/contracts/tests/arbitrum/InboxMock.sol new file mode 100644 index 000000000..b600ec3ac --- /dev/null +++ b/contracts/tests/arbitrum/InboxMock.sol @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../../arbitrum/IInbox.sol"; +import "../../arbitrum/AddressAliasHelper.sol"; + +/** + * @title Arbitrum Inbox mock contract + * @dev This contract implements (a subset of) Arbitrum's IInbox interface for testing purposes + */ +contract InboxMock is IInbox { + // Type indicator for a standard L2 message + uint8 internal constant L2_MSG = 3; + // Type indicator for a retryable ticket message + // solhint-disable-next-line const-name-snakecase + uint8 internal constant L1MessageType_submitRetryableTx = 9; + // Address of the Bridge (mock) contract + IBridge public override bridge; + + /** + * @dev Send a message to L2 (by delivering it to the Bridge) + * @param _messageData Encoded data to send in the message + * @return message number returned by the inbox + */ + function sendL2Message(bytes calldata _messageData) external override returns (uint256) { + uint256 msgNum = deliverToBridge(L2_MSG, msg.sender, keccak256(_messageData)); + emit InboxMessageDelivered(msgNum, _messageData); + return msgNum; + } + + /** + * @dev Set the address of the (mock) bridge + * @param _bridge Address of the bridge + */ + function setBridge(address _bridge) external { + bridge = IBridge(_bridge); + } + + /** + * @dev Unimplemented in this mock + */ + function sendUnsignedTransaction( + uint256, + uint256, + uint256, + address, + uint256, + bytes calldata + ) external pure override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function sendContractTransaction( + uint256, + uint256, + address, + uint256, + bytes calldata + ) external pure override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function sendL1FundedUnsignedTransaction( + uint256, + uint256, + uint256, + address, + bytes calldata + ) external payable override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function sendL1FundedContractTransaction( + uint256, + uint256, + address, + bytes calldata + ) external payable override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Creates a retryable ticket for an L2 transaction + * @param _destAddr Address of the contract to call in L2 + * @param _arbTxCallValue Callvalue to use in the L2 transaction + * @param _maxSubmissionCost Max cost of submitting the ticket, in Wei + * @param _submissionRefundAddress L2 address to refund for any remaining value from the submission cost + * @param _valueRefundAddress L2 address to refund if the ticket times out or gets cancelled + * @param _maxGas Max gas for the L2 transcation + * @param _gasPriceBid Gas price bid on L2 + * @param _data Encoded calldata for the L2 transaction (including function selector) + * @return message number returned by the bridge + */ + function createRetryableTicket( + address _destAddr, + uint256 _arbTxCallValue, + uint256 _maxSubmissionCost, + address _submissionRefundAddress, + address _valueRefundAddress, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes calldata _data + ) external payable override returns (uint256) { + _submissionRefundAddress = AddressAliasHelper.applyL1ToL2Alias(_submissionRefundAddress); + _valueRefundAddress = AddressAliasHelper.applyL1ToL2Alias(_valueRefundAddress); + return + _deliverMessage( + L1MessageType_submitRetryableTx, + msg.sender, + abi.encodePacked( + uint256(uint160(bytes20(_destAddr))), + _arbTxCallValue, + msg.value, + _maxSubmissionCost, + uint256(uint160(bytes20(_submissionRefundAddress))), + uint256(uint160(bytes20(_valueRefundAddress))), + _maxGas, + _gasPriceBid, + _data.length, + _data + ) + ); + } + + function depositEth(uint256) external payable override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function pauseCreateRetryables() external pure override { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function unpauseCreateRetryables() external pure override { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function startRewriteAddress() external pure override { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function stopRewriteAddress() external pure override { + revert("Unimplemented"); + } + + /** + * @dev Deliver a message to the bridge + * @param _kind Type of the message + * @param _sender Address that is sending the message + * @param _messageData Encoded message data + * @return Message number returned by the bridge + */ + function _deliverMessage( + uint8 _kind, + address _sender, + bytes memory _messageData + ) internal returns (uint256) { + uint256 msgNum = deliverToBridge(_kind, _sender, keccak256(_messageData)); + emit InboxMessageDelivered(msgNum, _messageData); + return msgNum; + } + + /** + * @dev Deliver a message to the bridge + * @param _kind Type of the message + * @param _sender Address that is sending the message + * @param _messageDataHash keccak256 hash of the encoded message data + * @return Message number returned by the bridge + */ + function deliverToBridge( + uint8 _kind, + address _sender, + bytes32 _messageDataHash + ) internal returns (uint256) { + return bridge.deliverMessageToInbox{ value: msg.value }(_kind, _sender, _messageDataHash); + } +} diff --git a/contracts/tests/arbitrum/OutboxMock.sol b/contracts/tests/arbitrum/OutboxMock.sol new file mode 100644 index 000000000..a529a975a --- /dev/null +++ b/contracts/tests/arbitrum/OutboxMock.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../../arbitrum/IOutbox.sol"; +import "../../arbitrum/IBridge.sol"; + +/** + * @title Arbitrum Outbox mock contract + * @dev This contract implements (a subset of) Arbitrum's IOutbox interface for testing purposes + */ +contract OutboxMock is IOutbox { + // Context of an L2-to-L1 function call + struct L2ToL1Context { + uint128 l2Block; + uint128 l1Block; + uint128 timestamp; + uint128 batchNum; + bytes32 outputId; + address sender; + } + // Context of the current L2-to-L1 function call (set and cleared in each transaction) + L2ToL1Context internal context; + + // Address of the (mock) Arbitrum Bridge + IBridge public bridge; + + /** + * @dev Set the address of the (mock) bridge + * @param _bridge Address of the bridge + */ + function setBridge(address _bridge) external { + bridge = IBridge(_bridge); + } + + /** + * @dev Getter for the L2 sender of the current incoming message + */ + function l2ToL1Sender() external view override returns (address) { + return context.sender; + } + + /** + * @dev Getter for the L2 block of the current incoming message + */ + function l2ToL1Block() external view override returns (uint256) { + return context.l2Block; + } + + /** + * @dev Getter for the L1 block of the current incoming message + */ + function l2ToL1EthBlock() external view override returns (uint256) { + return context.l1Block; + } + + /** + * @dev Getter for the L1 timestamp of the current incoming message + */ + function l2ToL1Timestamp() external view override returns (uint256) { + return context.timestamp; + } + + /** + * @dev Getter for the L2 batch number of the current incoming message + */ + function l2ToL1BatchNum() external view override returns (uint256) { + return context.batchNum; + } + + /** + * @dev Getter for the output ID of the current incoming message + */ + function l2ToL1OutputId() external view override returns (bytes32) { + return context.outputId; + } + + /** + * @dev Unimplemented in this mock + */ + function processOutgoingMessages(bytes calldata, uint256[] calldata) external pure override { + revert("Unimplemented"); + } + + /** + * @dev Check whether an outbox entry for a message exists. + * This mock returns always true. + */ + function outboxEntryExists(uint256) external pure override returns (bool) { + return true; + } + + /** + * @notice (Mock) Executes a messages in an Outbox entry. + * @dev This mocks what has to be called when finalizing an L2 to L1 transfer. + * In our mock scenario, we don't validate and execute unconditionally. + * @param _batchNum Index of OutboxEntry in outboxEntries array + * @param _l2Sender sender of original message (i.e., caller of ArbSys.sendTxToL1) + * @param _destAddr destination address for L1 contract call + * @param _l2Block l2 block number at which sendTxToL1 call was made + * @param _l1Block l1 block number at which sendTxToL1 call was made + * @param _l2Timestamp l2 Timestamp at which sendTxToL1 call was made + * @param _amount value in L1 message in wei + * @param _calldataForL1 abi-encoded L1 message data + */ + function executeTransaction( + uint256 _batchNum, + bytes32[] calldata, // proof + uint256, // index + address _l2Sender, + address _destAddr, + uint256 _l2Block, + uint256 _l1Block, + uint256 _l2Timestamp, + uint256 _amount, + bytes calldata _calldataForL1 + ) external virtual { + bytes32 outputId; + + context = L2ToL1Context({ + sender: _l2Sender, + l2Block: uint128(_l2Block), + l1Block: uint128(_l1Block), + timestamp: uint128(_l2Timestamp), + batchNum: uint128(_batchNum), + outputId: outputId + }); + + // set and reset vars around execution so they remain valid during call + executeBridgeCall(_destAddr, _amount, _calldataForL1); + } + + /** + * @dev Execute an L2-to-L1 function call by calling the bridge + * @param _destAddr Address of the contract to call + * @param _amount Callvalue for the function call + * @param _data Calldata for the function call + */ + function executeBridgeCall( + address _destAddr, + uint256 _amount, + bytes memory _data + ) internal { + (bool success, bytes memory returndata) = bridge.executeCall(_destAddr, _amount, _data); + if (!success) { + if (returndata.length > 0) { + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert("BRIDGE_CALL_FAILED"); + } + } + } +} diff --git a/contracts/token/IGraphToken.sol b/contracts/token/IGraphToken.sol index 2b7dbaa20..8255e18d5 100644 --- a/contracts/token/IGraphToken.sol +++ b/contracts/token/IGraphToken.sol @@ -9,6 +9,8 @@ interface IGraphToken is IERC20 { function burn(uint256 amount) external; + function burnFrom(address _from, uint256 amount) external; + function mint(address _to, uint256 _amount) external; // -- Mint Admin -- @@ -32,4 +34,10 @@ interface IGraphToken is IERC20 { bytes32 _r, bytes32 _s ) external; + + // -- Allowance -- + + function increaseAllowance(address spender, uint256 addedValue) external returns (bool); + + function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); } diff --git a/gre/gre.ts b/gre/gre.ts index aea6ee6f4..f30c41504 100644 --- a/gre/gre.ts +++ b/gre/gre.ts @@ -127,7 +127,7 @@ function buildGraphNetworkEnvironment( addressBook: lazyObject(() => getAddressBook(addressBookPath, chainId.toString())), graphConfig: lazyObject(() => readConfig(graphConfigPath, true)), contracts: lazyObject(() => - loadContracts(getAddressBook(addressBookPath, chainId.toString()), provider), + loadContracts(getAddressBook(addressBookPath, chainId.toString()), chainId, provider), ), getDeployer: lazyFunction(() => () => getDeployer(provider)), getNamedAccounts: lazyFunction(() => () => getNamedAccounts(provider, graphConfigPath)), diff --git a/hardhat.config.ts b/hardhat.config.ts index cc358feee..3502efd6c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -62,6 +62,13 @@ const networkConfigs: NetworkConfig[] = [ { network: 'rinkeby', chainId: 4 }, { network: 'goerli', chainId: 5 }, { network: 'kovan', chainId: 42 }, + { network: 'arbitrum-rinkeby', chainId: 421611, url: 'https://rinkeby.arbitrum.io/rpc' }, + { network: 'arbitrum-one', chainId: 42161, url: 'https://arb1.arbitrum.io/rpc' }, + { + network: 'arbitrum-goerli', + chainId: 421613, + url: 'https://goerli-rollup.arbitrum.io/rpc', + }, ] function getAccountsKeys() { diff --git a/package.json b/package.json index 93f52e23c..80ca7fa55 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,15 @@ "addresses.json" ], "dependencies": { - "ethers": "^5.4.4" + "ethers": "^5.6.0" }, "devDependencies": { + "@arbitrum/sdk": "^3.0.0-beta.5", "@commitlint/cli": "^13.2.1", "@commitlint/config-conventional": "^13.2.0", - "@ethersproject/experimental": "^5.4.0", - "@graphprotocol/common-ts": "^1.6.0", + "@defi-wonderland/smock": "^2.0.7", + "@ethersproject/experimental": "^5.6.0", + "@graphprotocol/common-ts": "^1.8.3", "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-etherscan": "^2.1.1", "@nomiclabs/hardhat-waffle": "^2.0.1", @@ -41,6 +43,7 @@ "@typescript-eslint/eslint-plugin": "^4.0.0", "@typescript-eslint/parser": "^4.0.0", "@urql/core": "^2.1.3", + "arbos-precompiles": "^1.0.2", "bignumber.js": "^9.0.0", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", diff --git a/test/epochs.test.ts b/test/epochs.test.ts index 2ff87084a..1e807c053 100644 --- a/test/epochs.test.ts +++ b/test/epochs.test.ts @@ -12,6 +12,7 @@ import { getAccounts, toBN, Account, + initNetwork, } from './lib/testHelpers' describe('EpochManager', () => { @@ -23,6 +24,7 @@ describe('EpochManager', () => { const epochLength: BigNumber = toBN('3') before(async function () { + await initNetwork() ;[me, governor] = await getAccounts() }) diff --git a/test/gateway/bridgeEscrow.test.ts b/test/gateway/bridgeEscrow.test.ts new file mode 100644 index 000000000..aea195159 --- /dev/null +++ b/test/gateway/bridgeEscrow.test.ts @@ -0,0 +1,77 @@ +import { expect } from 'chai' +import { BigNumber } from 'ethers' + +import { GraphToken } from '../../build/types/GraphToken' +import { BridgeEscrow } from '../../build/types/BridgeEscrow' + +import { NetworkFixture } from '../lib/fixtures' + +import { getAccounts, toGRT, Account } from '../lib/testHelpers' + +describe('BridgeEscrow', () => { + let governor: Account + let tokenReceiver: Account + let spender: Account + + let fixture: NetworkFixture + + let grt: GraphToken + let bridgeEscrow: BridgeEscrow + + const nTokens = toGRT('1000') + + before(async function () { + ;[governor, tokenReceiver, spender] = await getAccounts() + + fixture = new NetworkFixture() + ;({ grt, bridgeEscrow } = await fixture.load(governor.signer)) + + // Give some funds to the Escrow + await grt.connect(governor.signer).mint(bridgeEscrow.address, nTokens) + }) + + beforeEach(async function () { + await fixture.setUp() + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + describe('approveAll', function () { + it('cannot be called by someone other than the governor', async function () { + const tx = bridgeEscrow.connect(tokenReceiver.signer).approveAll(spender.address) + expect(tx).to.be.revertedWith('Caller must be Controller governor') + }) + it('allows a spender to transfer GRT held by the contract', async function () { + expect(await grt.allowance(bridgeEscrow.address, spender.address)).eq(0) + const tx = grt + .connect(spender.signer) + .transferFrom(bridgeEscrow.address, tokenReceiver.address, nTokens) + expect(tx).to.be.revertedWith('ERC20: transfer amount exceeds allowance') + await bridgeEscrow.connect(governor.signer).approveAll(spender.address) + expect( + await grt + .connect(spender.signer) + .transferFrom(bridgeEscrow.address, tokenReceiver.address, nTokens), + ).to.emit(grt, 'Transfer') + expect(await grt.balanceOf(tokenReceiver.address)).to.eq(nTokens) + }) + }) + + describe('revokeAll', function () { + it('cannot be called by someone other than the governor', async function () { + const tx = bridgeEscrow.connect(tokenReceiver.signer).revokeAll(spender.address) + expect(tx).to.be.revertedWith('Caller must be Controller governor') + }) + it("revokes a spender's permission to transfer GRT held by the contract", async function () { + await bridgeEscrow.connect(governor.signer).approveAll(spender.address) + await bridgeEscrow.connect(governor.signer).revokeAll(spender.address) + // We shouldn't be able to transfer _anything_ + const tx = grt + .connect(spender.signer) + .transferFrom(bridgeEscrow.address, tokenReceiver.address, BigNumber.from('1')) + expect(tx).to.be.revertedWith('ERC20: transfer amount exceeds allowance') + }) + }) +}) diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts new file mode 100644 index 000000000..a5a473d86 --- /dev/null +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -0,0 +1,630 @@ +import { expect } from 'chai' +import { constants, Signer, utils } from 'ethers' + +import { GraphToken } from '../../build/types/GraphToken' +import { BridgeMock } from '../../build/types/BridgeMock' +import { InboxMock } from '../../build/types/InboxMock' +import { OutboxMock } from '../../build/types/OutboxMock' +import { L1GraphTokenGateway } from '../../build/types/L1GraphTokenGateway' + +import { NetworkFixture, ArbitrumL1Mocks, L1FixtureContracts } from '../lib/fixtures' + +import { + getAccounts, + latestBlock, + toBN, + toGRT, + Account, + applyL1ToL2Alias, +} from '../lib/testHelpers' +import { BridgeEscrow } from '../../build/types/BridgeEscrow' + +const { AddressZero } = constants + +describe('L1GraphTokenGateway', () => { + let governor: Account + let tokenSender: Account + let l2Receiver: Account + let mockRouter: Account + let mockL2GRT: Account + let mockL2Gateway: Account + let pauseGuardian: Account + let fixture: NetworkFixture + + let grt: GraphToken + let l1GraphTokenGateway: L1GraphTokenGateway + let bridgeEscrow: BridgeEscrow + let bridgeMock: BridgeMock + let inboxMock: InboxMock + let outboxMock: OutboxMock + + let arbitrumMocks: ArbitrumL1Mocks + let fixtureContracts: L1FixtureContracts + + const senderTokens = toGRT('1000') + const maxGas = toBN('1000000') + const maxSubmissionCost = toBN('7') + const gasPriceBid = toBN('2') + const defaultEthValue = maxSubmissionCost.add(maxGas.mul(gasPriceBid)) + const emptyCallHookData = '0x' + const defaultData = utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [maxSubmissionCost, emptyCallHookData], + ) + const notEmptyCallHookData = '0x12' + const defaultDataWithNotEmptyCallHookData = utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [maxSubmissionCost, notEmptyCallHookData], + ) + + before(async function () { + ;[governor, tokenSender, l2Receiver, mockRouter, mockL2GRT, mockL2Gateway, pauseGuardian] = + await getAccounts() + + fixture = new NetworkFixture() + fixtureContracts = await fixture.load(governor.signer) + ;({ grt, l1GraphTokenGateway, bridgeEscrow } = fixtureContracts) + + // Give some funds to the token sender + await grt.connect(governor.signer).mint(tokenSender.address, senderTokens) + // Deploy contracts that mock Arbitrum's bridge contracts + arbitrumMocks = await fixture.loadArbitrumL1Mocks(governor.signer) + ;({ bridgeMock, inboxMock, outboxMock } = arbitrumMocks) + }) + + beforeEach(async function () { + await fixture.setUp() + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + context('> immediately after deploy', function () { + describe('calculateL2TokenAddress', function () { + it('should return address zero as it was not set', async function () { + expect(await l1GraphTokenGateway.calculateL2TokenAddress(grt.address)).eq(AddressZero) + }) + }) + + describe('outboundTransfer', function () { + it('reverts because it is paused', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + grt.address, + l2Receiver.address, + toGRT('10'), + maxGas, + gasPriceBid, + defaultData, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('Paused (contract)') + }) + }) + + describe('finalizeInboundTransfer', function () { + it('revert because it is paused', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .finalizeInboundTransfer( + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('Paused (contract)') + }) + }) + + describe('setArbitrumAddresses', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setArbitrumAddresses(inboxMock.address, mockRouter.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets inbox and router address', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .setArbitrumAddresses(inboxMock.address, mockRouter.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'ArbitrumAddressesSet') + .withArgs(inboxMock.address, mockRouter.address) + expect(await l1GraphTokenGateway.l1Router()).eq(mockRouter.address) + expect(await l1GraphTokenGateway.inbox()).eq(inboxMock.address) + }) + }) + + describe('setL2TokenAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setL2TokenAddress(mockL2GRT.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets l2GRT', async function () { + const tx = l1GraphTokenGateway.connect(governor.signer).setL2TokenAddress(mockL2GRT.address) + await expect(tx).emit(l1GraphTokenGateway, 'L2TokenAddressSet').withArgs(mockL2GRT.address) + expect(await l1GraphTokenGateway.l2GRT()).eq(mockL2GRT.address) + }) + }) + + describe('setL2CounterpartAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setL2CounterpartAddress(mockL2Gateway.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets L2Counterpart', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .setL2CounterpartAddress(mockL2Gateway.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'L2CounterpartAddressSet') + .withArgs(mockL2Gateway.address) + expect(await l1GraphTokenGateway.l2Counterpart()).eq(mockL2Gateway.address) + }) + }) + describe('setEscrowAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setEscrowAddress(bridgeEscrow.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets escrow', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .setEscrowAddress(bridgeEscrow.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'EscrowAddressSet') + .withArgs(bridgeEscrow.address) + expect(await l1GraphTokenGateway.escrow()).eq(bridgeEscrow.address) + }) + }) + describe('addToCallhookWhitelist', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .addToCallhookWhitelist(tokenSender.address) + await expect(tx).revertedWith('Caller must be Controller governor') + expect(await l1GraphTokenGateway.callhookWhitelist(tokenSender.address)).eq(false) + }) + it('adds an address to the callhook whitelist', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookWhitelist(tokenSender.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'AddedToCallhookWhitelist') + .withArgs(tokenSender.address) + expect(await l1GraphTokenGateway.callhookWhitelist(tokenSender.address)).eq(true) + }) + }) + describe('removeFromCallhookWhitelist', function () { + it('is not callable by addreses that are not the governor', async function () { + await l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookWhitelist(tokenSender.address) + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .removeFromCallhookWhitelist(tokenSender.address) + await expect(tx).revertedWith('Caller must be Controller governor') + expect(await l1GraphTokenGateway.callhookWhitelist(tokenSender.address)).eq(true) + }) + it('removes an address from the callhook whitelist', async function () { + await l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookWhitelist(tokenSender.address) + const tx = l1GraphTokenGateway + .connect(governor.signer) + .removeFromCallhookWhitelist(tokenSender.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'RemovedFromCallhookWhitelist') + .withArgs(tokenSender.address) + expect(await l1GraphTokenGateway.callhookWhitelist(tokenSender.address)).eq(false) + }) + }) + describe('Pausable behavior', () => { + it('cannot be paused or unpaused by someone other than governor or pauseGuardian', async () => { + let tx = l1GraphTokenGateway.connect(tokenSender.signer).setPaused(false) + await expect(tx).revertedWith('Only Governor or Guardian can call') + tx = l1GraphTokenGateway.connect(tokenSender.signer).setPaused(true) + await expect(tx).revertedWith('Only Governor or Guardian can call') + }) + it('can be paused and unpaused by the governor', async function () { + let tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l1GraphTokenGateway.paused()).eq(false) + tx = l1GraphTokenGateway.connect(governor.signer).setPaused(true) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(true) + await expect(await l1GraphTokenGateway.paused()).eq(true) + }) + describe('setPauseGuardian', function () { + it('cannot be called by someone other than governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setPauseGuardian(pauseGuardian.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets a new pause guardian', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .setPauseGuardian(pauseGuardian.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'NewPauseGuardian') + .withArgs(AddressZero, pauseGuardian.address) + }) + it('allows a pause guardian to pause and unpause', async function () { + await l1GraphTokenGateway.connect(governor.signer).setPauseGuardian(pauseGuardian.address) + let tx = l1GraphTokenGateway.connect(pauseGuardian.signer).setPaused(false) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l1GraphTokenGateway.paused()).eq(false) + tx = l1GraphTokenGateway.connect(pauseGuardian.signer).setPaused(true) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(true) + await expect(await l1GraphTokenGateway.paused()).eq(true) + }) + }) + }) + }) + + context('> after configuring and unpausing', function () { + const createMsgData = function (callHookData: string) { + const selector = l1GraphTokenGateway.interface.getSighash('finalizeInboundTransfer') + const params = utils.defaultAbiCoder.encode( + ['address', 'address', 'address', 'uint256', 'bytes'], + [ + grt.address, + tokenSender.address, + l2Receiver.address, + toGRT('10'), + utils.defaultAbiCoder.encode(['bytes', 'bytes'], [emptyCallHookData, callHookData]), + ], + ) + const outboundData = utils.hexlify(utils.concat([selector, params])) + + const msgData = utils.solidityPack( + [ + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'bytes', + ], + [ + toBN(mockL2Gateway.address), + toBN('0'), + defaultEthValue, + maxSubmissionCost, + applyL1ToL2Alias(tokenSender.address), + applyL1ToL2Alias(tokenSender.address), + maxGas, + gasPriceBid, + utils.hexDataLength(outboundData), + outboundData, + ], + ) + return msgData + } + const createInboxAccsEntry = function (msgDataHash: string) { + // The real bridge would emit the InboxAccs entry that came before this one, but our mock + // emits this, making it easier for us to validate here that all the parameters we sent are correct + const expectedInboxAccsEntry = utils.keccak256( + utils.solidityPack( + ['address', 'uint8', 'address', 'bytes32'], + [inboxMock.address, 9, l1GraphTokenGateway.address, msgDataHash], + ), + ) + return expectedInboxAccsEntry + } + const testValidOutboundTransfer = async function ( + signer: Signer, + data: string, + callHookData: string, + ) { + const tx = l1GraphTokenGateway + .connect(signer) + .outboundTransfer(grt.address, l2Receiver.address, toGRT('10'), maxGas, gasPriceBid, data, { + value: defaultEthValue, + }) + // Our bridge mock returns an incrementing seqNum starting at 1 + const expectedSeqNum = 1 + await expect(tx) + .emit(l1GraphTokenGateway, 'DepositInitiated') + .withArgs(grt.address, tokenSender.address, l2Receiver.address, expectedSeqNum, toGRT('10')) + + const msgData = createMsgData(callHookData) + const msgDataHash = utils.keccak256(msgData) + const expectedInboxAccsEntry = createInboxAccsEntry(msgDataHash) + + await expect(tx).emit(inboxMock, 'InboxMessageDelivered').withArgs(1, msgData) + await expect(tx) + .emit(bridgeMock, 'MessageDelivered') + .withArgs( + expectedSeqNum, + expectedInboxAccsEntry, + inboxMock.address, + 9, + l1GraphTokenGateway.address, + msgDataHash, + ) + const escrowBalance = await grt.balanceOf(bridgeEscrow.address) + const senderBalance = await grt.balanceOf(tokenSender.address) + await expect(escrowBalance).eq(toGRT('10')) + await expect(senderBalance).eq(toGRT('990')) + } + before(async function () { + await fixture.configureL1Bridge( + governor.signer, + arbitrumMocks, + fixtureContracts, + mockRouter.address, + mockL2GRT.address, + mockL2Gateway.address, + ) + }) + + describe('calculateL2TokenAddress', function () { + it('returns the L2 token address', async function () { + expect(await l1GraphTokenGateway.calculateL2TokenAddress(grt.address)).eq(mockL2GRT.address) + }) + it('returns the zero address if the input is any other address', async function () { + expect(await l1GraphTokenGateway.calculateL2TokenAddress(tokenSender.address)).eq( + AddressZero, + ) + }) + }) + + describe('outboundTransfer', function () { + it('reverts when called with the wrong token address', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + tokenSender.address, + l2Receiver.address, + toGRT('10'), + maxGas, + gasPriceBid, + defaultData, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('TOKEN_NOT_GRT') + }) + it('puts tokens in escrow and creates a retryable ticket', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer(tokenSender.signer, defaultData, emptyCallHookData) + }) + it('decodes the sender address from messages sent by the router', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + const routerEncodedData = utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [tokenSender.address, defaultData], + ) + await testValidOutboundTransfer(mockRouter.signer, routerEncodedData, emptyCallHookData) + }) + it('reverts when called with the wrong value', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + grt.address, + l2Receiver.address, + toGRT('10'), + maxGas, + gasPriceBid, + defaultData, + { + value: defaultEthValue.sub(1), + }, + ) + await expect(tx).revertedWith('WRONG_ETH_VALUE') + }) + it('reverts when called with nonempty calldata, if the sender is not whitelisted', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + grt.address, + l2Receiver.address, + toGRT('10'), + maxGas, + gasPriceBid, + defaultDataWithNotEmptyCallHookData, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('CALL_HOOK_DATA_NOT_ALLOWED') + }) + it('allows sending nonempty calldata, if the sender is whitelisted', async function () { + await l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookWhitelist(tokenSender.address) + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer( + tokenSender.signer, + defaultDataWithNotEmptyCallHookData, + notEmptyCallHookData, + ) + }) + it('reverts when the sender does not have enough GRT', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('1001')) + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + grt.address, + l2Receiver.address, + toGRT('1001'), + maxGas, + gasPriceBid, + defaultData, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('ERC20: transfer amount exceeds balance') + }) + }) + + describe('finalizeInboundTransfer', function () { + it('reverts when called by an account that is not the bridge', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .finalizeInboundTransfer( + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('NOT_FROM_BRIDGE') + }) + it('reverts when called by the bridge, but the tx was not started by the L2 gateway', async function () { + const encodedCalldata = l1GraphTokenGateway.interface.encodeFunctionData( + 'finalizeInboundTransfer', + [ + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('10'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ], + ) + // The real outbox would require a proof, which would + // validate that the tx was initiated by the L2 gateway but our mock + // just executes unconditionally + const tx = outboxMock.connect(tokenSender.signer).executeTransaction( + toBN('0'), + [], + toBN('0'), + l2Receiver.address, // Note this is not mockL2Gateway + l1GraphTokenGateway.address, + toBN('1337'), + await latestBlock(), + toBN('133701337'), + toBN('0'), + encodedCalldata, + ) + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + }) + it('reverts if the gateway does not have tokens', async function () { + // This scenario should never really happen, but we still + // test that the gateway reverts in this case + const encodedCalldata = l1GraphTokenGateway.interface.encodeFunctionData( + 'finalizeInboundTransfer', + [ + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('10'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ], + ) + // The real outbox would require a proof, which would + // validate that the tx was initiated by the L2 gateway but our mock + // just executes unconditionally + const tx = outboxMock + .connect(tokenSender.signer) + .executeTransaction( + toBN('0'), + [], + toBN('0'), + mockL2Gateway.address, + l1GraphTokenGateway.address, + toBN('1337'), + await latestBlock(), + toBN('133701337'), + toBN('0'), + encodedCalldata, + ) + await expect(tx).revertedWith('BRIDGE_OUT_OF_FUNDS') + }) + it('reverts if the gateway is revoked from escrow', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer(tokenSender.signer, defaultData, emptyCallHookData) + // At this point, the gateway holds 10 GRT in escrow + // But we revoke the gateway's permission to move the funds: + await bridgeEscrow.connect(governor.signer).revokeAll(l1GraphTokenGateway.address) + const encodedCalldata = l1GraphTokenGateway.interface.encodeFunctionData( + 'finalizeInboundTransfer', + [ + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('8'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ], + ) + // The real outbox would require a proof, which would + // validate that the tx was initiated by the L2 gateway but our mock + // just executes unconditionally + const tx = outboxMock + .connect(tokenSender.signer) + .executeTransaction( + toBN('0'), + [], + toBN('0'), + mockL2Gateway.address, + l1GraphTokenGateway.address, + toBN('1337'), + await latestBlock(), + toBN('133701337'), + toBN('0'), + encodedCalldata, + ) + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') + }) + it('sends tokens out of escrow', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer(tokenSender.signer, defaultData, emptyCallHookData) + // At this point, the gateway holds 10 GRT in escrow + const encodedCalldata = l1GraphTokenGateway.interface.encodeFunctionData( + 'finalizeInboundTransfer', + [ + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('8'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ], + ) + // The real outbox would require a proof, which would + // validate that the tx was initiated by the L2 gateway but our mock + // just executes unconditionally + const tx = outboxMock + .connect(tokenSender.signer) + .executeTransaction( + toBN('0'), + [], + toBN('0'), + mockL2Gateway.address, + l1GraphTokenGateway.address, + toBN('1337'), + await latestBlock(), + toBN('133701337'), + toBN('0'), + encodedCalldata, + ) + await expect(tx) + .emit(l1GraphTokenGateway, 'WithdrawalFinalized') + .withArgs(grt.address, l2Receiver.address, tokenSender.address, toBN('0'), toGRT('8')) + const escrowBalance = await grt.balanceOf(bridgeEscrow.address) + const senderBalance = await grt.balanceOf(tokenSender.address) + await expect(escrowBalance).eq(toGRT('2')) + await expect(senderBalance).eq(toGRT('998')) + }) + }) + }) +}) diff --git a/test/graphToken.test.ts b/test/graphToken.test.ts index 5f53e6866..b42274ac4 100644 --- a/test/graphToken.test.ts +++ b/test/graphToken.test.ts @@ -1,275 +1,5 @@ -import { expect } from 'chai' -import { constants, utils, BytesLike, BigNumber, Signature } from 'ethers' -import { eip712 } from '@graphprotocol/common-ts/dist/attestations' - -import { GraphToken } from '../build/types/GraphToken' - -import * as deployment from './lib/deployment' -import { getAccounts, getChainID, toBN, toGRT, Account } from './lib/testHelpers' - -const { AddressZero, MaxUint256 } = constants -const { keccak256, SigningKey } = utils - -const PERMIT_TYPE_HASH = eip712.typeHash( - 'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)', -) -const SALT = '0x51f3d585afe6dfeb2af01bba0889a36c1db03beec88c6a4d0c53817069026afa' - -interface Permit { - owner: string - spender: string - value: BigNumber - nonce: BigNumber - deadline: BigNumber -} - -function hashEncodePermit(permit: Permit) { - return eip712.hashStruct( - PERMIT_TYPE_HASH, - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [permit.owner, permit.spender, permit.value, permit.nonce, permit.deadline], - ) -} - -function signPermit( - signer: BytesLike, - chainId: number, - contractAddress: string, - permit: Permit, -): Signature { - const domainSeparator = eip712.domainSeparator({ - name: 'Graph Token', - version: '0', - chainId, - verifyingContract: contractAddress, - salt: SALT, - }) - const hashEncodedPermit = hashEncodePermit(permit) - const message = eip712.encode(domainSeparator, hashEncodedPermit) - const messageHash = keccak256(message) - const signingKey = new SigningKey(signer) - return signingKey.signDigest(messageHash) -} +import { grtTests } from './lib/graphTokenTests' describe('GraphToken', () => { - let me: Account - let other: Account - let governor: Account - - const mePrivateKey = '0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d' - const otherPrivateKey = '0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1' - - let grt: GraphToken - - async function permitMaxOK(): Promise { - return permitOK(MaxUint256) - } - - async function permitOK(value: BigNumber): Promise { - const nonce = await grt.nonces(me.address) - return { - owner: me.address, - spender: other.address, - value: value, - nonce: nonce, - deadline: toBN('0'), - } - } - - async function permitExpired(): Promise { - const permit = await permitMaxOK() - permit.deadline = toBN('1') - return permit - } - - async function permitDeny(): Promise { - const permit = await permitMaxOK() - permit.value = toBN('0') - return permit - } - - async function createPermitTransaction(permit: Permit, signer: string) { - const chainID = await getChainID() - const signature: Signature = signPermit(signer, chainID, grt.address, permit) - return grt.permit( - permit.owner, - permit.spender, - permit.value, - permit.deadline, - signature.v, - signature.r, - signature.s, - ) - } - - before(async function () { - ;[me, other, governor] = await getAccounts() - }) - - beforeEach(async function () { - // Deploy graph token - grt = await deployment.deployGRT(governor.signer) - - // Mint some tokens - const tokens = toGRT('10000') - await grt.connect(governor.signer).mint(me.address, tokens) - }) - - describe('permit', function () { - it('should permit max token allowance', async function () { - // Allow to transfer tokens - const tokensToApprove = toGRT('1000') - const permit = await permitOK(tokensToApprove) - const tx = createPermitTransaction(permit, mePrivateKey) - await expect(tx).emit(grt, 'Approval').withArgs(permit.owner, permit.spender, tokensToApprove) - - // Allowance updated - const allowance = await grt.allowance(me.address, other.address) - expect(allowance).eq(tokensToApprove) - - // Transfer tokens should work - const tokens = toGRT('100') - await grt.connect(other.signer).transferFrom(me.address, other.address, tokens) - }) - - it('should permit max token allowance', async function () { - // Allow to transfer tokens - const permit = await permitMaxOK() - const tx = createPermitTransaction(permit, mePrivateKey) - await expect(tx).emit(grt, 'Approval').withArgs(permit.owner, permit.spender, MaxUint256) - - // Allowance updated - const allowance = await grt.allowance(me.address, other.address) - expect(allowance).eq(MaxUint256) - - // Transfer tokens should work - const tokens = toGRT('100') - await grt.connect(other.signer).transferFrom(me.address, other.address, tokens) - }) - - it('reject to transfer more tokens than approved by permit', async function () { - // Allow to transfer tokens - const tokensToApprove = toGRT('1000') - const permit = await permitOK(tokensToApprove) - await createPermitTransaction(permit, mePrivateKey) - - // Should not transfer more than approved - const tooManyTokens = toGRT('1001') - const tx = grt.connect(other.signer).transferFrom(me.address, other.address, tooManyTokens) - await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') - - // Should transfer up to the approved amount - await grt.connect(other.signer).transferFrom(me.address, other.address, tokensToApprove) - }) - - it('reject use two permits with same nonce', async function () { - // Allow to transfer tokens - const permit = await permitMaxOK() - await createPermitTransaction(permit, mePrivateKey) - - // Try to re-use the permit - const tx = createPermitTransaction(permit, mePrivateKey) - await expect(tx).revertedWith('GRT: invalid permit') - }) - - it('reject use expired permit', async function () { - const permit = await permitExpired() - const tx = createPermitTransaction(permit, mePrivateKey) - await expect(tx).revertedWith('GRT: expired permit') - }) - - it('reject permit if holder address does not match', async function () { - const permit = await permitMaxOK() - const tx = createPermitTransaction(permit, otherPrivateKey) - await expect(tx).revertedWith('GRT: invalid permit') - }) - - it('should deny transfer from if permit was denied', async function () { - // Allow to transfer tokens - const permit1 = await permitMaxOK() - await createPermitTransaction(permit1, mePrivateKey) - - // Deny transfer tokens - const permit2 = await permitDeny() - await createPermitTransaction(permit2, mePrivateKey) - - // Allowance updated - const allowance = await grt.allowance(me.address, other.address) - expect(allowance).eq(toBN('0')) - - // Try to transfer without permit should fail - const tokens = toGRT('100') - const tx = grt.connect(other.signer).transferFrom(me.address, other.address, tokens) - await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') - }) - }) - - describe('mint', function () { - describe('addMinter', function () { - it('reject add a new minter if not allowed', async function () { - const tx = grt.connect(me.signer).addMinter(me.address) - await expect(tx).revertedWith('Only Governor can call') - }) - - it('should add a new minter', async function () { - expect(await grt.isMinter(me.address)).eq(false) - const tx = grt.connect(governor.signer).addMinter(me.address) - await expect(tx).emit(grt, 'MinterAdded').withArgs(me.address) - expect(await grt.isMinter(me.address)).eq(true) - }) - }) - - describe('mint', async function () { - it('reject mint if not minter', async function () { - const tx = grt.connect(me.signer).mint(me.address, toGRT('100')) - await expect(tx).revertedWith('Only minter can call') - }) - }) - - context('> when is minter', function () { - beforeEach(async function () { - await grt.connect(governor.signer).addMinter(me.address) - expect(await grt.isMinter(me.address)).eq(true) - }) - - describe('mint', async function () { - it('should mint', async function () { - const beforeTokens = await grt.balanceOf(me.address) - - const tokensToMint = toGRT('100') - const tx = grt.connect(me.signer).mint(me.address, tokensToMint) - await expect(tx).emit(grt, 'Transfer').withArgs(AddressZero, me.address, tokensToMint) - - const afterTokens = await grt.balanceOf(me.address) - expect(afterTokens).eq(beforeTokens.add(tokensToMint)) - }) - - it('should mint if governor', async function () { - const tokensToMint = toGRT('100') - await grt.connect(governor.signer).mint(me.address, tokensToMint) - }) - }) - - describe('removeMinter', function () { - it('reject remove a minter if not allowed', async function () { - const tx = grt.connect(me.signer).removeMinter(me.address) - await expect(tx).revertedWith('Only Governor can call') - }) - - it('should remove a minter', async function () { - const tx = grt.connect(governor.signer).removeMinter(me.address) - await expect(tx).emit(grt, 'MinterRemoved').withArgs(me.address) - expect(await grt.isMinter(me.address)).eq(false) - }) - }) - - describe('renounceMinter', function () { - it('should renounce to be a minter', async function () { - const tx = grt.connect(me.signer).renounceMinter() - await expect(tx).emit(grt, 'MinterRemoved').withArgs(me.address) - expect(await grt.isMinter(me.address)).eq(false) - }) - }) - }) - }) + grtTests.bind(this)(false) }) diff --git a/test/l2/l2GraphToken.test.ts b/test/l2/l2GraphToken.test.ts new file mode 100644 index 000000000..56d8e5ed6 --- /dev/null +++ b/test/l2/l2GraphToken.test.ts @@ -0,0 +1,107 @@ +import { expect } from 'chai' + +import { getAccounts, toGRT, Account, initNetwork } from '../lib/testHelpers' + +import { L2GraphToken } from '../../build/types/L2GraphToken' + +import { grtTests } from '../lib/graphTokenTests' +import { NetworkFixture } from '../lib/fixtures' + +describe('L2GraphToken', () => { + describe('Base GRT behavior', () => { + grtTests.bind(this)(true) + }) + describe('Extended L2 behavior', () => { + let mockL2Gateway: Account + let mockL1GRT: Account + let governor: Account + let user: Account + + let fixture: NetworkFixture + let grt: L2GraphToken + + before(async function () { + await initNetwork() + ;[mockL1GRT, mockL2Gateway, governor, user] = await getAccounts() + fixture = new NetworkFixture() + ;({ grt } = await fixture.loadL2(governor.signer)) + }) + + beforeEach(async function () { + await fixture.setUp() + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + describe('setGateway', async function () { + it('cannot be called by someone who is not the governor', async function () { + const tx = grt.connect(mockL2Gateway.signer).setGateway(mockL2Gateway.address) + await expect(tx).revertedWith('Only Governor can call') + }) + it('sets the L2 Gateway address when called by the governor', async function () { + const tx = grt.connect(governor.signer).setGateway(mockL2Gateway.address) + await expect(tx).emit(grt, 'GatewaySet').withArgs(mockL2Gateway.address) + await expect(await grt.gateway()).eq(mockL2Gateway.address) + }) + }) + describe('setL1Address', async function () { + it('cannot be called by someone who is not the governor', async function () { + const tx = grt.connect(mockL2Gateway.signer).setL1Address(mockL1GRT.address) + await expect(tx).revertedWith('Only Governor can call') + }) + it('sets the L1 GRT address when called by the governor', async function () { + const tx = grt.connect(governor.signer).setL1Address(mockL1GRT.address) + await expect(tx).emit(grt, 'L1AddressSet').withArgs(mockL1GRT.address) + await expect(await grt.l1Address()).eq(mockL1GRT.address) + }) + }) + describe('bridge minting and burning', async function () { + beforeEach(async function () { + // Configure the l1Address and gateway + await grt.connect(governor.signer).setL1Address(mockL1GRT.address) + await grt.connect(governor.signer).setGateway(mockL2Gateway.address) + }) + describe('bridgeMint', async function () { + it('cannot be called by someone who is not the gateway', async function () { + const tx = grt.connect(governor.signer).bridgeMint(user.address, toGRT('100')) + await expect(tx).revertedWith('NOT_GATEWAY') + }) + it('mints GRT into a destination account', async function () { + const tx = grt.connect(mockL2Gateway.signer).bridgeMint(user.address, toGRT('100')) + await expect(tx).emit(grt, 'BridgeMinted').withArgs(user.address, toGRT('100')) + await expect(await grt.balanceOf(user.address)).eq(toGRT('100')) + }) + }) + describe('bridgeBurn', async function () { + it('cannot be called by someone who is not the gateway', async function () { + const tx = grt.connect(governor.signer).bridgeBurn(user.address, toGRT('100')) + await expect(tx).revertedWith('NOT_GATEWAY') + }) + it('requires approval for burning', async function () { + await grt.connect(mockL2Gateway.signer).bridgeMint(user.address, toGRT('100')) + const tx = grt.connect(mockL2Gateway.signer).bridgeBurn(user.address, toGRT('20')) + await expect(tx).revertedWith('ERC20: burn amount exceeds allowance') + }) + it('fails if the user does not have enough funds', async function () { + await grt.connect(mockL2Gateway.signer).bridgeMint(user.address, toGRT('10')) + await grt.connect(user.signer).approve(mockL2Gateway.address, toGRT('20')) + const tx = grt.connect(mockL2Gateway.signer).bridgeBurn(user.address, toGRT('20')) + await expect(tx).revertedWith('ERC20: burn amount exceeds balance') + }) + it('burns GRT from an account when approved', async function () { + await grt.connect(mockL2Gateway.signer).bridgeMint(user.address, toGRT('100')) + await grt.connect(user.signer).approve(mockL2Gateway.address, toGRT('20')) + const tx = grt.connect(mockL2Gateway.signer).bridgeBurn(user.address, toGRT('20')) + await expect(tx).emit(grt, 'BridgeBurned').withArgs(user.address, toGRT('20')) + await expect(await grt.balanceOf(user.address)).eq(toGRT('80')) + }) + }) + it('does not allow the bridge to mint as a regular minter', async function () { + const tx = grt.connect(mockL2Gateway.signer).mint(user.address, toGRT('100')) + await expect(tx).revertedWith('Only minter can call') + }) + }) + }) +}) diff --git a/test/l2/l2GraphTokenGateway.test.ts b/test/l2/l2GraphTokenGateway.test.ts new file mode 100644 index 000000000..f4d57dbab --- /dev/null +++ b/test/l2/l2GraphTokenGateway.test.ts @@ -0,0 +1,409 @@ +import { expect, use } from 'chai' +import { constants, ContractTransaction, Signer, utils } from 'ethers' + +import { L2GraphToken } from '../../build/types/L2GraphToken' +import { L2GraphTokenGateway } from '../../build/types/L2GraphTokenGateway' + +import { L2FixtureContracts, NetworkFixture } from '../lib/fixtures' + +import { FakeContract, smock } from '@defi-wonderland/smock' + +import path from 'path' +import { Artifacts } from 'hardhat/internal/artifacts' +const ARTIFACTS_PATH = path.resolve('build/contracts') +const artifacts = new Artifacts(ARTIFACTS_PATH) +const rewardsManagerMockAbi = artifacts.readArtifactSync('RewardsManagerMock').abi + +use(smock.matchers) + +import { getAccounts, toGRT, Account, toBN, getL2SignerFromL1 } from '../lib/testHelpers' +import { Interface } from 'ethers/lib/utils' + +const { AddressZero } = constants + +describe('L2GraphTokenGateway', () => { + let me: Account + let governor: Account + let tokenSender: Account + let l1Receiver: Account + let l2Receiver: Account + let mockRouter: Account + let mockL1GRT: Account + let mockL1Gateway: Account + let pauseGuardian: Account + let fixture: NetworkFixture + let arbSysMock: FakeContract + + let fixtureContracts: L2FixtureContracts + let grt: L2GraphToken + let l2GraphTokenGateway: L2GraphTokenGateway + + const senderTokens = toGRT('1000') + const defaultData = '0x' + const mockIface = new Interface(rewardsManagerMockAbi) + const notEmptyCallHookData = mockIface.encodeFunctionData('pow', [toBN(1), toBN(2), toBN(3)]) + const defaultDataWithNotEmptyCallHookData = utils.defaultAbiCoder.encode( + ['bytes', 'bytes'], + ['0x', notEmptyCallHookData], + ) + + before(async function () { + ;[ + me, + governor, + tokenSender, + l1Receiver, + mockRouter, + mockL1GRT, + mockL1Gateway, + l2Receiver, + pauseGuardian, + ] = await getAccounts() + + fixture = new NetworkFixture() + fixtureContracts = await fixture.loadL2(governor.signer) + ;({ grt, l2GraphTokenGateway } = fixtureContracts) + + // Give some funds to the token sender + await grt.connect(governor.signer).mint(tokenSender.address, senderTokens) + }) + + beforeEach(async function () { + await fixture.setUp() + // Thanks to Livepeer: https://github.com/livepeer/arbitrum-lpt-bridge/blob/main/test/unit/L2/l2LPTGateway.test.ts#L86 + arbSysMock = await smock.fake('ArbSys', { + address: '0x0000000000000000000000000000000000000064', + }) + arbSysMock.sendTxToL1.returns(1) + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + context('> immediately after deploy', function () { + describe('calculateL2TokenAddress', function () { + it('should return the zero address', async function () { + expect(await l2GraphTokenGateway.calculateL2TokenAddress(grt.address)).eq(AddressZero) + }) + }) + + describe('outboundTransfer', function () { + it('reverts because it is paused', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + ['outboundTransfer(address,address,uint256,bytes)']( + grt.address, + l1Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('Paused (contract)') + }) + }) + + describe('finalizeInboundTransfer', function () { + it('revert because it is paused', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .finalizeInboundTransfer( + grt.address, + tokenSender.address, + l1Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('Paused (contract)') + }) + }) + + describe('setL2Router', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l2GraphTokenGateway.connect(tokenSender.signer).setL2Router(mockRouter.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets router address', async function () { + const tx = l2GraphTokenGateway.connect(governor.signer).setL2Router(mockRouter.address) + await expect(tx).emit(l2GraphTokenGateway, 'L2RouterSet').withArgs(mockRouter.address) + expect(await l2GraphTokenGateway.l2Router()).eq(mockRouter.address) + }) + }) + + describe('setL1TokenAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .setL1TokenAddress(mockL1GRT.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets l2GRT', async function () { + const tx = l2GraphTokenGateway.connect(governor.signer).setL1TokenAddress(mockL1GRT.address) + await expect(tx).emit(l2GraphTokenGateway, 'L1TokenAddressSet').withArgs(mockL1GRT.address) + expect(await l2GraphTokenGateway.l1GRT()).eq(mockL1GRT.address) + }) + }) + + describe('setL1CounterpartAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .setL1CounterpartAddress(mockL1Gateway.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets L1Counterpart', async function () { + const tx = l2GraphTokenGateway + .connect(governor.signer) + .setL1CounterpartAddress(mockL1Gateway.address) + await expect(tx) + .emit(l2GraphTokenGateway, 'L1CounterpartAddressSet') + .withArgs(mockL1Gateway.address) + expect(await l2GraphTokenGateway.l1Counterpart()).eq(mockL1Gateway.address) + }) + }) + describe('Pausable behavior', () => { + it('cannot be paused or unpaused by someone other than governor or pauseGuardian', async () => { + let tx = l2GraphTokenGateway.connect(tokenSender.signer).setPaused(false) + await expect(tx).revertedWith('Only Governor or Guardian can call') + tx = l2GraphTokenGateway.connect(tokenSender.signer).setPaused(true) + await expect(tx).revertedWith('Only Governor or Guardian can call') + }) + it('can be paused and unpaused by the governor', async function () { + let tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l2GraphTokenGateway.paused()).eq(false) + tx = l2GraphTokenGateway.connect(governor.signer).setPaused(true) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(true) + await expect(await l2GraphTokenGateway.paused()).eq(true) + }) + describe('setPauseGuardian', function () { + it('cannot be called by someone other than governor', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .setPauseGuardian(pauseGuardian.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + it('sets a new pause guardian', async function () { + const tx = l2GraphTokenGateway + .connect(governor.signer) + .setPauseGuardian(pauseGuardian.address) + await expect(tx) + .emit(l2GraphTokenGateway, 'NewPauseGuardian') + .withArgs(AddressZero, pauseGuardian.address) + }) + it('allows a pause guardian to pause and unpause', async function () { + await l2GraphTokenGateway.connect(governor.signer).setPauseGuardian(pauseGuardian.address) + let tx = l2GraphTokenGateway.connect(pauseGuardian.signer).setPaused(false) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l2GraphTokenGateway.paused()).eq(false) + tx = l2GraphTokenGateway.connect(pauseGuardian.signer).setPaused(true) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(true) + await expect(await l2GraphTokenGateway.paused()).eq(true) + }) + }) + }) + }) + + context('> after configuring and unpausing', function () { + const testValidOutboundTransfer = async function (signer: Signer, data: string) { + const tx = l2GraphTokenGateway + .connect(signer) + ['outboundTransfer(address,address,uint256,bytes)']( + mockL1GRT.address, + l1Receiver.address, + toGRT('10'), + data, + ) + const expectedId = 1 + await expect(tx) + .emit(l2GraphTokenGateway, 'WithdrawalInitiated') + .withArgs( + mockL1GRT.address, + tokenSender.address, + l1Receiver.address, + expectedId, + 0, + toGRT('10'), + ) + + // Should use the L1 Gateway's interface, but both come from ITokenGateway + const calldata = l2GraphTokenGateway.interface.encodeFunctionData('finalizeInboundTransfer', [ + mockL1GRT.address, + tokenSender.address, + l1Receiver.address, + toGRT('10'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ]) + await expect(tx) + .emit(l2GraphTokenGateway, 'TxToL1') + .withArgs(tokenSender.address, mockL1Gateway.address, 1, calldata) + + // For some reason the call count doesn't work properly, + // and each function call is counted 12 times. + // Possibly related to https://github.com/defi-wonderland/smock/issues/85 ? + //expect(arbSysMock.sendTxToL1).to.have.been.calledOnce + expect(arbSysMock.sendTxToL1).to.have.been.calledWith(mockL1Gateway.address, calldata) + const senderBalance = await grt.balanceOf(tokenSender.address) + await expect(senderBalance).eq(toGRT('990')) + } + before(async function () { + await fixture.configureL2Bridge( + governor.signer, + fixtureContracts, + mockRouter.address, + mockL1GRT.address, + mockL1Gateway.address, + ) + }) + + describe('calculateL2TokenAddress', function () { + it('returns the L2 token address', async function () { + expect(await l2GraphTokenGateway.calculateL2TokenAddress(mockL1GRT.address)).eq(grt.address) + }) + it('returns the zero address if the input is any other address', async function () { + expect(await l2GraphTokenGateway.calculateL2TokenAddress(tokenSender.address)).eq( + AddressZero, + ) + }) + }) + + describe('outboundTransfer', function () { + it('reverts when called with the wrong token address', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + ['outboundTransfer(address,address,uint256,bytes)']( + tokenSender.address, + l1Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('TOKEN_NOT_GRT') + }) + it('burns tokens and triggers an L1 call', async function () { + await grt.connect(tokenSender.signer).approve(l2GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer(tokenSender.signer, defaultData) + }) + it('decodes the sender address from messages sent by the router', async function () { + await grt.connect(tokenSender.signer).approve(l2GraphTokenGateway.address, toGRT('10')) + const routerEncodedData = utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [tokenSender.address, defaultData], + ) + await testValidOutboundTransfer(mockRouter.signer, routerEncodedData) + }) + it('reverts when called with nonempty calldata', async function () { + await grt.connect(tokenSender.signer).approve(l2GraphTokenGateway.address, toGRT('10')) + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + ['outboundTransfer(address,address,uint256,bytes)']( + mockL1GRT.address, + l1Receiver.address, + toGRT('10'), + defaultDataWithNotEmptyCallHookData, + ) + await expect(tx).revertedWith('CALL_HOOK_DATA_NOT_ALLOWED') + }) + it('reverts when the sender does not have enough GRT', async function () { + await grt.connect(tokenSender.signer).approve(l2GraphTokenGateway.address, toGRT('1001')) + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + ['outboundTransfer(address,address,uint256,bytes)']( + mockL1GRT.address, + l1Receiver.address, + toGRT('1001'), + defaultData, + ) + await expect(tx).revertedWith('ERC20: burn amount exceeds balance') + }) + }) + + describe('finalizeInboundTransfer', function () { + const testValidFinalizeTransfer = async function ( + data: string, + ): Promise { + const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) + await me.signer.sendTransaction({ + to: await mockL1GatewayL2Alias.getAddress(), + value: utils.parseUnits('1', 'ether'), + }) + const tx = l2GraphTokenGateway + .connect(mockL1GatewayL2Alias) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + l2Receiver.address, + toGRT('10'), + data, + ) + await expect(tx) + .emit(l2GraphTokenGateway, 'DepositFinalized') + .withArgs(mockL1GRT.address, tokenSender.address, l2Receiver.address, toGRT('10')) + + await expect(tx).emit(grt, 'BridgeMinted').withArgs(l2Receiver.address, toGRT('10')) + + // Unchanged + const senderBalance = await grt.balanceOf(tokenSender.address) + await expect(senderBalance).eq(toGRT('1000')) + // 10 newly minted GRT + const receiverBalance = await grt.balanceOf(l2Receiver.address) + await expect(receiverBalance).eq(toGRT('10')) + return tx + } + it('reverts when called by an account that is not the gateway', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + l2Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + }) + it('reverts when called by an account that is the gateway but without the L2 alias', async function () { + const tx = l2GraphTokenGateway + .connect(mockL1Gateway.signer) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + l2Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + }) + it('mints and sends tokens when called by the aliased gateway', async function () { + await testValidFinalizeTransfer(defaultData) + }) + it('calls a callhook if the sender is whitelisted', async function () { + const rewardsManagerMock = await smock.fake('RewardsManagerMock', { + address: l2Receiver.address, + }) + rewardsManagerMock.pow.returns(1) + await testValidFinalizeTransfer(defaultDataWithNotEmptyCallHookData) + expect(rewardsManagerMock.pow).to.have.been.calledWith(toBN(1), toBN(2), toBN(3)) + }) + it('reverts if a callhook reverts', async function () { + const rewardsManagerMock = await smock.fake('RewardsManagerMock', { + address: l2Receiver.address, + }) + rewardsManagerMock.pow.reverts() + const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) + await me.signer.sendTransaction({ + to: await mockL1GatewayL2Alias.getAddress(), + value: utils.parseUnits('1', 'ether'), + }) + const tx = l2GraphTokenGateway + .connect(mockL1GatewayL2Alias) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + l2Receiver.address, + toGRT('10'), + defaultDataWithNotEmptyCallHookData, + ) + await expect(tx).revertedWith('CALLHOOK_FAILED') + }) + }) + }) +}) diff --git a/test/lib/deployment.ts b/test/lib/deployment.ts index 68f0eb18a..a6afe9dbb 100644 --- a/test/lib/deployment.ts +++ b/test/lib/deployment.ts @@ -18,6 +18,10 @@ import { Staking } from '../../build/types/Staking' import { RewardsManager } from '../../build/types/RewardsManager' import { GraphGovernance } from '../../build/types/GraphGovernance' import { SubgraphNFT } from '../../build/types/SubgraphNFT' +import { L1GraphTokenGateway } from '../../build/types/L1GraphTokenGateway' +import { L2GraphTokenGateway } from '../../build/types/L2GraphTokenGateway' +import { L2GraphToken } from '../../build/types/L2GraphToken' +import { BridgeEscrow } from '../../build/types/BridgeEscrow' // Disable logging for tests logger.pause() @@ -53,6 +57,7 @@ export const defaults = { }, rewards: { issuanceRate: toGRT('1.000000023206889619'), // 5% annual rate + dripInterval: toBN('50400'), // 1 week in blocks (post-Merge) }, } @@ -243,3 +248,54 @@ export async function deployGraphGovernance( deployer, ) as unknown as GraphGovernance } + +export async function deployL1GraphTokenGateway( + deployer: Signer, + controller: string, + proxyAdmin: GraphProxyAdmin, +): Promise { + return network.deployContractWithProxy( + proxyAdmin, + 'L1GraphTokenGateway', + [controller], + deployer, + ) as unknown as L1GraphTokenGateway +} + +export async function deployBridgeEscrow( + deployer: Signer, + controller: string, + proxyAdmin: GraphProxyAdmin, +): Promise { + return network.deployContractWithProxy( + proxyAdmin, + 'BridgeEscrow', + [controller], + deployer, + ) as unknown as BridgeEscrow +} + +export async function deployL2GraphTokenGateway( + deployer: Signer, + controller: string, + proxyAdmin: GraphProxyAdmin, +): Promise { + return network.deployContractWithProxy( + proxyAdmin, + 'L2GraphTokenGateway', + [controller], + deployer, + ) as unknown as L2GraphTokenGateway +} + +export async function deployL2GRT( + deployer: Signer, + proxyAdmin: GraphProxyAdmin, +): Promise { + return network.deployContractWithProxy( + proxyAdmin, + 'L2GraphToken', + [await deployer.getAddress()], + deployer, + ) as unknown as L2GraphToken +} diff --git a/test/lib/fixtures.ts b/test/lib/fixtures.ts index 86541cf6d..ae6832938 100644 --- a/test/lib/fixtures.ts +++ b/test/lib/fixtures.ts @@ -2,7 +2,60 @@ import { utils, Wallet, Signer } from 'ethers' import * as deployment from './deployment' -import { evmSnapshot, evmRevert, provider } from './testHelpers' +import { evmSnapshot, evmRevert, initNetwork, toBN } from './testHelpers' +import { BridgeMock } from '../../build/types/BridgeMock' +import { InboxMock } from '../../build/types/InboxMock' +import { OutboxMock } from '../../build/types/OutboxMock' +import { deployContract } from './deployment' +import { Controller } from '../../build/types/Controller' +import { DisputeManager } from '../../build/types/DisputeManager' +import { EpochManager } from '../../build/types/EpochManager' +import { GraphToken } from '../../build/types/GraphToken' +import { Curation } from '../../build/types/Curation' +import { GNS } from '../../build/types/GNS' +import { Staking } from '../../build/types/Staking' +import { RewardsManager } from '../../build/types/RewardsManager' +import { ServiceRegistry } from '../../build/types/ServiceRegistry' +import { GraphProxyAdmin } from '../../build/types/GraphProxyAdmin' +import { L1GraphTokenGateway } from '../../build/types/L1GraphTokenGateway' +import { BridgeEscrow } from '../../build/types/BridgeEscrow' +import { L2GraphTokenGateway } from '../../build/types/L2GraphTokenGateway' +import { L2GraphToken } from '../../build/types/L2GraphToken' + +export interface L1FixtureContracts { + controller: Controller + disputeManager: DisputeManager + epochManager: EpochManager + grt: GraphToken + curation: Curation + gns: GNS + staking: Staking + rewardsManager: RewardsManager + serviceRegistry: ServiceRegistry + proxyAdmin: GraphProxyAdmin + l1GraphTokenGateway: L1GraphTokenGateway + bridgeEscrow: BridgeEscrow +} + +export interface L2FixtureContracts { + controller: Controller + disputeManager: DisputeManager + epochManager: EpochManager + grt: L2GraphToken + curation: Curation + gns: GNS + staking: Staking + rewardsManager: RewardsManager + serviceRegistry: ServiceRegistry + proxyAdmin: GraphProxyAdmin + l2GraphTokenGateway: L2GraphTokenGateway +} + +export interface ArbitrumL1Mocks { + bridgeMock: BridgeMock + inboxMock: InboxMock + outboxMock: OutboxMock +} export class NetworkFixture { lastSnapshotId: number @@ -11,16 +64,13 @@ export class NetworkFixture { this.lastSnapshotId = 0 } - async load( + async _loadLayer( deployer: Signer, slasher: Signer = Wallet.createRandom() as Signer, arbitrator: Signer = Wallet.createRandom() as Signer, - ): Promise { - // Enable automining with each transaction, and disable - // the mining interval. Individual tests may modify this - // behavior as needed. - provider().send('evm_setIntervalMining', [0]) - provider().send('evm_setAutomine', [true]) + isL2: boolean, + ): Promise { + await initNetwork() // Roles const arbitratorAddress = await arbitrator.getAddress() @@ -34,7 +84,13 @@ export class NetworkFixture { controller.address, proxyAdmin, ) - const grt = await deployment.deployGRT(deployer) + let grt: GraphToken | L2GraphToken + if (isL2) { + grt = await deployment.deployL2GRT(deployer, proxyAdmin) + } else { + grt = await deployment.deployGRT(deployer) + } + const curation = await deployment.deployCuration(deployer, controller.address, proxyAdmin) const gns = await deployment.deployGNS(deployer, controller.address, proxyAdmin) const staking = await deployment.deployStaking(deployer, controller.address, proxyAdmin) @@ -55,6 +111,24 @@ export class NetworkFixture { proxyAdmin, ) + let l1GraphTokenGateway: L1GraphTokenGateway + let l2GraphTokenGateway: L2GraphTokenGateway + let bridgeEscrow: BridgeEscrow + if (isL2) { + l2GraphTokenGateway = await deployment.deployL2GraphTokenGateway( + deployer, + controller.address, + proxyAdmin, + ) + } else { + l1GraphTokenGateway = await deployment.deployL1GraphTokenGateway( + deployer, + controller.address, + proxyAdmin, + ) + bridgeEscrow = await deployment.deployBridgeEscrow(deployer, controller.address, proxyAdmin) + } + // Setup controller await controller.setContractProxy(utils.id('EpochManager'), epochManager.address) await controller.setContractProxy(utils.id('GraphToken'), grt.address) @@ -63,6 +137,11 @@ export class NetworkFixture { await controller.setContractProxy(utils.id('DisputeManager'), staking.address) await controller.setContractProxy(utils.id('RewardsManager'), rewardsManager.address) await controller.setContractProxy(utils.id('ServiceRegistry'), serviceRegistry.address) + if (isL2) { + await controller.setContractProxy(utils.id('GraphTokenGateway'), l2GraphTokenGateway.address) + } else { + await controller.setContractProxy(utils.id('GraphTokenGateway'), l1GraphTokenGateway.address) + } // Setup contracts await curation.connect(deployer).syncAllContracts() @@ -71,32 +150,141 @@ export class NetworkFixture { await disputeManager.connect(deployer).syncAllContracts() await rewardsManager.connect(deployer).syncAllContracts() await staking.connect(deployer).syncAllContracts() + if (isL2) { + await l2GraphTokenGateway.connect(deployer).syncAllContracts() + } else { + await l1GraphTokenGateway.connect(deployer).syncAllContracts() + await bridgeEscrow.connect(deployer).syncAllContracts() + } await staking.connect(deployer).setSlasher(slasherAddress, true) - await grt.connect(deployer).addMinter(rewardsManager.address) await gns.connect(deployer).approveAll() - await rewardsManager.connect(deployer).setIssuanceRate(deployment.defaults.rewards.issuanceRate) + await grt.connect(deployer).addMinter(rewardsManager.address) // Unpause the protocol await controller.connect(deployer).setPaused(false) + if (isL2) { + return { + controller, + disputeManager, + epochManager, + grt: grt as L2GraphToken, + curation, + gns, + staking, + rewardsManager, + serviceRegistry, + proxyAdmin, + l2GraphTokenGateway, + } as L2FixtureContracts + } else { + return { + controller, + disputeManager, + epochManager, + grt: grt as GraphToken, + curation, + gns, + staking, + rewardsManager, + serviceRegistry, + proxyAdmin, + l1GraphTokenGateway, + bridgeEscrow, + } as L1FixtureContracts + } + } + + async load( + deployer: Signer, + slasher: Signer = Wallet.createRandom() as Signer, + arbitrator: Signer = Wallet.createRandom() as Signer, + ): Promise { + return this._loadLayer(deployer, slasher, arbitrator, false) as unknown as L1FixtureContracts + } + + async loadL2( + deployer: Signer, + slasher: Signer = Wallet.createRandom() as Signer, + arbitrator: Signer = Wallet.createRandom() as Signer, + ): Promise { + return this._loadLayer(deployer, slasher, arbitrator, true) as unknown as L2FixtureContracts + } + + async loadArbitrumL1Mocks(deployer: Signer): Promise { + const bridgeMock = (await deployContract('BridgeMock', deployer)) as unknown as BridgeMock + const inboxMock = (await deployContract('InboxMock', deployer)) as unknown as InboxMock + const outboxMock = (await deployContract('OutboxMock', deployer)) as unknown as OutboxMock return { - controller, - disputeManager, - epochManager, - grt, - curation, - gns, - staking, - rewardsManager, - serviceRegistry, - proxyAdmin, + bridgeMock, + inboxMock, + outboxMock, } } + async configureL1Bridge( + deployer: Signer, + arbitrumMocks: ArbitrumL1Mocks, + l1FixtureContracts: L1FixtureContracts, + mockRouterAddress: string, + mockL2GRTAddress: string, + mockL2GatewayAddress: string, + ): Promise { + // First configure the Arbitrum bridge mocks + await arbitrumMocks.bridgeMock.connect(deployer).setInbox(arbitrumMocks.inboxMock.address, true) + await arbitrumMocks.bridgeMock + .connect(deployer) + .setOutbox(arbitrumMocks.outboxMock.address, true) + await arbitrumMocks.inboxMock.connect(deployer).setBridge(arbitrumMocks.bridgeMock.address) + await arbitrumMocks.outboxMock.connect(deployer).setBridge(arbitrumMocks.bridgeMock.address) + + // Configure the gateway + await l1FixtureContracts.l1GraphTokenGateway + .connect(deployer) + .setArbitrumAddresses(arbitrumMocks.inboxMock.address, mockRouterAddress) + await l1FixtureContracts.l1GraphTokenGateway + .connect(deployer) + .setL2TokenAddress(mockL2GRTAddress) + await l1FixtureContracts.l1GraphTokenGateway + .connect(deployer) + .setL2CounterpartAddress(mockL2GatewayAddress) + await l1FixtureContracts.l1GraphTokenGateway + .connect(deployer) + .setEscrowAddress(l1FixtureContracts.bridgeEscrow.address) + await l1FixtureContracts.bridgeEscrow + .connect(deployer) + .approveAll(l1FixtureContracts.l1GraphTokenGateway.address) + await l1FixtureContracts.l1GraphTokenGateway.connect(deployer).setPaused(false) + } + + async configureL2Bridge( + deployer: Signer, + l2FixtureContracts: L2FixtureContracts, + mockRouterAddress: string, + mockL1GRTAddress: string, + mockL1GatewayAddress: string, + ): Promise { + // Configure the L2 GRT + // Configure the gateway + await l2FixtureContracts.grt + .connect(deployer) + .setGateway(l2FixtureContracts.l2GraphTokenGateway.address) + await l2FixtureContracts.grt.connect(deployer).setL1Address(mockL1GRTAddress) + // Configure the gateway + await l2FixtureContracts.l2GraphTokenGateway.connect(deployer).setL2Router(mockRouterAddress) + await l2FixtureContracts.l2GraphTokenGateway + .connect(deployer) + .setL1TokenAddress(mockL1GRTAddress) + await l2FixtureContracts.l2GraphTokenGateway + .connect(deployer) + .setL1CounterpartAddress(mockL1GatewayAddress) + await l2FixtureContracts.l2GraphTokenGateway.connect(deployer).setPaused(false) + } + async setUp(): Promise { this.lastSnapshotId = await evmSnapshot() - provider().send('evm_setAutomine', [true]) + await initNetwork() } async tearDown(): Promise { diff --git a/test/lib/graphTokenTests.ts b/test/lib/graphTokenTests.ts new file mode 100644 index 000000000..b0a1b7c7f --- /dev/null +++ b/test/lib/graphTokenTests.ts @@ -0,0 +1,287 @@ +import { expect } from 'chai' +import { constants, utils, BytesLike, BigNumber, Signature } from 'ethers' +import { eip712 } from '@graphprotocol/common-ts/dist/attestations' + +import * as deployment from './deployment' +import { getAccounts, getChainID, toBN, toGRT, Account, initNetwork } from './testHelpers' + +import { L2GraphToken } from '../../build/types/L2GraphToken' +import { GraphToken } from '../../build/types/GraphToken' + +const { AddressZero, MaxUint256 } = constants +const { keccak256, SigningKey } = utils + +const PERMIT_TYPE_HASH = eip712.typeHash( + 'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)', +) +const L1SALT = '0x51f3d585afe6dfeb2af01bba0889a36c1db03beec88c6a4d0c53817069026afa' +const L2SALT = '0xe33842a7acd1d5a1d28f25a931703e5605152dc48d64dc4716efdae1f5659591' + +interface Permit { + owner: string + spender: string + value: BigNumber + nonce: BigNumber + deadline: BigNumber +} + +function hashEncodePermit(permit: Permit) { + return eip712.hashStruct( + PERMIT_TYPE_HASH, + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [permit.owner, permit.spender, permit.value, permit.nonce, permit.deadline], + ) +} + +function signPermit( + signer: BytesLike, + chainId: number, + contractAddress: string, + permit: Permit, + salt: string, +): Signature { + const domainSeparator = eip712.domainSeparator({ + name: 'Graph Token', + version: '0', + chainId, + verifyingContract: contractAddress, + salt: salt, + }) + const hashEncodedPermit = hashEncodePermit(permit) + const message = eip712.encode(domainSeparator, hashEncodedPermit) + const messageHash = keccak256(message) + const signingKey = new SigningKey(signer) + return signingKey.signDigest(messageHash) +} + +export function grtTests(isL2: boolean): void { + let me: Account + let other: Account + let governor: Account + let salt: string + + const mePrivateKey = '0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d' + const otherPrivateKey = '0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1' + + let grt: GraphToken | L2GraphToken + + async function permitMaxOK(): Promise { + return permitOK(MaxUint256) + } + + async function permitOK(value: BigNumber): Promise { + const nonce = await grt.nonces(me.address) + return { + owner: me.address, + spender: other.address, + value: value, + nonce: nonce, + deadline: toBN('0'), + } + } + + async function permitExpired(): Promise { + const permit = await permitMaxOK() + permit.deadline = toBN('1') + return permit + } + + async function permitDeny(): Promise { + const permit = await permitMaxOK() + permit.value = toBN('0') + return permit + } + + async function createPermitTransaction(permit: Permit, signer: string, salt: string) { + const chainID = await getChainID() + const signature: Signature = signPermit(signer, chainID, grt.address, permit, salt) + return grt.permit( + permit.owner, + permit.spender, + permit.value, + permit.deadline, + signature.v, + signature.r, + signature.s, + ) + } + + before(async function () { + await initNetwork() + ;[me, other, governor] = await getAccounts() + }) + + beforeEach(async function () { + // Deploy graph token + if (isL2) { + const proxyAdmin = await deployment.deployProxyAdmin(governor.signer) + grt = await deployment.deployL2GRT(governor.signer, proxyAdmin) + salt = L2SALT + } else { + grt = await deployment.deployGRT(governor.signer) + salt = L1SALT + } + + // Mint some tokens + const tokens = toGRT('10000') + await grt.connect(governor.signer).mint(me.address, tokens) + }) + + describe('permit', function () { + it('should permit max token allowance', async function () { + // Allow to transfer tokens + const tokensToApprove = toGRT('1000') + const permit = await permitOK(tokensToApprove) + const tx = createPermitTransaction(permit, mePrivateKey, salt) + await expect(tx).emit(grt, 'Approval').withArgs(permit.owner, permit.spender, tokensToApprove) + + // Allowance updated + const allowance = await grt.allowance(me.address, other.address) + expect(allowance).eq(tokensToApprove) + + // Transfer tokens should work + const tokens = toGRT('100') + await grt.connect(other.signer).transferFrom(me.address, other.address, tokens) + }) + + it('should permit max token allowance', async function () { + // Allow to transfer tokens + const permit = await permitMaxOK() + const tx = createPermitTransaction(permit, mePrivateKey, salt) + await expect(tx).emit(grt, 'Approval').withArgs(permit.owner, permit.spender, MaxUint256) + + // Allowance updated + const allowance = await grt.allowance(me.address, other.address) + expect(allowance).eq(MaxUint256) + + // Transfer tokens should work + const tokens = toGRT('100') + await grt.connect(other.signer).transferFrom(me.address, other.address, tokens) + }) + + it('reject to transfer more tokens than approved by permit', async function () { + // Allow to transfer tokens + const tokensToApprove = toGRT('1000') + const permit = await permitOK(tokensToApprove) + await createPermitTransaction(permit, mePrivateKey, salt) + + // Should not transfer more than approved + const tooManyTokens = toGRT('1001') + const tx = grt.connect(other.signer).transferFrom(me.address, other.address, tooManyTokens) + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') + + // Should transfer up to the approved amount + await grt.connect(other.signer).transferFrom(me.address, other.address, tokensToApprove) + }) + + it('reject use two permits with same nonce', async function () { + // Allow to transfer tokens + const permit = await permitMaxOK() + await createPermitTransaction(permit, mePrivateKey, salt) + + // Try to re-use the permit + const tx = createPermitTransaction(permit, mePrivateKey, salt) + await expect(tx).revertedWith('GRT: invalid permit') + }) + + it('reject use expired permit', async function () { + const permit = await permitExpired() + const tx = createPermitTransaction(permit, mePrivateKey, salt) + await expect(tx).revertedWith('GRT: expired permit') + }) + + it('reject permit if holder address does not match', async function () { + const permit = await permitMaxOK() + const tx = createPermitTransaction(permit, otherPrivateKey, salt) + await expect(tx).revertedWith('GRT: invalid permit') + }) + + it('should deny transfer from if permit was denied', async function () { + // Allow to transfer tokens + const permit1 = await permitMaxOK() + await createPermitTransaction(permit1, mePrivateKey, salt) + + // Deny transfer tokens + const permit2 = await permitDeny() + await createPermitTransaction(permit2, mePrivateKey, salt) + + // Allowance updated + const allowance = await grt.allowance(me.address, other.address) + expect(allowance).eq(toBN('0')) + + // Try to transfer without permit should fail + const tokens = toGRT('100') + const tx = grt.connect(other.signer).transferFrom(me.address, other.address, tokens) + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') + }) + }) + + describe('mint', function () { + describe('addMinter', function () { + it('reject add a new minter if not allowed', async function () { + const tx = grt.connect(me.signer).addMinter(me.address) + await expect(tx).revertedWith('Only Governor can call') + }) + + it('should add a new minter', async function () { + expect(await grt.isMinter(me.address)).eq(false) + const tx = grt.connect(governor.signer).addMinter(me.address) + await expect(tx).emit(grt, 'MinterAdded').withArgs(me.address) + expect(await grt.isMinter(me.address)).eq(true) + }) + }) + + describe('mint', async function () { + it('reject mint if not minter', async function () { + const tx = grt.connect(me.signer).mint(me.address, toGRT('100')) + await expect(tx).revertedWith('Only minter can call') + }) + }) + + context('> when is minter', function () { + beforeEach(async function () { + await grt.connect(governor.signer).addMinter(me.address) + expect(await grt.isMinter(me.address)).eq(true) + }) + + describe('mint', async function () { + it('should mint', async function () { + const beforeTokens = await grt.balanceOf(me.address) + + const tokensToMint = toGRT('100') + const tx = grt.connect(me.signer).mint(me.address, tokensToMint) + await expect(tx).emit(grt, 'Transfer').withArgs(AddressZero, me.address, tokensToMint) + + const afterTokens = await grt.balanceOf(me.address) + expect(afterTokens).eq(beforeTokens.add(tokensToMint)) + }) + + it('should mint if governor', async function () { + const tokensToMint = toGRT('100') + await grt.connect(governor.signer).mint(me.address, tokensToMint) + }) + }) + + describe('removeMinter', function () { + it('reject remove a minter if not allowed', async function () { + const tx = grt.connect(me.signer).removeMinter(me.address) + await expect(tx).revertedWith('Only Governor can call') + }) + + it('should remove a minter', async function () { + const tx = grt.connect(governor.signer).removeMinter(me.address) + await expect(tx).emit(grt, 'MinterRemoved').withArgs(me.address) + expect(await grt.isMinter(me.address)).eq(false) + }) + }) + + describe('renounceMinter', function () { + it('should renounce to be a minter', async function () { + const tx = grt.connect(me.signer).renounceMinter() + await expect(tx).emit(grt, 'MinterRemoved').withArgs(me.address) + expect(await grt.isMinter(me.address)).eq(false) + }) + }) + }) + }) +} diff --git a/test/lib/testHelpers.ts b/test/lib/testHelpers.ts index ee7c87322..87e04beb1 100644 --- a/test/lib/testHelpers.ts +++ b/test/lib/testHelpers.ts @@ -2,7 +2,8 @@ import hre from 'hardhat' import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-waffle' import { providers, utils, BigNumber, Signer, Wallet } from 'ethers' -import { formatUnits, getAddress } from 'ethers/lib/utils' +import { formatUnits, getAddress, hexValue } from 'ethers/lib/utils' +import { BigNumber as BN } from 'bignumber.js' import { EpochManager } from '../../build/types/EpochManager' @@ -25,6 +26,14 @@ export interface Account { export const provider = (): providers.JsonRpcProvider => hre.waffle.provider +// Enable automining with each transaction, and disable +// the mining interval. Individual tests may modify this +// behavior as needed. +export async function initNetwork(): Promise { + await provider().send('evm_setIntervalMining', [0]) + await provider().send('evm_setAutomine', [true]) +} + export const getAccounts = async (): Promise => { const accounts = [] const signers: Signer[] = await hre.ethers.getSigners() @@ -57,24 +66,17 @@ export const advanceBlockTo = async (blockNumber: string | number | BigNumber): ? toBN(blockNumber) : blockNumber const currentBlock = await latestBlock() - const start = Date.now() - let notified - if (target.lt(currentBlock)) + if (target.lt(currentBlock)) { throw Error(`Target block #(${target}) is lower than current block #(${currentBlock})`) - while ((await latestBlock()).lt(target)) { - if (!notified && Date.now() - start >= 5000) { - notified = true - console.log(`advanceBlockTo: Advancing too ` + 'many blocks is causing this test to be slow.') - } - await advanceBlock() + } else if (target.eq(currentBlock)) { + return + } else { + await advanceBlocks(target.sub(currentBlock)) } } export const advanceBlocks = async (blocks: string | number | BigNumber): Promise => { - const steps = typeof blocks === 'number' || typeof blocks === 'string' ? toBN(blocks) : blocks - const currentBlock = await latestBlock() - const toBlock = currentBlock.add(steps) - return advanceBlockTo(toBlock) + await provider().send('hardhat_mine', [hexValue(BigNumber.from(blocks))]) } export const advanceToNextEpoch = async (epochManager: EpochManager): Promise => { @@ -119,3 +121,24 @@ export const deriveChannelKey = (): ChannelKey => { }, } } + +// Adapted from: +// https://github.com/livepeer/arbitrum-lpt-bridge/blob/e1a81edda3594e434dbcaa4f1ebc95b7e67ecf2a/utils/arbitrum/messaging.ts#L118 +export const applyL1ToL2Alias = (l1Address: string): string => { + const offset = toBN('0x1111000000000000000000000000000000001111') + const l1AddressAsNumber = toBN(l1Address) + const l2AddressAsNumber = l1AddressAsNumber.add(offset) + + const mask = toBN(2).pow(160) + return l2AddressAsNumber.mod(mask).toHexString() +} + +// Adapted from: +// https://github.com/livepeer/arbitrum-lpt-bridge/blob/e1a81edda3594e434dbcaa4f1ebc95b7e67ecf2a/test/utils/messaging.ts#L5 +export async function getL2SignerFromL1(l1Address: string): Promise { + const l2Address = applyL1ToL2Alias(l1Address) + await provider().send('hardhat_impersonateAccount', [l2Address]) + const l2Signer = await hre.ethers.getSigner(l2Address) + + return l2Signer +} diff --git a/test/staking/rebate.test.ts b/test/staking/rebate.test.ts index 2668b29f4..d6db5948b 100644 --- a/test/staking/rebate.test.ts +++ b/test/staking/rebate.test.ts @@ -4,7 +4,7 @@ import { BigNumber } from 'ethers' import { deployContract } from '../lib/deployment' import { RebatePoolMock } from '../../build/types/RebatePoolMock' -import { getAccounts, toBN, toGRT, formatGRT, Account } from '../lib/testHelpers' +import { getAccounts, toBN, toGRT, formatGRT, Account, initNetwork } from '../lib/testHelpers' const toFloat = (n: BigNumber) => parseFloat(formatGRT(n)) const toFixed = (n: number | BigNumber, precision = 12) => { @@ -194,6 +194,7 @@ describe('Staking:Rebate', () => { } beforeEach(async function () { + await initNetwork() ;[deployer] = await getAccounts() rebatePoolMock = (await deployContract( 'RebatePoolMock', diff --git a/yarn.lock b/yarn.lock index d82c896fd..c9086728b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,22 @@ # yarn lockfile v1 +"@arbitrum/sdk@^3.0.0-beta.5": + version "3.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.0.0-beta.5.tgz#ef1c81de58db9e76defb4a1971274316a375133b" + integrity sha512-qeNdK7es4uKRFciz4zznPEnGRZaAHkrwNqUN1F4U6d4i8olhK0KMdSodx2ZjajBvVVwOo5kFsw5ocAaTvkf28g== + dependencies: + "@ethersproject/address" "^5.0.8" + "@ethersproject/bignumber" "^5.1.1" + "@ethersproject/bytes" "^5.0.8" + "@typechain/ethers-v5" "9.0.0" + "@types/prompts" "^2.0.14" + "@types/yargs" "^17.0.9" + dotenv "^10.0.0" + ethers "^5.1.0" + ts-node "^10.2.1" + typechain "7.0.0" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -10,23 +26,23 @@ "@babel/highlight" "^7.10.4" "@babel/code-frame@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.16.7" -"@babel/helper-validator-identifier@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" - integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" + integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" @@ -175,6 +191,18 @@ dependencies: chalk "^4.0.0" +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -191,6 +219,18 @@ enabled "2.0.x" kuler "^2.0.0" +"@defi-wonderland/smock@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@defi-wonderland/smock/-/smock-2.0.7.tgz#59d5fc93e175ad120c5dcdd8294e07525606c855" + integrity sha512-RVpODLKZ/Cr0C1bCbhJ2aXbAr2Ll/K2WO7hDL96tqhMzCsA7ToWdDIgiNpV5Vtqqvpftu5ddO7v3TAurQNSU0w== + dependencies: + "@nomiclabs/ethereumjs-vm" "^4.2.2" + diff "^5.0.0" + lodash.isequal "^4.5.0" + lodash.isequalwith "^4.4.0" + rxjs "^7.2.0" + semver "^7.3.5" + "@endemolshinegroup/cosmiconfig-typescript-loader@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz#eea4635828dde372838b0909693ebd9aafeec22d" @@ -285,7 +325,17 @@ patch-package "^6.2.2" postinstall-postinstall "^2.1.0" -"@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.6.2", "@ethereumjs/block@^3.6.3": +"@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.6.0", "@ethereumjs/block@^3.6.2": + version "3.6.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.6.2.tgz#63d1e26d0b7a7a3684fce920de6ebabec1e5b674" + integrity sha512-mOqYWwMlAZpYUEOEqt7EfMFuVL2eyLqWWIzcf4odn6QgXY8jBI2NhVuJncrMCKeMZrsJAe7/auaRRB6YcdH+Qw== + dependencies: + "@ethereumjs/common" "^2.6.3" + "@ethereumjs/tx" "^3.5.1" + ethereumjs-util "^7.1.4" + merkle-patricia-tree "^4.2.4" + +"@ethereumjs/block@^3.6.3": version "3.6.3" resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.6.3.tgz#d96cbd7af38b92ebb3424223dbf773f5ccd27f84" integrity sha512-CegDeryc2DVKnDkg5COQrE0bJfw/p0v3GBk2W5/Dj5dOVfEmb50Ux0GLnSPypooLnfqjwFaorGuT9FokWB3GRg== @@ -295,7 +345,21 @@ ethereumjs-util "^7.1.5" merkle-patricia-tree "^4.2.4" -"@ethereumjs/blockchain@^5.5.2", "@ethereumjs/blockchain@^5.5.3": +"@ethereumjs/blockchain@^5.5.0", "@ethereumjs/blockchain@^5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.5.2.tgz#1848abd9dc1ee56acf8cec4c84304d7f4667d027" + integrity sha512-Jz26iJmmsQtngerW6r5BDFaew/f2mObLrRZo3rskLOx1lmtMZ8+TX/vJexmivrnWgmAsTdNWhlKUYY4thPhPig== + dependencies: + "@ethereumjs/block" "^3.6.2" + "@ethereumjs/common" "^2.6.3" + "@ethereumjs/ethash" "^1.1.0" + debug "^4.3.3" + ethereumjs-util "^7.1.4" + level-mem "^5.0.1" + lru-cache "^5.1.1" + semaphore-async-await "^1.5.1" + +"@ethereumjs/blockchain@^5.5.3": version "5.5.3" resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.5.3.tgz#aa49a6a04789da6b66b5bcbb0d0b98efc369f640" integrity sha512-bi0wuNJ1gw4ByNCV56H0Z4Q7D+SxUbwyG12Wxzbvqc89PXLRNR20LBcSUZRKpN0+YCPo6m0XZL/JLio3B52LTw== @@ -309,7 +373,7 @@ lru-cache "^5.1.1" semaphore-async-await "^1.5.1" -"@ethereumjs/common@^2.5.0", "@ethereumjs/common@^2.6.4", "@ethereumjs/common@^2.6.5": +"@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.6.5": version "2.6.5" resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA== @@ -317,6 +381,22 @@ crc-32 "^1.2.0" ethereumjs-util "^7.1.5" +"@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.6.0", "@ethereumjs/common@^2.6.3": + version "2.6.3" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.3.tgz#39ddece7300b336276bad6c02f6a9f1a082caa05" + integrity sha512-mQwPucDL7FDYIg9XQ8DL31CnIYZwGhU5hyOO5E+BMmT71G0+RHvIT5rIkLBirJEKxV6+Rcf9aEIY0kXInxUWpQ== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.4" + +"@ethereumjs/common@^2.6.4": + version "2.6.4" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.4.tgz#1b3cdd3aa4ee3b0ca366756fc35e4a03022a01cc" + integrity sha512-RDJh/R/EAr+B7ZRg5LfJ0BIpf/1LydFgYdvZEuTraojCbVypO2sQ+QnpP5u2wJf9DASyooKqu8O4FJEWUV6NXw== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.4" + "@ethereumjs/ethash@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.1.0.tgz#7c5918ffcaa9cb9c1dc7d12f77ef038c11fb83fb" @@ -328,7 +408,7 @@ ethereumjs-util "^7.1.1" miller-rabin "^4.0.0" -"@ethereumjs/tx@^3.3.2", "@ethereumjs/tx@^3.5.1", "@ethereumjs/tx@^3.5.2": +"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.2.tgz#197b9b6299582ad84f9527ca961466fce2296c1c" integrity sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw== @@ -336,6 +416,32 @@ "@ethereumjs/common" "^2.6.4" ethereumjs-util "^7.1.5" +"@ethereumjs/tx@^3.4.0", "@ethereumjs/tx@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.1.tgz#8d941b83a602b4a89949c879615f7ea9a90e6671" + integrity sha512-xzDrTiu4sqZXUcaBxJ4n4W5FrppwxLxZB4ZDGVLtxSQR4lVuOnFR6RcUHdg1mpUhAPVrmnzLJpxaeXnPxIyhWA== + dependencies: + "@ethereumjs/common" "^2.6.3" + ethereumjs-util "^7.1.4" + +"@ethereumjs/vm@^5.6.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.8.0.tgz#c9055f96afc13dd7b72893b57fa20027effea6fe" + integrity sha512-mn2G2SX79QY4ckVvZUfxlNUpzwT2AEIkvgJI8aHoQaNYEHhH8rmdVDIaVVgz6//PjK52BZsK23afz+WvSR0Qqw== + dependencies: + "@ethereumjs/block" "^3.6.2" + "@ethereumjs/blockchain" "^5.5.2" + "@ethereumjs/common" "^2.6.3" + "@ethereumjs/tx" "^3.5.1" + async-eventemitter "^0.2.4" + core-js-pure "^3.0.1" + debug "^4.3.3" + ethereumjs-util "^7.1.4" + functional-red-black-tree "^1.0.1" + mcl-wasm "^0.7.1" + merkle-patricia-tree "^4.2.4" + rustbn.js "~0.2.0" + "@ethereumjs/vm@^5.9.0": version "5.9.3" resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.9.3.tgz#6d69202e4c132a4a1e1628ac246e92062e230823" @@ -369,7 +475,22 @@ "@ethersproject/properties" ">=5.0.0-beta.131" "@ethersproject/strings" ">=5.0.0-beta.130" -"@ethersproject/abi@5.6.0": +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abi@5.6.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.0.tgz#ea07cbc1eec2374d32485679c12408005895e9f3" integrity sha512-AhVByTwdXCc2YQ20v300w6KVHle9g2OFc28ZAFCPnJyEpkv1xKXjZcSTgWOlv1i+0dqlgF8RCF2Rn2KC1t+1Vg== @@ -384,22 +505,7 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/abi@5.6.4", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.0", "@ethersproject/abi@^5.6.3": - version "5.6.4" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.4.tgz#f6e01b6ed391a505932698ecc0d9e7a99ee60362" - integrity sha512-TTeZUlCeIHG6527/2goZA6gW5F8Emoc7MrZDC7hhP84aRGvW3TEdTnZR08Ls88YXM1m2SuK42Osw/jSi3uO8gg== - dependencies: - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/abstract-provider@5.6.0": +"@ethersproject/abstract-provider@5.6.0", "@ethersproject/abstract-provider@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.0.tgz#0c4ac7054650dbd9c476cf5907f588bbb6ef3061" integrity sha512-oPMFlKLN+g+y7a79cLK3WiLcjWFnZQtXWgnLAbHZcN3s7L4v90UHpTOrLk+m3yr0gt+/h9STTM6zrr7PM8uoRw== @@ -412,20 +518,7 @@ "@ethersproject/transactions" "^5.6.0" "@ethersproject/web" "^5.6.0" -"@ethersproject/abstract-provider@5.6.1", "@ethersproject/abstract-provider@^5.6.0", "@ethersproject/abstract-provider@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.1.tgz#02ddce150785caf0c77fe036a0ebfcee61878c59" - integrity sha512-BxlIgogYJtp1FS8Muvj8YfdClk3unZH0vRMVX791Z9INBNT/kuACZ9GzaY1Y4yFq+YSy6/w4gzj3HCRKrK9hsQ== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.3" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/web" "^5.6.1" - -"@ethersproject/abstract-signer@5.6.0": +"@ethersproject/abstract-signer@5.6.0", "@ethersproject/abstract-signer@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.0.tgz#9cd7ae9211c2b123a3b29bf47aab17d4d016e3e7" integrity sha512-WOqnG0NJKtI8n0wWZPReHtaLkDByPL67tn4nBaDAhmVq8sjHTPbCdz4DRhVu/cfTOvfy9w3iq5QZ7BX7zw56BQ== @@ -436,18 +529,7 @@ "@ethersproject/logger" "^5.6.0" "@ethersproject/properties" "^5.6.0" -"@ethersproject/abstract-signer@5.6.2", "@ethersproject/abstract-signer@^5.6.0", "@ethersproject/abstract-signer@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz#491f07fc2cbd5da258f46ec539664713950b0b33" - integrity sha512-n1r6lttFBG0t2vNiI3HoWaS/KdOt8xyDjzlP2cuevlWLG6EX0OwcKLyG/Kp/cuwNxdy/ous+R/DEMdTUwWQIjQ== - dependencies: - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - -"@ethersproject/address@5.6.0": +"@ethersproject/address@5.6.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.8", "@ethersproject/address@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.0.tgz#13c49836d73e7885fc148ad633afad729da25012" integrity sha512-6nvhYXjbXsHPS+30sHZ+U4VMagFC/9zAk6Gd/h3S21YW4+yfb0WfRtaAIZ4kfM4rrVwqiy284LP0GtL5HXGLxQ== @@ -458,32 +540,14 @@ "@ethersproject/logger" "^5.6.0" "@ethersproject/rlp" "^5.6.0" -"@ethersproject/address@5.6.1", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.6.0", "@ethersproject/address@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.1.tgz#ab57818d9aefee919c5721d28cd31fd95eff413d" - integrity sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/rlp" "^5.6.1" - -"@ethersproject/base64@5.6.0": +"@ethersproject/base64@5.6.0", "@ethersproject/base64@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.0.tgz#a12c4da2a6fb86d88563216b0282308fc15907c9" integrity sha512-2Neq8wxJ9xHxCF9TUgmKeSh9BXJ6OAxWfeGWvbauPh8FuHEjamgHilllx8KkSd5ErxyHIX7Xv3Fkcud2kY9ezw== dependencies: "@ethersproject/bytes" "^5.6.0" -"@ethersproject/base64@5.6.1", "@ethersproject/base64@^5.6.0", "@ethersproject/base64@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.1.tgz#2c40d8a0310c9d1606c2c37ae3092634b41d87cb" - integrity sha512-qB76rjop6a0RIYYMiB4Eh/8n+Hxu2NIZm8S/Q7kNo5pmZfXhHGHmS4MinUainiBC54SCyRnwzL+KZjj8zbsSsw== - dependencies: - "@ethersproject/bytes" "^5.6.1" - -"@ethersproject/basex@5.6.0": +"@ethersproject/basex@5.6.0", "@ethersproject/basex@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.6.0.tgz#9ea7209bf0a1c3ddc2a90f180c3a7f0d7d2e8a69" integrity sha512-qN4T+hQd/Md32MoJpc69rOwLYRUXwjTlhHDIeUkUmiN/JyWkkLLMoG0TqvSQKNqZOMgN5stbUYN6ILC+eD7MEQ== @@ -491,15 +555,7 @@ "@ethersproject/bytes" "^5.6.0" "@ethersproject/properties" "^5.6.0" -"@ethersproject/basex@5.6.1", "@ethersproject/basex@^5.6.0", "@ethersproject/basex@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.6.1.tgz#badbb2f1d4a6f52ce41c9064f01eab19cc4c5305" - integrity sha512-a52MkVz4vuBXR06nvflPMotld1FJWSj2QT0985v7P/emPZO00PucFAkbcmq2vpVU7Ts7umKiSI6SppiLykVWsA== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/properties" "^5.6.0" - -"@ethersproject/bignumber@5.6.0": +"@ethersproject/bignumber@5.6.0", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.1.1", "@ethersproject/bignumber@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.0.tgz#116c81b075c57fa765a8f3822648cf718a8a0e26" integrity sha512-VziMaXIUHQlHJmkv1dlcd6GY2PmT0khtAqaMctCIDogxkrarMzA9L94KN1NeXqqOfFD6r0sJT3vCTOFSmZ07DA== @@ -508,36 +564,20 @@ "@ethersproject/logger" "^5.6.0" bn.js "^4.11.9" -"@ethersproject/bignumber@5.6.2", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.6.0", "@ethersproject/bignumber@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.2.tgz#72a0717d6163fab44c47bcc82e0c550ac0315d66" - integrity sha512-v7+EEUbhGqT3XJ9LMPsKvXYHFc8eHxTowFCG/HgJErmq4XHJ2WR7aeyICg3uTOAQ7Icn0GFHAohXEhxQHq4Ubw== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - bn.js "^5.2.1" - -"@ethersproject/bytes@5.6.1", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.6.0", "@ethersproject/bytes@^5.6.1": +"@ethersproject/bytes@5.6.1", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.8", "@ethersproject/bytes@^5.6.0": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.6.1.tgz#24f916e411f82a8a60412344bf4a813b917eefe7" integrity sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g== dependencies: "@ethersproject/logger" "^5.6.0" -"@ethersproject/constants@5.6.0": +"@ethersproject/constants@5.6.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.6.0.tgz#55e3eb0918584d3acc0688e9958b0cedef297088" integrity sha512-SrdaJx2bK0WQl23nSpV/b1aq293Lh0sUaZT/yYKPDKn4tlAbkH96SPJwIhwSwTsoQQZxuh1jnqsKwyymoiBdWA== dependencies: "@ethersproject/bignumber" "^5.6.0" -"@ethersproject/constants@5.6.1", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.6.0", "@ethersproject/constants@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.6.1.tgz#e2e974cac160dd101cf79fdf879d7d18e8cb1370" - integrity sha512-QSq9WVnZbxXYFftrjSjZDUshp6/eKp6qrtdBtUCm0QxCV5z1fG/w3kdlcsjMCQuQHUnAclKoK7XpXMezhRDOLg== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/contracts@5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.6.0.tgz#60f2cfc7addd99a865c6c8cfbbcec76297386067" @@ -554,32 +594,16 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/transactions" "^5.6.0" -"@ethersproject/contracts@5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.6.2.tgz#20b52e69ebc1b74274ff8e3d4e508de971c287bc" - integrity sha512-hguUA57BIKi6WY0kHvZp6PwPlWF87MCeB4B7Z7AbUpTxfFXFdn/3b0GmjZPagIHS+3yhcBJDnuEfU4Xz+Ks/8g== - dependencies: - "@ethersproject/abi" "^5.6.3" - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/transactions" "^5.6.2" - -"@ethersproject/experimental@^5.4.0": - version "5.6.3" - resolved "https://registry.yarnpkg.com/@ethersproject/experimental/-/experimental-5.6.3.tgz#d1cd8f3b886cbab86430fb2954eb65ddb7c75ffd" - integrity sha512-yMymv32XMr9sXvHc3S1On2wD0JMT6n4X9uKpfZ8jFFw5rEcI99yfovcCZ0tpUedh1b3IvReSain+RobeNQmmEg== +"@ethersproject/experimental@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/experimental/-/experimental-5.6.0.tgz#c72ef00a79b746c522eb79736712169d71c55f64" + integrity sha512-lSEM/6t+BicbeyRxat5meoQhXZLoBEziVrxZqeCIhsPntvq4DlMobPBKXF0Iz3m0dMvl9uga7fHEO4YD9SgCgw== dependencies: - "@ethersproject/web" "^5.6.1" - ethers "^5.6.8" + "@ethersproject/web" "^5.6.0" + ethers "^5.6.0" scrypt-js "3.0.1" -"@ethersproject/hash@5.6.0": +"@ethersproject/hash@5.6.0", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.4", "@ethersproject/hash@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.0.tgz#d24446a5263e02492f9808baa99b6e2b4c3429a2" integrity sha512-fFd+k9gtczqlr0/BruWLAu7UAOas1uRRJvOR84uDf4lNZ+bTkGl366qvniUZHKtlqxBRU65MkOobkmvmpHU+jA== @@ -593,21 +617,7 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/hash@5.6.1", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.6.0", "@ethersproject/hash@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.1.tgz#224572ea4de257f05b4abf8ae58b03a67e99b0f4" - integrity sha512-L1xAHurbaxG8VVul4ankNX5HgQ8PNCTrnVXEiFnE9xoRnaUcgfD12tZINtDinSllxPLCtGwguQxJ5E6keE84pA== - dependencies: - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/hdnode@5.6.0": +"@ethersproject/hdnode@5.6.0", "@ethersproject/hdnode@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.6.0.tgz#9dcbe8d629bbbcf144f2cae476337fe92d320998" integrity sha512-61g3Jp3nwDqJcL/p4nugSyLrpl/+ChXIOtCEM8UDmWeB3JCAt5FoLdOMXQc3WWkc0oM2C0aAn6GFqqMcS/mHTw== @@ -625,25 +635,7 @@ "@ethersproject/transactions" "^5.6.0" "@ethersproject/wordlists" "^5.6.0" -"@ethersproject/hdnode@5.6.2", "@ethersproject/hdnode@^5.6.0", "@ethersproject/hdnode@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.6.2.tgz#26f3c83a3e8f1b7985c15d1db50dc2903418b2d2" - integrity sha512-tERxW8Ccf9CxW2db3WsN01Qao3wFeRsfYY9TCuhmG0xNpl2IO8wgXU3HtWIZ49gUWPggRy4Yg5axU0ACaEKf1Q== - dependencies: - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/basex" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/pbkdf2" "^5.6.1" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/sha2" "^5.6.1" - "@ethersproject/signing-key" "^5.6.2" - "@ethersproject/strings" "^5.6.1" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/wordlists" "^5.6.1" - -"@ethersproject/json-wallets@5.6.0": +"@ethersproject/json-wallets@5.6.0", "@ethersproject/json-wallets@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.6.0.tgz#4c2fc27f17e36c583e7a252fb938bc46f98891e5" integrity sha512-fmh86jViB9r0ibWXTQipxpAGMiuxoqUf78oqJDlCAJXgnJF024hOOX7qVgqsjtbeoxmcLwpPsXNU0WEe/16qPQ== @@ -662,26 +654,7 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/json-wallets@5.6.1", "@ethersproject/json-wallets@^5.6.0", "@ethersproject/json-wallets@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.6.1.tgz#3f06ba555c9c0d7da46756a12ac53483fe18dd91" - integrity sha512-KfyJ6Zwz3kGeX25nLihPwZYlDqamO6pfGKNnVMWWfEVVp42lTfCZVXXy5Ie8IZTN0HKwAngpIPi7gk4IJzgmqQ== - dependencies: - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/hdnode" "^5.6.2" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/pbkdf2" "^5.6.1" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.1" - "@ethersproject/strings" "^5.6.1" - "@ethersproject/transactions" "^5.6.2" - aes-js "3.0.0" - scrypt-js "3.0.1" - -"@ethersproject/keccak256@5.6.0": +"@ethersproject/keccak256@5.6.0", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.6.0.tgz#fea4bb47dbf8f131c2e1774a1cecbfeb9d606459" integrity sha512-tk56BJ96mdj/ksi7HWZVWGjCq0WVl/QvfhFQNeL8fxhBlGoP+L80uDCiQcpJPd+2XxkivS3lwRm3E0CXTfol0w== @@ -689,34 +662,19 @@ "@ethersproject/bytes" "^5.6.0" js-sha3 "0.8.0" -"@ethersproject/keccak256@5.6.1", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.6.0", "@ethersproject/keccak256@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.6.1.tgz#b867167c9b50ba1b1a92bccdd4f2d6bd168a91cc" - integrity sha512-bB7DQHCTRDooZZdL3lk9wpL0+XuG3XLGHLh3cePnybsO3V0rdCAOQGpn/0R3aODmnTOOkCATJiD2hnL+5bwthA== - dependencies: - "@ethersproject/bytes" "^5.6.1" - js-sha3 "0.8.0" - -"@ethersproject/logger@5.6.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.6.0": +"@ethersproject/logger@5.6.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.6.0.tgz#d7db1bfcc22fd2e4ab574cba0bb6ad779a9a3e7a" integrity sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg== -"@ethersproject/networks@5.6.1": +"@ethersproject/networks@5.6.1", "@ethersproject/networks@^5.6.0": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.1.tgz#7a21ed1f83e86121737b16841961ec99ccf5c9c7" integrity sha512-b2rrupf3kCTcc3jr9xOWBuHylSFtbpJf79Ga7QR98ienU2UqGimPGEsYMgbI29KHJfA5Us89XwGVmxrlxmSrMg== dependencies: "@ethersproject/logger" "^5.6.0" -"@ethersproject/networks@5.6.4", "@ethersproject/networks@^5.6.0", "@ethersproject/networks@^5.6.3": - version "5.6.4" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.4.tgz#51296d8fec59e9627554f5a8a9c7791248c8dc07" - integrity sha512-KShHeHPahHI2UlWdtDMn2lJETcbtaJge4k7XSjDR9h79QTd6yQJmv6Cp2ZA4JdqWnhszAOLSuJEd9C0PRw7hSQ== - dependencies: - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/pbkdf2@5.6.0": +"@ethersproject/pbkdf2@5.6.0", "@ethersproject/pbkdf2@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.6.0.tgz#04fcc2d7c6bff88393f5b4237d906a192426685a" integrity sha512-Wu1AxTgJo3T3H6MIu/eejLFok9TYoSdgwRr5oGY1LTLfmGesDoSx05pemsbrPT2gG4cQME+baTSCp5sEo2erZQ== @@ -724,15 +682,7 @@ "@ethersproject/bytes" "^5.6.0" "@ethersproject/sha2" "^5.6.0" -"@ethersproject/pbkdf2@5.6.1", "@ethersproject/pbkdf2@^5.6.0", "@ethersproject/pbkdf2@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.6.1.tgz#f462fe320b22c0d6b1d72a9920a3963b09eb82d1" - integrity sha512-k4gRQ+D93zDRPNUfmduNKq065uadC2YjMP/CqwwX5qG6R05f47boq6pLZtV/RnC4NZAYOPH1Cyo54q0c9sshRQ== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/sha2" "^5.6.1" - -"@ethersproject/properties@5.6.0", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.6.0": +"@ethersproject/properties@5.6.0", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.6.0.tgz#38904651713bc6bdd5bdd1b0a4287ecda920fa04" integrity sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg== @@ -764,33 +714,7 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/providers@5.6.8": - version "5.6.8" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.8.tgz#22e6c57be215ba5545d3a46cf759d265bb4e879d" - integrity sha512-Wf+CseT/iOJjrGtAOf3ck9zS7AgPmr2fZ3N97r4+YXN3mBePTG2/bJ8DApl9mVwYL+RpYbNxMEkEp4mPGdwG/w== - dependencies: - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/base64" "^5.6.1" - "@ethersproject/basex" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.3" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.1" - "@ethersproject/rlp" "^5.6.1" - "@ethersproject/sha2" "^5.6.1" - "@ethersproject/strings" "^5.6.1" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/web" "^5.6.1" - bech32 "1.1.4" - ws "7.4.6" - -"@ethersproject/random@5.6.0": +"@ethersproject/random@5.6.0", "@ethersproject/random@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.6.0.tgz#1505d1ab6a250e0ee92f436850fa3314b2cb5ae6" integrity sha512-si0PLcLjq+NG/XHSZz90asNf+YfKEqJGVdxoEkSukzbnBgC8rydbgbUgBbBGLeHN4kAJwUFEKsu3sCXT93YMsw== @@ -798,15 +722,7 @@ "@ethersproject/bytes" "^5.6.0" "@ethersproject/logger" "^5.6.0" -"@ethersproject/random@5.6.1", "@ethersproject/random@^5.6.0", "@ethersproject/random@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.6.1.tgz#66915943981bcd3e11bbd43733f5c3ba5a790255" - integrity sha512-/wtPNHwbmng+5yi3fkipA8YBT59DdkGRoC2vWk09Dci/q5DlgnMkhIycjHlavrvrjJBkFjO/ueLyT+aUDfc4lA== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/rlp@5.6.0": +"@ethersproject/rlp@5.6.0", "@ethersproject/rlp@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.0.tgz#55a7be01c6f5e64d6e6e7edb6061aa120962a717" integrity sha512-dz9WR1xpcTL+9DtOT/aDO+YyxSSdO8YIS0jyZwHHSlAmnxA6cKU3TrTd4Xc/bHayctxTgGLYNuVVoiXE4tTq1g== @@ -814,15 +730,7 @@ "@ethersproject/bytes" "^5.6.0" "@ethersproject/logger" "^5.6.0" -"@ethersproject/rlp@5.6.1", "@ethersproject/rlp@^5.6.0", "@ethersproject/rlp@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.1.tgz#df8311e6f9f24dcb03d59a2bac457a28a4fe2bd8" - integrity sha512-uYjmcZx+DKlFUk7a5/W9aQVaoEC7+1MOBgNtvNg13+RnuUwT4F0zTovC0tmay5SmRslb29V1B7Y5KCri46WhuQ== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/sha2@5.6.0": +"@ethersproject/sha2@5.6.0", "@ethersproject/sha2@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.6.0.tgz#364c4c11cc753bda36f31f001628706ebadb64d9" integrity sha512-1tNWCPFLu1n3JM9t4/kytz35DkuF9MxqkGGEHNauEbaARdm2fafnOyw1s0tIQDPKF/7bkP1u3dbrmjpn5CelyA== @@ -831,16 +739,7 @@ "@ethersproject/logger" "^5.6.0" hash.js "1.1.7" -"@ethersproject/sha2@5.6.1", "@ethersproject/sha2@^5.6.0", "@ethersproject/sha2@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.6.1.tgz#211f14d3f5da5301c8972a8827770b6fd3e51656" - integrity sha512-5K2GyqcW7G4Yo3uenHegbXRPDgARpWUiXc6RiF7b6i/HXUoWlb7uCARh7BAHg7/qT/Q5ydofNwiZcim9qpjB6g== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - hash.js "1.1.7" - -"@ethersproject/signing-key@5.6.0": +"@ethersproject/signing-key@5.6.0", "@ethersproject/signing-key@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.0.tgz#4f02e3fb09e22b71e2e1d6dc4bcb5dafa69ce042" integrity sha512-S+njkhowmLeUu/r7ir8n78OUKx63kBdMCPssePS89So1TH4hZqnWFsThEd/GiXYp9qMxVrydf7KdM9MTGPFukA== @@ -852,18 +751,6 @@ elliptic "6.5.4" hash.js "1.1.7" -"@ethersproject/signing-key@5.6.2", "@ethersproject/signing-key@^5.6.0", "@ethersproject/signing-key@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.2.tgz#8a51b111e4d62e5a62aee1da1e088d12de0614a3" - integrity sha512-jVbu0RuP7EFpw82vHcL+GP35+KaNruVAZM90GxgQnGqB6crhBqW/ozBfFvdeImtmb4qPko0uxXjn8l9jpn0cwQ== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - bn.js "^5.2.1" - elliptic "6.5.4" - hash.js "1.1.7" - "@ethersproject/solidity@5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.6.0.tgz#64657362a596bf7f5630bdc921c07dd78df06dc3" @@ -876,19 +763,7 @@ "@ethersproject/sha2" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/solidity@5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.6.1.tgz#5845e71182c66d32e6ec5eefd041fca091a473e2" - integrity sha512-KWqVLkUUoLBfL1iwdzUVlkNqAUIFMpbbeH0rgCfKmJp0vFtY4AsaN91gHKo9ZZLkC4UOm3cI3BmMV4N53BOq4g== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/sha2" "^5.6.1" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/strings@5.6.0": +"@ethersproject/strings@5.6.0", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.0.tgz#9891b26709153d996bf1303d39a7f4bc047878fd" integrity sha512-uv10vTtLTZqrJuqBZR862ZQjTIa724wGPWQqZrofaPI/kUsf53TBG0I0D+hQ1qyNtllbNzaW+PDPHHUI6/65Mg== @@ -897,16 +772,7 @@ "@ethersproject/constants" "^5.6.0" "@ethersproject/logger" "^5.6.0" -"@ethersproject/strings@5.6.1", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.6.0", "@ethersproject/strings@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.1.tgz#dbc1b7f901db822b5cafd4ebf01ca93c373f8952" - integrity sha512-2X1Lgk6Jyfg26MUnsHiT456U9ijxKUybz8IM1Vih+NJxYtXhmvKBcHOmvGqpFSVJ0nQ4ZCoIViR8XlRw1v/+Cw== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/transactions@5.6.0": +"@ethersproject/transactions@5.6.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.0.tgz#4b594d73a868ef6e1529a2f8f94a785e6791ae4e" integrity sha512-4HX+VOhNjXHZyGzER6E/LVI2i6lf9ejYeWD6l4g50AdmimyuStKc39kvKf1bXWQMg7QNVh+uC7dYwtaZ02IXeg== @@ -921,21 +787,6 @@ "@ethersproject/rlp" "^5.6.0" "@ethersproject/signing-key" "^5.6.0" -"@ethersproject/transactions@5.6.2", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.6.0", "@ethersproject/transactions@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.2.tgz#793a774c01ced9fe7073985bb95a4b4e57a6370b" - integrity sha512-BuV63IRPHmJvthNkkt9G70Ullx6AcM+SDc+a8Aw/8Yew6YwT51TcBKEp1P4oOQ/bP25I18JJr7rcFRgFtU9B2Q== - dependencies: - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/rlp" "^5.6.1" - "@ethersproject/signing-key" "^5.6.2" - "@ethersproject/units@5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.6.0.tgz#e5cbb1906988f5740254a21b9ded6bd51e826d9c" @@ -945,15 +796,6 @@ "@ethersproject/constants" "^5.6.0" "@ethersproject/logger" "^5.6.0" -"@ethersproject/units@5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.6.1.tgz#ecc590d16d37c8f9ef4e89e2005bda7ddc6a4e6f" - integrity sha512-rEfSEvMQ7obcx3KWD5EWWx77gqv54K6BKiZzKxkQJqtpriVsICrktIQmKl8ReNToPeIYPnFHpXvKpi068YFZXw== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/wallet@5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.6.0.tgz#33d11a806d783864208f348709a5a3badac8e22a" @@ -975,28 +817,7 @@ "@ethersproject/transactions" "^5.6.0" "@ethersproject/wordlists" "^5.6.0" -"@ethersproject/wallet@5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.6.2.tgz#cd61429d1e934681e413f4bc847a5f2f87e3a03c" - integrity sha512-lrgh0FDQPuOnHcF80Q3gHYsSUODp6aJLAdDmDV0xKCN/T7D99ta1jGVhulg3PY8wiXEngD0DfM0I2XKXlrqJfg== - dependencies: - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/hdnode" "^5.6.2" - "@ethersproject/json-wallets" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.1" - "@ethersproject/signing-key" "^5.6.2" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/wordlists" "^5.6.1" - -"@ethersproject/web@5.6.0": +"@ethersproject/web@5.6.0", "@ethersproject/web@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.0.tgz#4bf8b3cbc17055027e1a5dd3c357e37474eaaeb8" integrity sha512-G/XHj0hV1FxI2teHRfCGvfBUHFmU+YOSbCxlAMqJklxSa7QMiHFQfAxvwY2PFqgvdkxEKwRNr/eCjfAPEm2Ctg== @@ -1007,18 +828,7 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/web@5.6.1", "@ethersproject/web@^5.6.0", "@ethersproject/web@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.1.tgz#6e2bd3ebadd033e6fe57d072db2b69ad2c9bdf5d" - integrity sha512-/vSyzaQlNXkO1WV+RneYKqCJwualcUdx/Z3gseVovZP0wIlOFcCE1hkRhKBH8ImKbGQbMl9EAAyJFrJu7V0aqA== - dependencies: - "@ethersproject/base64" "^5.6.1" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/wordlists@5.6.0": +"@ethersproject/wordlists@5.6.0", "@ethersproject/wordlists@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.6.0.tgz#79e62c5276e091d8575f6930ba01a29218ded032" integrity sha512-q0bxNBfIX3fUuAo9OmjlEYxP40IB8ABgb7HjEZCL5IKubzV3j30CWi2rqQbjTS2HfoyQbfINoKcTVWP4ejwR7Q== @@ -1029,23 +839,12 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/wordlists@5.6.1", "@ethersproject/wordlists@^5.6.0", "@ethersproject/wordlists@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.6.1.tgz#1e78e2740a8a21e9e99947e47979d72e130aeda1" - integrity sha512-wiPRgBpNbNwCQFoCr8bcWO8o5I810cqO6mkdtKfLKFlLxeCWcnzDi4Alu8iyNzlhYuS9npCwivMbRWF19dyblw== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@graphprotocol/common-ts@^1.6.0": - version "1.8.4" - resolved "https://registry.yarnpkg.com/@graphprotocol/common-ts/-/common-ts-1.8.4.tgz#a20e8126a7da34239904b0d3ad7c225d56d6cc17" - integrity sha512-qHAW4gNYBxD2SahGsKCzn1dmeVII+POws4dNQ5h8vvq4DWjdDzhUrb/qQWcmqmsZQ9MI4lQEoB/s5WdFMeRMNw== +"@graphprotocol/common-ts@^1.8.3": + version "1.8.6" + resolved "https://registry.yarnpkg.com/@graphprotocol/common-ts/-/common-ts-1.8.6.tgz#d12fe02d3f4ce544087cf9b57af26e95ca1b3b80" + integrity sha512-HSJ5hv1CZ1JSx0tGkEWrlG7G1QranrnipCmqkrl0U3gwfKss1XwCXuFtqMarE1NxMmYj46xKsrBm4/drlw7wLg== dependencies: - "@graphprotocol/contracts" "1.11.1" + "@graphprotocol/contracts" "1.13.0" "@graphprotocol/pino-sentry-simple" "0.7.1" "@urql/core" "2.4.4" "@urql/exchange-execute" "1.2.2" @@ -1067,10 +866,10 @@ prom-client "14.0.1" sequelize "6.19.0" -"@graphprotocol/contracts@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@graphprotocol/contracts/-/contracts-1.11.1.tgz#22df943388b3349218b92d570722f663b890266e" - integrity sha512-z4klBGRf9X08iE+JQKHH5VfmPh/8btDVVof9pckDd5bSsnPG4TvvpDHJnPAw+TQRRtoiKmDIvNIGBajRTAtQzA== +"@graphprotocol/contracts@1.13.0": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@graphprotocol/contracts/-/contracts-1.13.0.tgz#0289f9ef725f342ad1c69a9ac70bc5129c3eb0a3" + integrity sha512-1s6559hsvOQv6bbEGYOvkvuO4DkurwNKeHQ4wM3qT3j0v96sEd1xJSXL9fIOTPyg53BDfz8pZHe2+xaohXvVbg== dependencies: ethers "^5.4.4" @@ -1122,9 +921,9 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@metamask/eth-sig-util@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" - integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + version "4.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.0.tgz#11553ba06de0d1352332c1bde28c8edd00e0dcf6" + integrity sha512-LczOjjxY4A7XYloxzyxJIHONELmUxVZncpOLoClpEcTiebiVdM46KRPYXGuULro9oNNR2xdVx3yoKiQjdfWmoA== dependencies: ethereumjs-abi "^0.6.8" ethereumjs-util "^6.2.1" @@ -1137,15 +936,15 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== -"@noble/hashes@1.1.2", "@noble/hashes@~1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" - integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== +"@noble/hashes@1.0.0", "@noble/hashes@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.0.0.tgz#d5e38bfbdaba174805a4e649f13be9a9ed3351ae" + integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg== -"@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" - integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== +"@noble/secp256k1@1.5.5", "@noble/secp256k1@~1.5.2": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.5.5.tgz#315ab5745509d1a8c8e90d0bdf59823ccf9bcfc3" + integrity sha512-sZ1W6gQzYnu45wPrWx8D3kwI2/U29VYTx9OjbDAd7jwRItJ0cSTMPRL/C8AWZFn9kWFLQGqEXVEE86w4Z8LpIQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1168,10 +967,31 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nomiclabs/hardhat-ethers@^2.0.2", "@nomiclabs/hardhat-ethers@^2.0.6": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.1.0.tgz#9b7dc94d669ad9dc286b94f6f2f1513118c7027b" - integrity sha512-vlW90etB3675QWG7tMrHaDoTa7ymMB7irM4DAQ98g8zJoe9YqEggeDnbO6v5b+BLth/ty4vN6Ko/kaqRN1krHw== +"@nomiclabs/ethereumjs-vm@^4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@nomiclabs/ethereumjs-vm/-/ethereumjs-vm-4.2.2.tgz#2f8817113ca0fb6c44c1b870d0a809f0e026a6cc" + integrity sha512-8WmX94mMcJaZ7/m7yBbyuS6B+wuOul+eF+RY9fBpGhNaUpyMR/vFIcDojqcWQ4Yafe1tMKY5LDu2yfT4NZgV4Q== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + core-js-pure "^3.0.1" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.2" + ethereumjs-blockchain "^4.0.3" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.2.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "3.0.0" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + util.promisify "^1.0.0" + +"@nomiclabs/hardhat-ethers@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.5.tgz#131b0da1b71680d5a01569f916ae878229d326d3" + integrity sha512-A2gZAGB6kUvLx+kzM92HKuUF33F1FSe90L0TmkXkT2Hh0OKRpvWZURUSU2nghD2yC4DzfEZ3DftfeHGvZ2JTUw== "@nomiclabs/hardhat-etherscan@^2.1.1": version "2.1.8" @@ -1205,18 +1025,18 @@ integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== "@openzeppelin/hardhat-upgrades@^1.6.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.19.0.tgz#8f17210b924eb04c3ea4aa9795c1bb01d8a7dc3f" - integrity sha512-351QqDO3nZ96g0BO/bQnaTflix4Nw4ELWC/hZ8PwicsuVxRmxN/GZhxPrs62AOdiQdZ+xweawrVXa5knliRd4A== + version "1.17.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.17.0.tgz#24ea0f366c3b2df985263cf8b1b796afd04d7e13" + integrity sha512-GNxR3/3fCKQsFpBi/r+5ib6U81UM9KCypmcOQxuCkVp9JKJ80/3hQdg1R+AQku/dlnhutPsfkCokH2LZFc5mNA== dependencies: - "@openzeppelin/upgrades-core" "^1.16.0" + "@openzeppelin/upgrades-core" "^1.14.1" chalk "^4.1.0" proper-lockfile "^4.1.1" -"@openzeppelin/upgrades-core@^1.16.0": - version "1.16.1" - resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.16.1.tgz#a4c383fc628cc9d37d5a276def99a093c951644b" - integrity sha512-+hejbeAfsZWIQL5Ih13gkdm2KO6kbERc1ektzcyb25/OtUwaRjIIHxW++LdC/3Hg5uzThVOzJBfiLdAbgwD+OA== +"@openzeppelin/upgrades-core@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.14.1.tgz#a0e1c83f9811186ac49d286e6b43ae129097422b" + integrity sha512-iKlh1mbUxyfdjdEiUFyhMkqirfas+DMUu7ED53nZbHEyhcYsm+5Fl/g0Bv6bZA+a7k8kO8+22DNEKsqaDUBc2Q== dependencies: bn.js "^5.1.2" cbor "^8.0.0" @@ -1264,27 +1084,27 @@ path-browserify "^1.0.0" url "^0.11.0" -"@scure/base@~1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" - integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@scure/base@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.0.0.tgz#109fb595021de285f05a7db6806f2f48296fcee7" + integrity sha512-gIVaYhUsy+9s58m/ETjSJVKHhKTBMmcRb9cEV5/5dwvfDlfORjKrFsDeDHWRrm6RjcPvCLZFwGJjAjLj1gg4HA== -"@scure/bip32@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.0.tgz#dea45875e7fbc720c2b4560325f1cf5d2246d95b" - integrity sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q== +"@scure/bip32@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.0.1.tgz#1409bdf9f07f0aec99006bb0d5827693418d3aa5" + integrity sha512-AU88KKTpQ+YpTLoicZ/qhFhRRIo96/tlb+8YmDDHR9yiKVjSsFZiefJO4wjS2PMTkz5/oIcw84uAq/8pleQURA== dependencies: - "@noble/hashes" "~1.1.1" - "@noble/secp256k1" "~1.6.0" - "@scure/base" "~1.1.0" + "@noble/hashes" "~1.0.0" + "@noble/secp256k1" "~1.5.2" + "@scure/base" "~1.0.0" -"@scure/bip39@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a" - integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w== +"@scure/bip39@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.0.0.tgz#47504e58de9a56a4bbed95159d2d6829fa491bb0" + integrity sha512-HrtcikLbd58PWOkl02k9V6nXWQyoa7A0+Ek9VF7z17DDk9XZAFUcIdqfh0jJXLypmizc5/8P6OxoUeKliiWv4w== dependencies: - "@noble/hashes" "~1.1.1" - "@scure/base" "~1.1.0" + "@noble/hashes" "~1.0.0" + "@scure/base" "~1.0.0" "@sentry/core@5.30.0": version "5.30.0" @@ -1359,7 +1179,14 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1", "@solidity-parser/parser@^0.14.2", "@solidity-parser/parser@^0.14.3": +"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1": + version "0.14.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.1.tgz#179afb29f4e295a77cc141151f26b3848abc3c46" + integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@solidity-parser/parser@^0.14.2": version "0.14.3" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.3.tgz#0d627427b35a40d8521aaa933cc3df7d07bfa36f" integrity sha512-29g2SZ29HtsqA58pLCtopI1P/cPy5/UAzlcAXO6T/CNJimG6yA8kx4NaseMyJULiC+TEs02Y9/yeHzClqoA0hw== @@ -1374,15 +1201,12 @@ defer-to-connect "^1.0.1" "@tenderly/hardhat-tenderly@^1.0.11": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@tenderly/hardhat-tenderly/-/hardhat-tenderly-1.1.4.tgz#cb84ebf05eaf75c6fffae4cee52d132b3eed986a" - integrity sha512-HqtJSaX/EVxUcNqoHSBtZzrwAF93hPXBV4buPU2UWCuL36na6wImqzjSVhrxghgrxP6MQv5BbU5THxOkKQ0gdw== + version "1.0.13" + resolved "https://registry.yarnpkg.com/@tenderly/hardhat-tenderly/-/hardhat-tenderly-1.0.13.tgz#6182a2d32bf12d110622f0b24263dc964ed7aa6d" + integrity sha512-XsrF2QIUh8YmzCcWHmPnSNQjZNBQkyrHER8bcrWsFIgL7ub49hmPbpGVB2Isb6gUV/IGBpBm7R69jpmoTvJ17g== dependencies: - "@nomiclabs/hardhat-ethers" "^2.0.6" axios "^0.21.1" - ethers "^5.6.8" fs-extra "^9.0.1" - hardhat-deploy "^0.11.10" js-yaml "^3.14.0" "@truffle/error@^0.1.0": @@ -1390,44 +1214,51 @@ resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.1.0.tgz#5e9fed79e6cda624c926d314b280a576f8b22a36" integrity sha512-RbUfp5VreNhsa2Q4YbBjz18rOQI909pG32bghl1hulO7IpvcqTS+C3Ge5cNbiWQ1WGzy1wIeKLW0tmQtHFB7qg== -"@truffle/interface-adapter@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.19.tgz#57529cacb09c72ebfd584ec003af55face18a3de" - integrity sha512-x7IZvsyx36DAJCJVZ9gUe1Lh8AhODhJoW7I+lJXIlGxj3EmZbao4/sHo+cN4u9i94yVTyGwYd78NzbP0a/LAog== +"@truffle/interface-adapter@^0.5.12": + version "0.5.12" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.12.tgz#8cc34e9a5363970bfec1001520ae55fad6a26bfd" + integrity sha512-Qrc5VARnvSILYqZNsAM0xsUHqGqphLXVdIvDnhUA1Xj1xyNz8iboTr8bXorMd+Uspw+PXmsW44BJ/Wioo/jL2A== dependencies: bn.js "^5.1.3" ethers "^4.0.32" - web3 "1.7.4" + web3 "1.5.3" "@truffle/provider@^0.2.24": - version "0.2.57" - resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.57.tgz#c6d079748c99427c1ce283a19921b450aa9920ee" - integrity sha512-O8VxF2uQwa+KB4HDg9lG7uhQ/+AOvchX+1STpQBSSAGfov1+EROM0iRZUNoPm71Pu0C9ji2WmXbNW/COjUMaMA== + version "0.2.50" + resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.50.tgz#5c8a8fd7724bacac15cf7396cecc51c0ee1e597d" + integrity sha512-GCoyX8SncfgizXYJfordv5kiysQS7S1311D3ewciixaoQpTkbqC3s0wxwHlPxU7m5wJOCw2K8rQtL3oIFfeHwA== dependencies: "@truffle/error" "^0.1.0" - "@truffle/interface-adapter" "^0.5.19" - debug "^4.3.1" - web3 "1.7.4" + "@truffle/interface-adapter" "^0.5.12" + web3 "1.5.3" "@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== "@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== "@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + +"@typechain/ethers-v5@9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-9.0.0.tgz#6aa93bea7425c0463bd8a61eea3643540ef851bd" + integrity sha512-bAanuPl1L2itaUdMvor/QvwnIH+TM/CmG00q17Ilv3ZZMeJ2j8HcarhgJUZ9pBY1teBb85P8cC03dz3mSSx+tQ== + dependencies: + lodash "^4.17.15" + ts-essentials "^7.0.1" "@typechain/ethers-v5@^2.0.0": version "2.0.0" @@ -1485,9 +1316,9 @@ "@types/chai" "*" "@types/chai@*": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.1.tgz#e2c6e73e0bdeb2521d00756d099218e9f5d90a04" - integrity sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ== + version "4.3.0" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc" + integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw== "@types/concat-stream@^1.6.0": version "1.6.1" @@ -1585,17 +1416,17 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node-fetch@^2.5.5": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" - integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" + integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== dependencies: "@types/node" "*" form-data "^3.0.0" "@types/node@*": - version "18.0.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.6.tgz#0ba49ac517ad69abe7a1508bc9b3a5483df9d5d7" - integrity sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw== + version "17.0.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" + integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== "@types/node@^10.0.3": version "10.17.60" @@ -1603,9 +1434,9 @@ integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== "@types/node@^12.12.6": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + version "12.20.47" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.47.tgz#ca9237d51f2a2557419688511dab1c8daf475188" + integrity sha512-BzcaRsnFuznzOItW1WpQrDHM7plAa7GIDMZ6b5pnMbkqEtM/6WCOhvZar39oeMQP79gwvFUWjjptE7/KGcNqFg== "@types/node@^15.0.1": version "15.14.9" @@ -1635,11 +1466,18 @@ "@types/node" "*" "@types/prettier@^2.1.1": - version "2.6.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" - integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" + integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== -"@types/qs@^6.2.31", "@types/qs@^6.9.7": +"@types/prompts@^2.0.14": + version "2.0.14" + resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.0.14.tgz#10cb8899844bb0771cabe57c1becaaaca9a3b521" + integrity sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA== + dependencies: + "@types/node" "*" + +"@types/qs@^6.2.31": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== @@ -1667,9 +1505,9 @@ "@types/sinon" "*" "@types/sinon@*": - version "10.0.13" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.13.tgz#60a7a87a70d9372d0b7b38cc03e825f46981fb83" - integrity sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ== + version "10.0.11" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.11.tgz#8245827b05d3fc57a6601bd35aee1f7ad330fc42" + integrity sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g== dependencies: "@types/sinonjs__fake-timers" "*" @@ -1691,9 +1529,9 @@ integrity sha512-uO4CD2ELOjw8tasUrAhvnn2W4A0ZECOvMjCivJr4gA9pGgjv+qxKWY9GLTMVEK8ej85BxQOocUyE7hImmSQYcg== "@types/validator@^13.7.1": - version "13.7.4" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.4.tgz#33cc949ee87dd47c63e35ba4ad94f6888852be04" - integrity sha512-uAaSWegu2lymY18l+s5nmcXu3sFeeTOl1zhSGoYzcr6T3wz1M+3OcW4UjfPhIhHGd13tIMRDsEpR+d8w/MexwQ== + version "13.7.6" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.6.tgz#631f1acd15cbac9cb0a114da7e87575f1c95b46a" + integrity sha512-uBsnWETsUagQ0n6G2wcXNIufpTNJir0zqzG4p62fhnwzs48d/iuOWEEo0d3iUxN7D+9R/8CSvWGKS+KmaD0mWA== "@types/web3@1.0.19": version "1.0.19" @@ -1722,6 +1560,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^17.0.9": + version "17.0.10" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a" + integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA== + dependencies: + "@types/yargs-parser" "*" + "@typescript-eslint/eslint-plugin@^4.0.0": version "4.33.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" @@ -1806,9 +1651,9 @@ wonka "^4.0.14" "@urql/core@>=2.3.6", "@urql/core@^2.1.3": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.6.0.tgz#1ce8b3a471312986378ce42ea800478595239315" - integrity sha512-JuIbzDu2oASSe+m5u410ceqWQGTF6QK5W/UZaHuzQX+I2wX4aVdPN/NeZnaFFqG3UYw3+ttFHpQK0vrsU6QLFw== + version "2.4.3" + resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.4.3.tgz#af35355cd2e3eeef4657f91616098e7cba8154dc" + integrity sha512-FpapxUKF0nLdzRLoB1QsudDjeLXJhBwzkzl8bSOJ6Cnj7LRRKJ+dYdqHfqGykswB/ILrkZks2Isp4a4BhqyUow== dependencies: "@graphql-typed-document-node/core" "^3.1.1" wonka "^4.0.14" @@ -1935,9 +1780,9 @@ acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.4.1: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== address@^1.0.1: version "1.2.0" @@ -2090,6 +1935,13 @@ anymatch@~3.1.1, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +arbos-precompiles@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arbos-precompiles/-/arbos-precompiles-1.0.2.tgz#7bebd5963aef972cd259eb41f3116ea065013ea6" + integrity sha512-1dOFYFJUN0kKoofh6buZJ8qCqTs+oLGSsGzHI0trA/Pka/TCERflCRsNVxez2lihOvK7MT/a2RA8AepKtBXdPQ== + dependencies: + hardhat "^2.6.4" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -2136,6 +1988,16 @@ array-back@^2.0.0: dependencies: typical "^2.6.1" +array-back@^3.0.1, array-back@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" + integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== + +array-back@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" + integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -2154,24 +2016,13 @@ array-union@^2.1.0: array-uniq@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.reduce@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" - integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.7" - arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -2261,9 +2112,9 @@ async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: lodash "^4.17.14" async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + version "3.2.3" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" + integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== asynckit@^0.4.0: version "0.4.0" @@ -2944,10 +2795,10 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0, bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== body-parser@1.19.1: version "1.19.1" @@ -2981,7 +2832,7 @@ body-parser@1.19.2: raw-body "2.4.3" type-is "~1.6.18" -body-parser@1.20.0, body-parser@^1.16.0: +body-parser@^1.16.0: version "1.20.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== @@ -3303,9 +3154,9 @@ camelcase@^6.0.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30000844: - version "1.0.30001368" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001368.tgz#c5c06381c6051cd863c45021475434e81936f713" - integrity sha512-wgfRYa9DenEomLG/SdWgQxpIyvdtH3NW8Vq+tB6AwR9e56iOIcu1im5F/wNdDf04XlKHXqIx4N8Jo0PemeBenQ== + version "1.0.30001325" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz#2b4ad19b77aa36f61f2eaf72e636d7481d55e606" + integrity sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ== caseless@^0.12.0, caseless@~0.12.0: version "0.12.0" @@ -3367,7 +3218,7 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3412,7 +3263,7 @@ chokidar@3.3.0: optionalDependencies: fsevents "~2.1.1" -chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.2: +chokidar@3.5.3, chokidar@^3.4.0: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3516,13 +3367,13 @@ cli-table3@^0.5.0: colors "^1.1.2" cli-table3@^0.6.0: - version "0.6.2" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" - integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw== + version "0.6.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.1.tgz#36ce9b7af4847f288d3cdd081fbd09bf7bd237b8" + integrity sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA== dependencies: string-width "^4.2.0" optionalDependencies: - "@colors/colors" "1.5.0" + colors "1.4.0" cli-table@^0.3.6: version "0.3.11" @@ -3631,9 +3482,9 @@ color-name@^1.0.0, color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -3647,9 +3498,9 @@ color@^3.1.3: color-string "^1.6.0" colorette@^2.0.16: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + version "2.0.16" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" + integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== colors@1.0.3: version "1.0.3" @@ -3690,6 +3541,26 @@ command-line-args@^4.0.7: find-replace "^1.0.3" typical "^2.6.1" +command-line-args@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" + integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== + dependencies: + array-back "^3.1.0" + find-replace "^3.0.0" + lodash.camelcase "^4.3.0" + typical "^4.0.0" + +command-line-usage@^6.1.0: + version "6.1.2" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.2.tgz#2b7ccd48a93fb19bd71ca8fe9900feab00e557b0" + integrity sha512-I+0XN613reAhpBQ6icsPOTwu9cvhc9NtLtUcY2fGYuwm9JZiWBzFDA8w0PHqQjru7Xth7fM/y9TJ13+VKdjh7Q== + dependencies: + array-back "^4.0.1" + chalk "^2.4.2" + table-layout "^1.0.1" + typical "^5.2.0" + commander@2.18.0: version "2.18.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" @@ -3817,11 +3688,6 @@ cookie@0.4.2, cookie@^0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - cookiejar@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" @@ -3833,9 +3699,9 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-pure@^3.0.1: - version "3.23.5" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.5.tgz#23daaa9af9230e50f10b0fa4b8e6b87402be4c33" - integrity sha512-8t78LdpKSuCq4pJYCYk8hl7XEkAX+BP16yRIwL3AanTksxuEf7CM83vRyctmiEL8NDZ3jpUcv56fk9/zG3aIuw== + version "3.21.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51" + integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== core-js@^2.4.0, core-js@^2.5.0: version "2.6.12" @@ -4018,13 +3884,20 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3: +debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.3: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + debug@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -4086,6 +3959,11 @@ deep-equal@~1.1.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-extend@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -4189,7 +4067,7 @@ depd@2.0.0, depd@~2.0.0: depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= des.js@^1.0.0: version "1.0.1" @@ -4229,7 +4107,7 @@ diff@3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@5.0.0: +diff@5.0.0, diff@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== @@ -4284,9 +4162,14 @@ dot-prop@^5.1.0: is-obj "^2.0.0" dotenv@*: - version "16.0.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d" - integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ== + version "16.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" + integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== + +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== dotenv@^9.0.0: version "9.0.2" @@ -4334,9 +4217,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.3.47: - version "1.4.196" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.196.tgz#e18cdc5c1c2c2ebf78da237d0c374cc3b244d4cb" - integrity sha512-uxMa/Dt7PQsLBVXwH+t6JvpHJnrsYBaxWKi/J6HE+/nBtoHENhwBoNkgkm226/Kfxeg0z1eMQLBRPPKcDH8xWA== + version "1.4.104" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.104.tgz#60973b0a7d398efa877196e8ccb0c93d48b918d8" + integrity sha512-2kjoAyiG7uMyGRM9mx25s3HAzmQG2ayuYXxsFmYugHSDcwxREgLtscZvbL1JcW9S/OemeQ3f/SG6JhDwpnCclQ== elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" @@ -4351,7 +4234,7 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5 minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emoji-regex@^10.1.0: +emoji-regex@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.1.0.tgz#d50e383743c0f7a5945c47087295afc112e3cf66" integrity sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg== @@ -4371,11 +4254,6 @@ enabled@2.0.x: resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== -encode-utf8@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" - integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4452,7 +4330,33 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.0, es-abstract@^1.20.1: +es-abstract@^1.18.5, es-abstract@^1.19.1: + version "1.19.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.2.tgz#8f7b696d8f15b167ae3640b4060670f3d054143f" + integrity sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-abstract@^1.19.0, es-abstract@^1.19.5: version "1.20.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== @@ -4481,11 +4385,6 @@ es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20 string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -4496,9 +4395,9 @@ es-to-primitive@^1.2.1: is-symbol "^1.0.2" es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.61" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.61.tgz#311de37949ef86b6b0dcea894d1ffedb909d3269" - integrity sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA== + version "0.10.59" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.59.tgz#71038939730eb6f4f165f1421308fb60be363bc6" + integrity sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw== dependencies: es6-iterator "^2.0.3" es6-symbol "^3.1.3" @@ -4935,7 +4834,7 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha512-EoltVQTRNg2Uy4o84qpa2aXymXDJhxm7eos/ACOg0DG4baAbMjhbdAEsx9GeE8sC3XCxnYvrrzZDH8D8MtA2iQ== -ethereum-cryptography@^0.1.3: +ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -4957,14 +4856,14 @@ ethereum-cryptography@^0.1.3: setimmediate "^1.0.5" ethereum-cryptography@^1.0.3: - version "1.1.2" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz#74f2ac0f0f5fe79f012c889b3b8446a9a6264e6d" - integrity sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.0.3.tgz#b1f8f4e702434b2016248dbb2f9fdd60c54772d8" + integrity sha512-NQLTW0x0CosoVb/n79x/TRHtfvS3hgNUPTUSCu0vM+9k6IIhHFFrAOJReneexjZsoZxMjJHnJn4lrE8EbnSyqQ== dependencies: - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.6.3" - "@scure/bip32" "1.1.0" - "@scure/bip39" "1.1.0" + "@noble/hashes" "1.0.0" + "@noble/secp256k1" "1.5.5" + "@scure/bip32" "1.0.1" + "@scure/bip39" "1.0.0" ethereum-waffle@^3.3.0: version "3.4.4" @@ -5119,7 +5018,18 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereum rlp "^2.0.0" safe-buffer "^5.1.1" -ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.4, ethereumjs-util@^7.1.5: +ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.3, ethereumjs-util@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz#a6885bcdd92045b06f596c7626c3e89ab3312458" + integrity sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethereumjs-util@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== @@ -5183,7 +5093,7 @@ ethereumjs-wallet@0.6.5: utf8 "^3.0.0" uuid "^3.3.2" -ethers@5.6.2: +ethers@5.6.2, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.0.24, ethers@^5.1.0, ethers@^5.4.4, ethers@^5.5.2, ethers@^5.6.0: version "5.6.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.2.tgz#e75bac7f038c5e0fdde667dba62fc223924143a2" integrity sha512-EzGCbns24/Yluu7+ToWnMca3SXJ1Jk1BvWB7CCmVNxyOeM4LLvw2OLuIHhlkhQk1dtOcj9UMsdkxUh8RiG1dxQ== @@ -5234,42 +5144,6 @@ ethers@^4.0.32, ethers@^4.0.40: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@^5.0.1, ethers@^5.0.2, ethers@^5.0.24, ethers@^5.4.4, ethers@^5.5.2, ethers@^5.5.3, ethers@^5.6.8: - version "5.6.9" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.9.tgz#4e12f8dfcb67b88ae7a78a9519b384c23c576a4d" - integrity sha512-lMGC2zv9HC5EC+8r429WaWu3uWJUCgUCt8xxKCFqkrFuBDZXDYIdzDUECxzjf2BMF8IVBByY1EBoGSL3RTm8RA== - dependencies: - "@ethersproject/abi" "5.6.4" - "@ethersproject/abstract-provider" "5.6.1" - "@ethersproject/abstract-signer" "5.6.2" - "@ethersproject/address" "5.6.1" - "@ethersproject/base64" "5.6.1" - "@ethersproject/basex" "5.6.1" - "@ethersproject/bignumber" "5.6.2" - "@ethersproject/bytes" "5.6.1" - "@ethersproject/constants" "5.6.1" - "@ethersproject/contracts" "5.6.2" - "@ethersproject/hash" "5.6.1" - "@ethersproject/hdnode" "5.6.2" - "@ethersproject/json-wallets" "5.6.1" - "@ethersproject/keccak256" "5.6.1" - "@ethersproject/logger" "5.6.0" - "@ethersproject/networks" "5.6.4" - "@ethersproject/pbkdf2" "5.6.1" - "@ethersproject/properties" "5.6.0" - "@ethersproject/providers" "5.6.8" - "@ethersproject/random" "5.6.1" - "@ethersproject/rlp" "5.6.1" - "@ethersproject/sha2" "5.6.1" - "@ethersproject/signing-key" "5.6.2" - "@ethersproject/solidity" "5.6.1" - "@ethersproject/strings" "5.6.1" - "@ethersproject/transactions" "5.6.2" - "@ethersproject/units" "5.6.1" - "@ethersproject/wallet" "5.6.2" - "@ethersproject/web" "5.6.1" - "@ethersproject/wordlists" "5.6.1" - ethjs-unit@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" @@ -5357,7 +5231,7 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -express@4.17.3: +express@4.17.3, express@^4.14.0: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== @@ -5393,43 +5267,6 @@ express@4.17.3: utils-merge "1.0.1" vary "~1.1.2" -express@^4.14.0: - version "4.18.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" - integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.0" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.10.3" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - ext@^1.1.2: version "1.6.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" @@ -5604,21 +5441,8 @@ fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" + dependencies: + to-regex-range "^5.0.1" finalhandler@~1.1.2: version "1.1.2" @@ -5641,6 +5465,13 @@ find-replace@^1.0.3: array-back "^1.0.4" test-value "^2.1.0" +find-replace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" + integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== + dependencies: + array-back "^3.0.1" + find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -5729,31 +5560,24 @@ flatted@^2.0.0: integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== flatted@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" - integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flow-stoplight@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" integrity sha512-rDjbZUKpN8OYhB0IE/vY/I8UWO/602IIJEU/76Tv4LvYnwHCk0BCsvz4eRr9n+FQcri7L5cyaXOo0+/Kh4HisA== -fmix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fmix/-/fmix-0.1.0.tgz#c7bbf124dec42c9d191cfb947d0a9778dd986c0c" - integrity sha512-Y6hyofImk9JdzU8k5INtTXX1cu8LDlePWDFU5sftm9H+zKCr5SGrVjdhkvsim646cw5zD0nADj8oHyXMZmCZ9w== - dependencies: - imul "^1.0.0" - fn.name@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== follow-redirects@^1.12.1, follow-redirects@^1.14.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + version "1.14.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" + integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" @@ -5767,6 +5591,11 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== +foreach@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e" + integrity sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg== + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -5847,9 +5676,9 @@ fs-extra@^0.30.0: rimraf "^2.2.8" fs-extra@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + version "10.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" + integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -6102,7 +5931,7 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.2.0: +glob@7.2.0, glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -6125,18 +5954,6 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.2.0: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^8.0.3: version "8.0.3" resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" @@ -6185,9 +6002,9 @@ globals@^11.7.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + version "13.13.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" + integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== dependencies: type-fest "^0.20.2" @@ -6312,40 +6129,21 @@ hard-rejection@^2.1.0: integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== hardhat-abi-exporter@^2.2.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/hardhat-abi-exporter/-/hardhat-abi-exporter-2.10.0.tgz#4498982bb8942178a3b6d9368b0def8d18b49cc8" - integrity sha512-ynYGUptkal3leyUNGP3aJ6GRYtSHed3lHcfMv6imawqEd7MERueEkeFy+X4kWH3Vf5w4k6xeeCJHJsegGBJUPA== + version "2.8.0" + resolved "https://registry.yarnpkg.com/hardhat-abi-exporter/-/hardhat-abi-exporter-2.8.0.tgz#e7ed6216c16acf84158909d856577f0a8832ec55" + integrity sha512-HQwd9Agr2O5znUg9Dzicw8grsXacoMSQsS5ZhBBNyaxKeVbvxL1Ubm9ss8sSVGr74511a8qiR2Ljm/lsLS9Mew== dependencies: "@ethersproject/abi" "^5.5.0" delete-empty "^3.0.0" hardhat-contract-sizer@^2.0.3: - version "2.6.1" - resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.6.1.tgz#2b0046a55fa1ec96f19fdab7fde372377401c874" - integrity sha512-b8wS7DBvyo22kmVwpzstAQTdDCThpl/ySBqZh5ga9Yxjf61/uTL12TEg5nl7lDeWy73ntEUzxMwY6XxbQEc2wA== + version "2.5.1" + resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.5.1.tgz#cb0b8dd32593b7a28c8d96ecde04841292bbd603" + integrity sha512-28yRb73e30aBVaZOOHTlHZFIdIasA/iFunIehrUviIJTubvdQjtSiQUo2wexHFtt71mQeMPP8qjw2sdbgatDnQ== dependencies: chalk "^4.0.0" cli-table3 "^0.6.0" -hardhat-deploy@^0.11.10: - version "0.11.11" - resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.11.11.tgz#479040ba84d5b6d582d8fc719a3e77656ba8dc66" - integrity sha512-/37No1l6aFMMc4+t+a0/8gk6OwwD4tyW8bSQqV/0IZEiaHiNa7hOV4rTbC9B7277SDHVoFUrV/fwmk7IDiB8sQ== - dependencies: - "@types/qs" "^6.9.7" - axios "^0.21.1" - chalk "^4.1.2" - chokidar "^3.5.2" - debug "^4.3.2" - enquirer "^2.3.6" - ethers "^5.5.3" - form-data "^4.0.0" - fs-extra "^10.0.0" - match-all "^1.2.6" - murmur-128 "^0.2.1" - qs "^6.9.4" - zksync-web3 "^0.7.8" - hardhat-gas-reporter@^1.0.4: version "1.0.8" resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.8.tgz#93ce271358cd748d9c4185dbb9d1d5525ec145e0" @@ -6369,6 +6167,60 @@ hardhat-tracer@^1.0.0-alpha.6: dependencies: ethers "^5.0.24" +hardhat@^2.6.4: + version "2.9.3" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.9.3.tgz#4759dc3c468c7d15f34334ca1be7d59b04e47b1e" + integrity sha512-7Vw99RbYbMZ15UzegOR/nqIYIqddZXvLwJGaX5sX4G5bydILnbjmDU6g3jMKJSiArEixS3vHAEaOs5CW1JQ3hg== + dependencies: + "@ethereumjs/block" "^3.6.0" + "@ethereumjs/blockchain" "^5.5.0" + "@ethereumjs/common" "^2.6.0" + "@ethereumjs/tx" "^3.4.0" + "@ethereumjs/vm" "^5.6.0" + "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.14.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + ethereum-cryptography "^0.1.2" + ethereumjs-abi "^0.6.8" + ethereumjs-util "^7.1.3" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "^7.1.3" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + lodash "^4.17.11" + merkle-patricia-tree "^4.2.2" + mnemonist "^0.38.0" + mocha "^9.2.0" + p-map "^4.0.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + slash "^3.0.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + "true-case-path" "^2.2.1" + tsort "0.0.1" + undici "^4.14.1" + uuid "^8.3.2" + ws "^7.4.6" + hardhat@^2.9.5: version "2.10.1" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.10.1.tgz#37fdc0c96d6a5d16b322269db2ad8f9f115c4046" @@ -6716,9 +6568,9 @@ immediate@~3.2.3: integrity sha512-RrGCXRm/fRVqMIhqXrGEX9rRADavPiDFSoMb/k64i9XMk8uH4r/Omi5Ctierj6XzNecwDbO4WuFbDD1zmpl3Tg== immutable@^4.0.0-rc.12: - version "4.1.0" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" - integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== + version "4.0.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" + integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== import-fresh@^2.0.0: version "2.0.0" @@ -6736,11 +6588,6 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -imul@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/imul/-/imul-1.0.1.tgz#9d5867161e8b3de96c2c38d5dc7cb102f35e2ac9" - integrity sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA== - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -6794,9 +6641,9 @@ inquirer@^6.2.2: through "^2.3.6" inquirer@^8.0.0: - version "8.2.4" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" - integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== + version "8.2.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.2.tgz#1310517a87a0814d25336c78a20b44c3d9b7629d" + integrity sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -6812,7 +6659,6 @@ inquirer@^8.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" - wrap-ansi "^7.0.0" internal-slot@^1.0.3: version "1.0.3" @@ -6838,7 +6684,7 @@ invariant@^2.2.2: invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ== + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= io-ts@1.10.4: version "1.10.4" @@ -7050,10 +6896,10 @@ is-circular@^1.0.2: resolved "https://registry.yarnpkg.com/is-circular/-/is-circular-1.0.2.tgz#2e0ab4e9835f4c6b0ea2b9855a84acd501b8366c" integrity sha512-YttjnrswnUYRVJvxCvu8z+PGMUSzC2JttP0OEXezlAEdp3EXzhf7IZ3j0gRAybJBQupedIZFhY61Tga6E0qASA== -is-core-module@^2.5.0, is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== +is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" @@ -7265,7 +7111,7 @@ is-retry-allowed@^1.0.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== -is-shared-array-buffer@^1.0.2: +is-shared-array-buffer@^1.0.1, is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== @@ -7303,15 +7149,15 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.3, is-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.9.tgz#246d77d2871e7d9f5aeb1d54b9f52c71329ece67" - integrity sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A== +is-typed-array@^1.1.3, is-typed-array@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" + integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== dependencies: available-typed-arrays "^1.0.5" call-bind "^1.0.2" - es-abstract "^1.20.0" - for-each "^0.3.3" + es-abstract "^1.18.5" + foreach "^2.0.5" has-tostringtag "^1.0.0" is-typedarray@^1.0.0, is-typedarray@~1.0.0: @@ -8044,13 +7890,28 @@ locate-path@^6.0.0: lodash.assign@^4.0.3, lodash.assign@^4.0.6: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - integrity sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw== + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= lodash.get@^4: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.isequalwith@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz#266726ddd528f854f21f4ea98a065606e0fbc6b0" + integrity sha1-Jmcm3dUo+FTyH06pigZWBuD7xrA= + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -8097,9 +7958,9 @@ log-update@^4.0.0: wrap-ansi "^6.2.0" logform@^2.3.2, logform@^2.4.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.2.tgz#a617983ac0334d0c3b942c34945380062795b47c" - integrity sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw== + version "2.4.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe" + integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw== dependencies: "@colors/colors" "1.5.0" fecha "^4.2.0" @@ -8162,6 +8023,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.4.0: + version "7.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.7.3.tgz#98cd19eef89ce6a4a3c4502c17c833888677c252" + integrity sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw== + lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -8209,11 +8075,6 @@ markdown-table@^1.1.3: resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== -match-all@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/match-all/-/match-all-1.2.6.tgz#66d276ad6b49655551e63d3a6ee53e8be0566f8d" - integrity sha512-0EESkXiTkWzrQQntBu2uzKvLu6vVkUGz40nGPbSZuegcfE5UuSzNjLaIu76zJWuaT/2I3Z/8M06OlUOZLGwLlQ== - mcl-wasm@^0.7.1: version "0.7.9" resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" @@ -8231,7 +8092,7 @@ md5.js@^1.3.4: media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= memdown@^1.0.0: version "1.4.1" @@ -8340,7 +8201,7 @@ merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: rlp "^2.0.0" semaphore ">=1.0.1" -merkle-patricia-tree@^4.2.4: +merkle-patricia-tree@^4.2.2, merkle-patricia-tree@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.2.4.tgz#ff988d045e2bf3dfa2239f7fabe2d59618d57413" integrity sha512-eHbf/BG6eGNsqqfbLED9rIqbsF4+sykEaBn6OLNs71tjclbMcMOk1tEPmJKcNcNCLkvbpY/lwyOlizWsqPNo8w== @@ -8446,14 +8307,14 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@*, minimatch@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" - integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== +minimatch@*, minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: brace-expansion "^2.0.1" -"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.1.1: +"minimatch@2 || 3", minimatch@^3.0.4: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -8467,10 +8328,17 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== +minimatch@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" + integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== dependencies: brace-expansion "^2.0.1" @@ -8483,7 +8351,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.6: +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -8602,6 +8470,36 @@ mocha@^7.1.1: yargs-parser "13.1.2" yargs-unparser "1.6.0" +mocha@^9.2.0: + version "9.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" + integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.3" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + growl "1.10.5" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "4.2.1" + ms "2.1.3" + nanoid "3.3.1" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + workerpool "6.2.0" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + mock-fs@^4.1.0: version "4.14.0" resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" @@ -8615,9 +8513,9 @@ moment-timezone@^0.5.34: moment ">= 2.9.0" "moment@>= 2.9.0", moment@^2.29.1: - version "2.29.4" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" - integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + version "2.29.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" + integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== morgan@1.10.0: version "1.10.0" @@ -8734,9 +8632,9 @@ multicodec@^3.0.1: varint "^6.0.0" multiformats@^9.4.2: - version "9.7.0" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.7.0.tgz#845799e8df70fbb6b15922500e45cb87cf12f7e5" - integrity sha512-uv/tcgwk0yN4DStopnBN4GTgvaAlYdy6KnZpuzEPFOYQd71DYFJjs0MN1ERElAflrZaYyGBWXyGxL5GgrxIx0Q== + version "9.6.4" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.4.tgz#5dce1f11a407dbb69aa612cb7e5076069bb759ca" + integrity sha512-fCCB6XMrr6CqJiHNjfFNGT0v//dxOBMrOMqUIzpPc/mmITweLEyhvMpY9bF+jZ9z3vaMAau5E8B68DW77QMXkg== multihashes@^0.4.15, multihashes@~0.4.15: version "0.4.21" @@ -8777,15 +8675,6 @@ multihashing-async@^2.0.0: murmurhash3js-revisited "^3.0.0" uint8arrays "^3.0.0" -murmur-128@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/murmur-128/-/murmur-128-0.2.1.tgz#a9f6568781d2350ecb1bf80c14968cadbeaa4b4d" - integrity sha512-WseEgiRkI6aMFBbj8Cg9yBj/y+OdipwVC7zUo3W2W1JAJITwouUOtpqsmGSg67EQmwwSyod7hsVsWY5LsrfQVg== - dependencies: - encode-utf8 "^1.0.2" - fmix "^0.1.0" - imul "^1.0.0" - murmurhash3js-revisited@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/murmurhash3js-revisited/-/murmurhash3js-revisited-3.0.0.tgz#6bd36e25de8f73394222adc6e41fa3fac08a5869" @@ -8806,15 +8695,20 @@ nano-json-stream-parser@^0.1.2: resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew== +nanoid@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== nanoid@^3.0.2, nanoid@^3.1.3: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + version "3.3.2" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557" + integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA== nanomatch@^1.2.9: version "1.2.13" @@ -8904,9 +8798,9 @@ node-fetch@~1.7.1: is-stream "^1.0.1" node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.4.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" + integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== nofilter@^1.0.4: version "1.0.4" @@ -8995,9 +8889,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.12.0, object-inspect@^1.9.0, object-inspect@~1.12.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== object-is@^1.0.1: version "1.1.5" @@ -9045,14 +8939,13 @@ object.assign@^4.1.2: object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" - integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== + version "2.1.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== dependencies: - array.prototype.reduce "^1.0.4" call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.1" + define-properties "^1.1.3" + es-abstract "^1.19.1" object.pick@^1.3.0: version "1.3.0" @@ -9062,9 +8955,9 @@ object.pick@^1.3.0: isobject "^3.0.1" obliterator@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" - integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + version "2.0.2" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.2.tgz#25f50dc92e1181371b9d8209d11890f1a3c2fc21" + integrity sha512-g0TrA7SbUggROhDPK8cEu/qpItwH2LSKcNl4tlfBNT54XY+nOsqrs0Q68h1V9b3HOSpIWv15jb1lax2hAggdIg== oboe@2.1.4: version "2.1.4" @@ -9222,7 +9115,7 @@ p-fifo@^1.0.0: p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^1.1.0: version "1.3.0" @@ -9532,9 +9425,9 @@ pg-int8@1.0.1: integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== pg-pool@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.1.tgz#f499ce76f9bf5097488b3b83b19861f28e4ed905" - integrity sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ== + version "3.5.2" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.2.tgz#ed1bed1fb8d79f1c6fd5fb1c99e990fbf9ddf178" + integrity sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w== pg-protocol@^1.5.0: version "1.5.0" @@ -9636,11 +9529,10 @@ pino@7.6.0: thread-stream "^0.13.0" pino@^7.0.0: - version "7.11.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6" - integrity sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg== + version "7.9.2" + resolved "https://registry.yarnpkg.com/pino/-/pino-7.9.2.tgz#0519ac79b9db17f0c63bb4267f7e8133f5c517ad" + integrity sha512-c8wmB2PuhdJurYPRl/eo3+PosHe7Ep6GZvBJFIrp9oV1YRZQ3qm3MujaEolaKUfwX8cDL96WKCWWMedB2drXqw== dependencies: - atomic-sleep "^1.0.0" fast-redact "^3.0.0" on-exit-leak-free "^0.2.0" pino-abstract-transport v0.5.0 @@ -9650,7 +9542,7 @@ pino@^7.0.0: real-require "^0.1.0" safe-stable-stringify "^2.1.0" sonic-boom "^2.2.1" - thread-stream "^0.15.1" + thread-stream "^0.15.0" please-upgrade-node@^3.2.0: version "3.2.0" @@ -9724,14 +9616,14 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier-plugin-solidity@^1.0.0-beta.9: - version "1.0.0-dev.23" - resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-dev.23.tgz#e1edf0693d69fe1518519ab704d5e46ee4f842fc" - integrity sha512-440/jZzvtDJcqtoRCQiigo1DYTPAZ85pjNg7gvdd+Lds6QYgID8RyOdygmudzHdFmV2UfENt//A8tzx7iS58GA== + version "1.0.0-beta.19" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz#7c3607fc4028f5e6a425259ff03e45eedf733df3" + integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== dependencies: - "@solidity-parser/parser" "^0.14.3" - emoji-regex "^10.1.0" + "@solidity-parser/parser" "^0.14.0" + emoji-regex "^10.0.0" escape-string-regexp "^4.0.0" - semver "^7.3.7" + semver "^7.3.5" solidity-comments-extractor "^0.0.7" string-width "^4.2.3" @@ -9741,9 +9633,9 @@ prettier@^1.14.3: integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== prettier@^2.1.2, prettier@^2.2.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + version "2.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== private@^0.1.6, private@^0.1.8: version "0.1.8" @@ -9936,7 +9828,7 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@6.10.3: +qs@6.10.3, qs@^6.4.0, qs@^6.7.0: version "6.10.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== @@ -9953,13 +9845,6 @@ qs@6.9.7: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== -qs@^6.4.0, qs@^6.7.0, qs@^6.9.4: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -10170,6 +10055,11 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +reduce-flatten@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" + integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== + regenerate@^1.2.1: version "1.4.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" @@ -10197,7 +10087,15 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" + integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -10380,11 +10278,11 @@ resolve@1.17.0: path-parse "^1.0.6" resolve@^1.1.6, resolve@^1.10.0, resolve@^1.8.1, resolve@~1.22.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.8.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -10510,10 +10408,10 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" -rxjs@^7.5.1, rxjs@^7.5.5: - version "7.5.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" - integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== +rxjs@^7.2.0, rxjs@^7.5.1, rxjs@^7.5.5: + version "7.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" + integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== dependencies: tslib "^2.1.0" @@ -10634,12 +10532,12 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@^7.2.1, semver@^7.3.4, semver@^7.3.5: + version "7.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b" + integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w== dependencies: - lru-cache "^6.0.0" + lru-cache "^7.4.0" semver@~5.4.1: version "5.4.1" @@ -10665,25 +10563,6 @@ send@0.17.2: range-parser "~1.2.1" statuses "~1.5.0" -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - sequelize-pool@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-7.1.0.tgz#210b391af4002762f823188fd6ecfc7413020768" @@ -10728,16 +10607,6 @@ serve-static@1.14.2: parseurl "~1.3.3" send "0.17.2" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - servify@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" @@ -11022,9 +10891,9 @@ solhint@^3.3.6: prettier "^1.14.3" solidity-ast@^0.4.15: - version "0.4.35" - resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.35.tgz#82e064b14dc989338123264bde2235cad751f128" - integrity sha512-F5bTDLh3rmDxRmLSrs3qt3nvxJprWSEkS7h2KmuXDx7XTfJ6ZKVTV1rtPIYCqJAuPsU/qa8YUeFn7jdOAZcTPA== + version "0.4.31" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.31.tgz#c63e42f894cd047826a05dbb8d1e1dfc17282d39" + integrity sha512-kX6o4XE4ihaqENuRRTMJfwQNHoqWusPENZUlX4oVb19gQdfi7IswFWnThONHSW/61umgfWdKtCBgW45iuOTryQ== solidity-comments-extractor@^0.0.7: version "0.0.7" @@ -11032,9 +10901,9 @@ solidity-comments-extractor@^0.0.7: integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== solidity-coverage@^0.7.16: - version "0.7.21" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.21.tgz#20c5615a3a543086b243c2ca36e2951a75316b40" - integrity sha512-O8nuzJ9yXiKUx3NdzVvHrUW0DxoNVcGzq/I7NzewNO9EZE3wYAQ4l8BwcnV64r4aC/HB6Vnw/q2sF0BQHv/3fg== + version "0.7.20" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.20.tgz#246e9b0dd62f698bb8ddeecdcc46cab26c48b637" + integrity sha512-edOXTugUYdqxrtEnIn4vgrGjLPxdexcL0WD8LzAvVA3d1dwgcfRO3k8xQR02ZQnOnWMBi8Cqs0F+kAQQp3JW8g== dependencies: "@solidity-parser/parser" "^0.14.0" "@truffle/provider" "^0.2.24" @@ -11056,9 +10925,9 @@ solidity-coverage@^0.7.16: web3-utils "^1.3.0" sonic-boom@^2.2.1: - version "2.8.0" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" - integrity sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg== + version "2.6.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.6.0.tgz#8786fc78be07c18a90381acd816d1d4afe3537a2" + integrity sha512-6xYZFRmDEtxGqfOKcDQ4cPLrNa0SPEDI+wlzDAHowXE6YV42NeXqg9mP2KkiM8JVu3lHfZ2iQKYlGOz+kTpphg== dependencies: atomic-sleep "^1.0.0" @@ -11253,6 +11122,11 @@ string-argv@0.3.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== +string-format@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" + integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -11289,15 +11163,15 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: strip-ansi "^6.0.1" string.prototype.trim@~1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.6.tgz#824960787db37a9e24711802ed0c1d1c0254f83e" - integrity sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ== + version "1.2.5" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz#a587bcc8bfad8cb9829a577f5de30dd170c1682c" + integrity sha512-Lnh17webJVsD6ECeovpVN17RlAKjmz4rF9S+8Y45CkMc/ufVpTkU3vZIyIC7sllQ1FCvObZnnCdNs/HXTUOTlg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" + define-properties "^1.1.3" + es-abstract "^1.19.1" -string.prototype.trimend@^1.0.5: +string.prototype.trimend@^1.0.4, string.prototype.trimend@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== @@ -11306,7 +11180,7 @@ string.prototype.trimend@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" -string.prototype.trimstart@^1.0.5: +string.prototype.trimstart@^1.0.4, string.prototype.trimstart@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== @@ -11485,6 +11359,16 @@ sync-rpc@^1.2.1: dependencies: get-port "^3.1.0" +table-layout@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" + integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== + dependencies: + array-back "^4.0.1" + deep-extend "~0.6.0" + typical "^5.2.0" + wordwrapjs "^4.0.0" + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -11507,9 +11391,9 @@ table@^6.0.9: strip-ansi "^6.0.1" tape@^4.6.3: - version "4.15.1" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.15.1.tgz#88fb662965a11f9be1bddb04c11662d7eceb129e" - integrity sha512-k7F5pyr91n9D/yjSJwbLLYDCrTWXxMSXbbmHX2n334lSIc2rxeXyFkaBv4UuUd2gBYMrAOalPutAiCxC6q1qbw== + version "4.15.0" + resolved "https://registry.yarnpkg.com/tape/-/tape-4.15.0.tgz#1b8a9563b4bc7e51302216c137732fb2ce6d1a99" + integrity sha512-SfRmG2I8QGGgJE/MCiLH8c11L5XxyUXxwK9xLRD0uiK5fehRkkSZGmR6Y1pxOt8vJ19m3sY+POTQpiaVv45/LQ== dependencies: call-bind "~1.0.2" deep-equal "~1.1.1" @@ -11520,7 +11404,7 @@ tape@^4.6.3: has "~1.0.3" inherits "~2.0.4" is-regex "~1.1.4" - minimist "~1.2.6" + minimist "~1.2.5" object-inspect "~1.12.0" resolve "~1.22.0" resumer "~0.0.0" @@ -11599,10 +11483,10 @@ thread-stream@^0.13.0: dependencies: real-require "^0.1.0" -thread-stream@^0.15.1: - version "0.15.2" - resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.15.2.tgz#fb95ad87d2f1e28f07116eb23d85aba3bc0425f4" - integrity sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA== +thread-stream@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.15.1.tgz#5aba24a35aa5e9e4eb66173826443d7167b34b07" + integrity sha512-SCnuIT27gc2h/F/RY2peuC7brgLy+1oXU+7yOIAITz1z5stDpXCF5rAoFcykjuK6ifbTlKAHL7Ccq8oc5Btv1w== dependencies: real-require "^0.1.0" @@ -11738,6 +11622,16 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== +ts-command-line-args@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.2.1.tgz#fd6913e542099012c0ffb2496126a8f38305c7d6" + integrity sha512-mnK68QA86FYzQYTSA/rxIjT/8EpKsvQw9QkawPic8I8t0gjAOw3Oa509NIRoaY1FmH7hdrncMp7t7o+vYoceNQ== + dependencies: + chalk "^4.1.0" + command-line-args "^5.1.1" + command-line-usage "^6.1.0" + string-format "^2.0.0" + ts-essentials@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-1.0.4.tgz#ce3b5dade5f5d97cf69889c11bf7d2da8555b15a" @@ -11768,6 +11662,25 @@ ts-generator@^0.1.1: resolve "^1.8.1" ts-essentials "^1.0.0" +ts-node@^10.2.1: + version "10.7.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" + integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== + dependencies: + "@cspotcode/source-map-support" "0.7.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.0" + yn "3.1.1" + ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -11805,9 +11718,9 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2, tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== tsort@0.0.1: version "0.0.1" @@ -11910,6 +11823,22 @@ type@^2.5.0: resolved "https://registry.yarnpkg.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f" integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== +typechain@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-7.0.0.tgz#258ca136de1d451368bde01c318976a83062f110" + integrity sha512-ILfvBBFJ7j9aIk0crX03+N2GmzoDN1gtk32G1+XrasjuvXS0XAw2XxwQeQMMgKwlnxViJjIkG87sTMYXPkXA9g== + dependencies: + "@types/prettier" "^2.1.1" + debug "^4.1.1" + fs-extra "^7.0.0" + glob "^7.1.6" + js-sha3 "^0.8.0" + lodash "^4.17.15" + mkdirp "^1.0.4" + prettier "^2.1.2" + ts-command-line-args "^2.2.0" + ts-essentials "^7.0.1" + typechain@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/typechain/-/typechain-3.0.0.tgz#d5a47700831f238e43f7429b987b4bb54849b92e" @@ -11951,7 +11880,12 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@^4.4.3, typescript@^4.7.4: +typescript@^4.4.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" + integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== + +typescript@^4.7.4: version "4.7.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== @@ -11978,10 +11912,20 @@ typical@^2.6.0, typical@^2.6.1: resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" integrity sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg== +typical@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" + integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== + +typical@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" + integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== + uglify-js@^3.1.4: - version "3.16.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.2.tgz#0481e1dbeed343ad1c2ddf3c6d42e89b7a6d4def" - integrity sha512-AaQNokTNgExWrkEYA24BTNMSjyqEXPSfhqoS0AxmHkCJ4U+Dyy5AvbGV/sqxuxficEfGGoX3zWw9R7QpLFfEsg== + version "3.15.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.3.tgz#9aa82ca22419ba4c0137642ba0df800cb06e0471" + integrity sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg== uint8arrays@1.1.0, uint8arrays@^1.0.0, uint8arrays@^1.1.0: version "1.1.0" @@ -12010,7 +11954,7 @@ ultron@~1.1.0: resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== -unbox-primitive@^1.0.2: +unbox-primitive@^1.0.1, unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== @@ -12026,9 +11970,14 @@ underscore@1.9.1: integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== underscore@^1.13.1: - version "1.13.4" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee" - integrity sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ== + version "1.13.2" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.2.tgz#276cea1e8b9722a8dbed0100a407dda572125881" + integrity sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g== + +undici@^4.14.1: + version "4.16.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-4.16.0.tgz#469bb87b3b918818d3d7843d91a1d08da357d5ff" + integrity sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw== undici@^5.4.0: version "5.8.0" @@ -12187,6 +12136,11 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" + integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -12260,10 +12214,10 @@ web3-bzz@1.2.11: swarm-js "^0.1.40" underscore "1.9.1" -web3-bzz@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.7.4.tgz#9419e606e38a9777443d4ce40506ebd796e06075" - integrity sha512-w9zRhyEqTK/yi0LGRHjZMcPCfP24LBjYXI/9YxFw9VqsIZ9/G0CRCnUt12lUx0A56LRAMpF7iQ8eA73aBcO29Q== +web3-bzz@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.5.3.tgz#e36456905ce051138f9c3ce3623cbc73da088c2b" + integrity sha512-SlIkAqG0eS6cBS9Q2eBOTI1XFzqh83RqGJWnyrNZMDxUwsTVHL+zNnaPShVPvrWQA1Ub5b0bx1Kc5+qJVxsTJg== dependencies: "@types/node" "^12.12.6" got "9.6.0" @@ -12278,13 +12232,13 @@ web3-core-helpers@1.2.11: web3-eth-iban "1.2.11" web3-utils "1.2.11" -web3-core-helpers@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.7.4.tgz#f8f808928560d3e64e0c8d7bdd163aa4766bcf40" - integrity sha512-F8PH11qIkE/LpK4/h1fF/lGYgt4B6doeMi8rukeV/s4ivseZHHslv1L6aaijLX/g/j4PsFmR42byynBI/MIzFg== +web3-core-helpers@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.5.3.tgz#099030235c477aadf39a94199ef40092151d563c" + integrity sha512-Ip1IjB3S8vN7Kf1PPjK41U5gskmMk6IJQlxIVuS8/1U7n/o0jC8krqtpRwiMfAgYyw3TXwBFtxSRTvJtnLyXZw== dependencies: - web3-eth-iban "1.7.4" - web3-utils "1.7.4" + web3-eth-iban "1.5.3" + web3-utils "1.5.3" web3-core-method@1.2.11: version "1.2.11" @@ -12298,16 +12252,17 @@ web3-core-method@1.2.11: web3-core-subscriptions "1.2.11" web3-utils "1.2.11" -web3-core-method@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.7.4.tgz#3873c6405e1a0a8a1efc1d7b28de8b7550b00c15" - integrity sha512-56K7pq+8lZRkxJyzf5MHQPI9/VL3IJLoy4L/+q8HRdZJ3CkB1DkXYaXGU2PeylG1GosGiSzgIfu1ljqS7CP9xQ== +web3-core-method@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.5.3.tgz#6cff97ed19fe4ea2e9183d6f703823a079f5132c" + integrity sha512-8wJrwQ2qD9ibWieF9oHXwrJsUGrv3XAtEkNeyvyNMpktNTIjxJ2jaFGQUuLiyUrMubD18XXgLk4JS6PJU4Loeg== dependencies: - "@ethersproject/transactions" "^5.6.2" - web3-core-helpers "1.7.4" - web3-core-promievent "1.7.4" - web3-core-subscriptions "1.7.4" - web3-utils "1.7.4" + "@ethereumjs/common" "^2.4.0" + "@ethersproject/transactions" "^5.0.0-beta.135" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-core-subscriptions "1.5.3" + web3-utils "1.5.3" web3-core-promievent@1.2.11: version "1.2.11" @@ -12316,10 +12271,10 @@ web3-core-promievent@1.2.11: dependencies: eventemitter3 "4.0.4" -web3-core-promievent@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.7.4.tgz#80a75633fdfe21fbaae2f1e38950edb2f134868c" - integrity sha512-o4uxwXKDldN7ER7VUvDfWsqTx9nQSP1aDssi1XYXeYC2xJbVo0n+z6ryKtmcoWoRdRj7uSpVzal3nEmlr480mA== +web3-core-promievent@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.5.3.tgz#3f11833c3dc6495577c274350b61144e0a4dba01" + integrity sha512-CFfgqvk3Vk6PIAxtLLuX+pOMozxkKCY+/GdGr7weMh033mDXEPvwyVjoSRO1PqIKj668/hMGQsVoIgbyxkJ9Mg== dependencies: eventemitter3 "4.0.4" @@ -12334,16 +12289,16 @@ web3-core-requestmanager@1.2.11: web3-providers-ipc "1.2.11" web3-providers-ws "1.2.11" -web3-core-requestmanager@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.7.4.tgz#2dc8a526dab8183dca3fef54658621801b1d0469" - integrity sha512-IuXdAm65BQtPL4aI6LZJJOrKAs0SM5IK2Cqo2/lMNvVMT9Kssq6qOk68Uf7EBDH0rPuINi+ReLP+uH+0g3AnPA== +web3-core-requestmanager@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.5.3.tgz#b339525815fd40e3a2a81813c864ddc413f7b6f7" + integrity sha512-9k/Bze2rs8ONix5IZR+hYdMNQv+ark2Ek2kVcrFgWO+LdLgZui/rn8FikPunjE+ub7x7pJaKCgVRbYFXjo3ZWg== dependencies: util "^0.12.0" - web3-core-helpers "1.7.4" - web3-providers-http "1.7.4" - web3-providers-ipc "1.7.4" - web3-providers-ws "1.7.4" + web3-core-helpers "1.5.3" + web3-providers-http "1.5.3" + web3-providers-ipc "1.5.3" + web3-providers-ws "1.5.3" web3-core-subscriptions@1.2.11: version "1.2.11" @@ -12354,13 +12309,13 @@ web3-core-subscriptions@1.2.11: underscore "1.9.1" web3-core-helpers "1.2.11" -web3-core-subscriptions@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.7.4.tgz#cfbd3fa71081a8c8c6f1a64577a1a80c5bd9826f" - integrity sha512-VJvKWaXRyxk2nFWumOR94ut9xvjzMrRtS38c4qj8WBIRSsugrZr5lqUwgndtj0qx4F+50JhnU++QEqUEAtKm3g== +web3-core-subscriptions@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.5.3.tgz#d7d69c4caad65074212028656e9dc56ca5c2159d" + integrity sha512-L2m9vG1iRN6thvmv/HQwO2YLhOQlmZU8dpLG6GSo9FBN14Uch868Swk0dYVr3rFSYjZ/GETevSXU+O+vhCummA== dependencies: eventemitter3 "4.0.4" - web3-core-helpers "1.7.4" + web3-core-helpers "1.5.3" web3-core@1.2.11: version "1.2.11" @@ -12375,18 +12330,18 @@ web3-core@1.2.11: web3-core-requestmanager "1.2.11" web3-utils "1.2.11" -web3-core@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.7.4.tgz#943fff99134baedafa7c65b4a0bbd424748429ff" - integrity sha512-L0DCPlIh9bgIED37tYbe7bsWrddoXYc897ANGvTJ6MFkSNGiMwDkTLWSgYd9Mf8qu8b4iuPqXZHMwIo4atoh7Q== +web3-core@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.5.3.tgz#59f8728b27c8305b349051326aa262b9b7e907bf" + integrity sha512-ACTbu8COCu+0eUNmd9pG7Q9EVsNkAg2w3Y7SqhDr+zjTgbSHZV01jXKlapm9z+G3AN/BziV3zGwudClJ4u4xXQ== dependencies: - "@types/bn.js" "^5.1.0" + "@types/bn.js" "^4.11.5" "@types/node" "^12.12.6" bignumber.js "^9.0.0" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-core-requestmanager "1.7.4" - web3-utils "1.7.4" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-requestmanager "1.5.3" + web3-utils "1.5.3" web3-eth-abi@1.2.11: version "1.2.11" @@ -12397,13 +12352,13 @@ web3-eth-abi@1.2.11: underscore "1.9.1" web3-utils "1.2.11" -web3-eth-abi@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.7.4.tgz#3fee967bafd67f06b99ceaddc47ab0970f2a614a" - integrity sha512-eMZr8zgTbqyL9MCTCAvb67RbVyN5ZX7DvA0jbLOqRWCiw+KlJKTGnymKO6jPE8n5yjk4w01e165Qb11hTDwHgg== +web3-eth-abi@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.5.3.tgz#5aea9394d797f99ca0d9bd40c3417eb07241c96c" + integrity sha512-i/qhuFsoNrnV130CSRYX/z4SlCfSQ4mHntti5yTmmQpt70xZKYZ57BsU0R29ueSQ9/P+aQrL2t2rqkQkAloUxg== dependencies: - "@ethersproject/abi" "^5.6.3" - web3-utils "1.7.4" + "@ethersproject/abi" "5.0.7" + web3-utils "1.5.3" web3-eth-accounts@1.2.11: version "1.2.11" @@ -12422,22 +12377,22 @@ web3-eth-accounts@1.2.11: web3-core-method "1.2.11" web3-utils "1.2.11" -web3-eth-accounts@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.7.4.tgz#7a24a4dfe947f7e9d1bae678529e591aa146167a" - integrity sha512-Y9vYLRKP7VU7Cgq6wG1jFaG2k3/eIuiTKAG8RAuQnb6Cd9k5BRqTm5uPIiSo0AP/u11jDomZ8j7+WEgkU9+Btw== +web3-eth-accounts@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.5.3.tgz#076c816ff4d68c9dffebdc7fd2bfaddcfc163d77" + integrity sha512-pdGhXgeBaEJENMvRT6W9cmji3Zz/46ugFSvmnLLw79qi5EH7XJhKISNVb41eWCrs4am5GhI67GLx5d2s2a72iw== dependencies: - "@ethereumjs/common" "^2.5.0" - "@ethereumjs/tx" "^3.3.2" + "@ethereumjs/common" "^2.3.0" + "@ethereumjs/tx" "^3.2.1" crypto-browserify "3.12.0" eth-lib "0.2.8" ethereumjs-util "^7.0.10" scrypt-js "^3.0.1" uuid "3.3.2" - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-utils "1.7.4" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-utils "1.5.3" web3-eth-contract@1.2.11: version "1.2.11" @@ -12454,19 +12409,19 @@ web3-eth-contract@1.2.11: web3-eth-abi "1.2.11" web3-utils "1.2.11" -web3-eth-contract@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.7.4.tgz#e5761cfb43d453f57be4777b2e5e7e1082078ff7" - integrity sha512-ZgSZMDVI1pE9uMQpK0T0HDT2oewHcfTCv0osEqf5qyn5KrcQDg1GT96/+S0dfqZ4HKj4lzS5O0rFyQiLPQ8LzQ== +web3-eth-contract@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.5.3.tgz#12b03a4a16ce583a945f874bea2ff2fb4c5b81ad" + integrity sha512-Gdlt1L6cdHe83k7SdV6xhqCytVtOZkjD0kY/15x441AuuJ4JLubCHuqu69k2Dr3tWifHYVys/vG8QE/W16syGg== dependencies: - "@types/bn.js" "^5.1.0" - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-core-promievent "1.7.4" - web3-core-subscriptions "1.7.4" - web3-eth-abi "1.7.4" - web3-utils "1.7.4" + "@types/bn.js" "^4.11.5" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-promievent "1.5.3" + web3-core-subscriptions "1.5.3" + web3-eth-abi "1.5.3" + web3-utils "1.5.3" web3-eth-ens@1.2.11: version "1.2.11" @@ -12483,19 +12438,19 @@ web3-eth-ens@1.2.11: web3-eth-contract "1.2.11" web3-utils "1.2.11" -web3-eth-ens@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.7.4.tgz#346720305379c0a539e226141a9602f1da7bc0c8" - integrity sha512-Gw5CVU1+bFXP5RVXTCqJOmHn71X2ghNk9VcEH+9PchLr0PrKbHTA3hySpsPco1WJAyK4t8SNQVlNr3+bJ6/WZA== +web3-eth-ens@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.5.3.tgz#ef6eee1ddf32b1ff9536fc7c599a74f2656bafe1" + integrity sha512-QmGFFtTGElg0E+3xfCIFhiUF+1imFi9eg/cdsRMUZU4F1+MZCC/ee+IAelYLfNTGsEslCqfAusliKOT9DdGGnw== dependencies: content-hash "^2.5.2" eth-ens-namehash "2.0.8" - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-promievent "1.7.4" - web3-eth-abi "1.7.4" - web3-eth-contract "1.7.4" - web3-utils "1.7.4" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-eth-abi "1.5.3" + web3-eth-contract "1.5.3" + web3-utils "1.5.3" web3-eth-iban@1.2.11: version "1.2.11" @@ -12505,13 +12460,13 @@ web3-eth-iban@1.2.11: bn.js "^4.11.9" web3-utils "1.2.11" -web3-eth-iban@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.7.4.tgz#711fb2547fdf0f988060027331b2b6c430505753" - integrity sha512-XyrsgWlZQMv5gRcjXMsNvAoCRvV5wN7YCfFV5+tHUCqN8g9T/o4XUS20vDWD0k4HNiAcWGFqT1nrls02MGZ08w== +web3-eth-iban@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.5.3.tgz#91b1475893a877b10eac1de5cce6eb379fb81b5d" + integrity sha512-vMzmGqolYZvRHwP9P4Nf6G8uYM5aTLlQu2a34vz78p0KlDC+eV1th3+90Qeaupa28EG7OO0IT1F0BejiIauOPw== dependencies: - bn.js "^5.2.1" - web3-utils "1.7.4" + bn.js "^4.11.9" + web3-utils "1.5.3" web3-eth-personal@1.2.11: version "1.2.11" @@ -12525,17 +12480,17 @@ web3-eth-personal@1.2.11: web3-net "1.2.11" web3-utils "1.2.11" -web3-eth-personal@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.7.4.tgz#22c399794cb828a75703df8bb4b3c1331b471546" - integrity sha512-O10C1Hln5wvLQsDhlhmV58RhXo+GPZ5+W76frSsyIrkJWLtYQTCr5WxHtRC9sMD1idXLqODKKgI2DL+7xeZ0/g== +web3-eth-personal@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.5.3.tgz#4ebe09e9a77dd49d23d93b36b36cfbf4a6dae713" + integrity sha512-JzibJafR7ak/Icas8uvos3BmUNrZw1vShuNR5Cxjo+vteOC8XMqz1Vr7RH65B4bmlfb3bm9xLxetUHO894+Sew== dependencies: "@types/node" "^12.12.6" - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-net "1.7.4" - web3-utils "1.7.4" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-net "1.5.3" + web3-utils "1.5.3" web3-eth@1.2.11: version "1.2.11" @@ -12556,23 +12511,23 @@ web3-eth@1.2.11: web3-net "1.2.11" web3-utils "1.2.11" -web3-eth@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.7.4.tgz#a7c1d3ccdbba4de4a82df7e3c4db716e4a944bf2" - integrity sha512-JG0tTMv0Ijj039emXNHi07jLb0OiWSA9O24MRSk5vToTQyDNXihdF2oyq85LfHuF690lXZaAXrjhtLNlYqb7Ug== - dependencies: - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-core-subscriptions "1.7.4" - web3-eth-abi "1.7.4" - web3-eth-accounts "1.7.4" - web3-eth-contract "1.7.4" - web3-eth-ens "1.7.4" - web3-eth-iban "1.7.4" - web3-eth-personal "1.7.4" - web3-net "1.7.4" - web3-utils "1.7.4" +web3-eth@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.5.3.tgz#d7d1ac7198f816ab8a2088c01e0bf1eda45862fe" + integrity sha512-saFurA1L23Bd7MEf7cBli6/jRdMhD4X/NaMiO2mdMMCXlPujoudlIJf+VWpRWJpsbDFdu7XJ2WHkmBYT5R3p1Q== + dependencies: + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-subscriptions "1.5.3" + web3-eth-abi "1.5.3" + web3-eth-accounts "1.5.3" + web3-eth-contract "1.5.3" + web3-eth-ens "1.5.3" + web3-eth-iban "1.5.3" + web3-eth-personal "1.5.3" + web3-net "1.5.3" + web3-utils "1.5.3" web3-net@1.2.11: version "1.2.11" @@ -12583,14 +12538,14 @@ web3-net@1.2.11: web3-core-method "1.2.11" web3-utils "1.2.11" -web3-net@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.7.4.tgz#3153dfd3423262dd6fbec7aae5467202c4cad431" - integrity sha512-d2Gj+DIARHvwIdmxFQ4PwAAXZVxYCR2lET0cxz4KXbE5Og3DNjJi+MoPkX+WqoUXqimu/EOd4Cd+7gefqVAFDg== +web3-net@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.5.3.tgz#545fee49b8e213b0c55cbe74ffd0295766057463" + integrity sha512-0W/xHIPvgVXPSdLu0iZYnpcrgNnhzHMC888uMlGP5+qMCt8VuflUZHy7tYXae9Mzsg1kxaJAS5lHVNyeNw4CoQ== dependencies: - web3-core "1.7.4" - web3-core-method "1.7.4" - web3-utils "1.7.4" + web3-core "1.5.3" + web3-core-method "1.5.3" + web3-utils "1.5.3" web3-provider-engine@14.2.1: version "14.2.1" @@ -12626,12 +12581,12 @@ web3-providers-http@1.2.11: web3-core-helpers "1.2.11" xhr2-cookies "1.1.0" -web3-providers-http@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.7.4.tgz#8209cdcb115db5ccae1f550d1c4e3005e7538d02" - integrity sha512-AU+/S+49rcogUER99TlhW+UBMk0N2DxvN54CJ2pK7alc2TQ7+cprNPLHJu4KREe8ndV0fT6JtWUfOMyTvl+FRA== +web3-providers-http@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.5.3.tgz#74f170fc3d79eb7941d9fbc34e2a067d61ced0b2" + integrity sha512-5DpUyWGHtDAr2RYmBu34Fu+4gJuBAuNx2POeiJIooUtJ+Mu6pIx4XkONWH6V+Ez87tZAVAsFOkJRTYuzMr3rPw== dependencies: - web3-core-helpers "1.7.4" + web3-core-helpers "1.5.3" xhr2-cookies "1.1.0" web3-providers-ipc@1.2.11: @@ -12643,13 +12598,13 @@ web3-providers-ipc@1.2.11: underscore "1.9.1" web3-core-helpers "1.2.11" -web3-providers-ipc@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.7.4.tgz#02e85e99e48f432c9d34cee7d786c3685ec9fcfa" - integrity sha512-jhArOZ235dZy8fS8090t60nTxbd1ap92ibQw5xIrAQ9m7LcZKNfmLAQUVsD+3dTFvadRMi6z1vCO7zRi84gWHw== +web3-providers-ipc@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.5.3.tgz#4bd7f5e445c2f3c2595fce0929c72bb879320a3f" + integrity sha512-JmeAptugVpmXI39LGxUSAymx0NOFdgpuI1hGQfIhbEAcd4sv7fhfd5D+ZU4oLHbRI8IFr4qfGU0uhR8BXhDzlg== dependencies: oboe "2.1.5" - web3-core-helpers "1.7.4" + web3-core-helpers "1.5.3" web3-providers-ws@1.2.11: version "1.2.11" @@ -12661,13 +12616,13 @@ web3-providers-ws@1.2.11: web3-core-helpers "1.2.11" websocket "^1.0.31" -web3-providers-ws@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.7.4.tgz#6e60bcefb456f569a3e766e386d7807a96f90595" - integrity sha512-g72X77nrcHMFU8hRzQJzfgi/072n8dHwRCoTw+WQrGp+XCQ71fsk2qIu3Tp+nlp5BPn8bRudQbPblVm2uT4myQ== +web3-providers-ws@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.5.3.tgz#eec6cfb32bb928a4106de506f13a49070a21eabf" + integrity sha512-6DhTw4Q7nm5CFYEUHOJM0gAb3xFx+9gWpVveg3YxJ/ybR1BUvEWo3bLgIJJtX56cYX0WyY6DS35a7f0LOI1kVg== dependencies: eventemitter3 "4.0.4" - web3-core-helpers "1.7.4" + web3-core-helpers "1.5.3" websocket "^1.0.32" web3-shh@1.2.11: @@ -12680,15 +12635,15 @@ web3-shh@1.2.11: web3-core-subscriptions "1.2.11" web3-net "1.2.11" -web3-shh@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.7.4.tgz#bee91cce2737c529fd347274010b548b6ea060f1" - integrity sha512-mlSZxSYcMkuMCxqhTYnZkUdahZ11h+bBv/8TlkXp/IHpEe4/Gg+KAbmfudakq3EzG/04z70XQmPgWcUPrsEJ+A== +web3-shh@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.5.3.tgz#3c04aa4cda9ba0b746d7225262401160f8e38b13" + integrity sha512-COfEXfsqoV/BkcsNLRxQqnWc1Teb8/9GxdGag5GtPC5gQC/vsN+7hYVJUwNxY9LtJPKYTij2DHHnx6UkITng+Q== dependencies: - web3-core "1.7.4" - web3-core-method "1.7.4" - web3-core-subscriptions "1.7.4" - web3-net "1.7.4" + web3-core "1.5.3" + web3-core-method "1.5.3" + web3-core-subscriptions "1.5.3" + web3-net "1.5.3" web3-utils@1.2.11: version "1.2.11" @@ -12704,12 +12659,25 @@ web3-utils@1.2.11: underscore "1.9.1" utf8 "3.0.0" -web3-utils@1.7.4, web3-utils@^1.0.0-beta.31, web3-utils@^1.3.0: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.7.4.tgz#eb6fa3706b058602747228234453811bbee017f5" - integrity sha512-acBdm6Evd0TEZRnChM/MCvGsMwYKmSh7OaUfNf5OKG0CIeGWD/6gqLOWIwmwSnre/2WrA1nKGId5uW2e5EfluA== +web3-utils@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.5.3.tgz#e914c9320cd663b2a09a5cb920ede574043eb437" + integrity sha512-56nRgA+Ad9SEyCv39g36rTcr5fpsd4L9LgV3FK0aB66nAMazLAA6Qz4lH5XrUKPDyBIPGJIR+kJsyRtwcu2q1Q== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3-utils@^1.0.0-beta.31, web3-utils@^1.3.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.7.1.tgz#77d8bacaf426c66027d8aa4864d77f0ed211aacd" + integrity sha512-fef0EsqMGJUgiHPdX+KN9okVWshbIumyJPmR+btnD1HgvoXijKEkuKBv0OmUqjbeqmLKP2/N9EiXKJel5+E1Dw== dependencies: - bn.js "^5.2.1" + bn.js "^4.11.9" ethereum-bloom-filters "^1.0.6" ethereumjs-util "^7.1.0" ethjs-unit "0.1.6" @@ -12730,18 +12698,18 @@ web3@1.2.11: web3-shh "1.2.11" web3-utils "1.2.11" -web3@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.7.4.tgz#00c9aef8e13ade92fd773d845fff250535828e93" - integrity sha512-iFGK5jO32vnXM/ASaJBaI0+gVR6uHozvYdxkdhaeOCD6HIQ4iIXadbO2atVpE9oc/H8l2MovJ4LtPhG7lIBN8A== +web3@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.5.3.tgz#11882679453c645bf33620fbc255a243343075aa" + integrity sha512-eyBg/1K44flfv0hPjXfKvNwcUfIVDI4NX48qHQe6wd7C8nPSdbWqo9vLy6ksZIt9NLa90HjI8HsGYgnMSUxn6w== dependencies: - web3-bzz "1.7.4" - web3-core "1.7.4" - web3-eth "1.7.4" - web3-eth-personal "1.7.4" - web3-net "1.7.4" - web3-shh "1.7.4" - web3-utils "1.7.4" + web3-bzz "1.5.3" + web3-core "1.5.3" + web3-eth "1.5.3" + web3-eth-personal "1.5.3" + web3-net "1.5.3" + web3-shh "1.5.3" + web3-utils "1.5.3" webidl-conversions@^3.0.0: version "3.0.1" @@ -12812,16 +12780,16 @@ which-module@^2.0.0: integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== which-typed-array@^1.1.2: - version "1.1.8" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.8.tgz#0cfd53401a6f334d90ed1125754a42ed663eb01f" - integrity sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw== + version "1.1.7" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" + integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== dependencies: available-typed-arrays "^1.0.5" call-bind "^1.0.2" - es-abstract "^1.20.0" - for-each "^0.3.3" + es-abstract "^1.18.5" + foreach "^2.0.5" has-tostringtag "^1.0.0" - is-typed-array "^1.1.9" + is-typed-array "^1.1.7" which@1.3.1, which@^1.1.1, which@^1.2.9, which@^1.3.1: version "1.3.1" @@ -12830,7 +12798,7 @@ which@1.3.1, which@^1.1.1, which@^1.2.9, which@^1.3.1: dependencies: isexe "^2.0.0" -which@^2.0.1: +which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -12859,9 +12827,9 @@ winston-transport@^4.5.0: triple-beam "^1.3.0" winston@*, winston@^3.3.3: - version "3.8.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.1.tgz#76f15b3478cde170b780234e0c4cf805c5a7fb57" - integrity sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w== + version "3.7.2" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.7.2.tgz#95b4eeddbec902b3db1424932ac634f887c400b1" + integrity sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng== dependencies: "@dabh/diagnostics" "^2.0.2" async "^3.2.3" @@ -12896,6 +12864,19 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== +wordwrapjs@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" + integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== + dependencies: + reduce-flatten "^2.0.0" + typical "^5.2.0" + +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== + workerpool@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" @@ -12970,9 +12951,9 @@ ws@^5.1.1: async-limiter "~1.0.0" ws@^7.4.6: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== xhr-request-promise@^0.1.2: version "0.1.3" @@ -13143,9 +13124,9 @@ yargs@16.2.0: yargs-parser "^20.2.2" yargs@^17.0.0: - version "17.5.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" - integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== + version "17.4.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.0.tgz#9fc9efc96bd3aa2c1240446af28499f0e7593d00" + integrity sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA== dependencies: cliui "^7.0.2" escalade "^3.1.1" @@ -13184,8 +13165,3 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zksync-web3@^0.7.8: - version "0.7.9" - resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.7.9.tgz#fbb9a17f4b297c0fb9361de2a0d85ff2aac5becc" - integrity sha512-B0pitKvEQGJuWkY2UWjXrL1YgHghXEoDaq6acVZnB62TRF099GV58Fzi7Fnqt+Nw14A7Wc9iJ2AHD4GBTLFgkg== From 8e356c040c4ea3c650c945aa1bdcb7f7fca8763b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Wed, 7 Sep 2022 12:04:28 +0200 Subject: [PATCH 002/100] fix: improve onlyL2Counterpart error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/gateway/L1GraphTokenGateway.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 19fd73be5..e1f34efe5 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -71,6 +71,8 @@ contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { * can tell us who the sender from L2 is. */ modifier onlyL2Counterpart() { + require(inbox != address(0), "INBOX_NOT_SET"); + // a message coming from the counterpart gateway was executed by the bridge IBridge bridge = IInbox(inbox).bridge(); require(msg.sender == address(bridge), "NOT_FROM_BRIDGE"); From da4619d3a5c71fa68970f432aa6182f1fb86da56 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 15 Sep 2022 15:08:23 -0300 Subject: [PATCH 003/100] chore: remove unused dependency in GraphTokenUpgradeable --- contracts/l2/token/GraphTokenUpgradeable.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index 0df9b8e06..6d003c1c3 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -7,7 +7,6 @@ import "@openzeppelin/contracts/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../upgrades/GraphUpgradeable.sol"; -import "../../token/GraphToken.sol"; import "../../governance/Governed.sol"; /** From 3f6c55e4dd2a86346589c6499a2a8c26e4616a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Thu, 22 Sep 2022 17:16:59 -0400 Subject: [PATCH 004/100] Sync: l2-testnet > pcv/l2-bridge (#704) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone Co-authored-by: Pablo Carranza Vélez Co-authored-by: Ariel Barmat --- TESTING.md | 24 ++- cli/commands/bridge/to-l1.ts | 25 ++- cli/commands/bridge/to-l2.ts | 152 +++++++++++------- cli/commands/migrate.ts | 14 +- cli/commands/protocol/configure-bridge.ts | 2 +- cli/contracts.ts | 2 +- cli/cross-chain.ts | 63 ++++++++ cli/env.ts | 2 +- cli/network.ts | 26 +++ cli/utils.ts | 30 ---- config/graph.arbitrum-goerli.yml | 140 ++++++++++++++++ ...nkeby.yml => graph.arbitrum-localhost.yml} | 54 +++++-- config/graph.arbitrum-one.yml | 59 ++++--- config/graph.goerli.yml | 11 ++ config/graph.localhost.yml | 11 ++ e2e/deployment/config/controller.test.ts | 36 ++++- e2e/deployment/config/graphToken.test.ts | 2 +- e2e/deployment/config/l1/bridgeEscrow.test.ts | 17 ++ .../config/l1/l1GraphTokenGateway.test.ts | 83 ++++++++++ .../config/l1/rewardsManager.test.ts | 17 ++ e2e/deployment/config/l2/l2GraphToken.test.ts | 39 +++++ .../config/l2/l2GraphTokenGateway.test.ts | 61 +++++++ .../config/l2/rewardsManager.test.ts | 17 ++ e2e/deployment/config/protocol.test.ts | 11 -- e2e/deployment/config/rewardsManager.test.ts | 5 - e2e/deployment/init/graphToken.test.ts | 16 -- e2e/deployment/init/l1/graphToken.test.ts | 19 +++ e2e/deployment/init/l2/graphToken.test.ts | 17 ++ e2e/scenarios/lib/accounts.ts | 16 +- e2e/scenarios/lib/staking.ts | 4 +- hardhat.config.ts | 10 ++ package.json | 3 +- scripts/e2e | 62 ++++--- tasks/deployment/accounts.ts | 67 ++++++++ tasks/deployment/ownership.ts | 13 +- tasks/deployment/sync.ts | 71 ++++++++ tasks/deployment/unpause.ts | 18 ++- yarn.lock | 8 +- 38 files changed, 1005 insertions(+), 222 deletions(-) create mode 100644 cli/cross-chain.ts delete mode 100644 cli/utils.ts create mode 100644 config/graph.arbitrum-goerli.yml rename config/{graph.rinkeby.yml => graph.arbitrum-localhost.yml} (66%) create mode 100644 e2e/deployment/config/l1/bridgeEscrow.test.ts create mode 100644 e2e/deployment/config/l1/l1GraphTokenGateway.test.ts create mode 100644 e2e/deployment/config/l1/rewardsManager.test.ts create mode 100644 e2e/deployment/config/l2/l2GraphToken.test.ts create mode 100644 e2e/deployment/config/l2/l2GraphTokenGateway.test.ts create mode 100644 e2e/deployment/config/l2/rewardsManager.test.ts delete mode 100644 e2e/deployment/config/protocol.test.ts delete mode 100644 e2e/deployment/init/graphToken.test.ts create mode 100644 e2e/deployment/init/l1/graphToken.test.ts create mode 100644 e2e/deployment/init/l2/graphToken.test.ts create mode 100644 tasks/deployment/sync.ts diff --git a/TESTING.md b/TESTING.md index e9ac1b0e9..88b72a31c 100644 --- a/TESTING.md +++ b/TESTING.md @@ -82,4 +82,26 @@ Scenarios are defined by an optional script and a test file: - They run before the test file. - Test file - Should be named e2e/scenarios/{scenario-name}.test.ts. - - Standard chai/mocha/hardhat/ethers test file. \ No newline at end of file + - Standard chai/mocha/hardhat/ethers test file. + +## Setting up Arbitrum's testnodes + +Arbitrum provides a quick way of setting up L1 and L2 testnodes for local development and testing. The following steps will guide you through the process of setting them up. Note that a local installation of Docker and Docker Compose is required. + +```bash +git clone https://github.com/offchainlabs/nitro +cd nitro +git submodule update --init --recursive + +# Apply any changes you might want, see below for more info, and then start the testnodes +./test-node.bash --init +``` + +**Useful information** +- L1 RPC: [http://localhost:8545](http://localhost:8545/) +- L2 RPC: [http://localhost:8547](http://localhost:8547/) +- Blockscout explorer (L2 only): [http://localhost:4000/](http://localhost:4000/) +- Prefunded genesis key (L1 and L2): `e887f7d17d07cc7b8004053fb8826f6657084e88904bb61590e498ca04704cf2` + +**Enable automine on L1** +In `docker-compose.yml` file edit the `geth` service command by removing the `--dev.period 1` flag. \ No newline at end of file diff --git a/cli/commands/bridge/to-l1.ts b/cli/commands/bridge/to-l1.ts index f3673a53a..42f319eb4 100644 --- a/cli/commands/bridge/to-l1.ts +++ b/cli/commands/bridge/to-l1.ts @@ -2,18 +2,14 @@ import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' import { logger } from '../../logging' import { getAddressBook } from '../../address-book' import { getProvider, sendTransaction, toGRT } from '../../network' -import { chainIdIsL2 } from '../../utils' +import { chainIdIsL2 } from '../../cross-chain' import { loadAddressBookContract } from '../../contracts' -import { - L2TransactionReceipt, - getL2Network, - L2ToL1MessageStatus, - L2ToL1MessageWriter, -} from '@arbitrum/sdk' +import { L2TransactionReceipt, L2ToL1MessageStatus, L2ToL1MessageWriter } from '@arbitrum/sdk' import { L2GraphTokenGateway } from '../../../build/types/L2GraphTokenGateway' import { BigNumber } from 'ethers' import { JsonRpcProvider } from '@ethersproject/providers' import { providers } from 'ethers' +import { L2GraphToken } from '../../../build/types/L2GraphToken' const FOURTEEN_DAYS_IN_SECONDS = 24 * 3600 * 14 @@ -82,12 +78,17 @@ export const startSendToL1 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Prom const l2AddressBook = getAddressBook(cliArgs.addressBook, l2ChainId.toString()) const gateway = loadAddressBookContract('L2GraphTokenGateway', l2AddressBook, l2Wallet) - const l2GRT = loadAddressBookContract('L2GraphToken', l2AddressBook, l2Wallet) + const l2GRT = loadAddressBookContract('L2GraphToken', l2AddressBook, l2Wallet) as L2GraphToken const l1Gateway = cli.contracts['L1GraphTokenGateway'] logger.info(`Will send ${cliArgs.amount} GRT to ${recipient}`) logger.info(`Using L2 gateway ${gateway.address} and L1 gateway ${l1Gateway.address}`) + const senderBalance = await l2GRT.balanceOf(cli.wallet.address) + if (senderBalance.lt(amount)) { + throw new Error('Sender balance is insufficient for the transfer') + } + const params = [l1GRTAddress, recipient, amount, '0x'] logger.info('Approving token transfer') await sendTransaction(l2Wallet, l2GRT, 'approve', [gateway.address, amount]) @@ -99,9 +100,7 @@ export const startSendToL1 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Prom params, ) const l2Receipt = new L2TransactionReceipt(receipt) - const l2ToL1Message = ( - await l2Receipt.getL2ToL1Messages(cli.wallet, await getL2Network(l2Provider)) - )[0] + const l2ToL1Message = (await l2Receipt.getL2ToL1Messages(cli.wallet))[0] logger.info(`The transaction generated an L2 to L1 message in outbox with eth block number:`) logger.info(l2ToL1Message.event.ethBlockNum.toString()) @@ -157,9 +156,7 @@ export const finishSendToL1 = async ( const l2Receipt = new L2TransactionReceipt(receipt) logger.info(`Getting L2 to L1 message...`) - const l2ToL1Message = ( - await l2Receipt.getL2ToL1Messages(cli.wallet, await getL2Network(l2Provider)) - )[0] + const l2ToL1Message = (await l2Receipt.getL2ToL1Messages(cli.wallet))[0] if (wait) { const retryDelayMs = cliArgs.retryDelaySeconds ? cliArgs.retryDelaySeconds * 1000 : 60000 diff --git a/cli/commands/bridge/to-l2.ts b/cli/commands/bridge/to-l2.ts index f176bc482..307886e30 100644 --- a/cli/commands/bridge/to-l2.ts +++ b/cli/commands/bridge/to-l2.ts @@ -1,15 +1,11 @@ +import { Argv } from 'yargs' +import { utils } from 'ethers' +import { L1TransactionReceipt, L1ToL2MessageStatus, L1ToL2MessageWriter } from '@arbitrum/sdk' + import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' import { logger } from '../../logging' -import { getProvider, sendTransaction, toGRT } from '../../network' -import { utils } from 'ethers' -import { parseEther } from '@ethersproject/units' -import { - L1TransactionReceipt, - L1ToL2MessageStatus, - L1ToL2MessageWriter, - L1ToL2MessageGasEstimator, -} from '@arbitrum/sdk' -import { chainIdIsL2 } from '../../utils' +import { getProvider, sendTransaction, toGRT, ensureAllowance, toBN } from '../../network' +import { chainIdIsL2, estimateRetryableTxGas } from '../../cross-chain' const logAutoRedeemReason = (autoRedeemRec) => { if (autoRedeemRec == null) { @@ -32,7 +28,12 @@ const checkAndRedeemMessage = async (l1ToL2Message: L1ToL2MessageWriter) => { logAutoRedeemReason(autoRedeemRec) logger.info('Attempting to redeem...') await l1ToL2Message.redeem() - l2TxHash = (await l1ToL2Message.getSuccessfulRedeem()).transactionHash + const redeemAttempt = await l1ToL2Message.getSuccessfulRedeem() + if (redeemAttempt.status == L1ToL2MessageStatus.REDEEMED) { + l2TxHash = redeemAttempt.l2TxReceipt ? redeemAttempt.l2TxReceipt.transactionHash : 'null' + } else { + throw new Error(`Unexpected L1ToL2MessageStatus after redeem attempt: ${res.status}`) + } } else if (res.status != L1ToL2MessageStatus.REDEEMED) { throw new Error(`Unexpected L1ToL2MessageStatus ${res.status}`) } @@ -41,75 +42,78 @@ const checkAndRedeemMessage = async (l1ToL2Message: L1ToL2MessageWriter) => { export const sendToL2 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { logger.info(`>>> Sending tokens to L2 <<<\n`) + + // parse provider + const l1Provider = cli.wallet.provider const l2Provider = getProvider(cliArgs.l2ProviderUrl) + const l1ChainId = cli.chainId const l2ChainId = (await l2Provider.getNetwork()).chainId - - if (chainIdIsL2(cli.chainId) || !chainIdIsL2(l2ChainId)) { + if (chainIdIsL2(l1ChainId) || !chainIdIsL2(l2ChainId)) { throw new Error( 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', ) } - const gateway = cli.contracts['L1GraphTokenGateway'] - const l1GRT = cli.contracts['GraphToken'] - const l1GRTAddress = l1GRT.address + + // parse params + const { L1GraphTokenGateway: l1Gateway, GraphToken: l1GRT } = cli.contracts const amount = toGRT(cliArgs.amount) - const recipient = cliArgs.recipient ? cliArgs.recipient : cli.wallet.address - const l2Dest = await gateway.l2Counterpart() + const recipient = cliArgs.recipient ?? cli.wallet.address + const l1GatewayAddress = l1Gateway.address + const l2GatewayAddress = await l1Gateway.l2Counterpart() + const calldata = cliArgs.calldata ?? '0x' + // transport tokens logger.info(`Will send ${cliArgs.amount} GRT to ${recipient}`) - logger.info(`Using L1 gateway ${gateway.address} and L2 gateway ${l2Dest}`) + logger.info(`Using L1 gateway ${l1GatewayAddress} and L2 gateway ${l2GatewayAddress}`) + await ensureAllowance(cli.wallet, l1GatewayAddress, l1GRT, amount) + + // estimate L2 ticket // See https://github.com/OffchainLabs/arbitrum/blob/master/packages/arb-ts/src/lib/bridge.ts - const depositCalldata = await gateway.getOutboundCalldata( - l1GRTAddress, + const depositCalldata = await l1Gateway.getOutboundCalldata( + l1GRT.address, cli.wallet.address, recipient, amount, - '0x', + calldata, ) - - // Comment from Offchain Labs' implementation: - // we add a 0.05 ether "deposit" buffer to pay for execution in the gas estimation - logger.info('Estimating retryable ticket gas:') - const baseFee = (await cli.wallet.provider.getBlock('latest')).baseFeePerGas - const gasEstimator = new L1ToL2MessageGasEstimator(l2Provider) - const gasParams = await gasEstimator.estimateMessage( - gateway.address, - l2Dest, + const { maxGas, gasPriceBid, maxSubmissionCost } = await estimateRetryableTxGas( + l1Provider, + l2Provider, + l1GatewayAddress, + l2GatewayAddress, depositCalldata, - parseEther('0'), - baseFee, - gateway.address, - gateway.address, + { + maxGas: cliArgs.maxGas, + gasPriceBid: cliArgs.gasPriceBid, + maxSubmissionCost: cliArgs.maxSubmissionCost, + }, ) - const maxGas = gasParams.maxGasBid - const gasPriceBid = gasParams.maxGasPriceBid - const maxSubmissionPrice = gasParams.maxSubmissionPriceBid + const ethValue = maxSubmissionCost.add(gasPriceBid.mul(maxGas)) logger.info( - `Using max gas: ${maxGas}, gas price bid: ${gasPriceBid}, max submission price: ${maxSubmissionPrice}`, + `Using maxGas:${maxGas}, gasPriceBid:${gasPriceBid}, maxSubmissionCost:${maxSubmissionCost} = tx value: ${ethValue}`, ) - const ethValue = maxSubmissionPrice.add(gasPriceBid.mul(maxGas)) - logger.info(`tx value: ${ethValue}`) - const data = utils.defaultAbiCoder.encode(['uint256', 'bytes'], [maxSubmissionPrice, '0x']) - - const params = [l1GRTAddress, recipient, amount, maxGas, gasPriceBid, data] - logger.info('Approving token transfer') - await sendTransaction(cli.wallet, l1GRT, 'approve', [gateway.address, amount]) + // build transaction logger.info('Sending outbound transfer transaction') - const receipt = await sendTransaction(cli.wallet, gateway, 'outboundTransfer', params, { + const txData = utils.defaultAbiCoder.encode(['uint256', 'bytes'], [maxSubmissionCost, calldata]) + const txParams = [l1GRT.address, recipient, amount, maxGas, gasPriceBid, txData] + const txReceipt = await sendTransaction(cli.wallet, l1Gateway, 'outboundTransfer', txParams, { value: ethValue, }) - const l1Receipt = new L1TransactionReceipt(receipt) - const l1ToL2Message = await l1Receipt.getL1ToL2Message(cli.wallet.connect(l2Provider)) - - logger.info('Waiting for message to propagate to L2...') - try { - await checkAndRedeemMessage(l1ToL2Message) - } catch (e) { - logger.error('Auto redeem failed') - logger.error(e) - logger.error('You can re-attempt using redeem-send-to-l2 with the following txHash:') - logger.error(receipt.transactionHash) + // get l2 ticket status + if (txReceipt.status == 1) { + logger.info('Waiting for message to propagate to L2...') + const l1Receipt = new L1TransactionReceipt(txReceipt) + const l1ToL2Messages = await l1Receipt.getL1ToL2Messages(cli.wallet.connect(l2Provider)) + const l1ToL2Message = l1ToL2Messages[0] + try { + await checkAndRedeemMessage(l1ToL2Message) + } catch (e) { + logger.error('Auto redeem failed') + logger.error(e) + logger.error('You can re-attempt using redeem-send-to-l2 with the following txHash:') + logger.error(txReceipt.transactionHash) + } } } @@ -135,8 +139,38 @@ export const redeemSendToL2 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Pro } export const sendToL2Command = { - command: 'send-to-l2 [recipient]', + command: 'send-to-l2 [recipient] [calldata]', describe: 'Perform an L1-to-L2 Graph Token transaction', + builder: (yargs: Argv): Argv => { + return yargs + .option('max-gas', { + description: 'Max gas for the L2 redemption attempt', + requiresArg: true, + type: 'string', + }) + .option('gas-price-bid', { + description: 'Gas price for the L2 redemption attempt', + requiresArg: true, + type: 'string', + }) + .option('max-submission-cost', { + description: 'Max submission cost for the retryable ticket', + requiresArg: true, + type: 'string', + }) + .positional('amount', { description: 'Amount to send (will be converted to wei)' }) + .positional('recipient', { + description: 'Receiving address in L2. Same to L1 address if empty', + }) + .positional('calldata', { + description: 'Calldata to pass to the recipient. Must be whitelisted in the bridge', + }) + .coerce({ + maxGas: toBN, + gasPriceBid: toBN, + maxSubmissionCost: toBN, + }) + }, handler: async (argv: CLIArgs): Promise => { return sendToL2(await loadEnv(argv), argv) }, diff --git a/cli/commands/migrate.ts b/cli/commands/migrate.ts index 374f28b16..335b6fa32 100644 --- a/cli/commands/migrate.ts +++ b/cli/commands/migrate.ts @@ -11,8 +11,8 @@ import { sendTransaction, } from '../network' import { loadEnv, CLIArgs, CLIEnvironment } from '../env' +import { chainIdIsL2 } from '../cross-chain' import { confirm } from '../helpers' -import { chainIdIsL2 } from '../utils' const { EtherSymbol } = constants const { formatEther } = utils @@ -73,8 +73,8 @@ export const migrate = async ( if (!sure) return if (chainId == 1337) { - await (cli.wallet.provider as providers.JsonRpcProvider).send('evm_setAutomine', [true]) allContracts = ['EthereumDIDRegistry', ...allContracts] + await setAutoMine(cli.wallet.provider as providers.JsonRpcProvider, true) } else if (chainIdIsL2(chainId)) { allContracts = l2Contracts } @@ -169,7 +169,15 @@ export const migrate = async ( logger.info(`Sent ${nTx} transaction${nTx === 1 ? '' : 's'} & spent ${EtherSymbol} ${spent}`) if (chainId == 1337) { - await (cli.wallet.provider as providers.JsonRpcProvider).send('evm_setAutomine', [autoMine]) + await setAutoMine(cli.wallet.provider as providers.JsonRpcProvider, autoMine) + } +} + +const setAutoMine = async (provider: providers.JsonRpcProvider, automine: boolean) => { + try { + await provider.send('evm_setAutomine', [automine]) + } catch (error) { + logger.warn('The method evm_setAutomine does not exist/is not available!') } } diff --git a/cli/commands/protocol/configure-bridge.ts b/cli/commands/protocol/configure-bridge.ts index d96d462a5..057ebc653 100644 --- a/cli/commands/protocol/configure-bridge.ts +++ b/cli/commands/protocol/configure-bridge.ts @@ -2,7 +2,7 @@ import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' import { logger } from '../../logging' import { getAddressBook } from '../../address-book' import { sendTransaction } from '../../network' -import { chainIdIsL2, l1ToL2ChainIdMap, l2ToL1ChainIdMap } from '../../utils' +import { chainIdIsL2, l1ToL2ChainIdMap, l2ToL1ChainIdMap } from '../../cross-chain' export const configureL1Bridge = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { logger.info(`>>> Setting L1 Bridge Configuration <<<\n`) diff --git a/cli/contracts.ts b/cli/contracts.ts index 9af5c126d..b01727c20 100644 --- a/cli/contracts.ts +++ b/cli/contracts.ts @@ -1,6 +1,7 @@ import { BaseContract, providers, Signer } from 'ethers' import { AddressBook } from './address-book' +import { chainIdIsL2 } from './cross-chain' import { logger } from './logging' import { getContractAt } from './network' @@ -25,7 +26,6 @@ import { L1GraphTokenGateway } from '../build/types/L1GraphTokenGateway' import { L2GraphToken } from '../build/types/L2GraphToken' import { L2GraphTokenGateway } from '../build/types/L2GraphTokenGateway' import { BridgeEscrow } from '../build/types/BridgeEscrow' -import { chainIdIsL2 } from './utils' export interface NetworkContracts { EpochManager: EpochManager diff --git a/cli/cross-chain.ts b/cli/cross-chain.ts new file mode 100644 index 000000000..ea3b081ac --- /dev/null +++ b/cli/cross-chain.ts @@ -0,0 +1,63 @@ +import { L1ToL2MessageGasEstimator } from '@arbitrum/sdk' +import { BigNumber, providers } from 'ethers' +import { parseEther } from 'ethers/lib/utils' + +import { logger } from './logging' + +export const l1ToL2ChainIdMap = { + '1': '42161', + '4': '421611', + '5': '421613', + '1337': '412346', +} + +export const l2ChainIds = Object.values(l1ToL2ChainIdMap).map(Number) +export const l2ToL1ChainIdMap = Object.fromEntries( + Object.entries(l1ToL2ChainIdMap).map(([k, v]) => [v, k]), +) + +export const chainIdIsL2 = (chainId: number | string): boolean => { + return l2ChainIds.includes(Number(chainId)) +} + +interface L2GasParams { + maxGas: BigNumber + gasPriceBid: BigNumber + maxSubmissionCost: BigNumber +} + +export const estimateRetryableTxGas = async ( + l1Provider: providers.Provider, + l2Provider: providers.Provider, + gatewayAddress: string, + l2Dest: string, + depositCalldata: string, + opts: L2GasParams, +): Promise => { + const autoEstimate = opts && (!opts.maxGas || !opts.gasPriceBid || !opts.maxSubmissionCost) + if (!autoEstimate) { + return opts + } + + // Comment from Offchain Labs' implementation: + // we add a 0.05 ether "deposit" buffer to pay for execution in the gas estimation + logger.info('Estimating retryable ticket gas:') + const baseFee = (await l1Provider.getBlock('latest')).baseFeePerGas + const gasEstimator = new L1ToL2MessageGasEstimator(l2Provider) + const gasParams = await gasEstimator.estimateAll( + gatewayAddress, + l2Dest, + depositCalldata, + parseEther('0'), + baseFee as BigNumber, + gatewayAddress, + gatewayAddress, + l1Provider, + ) + // override fixed values + return { + maxGas: opts.maxGas ?? gasParams.gasLimit, + gasPriceBid: opts.gasPriceBid ?? gasParams.maxFeePerGas, + maxSubmissionCost: opts.maxSubmissionCost ?? gasParams.maxSubmissionFee, + } +} diff --git a/cli/env.ts b/cli/env.ts index ab89c9d85..d607e4be5 100644 --- a/cli/env.ts +++ b/cli/env.ts @@ -4,7 +4,7 @@ import { Argv } from 'yargs' import { logger } from './logging' import { getAddressBook, AddressBook } from './address-book' import { defaultOverrides } from './defaults' -import { getProvider } from './utils' +import { getProvider } from './network' import { loadContracts, NetworkContracts } from './contracts' const { formatEther } = utils diff --git a/cli/network.ts b/cli/network.ts index 3dc2e69c1..6667a960d 100644 --- a/cli/network.ts +++ b/cli/network.ts @@ -10,16 +10,19 @@ import { Overrides, BigNumber, PayableOverrides, + Wallet, } from 'ethers' import { logger } from './logging' import { AddressBook } from './address-book' import { loadArtifact } from './artifacts' import { defaultOverrides } from './defaults' +import { GraphToken } from '../build/types/GraphToken' const { keccak256, randomBytes, parseUnits, hexlify } = utils export const randomHexBytes = (n = 32): string => hexlify(randomBytes(n)) +export const toBN = (value: string | number | BigNumber): BigNumber => BigNumber.from(value) export const toGRT = (value: string | number): BigNumber => { return parseUnits(typeof value === 'number' ? value.toString() : value, '18') } @@ -368,3 +371,26 @@ export const linkLibraries = ( } return bytecode } + +export const ensureAllowance = async ( + sender: Wallet, + spenderAddress: string, + token: GraphToken, + amount: BigNumber, +) => { + // check balance + const senderBalance = await token.balanceOf(sender.address) + if (senderBalance.lt(amount)) { + throw new Error('Sender balance is insufficient for the transfer') + } + + // check allowance + const allowance = await token.allowance(sender.address, spenderAddress) + if (allowance.gte(amount)) { + return + } + + // approve + logger.info('Approving token transfer') + return sendTransaction(sender, token, 'approve', [spenderAddress, amount]) +} diff --git a/cli/utils.ts b/cli/utils.ts deleted file mode 100644 index 846c5ce9d..000000000 --- a/cli/utils.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { addCustomNetwork } from '@arbitrum/sdk' -import { Contract, Wallet, providers } from 'ethers' - -import { loadArtifact } from './artifacts' - -export const l1ToL2ChainIdMap = { - '1': '42161', - '4': '421611', - '5': '421613', -} - -export const l2ChainIds = Object.values(l1ToL2ChainIdMap).map(Number) -export const l2ToL1ChainIdMap = Object.fromEntries( - Object.entries(l1ToL2ChainIdMap).map(([k, v]) => [v, k]), -) - -export const contractAt = ( - contractName: string, - contractAddress: string, - wallet: Wallet, -): Contract => { - return new Contract(contractAddress, loadArtifact(contractName).abi, wallet.provider) -} - -export const getProvider = (providerUrl: string, network?: number): providers.JsonRpcProvider => - new providers.JsonRpcProvider(providerUrl, network) - -export const chainIdIsL2 = (chainId: number | string): boolean => { - return l2ChainIds.includes(Number(chainId)) -} diff --git a/config/graph.arbitrum-goerli.yml b/config/graph.arbitrum-goerli.yml new file mode 100644 index 000000000..73021f042 --- /dev/null +++ b/config/graph.arbitrum-goerli.yml @@ -0,0 +1,140 @@ +general: + arbitrator: &arbitrator "0x113DC95e796836b8F0Fa71eE7fB42f221740c3B0" # Arbitration Council (TODO: update) + governor: &governor "0x3e43EF77fAAd296F65eF172E8eF06F8231c9DeAd" # Graph Council (TODO: update) + authority: &authority "0x79fd74da4c906509862c8fe93e87a9602e370bc4" # Authority that signs payment vouchers (TODO: update) + availabilityOracle: &availabilityOracle "0x5d3B6F98F1cCdF873Df0173CDE7335874a396c4d" # Subgraph Availability Oracle (TODO: update) + pauseGuardian: &pauseGuardian "0x8290362Aba20D17c51995085369E001Bad99B21c" # Protocol pause guardian (TODO: update) + allocationExchangeOwner: &allocationExchangeOwner "0x74Db79268e63302d3FC69FB5a7627F7454a41732" # Allocation Exchange owner (TODO: update) + +contracts: + Controller: + calls: + - fn: "setContractProxy" + id: "0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f" # keccak256('Curation') + contractAddress: "${{Curation.address}}" + - fn: "setContractProxy" + id: "0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3" # keccak256('GNS') + contractAddress: "${{GNS.address}}" + - fn: "setContractProxy" + id: "0xf942813d07d17b56de9a9afc8de0ced6e8c053bbfdcc87b7badea4ddcf27c307" # keccak256('DisputeManager') + contractAddress: "${{DisputeManager.address}}" + - fn: "setContractProxy" + id: "0xc713c3df6d14cdf946460395d09af88993ee2b948b1a808161494e32c5f67063" # keccak256('EpochManager') + contractAddress: "${{EpochManager.address}}" + - fn: "setContractProxy" + id: "0x966f1e8d8d8014e05f6ec4a57138da9be1f7c5a7f802928a18072f7c53180761" # keccak256('RewardsManager') + contractAddress: "${{RewardsManager.address}}" + - fn: "setContractProxy" + id: "0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034" # keccak256('Staking') + contractAddress: "${{Staking.address}}" + - fn: "setContractProxy" + id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') + contractAddress: "${{L2GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L2GraphTokenGateway.address}}" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + - fn: "transferOwnership" + owner: *governor + GraphProxyAdmin: + calls: + - fn: "transferOwnership" + owner: *governor + ServiceRegistry: + proxy: true + init: + controller: "${{Controller.address}}" + EpochManager: + proxy: true + init: + controller: "${{Controller.address}}" + lengthInBlocks: 554 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) + L2GraphToken: + proxy: true + init: + governor: "${{Env.deployer}}" + calls: + - fn: "addMinter" + minter: "${{RewardsManager.address}}" + - fn: "renounceMinter" + - fn: "transferOwnership" + owner: *governor + Curation: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + curationTokenMaster: "${{GraphCurationToken.address}}" + reserveRatio: 500000 # in parts per million + curationTaxPercentage: 10000 # in parts per million + minimumCurationDeposit: "1000000000000000000" # in wei + DisputeManager: + proxy: true + init: + controller: "${{Controller.address}}" + arbitrator: *arbitrator + minimumDeposit: "10000000000000000000000" # in wei + fishermanRewardPercentage: 500000 # in parts per million + idxSlashingPercentage: 25000 # in parts per million + qrySlashingPercentage: 25000 # in parts per million + GNS: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + subgraphNFT: "${{SubgraphNFT.address}}" + calls: + - fn: "approveAll" + SubgraphNFT: + init: + governor: "${{Env.deployer}}" + calls: + - fn: "setTokenDescriptor" + tokenDescriptor: "${{SubgraphNFTDescriptor.address}}" + - fn: "setMinter" + minter: "${{GNS.address}}" + - fn: "transferOwnership" + owner: *governor + Staking: + proxy: true + init: + controller: "${{Controller.address}}" + minimumIndexerStake: "100000000000000000000000" # in wei + thawingPeriod: 6646 # in blocks + protocolPercentage: 10000 # in parts per million + curationPercentage: 100000 # in parts per million + channelDisputeEpochs: 2 # in epochs + maxAllocationEpochs: 4 # in epochs + delegationUnbondingPeriod: 12 # in epochs + delegationRatio: 16 # delegated stake to indexer stake multiplier + rebateAlphaNumerator: 77 # rebateAlphaNumerator / rebateAlphaDenominator + rebateAlphaDenominator: 100 # rebateAlphaNumerator / rebateAlphaDenominator + calls: + - fn: "setDelegationTaxPercentage" + delegationTaxPercentage: 5000 # parts per million + - fn: "setSlasher" + slasher: "${{DisputeManager.address}}" + allowed: true + - fn: "setAssetHolder" + assetHolder: "${{AllocationExchange.address}}" + allowed: true + RewardsManager: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "setSubgraphAvailabilityOracle" + subgraphAvailabilityOracle: *availabilityOracle + AllocationExchange: + init: + graphToken: "${{L2GraphToken.address}}" + staking: "${{Staking.address}}" + governor: *allocationExchangeOwner + authority: *authority + calls: + - fn: "approveAll" + L2GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" diff --git a/config/graph.rinkeby.yml b/config/graph.arbitrum-localhost.yml similarity index 66% rename from config/graph.rinkeby.yml rename to config/graph.arbitrum-localhost.yml index bd8ab6bf2..2e185a14a 100644 --- a/config/graph.rinkeby.yml +++ b/config/graph.arbitrum-localhost.yml @@ -1,7 +1,10 @@ general: - arbitrator: &arbitrator "0x87D11BD744b882b7bc5A6b5450cbA8C35D90eb10" # Arbitration Council - governor: &governor "0x1679A1D1caf1252BA43Fb8Fc17ebF914a0C725AE" # Graph Council - authority: &authority "0xe1EC4339019eC9628438F8755f847e3023e4ff9c" # Authority that signs payment vouchers + arbitrator: &arbitrator "0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0" # Arbitration Council + governor: &governor "0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b" # Graph Council + authority: &authority "0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d" # Authority that signs payment vouchers + availabilityOracle: &availabilityOracle "0xd03ea8624C8C5987235048901fB614fDcA89b117" # Subgraph Availability Oracle + pauseGuardian: &pauseGuardian "0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC" # Protocol pause guardian + allocationExchangeOwner: &allocationExchangeOwner "0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9" # Allocation Exchange owner contracts: Controller: @@ -26,7 +29,18 @@ contracts: contractAddress: "${{Staking.address}}" - fn: "setContractProxy" id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') - contractAddress: "${{GraphToken.address}}" + contractAddress: "${{L2GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L2GraphTokenGateway.address}}" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + - fn: "transferOwnership" + owner: *governor + GraphProxyAdmin: + calls: + - fn: "transferOwnership" + owner: *governor ServiceRegistry: proxy: true init: @@ -35,13 +49,17 @@ contracts: proxy: true init: controller: "${{Controller.address}}" - lengthInBlocks: 277 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) - GraphToken: + lengthInBlocks: 554 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) + L2GraphToken: + proxy: true init: - initialSupply: "10000000000000000000000000000" # in wei + governor: "${{Env.deployer}}" calls: - fn: "addMinter" minter: "${{RewardsManager.address}}" + - fn: "renounceMinter" + - fn: "transferOwnership" + owner: *governor Curation: proxy: true init: @@ -58,8 +76,8 @@ contracts: arbitrator: *arbitrator minimumDeposit: "10000000000000000000000" # in wei fishermanRewardPercentage: 500000 # in parts per million - idxSlashingPercentage: 20000 # in parts per million - qrySlashingPercentage: 5000 # in parts per million + idxSlashingPercentage: 25000 # in parts per million + qrySlashingPercentage: 25000 # in parts per million GNS: proxy: true init: @@ -76,6 +94,8 @@ contracts: tokenDescriptor: "${{SubgraphNFTDescriptor.address}}" - fn: "setMinter" minter: "${{GNS.address}}" + - fn: "transferOwnership" + owner: *governor Staking: proxy: true init: @@ -85,8 +105,8 @@ contracts: protocolPercentage: 10000 # in parts per million curationPercentage: 100000 # in parts per million channelDisputeEpochs: 2 # in epochs - maxAllocationEpochs: 2 # in epochs - delegationUnbondingPeriod: 6 # in epochs + maxAllocationEpochs: 4 # in epochs + delegationUnbondingPeriod: 12 # in epochs delegationRatio: 16 # delegated stake to indexer stake multiplier rebateAlphaNumerator: 77 # rebateAlphaNumerator / rebateAlphaDenominator rebateAlphaDenominator: 100 # rebateAlphaNumerator / rebateAlphaDenominator @@ -103,12 +123,18 @@ contracts: proxy: true init: controller: "${{Controller.address}}" - issuanceRate: "1000000012184945188" # per block increase of total supply, blocks in a year = 365*60*60*24/13 + calls: + - fn: "setSubgraphAvailabilityOracle" + subgraphAvailabilityOracle: *availabilityOracle AllocationExchange: init: - graphToken: "${{GraphToken.address}}" + graphToken: "${{L2GraphToken.address}}" staking: "${{Staking.address}}" - governor: *governor + governor: *allocationExchangeOwner authority: *authority calls: - fn: "approveAll" + L2GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml index 53117372b..8fb876d8f 100644 --- a/config/graph.arbitrum-one.yml +++ b/config/graph.arbitrum-one.yml @@ -2,6 +2,9 @@ general: arbitrator: &arbitrator "0x113DC95e796836b8F0Fa71eE7fB42f221740c3B0" # Arbitration Council governor: &governor "0x3e43EF77fAAd296F65eF172E8eF06F8231c9DeAd" # Graph Council authority: &authority "0x79fd74da4c906509862c8fe93e87a9602e370bc4" # Authority that signs payment vouchers + availabilityOracle: &availabilityOracle "0x5d3B6F98F1cCdF873Df0173CDE7335874a396c4d" # Subgraph Availability Oracle (TODO: update) + pauseGuardian: &pauseGuardian "0x8290362Aba20D17c51995085369E001Bad99B21c" # Protocol pause guardian (TODO: update) + allocationExchangeOwner: &allocationExchangeOwner "0x74Db79268e63302d3FC69FB5a7627F7454a41732" # Allocation Exchange owner (TODO: update) contracts: Controller: @@ -30,6 +33,14 @@ contracts: - fn: "setContractProxy" id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') contractAddress: "${{L2GraphTokenGateway.address}}" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + - fn: "transferOwnership" + owner: *governor + GraphProxyAdmin: + calls: + - fn: "transferOwnership" + owner: *governor ServiceRegistry: proxy: true init: @@ -38,32 +49,35 @@ contracts: proxy: true init: controller: "${{Controller.address}}" - lengthInBlocks: 1108 # 4 hours (in 13 second blocks) + lengthInBlocks: 6646 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) L2GraphToken: proxy: true init: - owner: *governor + governor: "${{Env.deployer}}" calls: - fn: "addMinter" minter: "${{RewardsManager.address}}" + - fn: "renounceMinter" + - fn: "transferOwnership" + owner: *governor Curation: proxy: true init: controller: "${{Controller.address}}" bondingCurve: "${{BancorFormula.address}}" curationTokenMaster: "${{GraphCurationToken.address}}" - reserveRatio: 500000 # 50% (parts per million) - curationTaxPercentage: 10000 # 1% (parts per million) - minimumCurationDeposit: "1000000000000000000" # 1 GRT + reserveRatio: 500000 # in parts per million + curationTaxPercentage: 10000 # in parts per million + minimumCurationDeposit: "1000000000000000000" # in wei DisputeManager: proxy: true init: controller: "${{Controller.address}}" arbitrator: *arbitrator - minimumDeposit: "10000000000000000000000" # 10,000 GRT (in wei) - fishermanRewardPercentage: 500000 # 50% (parts per million) - idxSlashingPercentage: 25000 # 2.5% (parts per million) - qrySlashingPercentage: 5000 # 0.5% (parts per million) + minimumDeposit: "10000000000000000000000" # in wei + fishermanRewardPercentage: 500000 # in parts per million + idxSlashingPercentage: 25000 # in parts per million + qrySlashingPercentage: 25000 # in parts per million GNS: proxy: true init: @@ -80,23 +94,25 @@ contracts: tokenDescriptor: "${{SubgraphNFTDescriptor.address}}" - fn: "setMinter" minter: "${{GNS.address}}" + - fn: "transferOwnership" + owner: *governor Staking: proxy: true init: controller: "${{Controller.address}}" - minimumIndexerStake: "100000000000000000000000" # 100,000 GRT (in wei) - thawingPeriod: 6646 # 10 days (in blocks) - protocolPercentage: 10000 # 1% (parts per million) - curationPercentage: 100000 # 10% (parts per million) - channelDisputeEpochs: 2 # (in epochs) - maxAllocationEpochs: 6 # Based on epoch length this is 28 days (in epochs) - delegationUnbondingPeriod: 6 # Based on epoch length this is 28 days (in epochs) - delegationRatio: 16 # 16x (delegated stake to indexer stake multiplier) + minimumIndexerStake: "100000000000000000000000" # in wei + thawingPeriod: 186092 # in blocks + protocolPercentage: 10000 # in parts per million + curationPercentage: 100000 # in parts per million + channelDisputeEpochs: 7 # in epochs + maxAllocationEpochs: 28 # in epochs + delegationUnbondingPeriod: 28 # in epochs + delegationRatio: 16 # delegated stake to indexer stake multiplier rebateAlphaNumerator: 77 # rebateAlphaNumerator / rebateAlphaDenominator rebateAlphaDenominator: 100 # rebateAlphaNumerator / rebateAlphaDenominator calls: - fn: "setDelegationTaxPercentage" - delegationTaxPercentage: 5000 # 0.5% (parts per million) + delegationTaxPercentage: 5000 # parts per million - fn: "setSlasher" slasher: "${{DisputeManager.address}}" allowed: true @@ -107,11 +123,14 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "setSubgraphAvailabilityOracle" + subgraphAvailabilityOracle: *availabilityOracle AllocationExchange: init: - graphToken: "${{GraphToken.address}}" + graphToken: "${{L2GraphToken.address}}" staking: "${{Staking.address}}" - governor: *governor + governor: *allocationExchangeOwner authority: *authority calls: - fn: "approveAll" diff --git a/config/graph.goerli.yml b/config/graph.goerli.yml index b79890149..d24e3c23d 100644 --- a/config/graph.goerli.yml +++ b/config/graph.goerli.yml @@ -30,6 +30,9 @@ contracts: - fn: "setContractProxy" id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') contractAddress: "${{GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L1GraphTokenGateway.address}}" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian - fn: "transferOwnership" @@ -132,3 +135,11 @@ contracts: authority: *authority calls: - fn: "approveAll" + L1GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + BridgeEscrow: + proxy: true + init: + controller: "${{Controller.address}}" \ No newline at end of file diff --git a/config/graph.localhost.yml b/config/graph.localhost.yml index bb039f405..d6268c269 100644 --- a/config/graph.localhost.yml +++ b/config/graph.localhost.yml @@ -30,6 +30,9 @@ contracts: - fn: "setContractProxy" id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') contractAddress: "${{GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L1GraphTokenGateway.address}}" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian - fn: "transferOwnership" @@ -132,3 +135,11 @@ contracts: authority: *authority calls: - fn: "approveAll" + L1GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + BridgeEscrow: + proxy: true + init: + controller: "${{Controller.address}}" diff --git a/e2e/deployment/config/controller.test.ts b/e2e/deployment/config/controller.test.ts index 9bde5d85e..8fbdf4834 100644 --- a/e2e/deployment/config/controller.test.ts +++ b/e2e/deployment/config/controller.test.ts @@ -1,12 +1,13 @@ import { expect } from 'chai' import hre, { ethers } from 'hardhat' import { NamedAccounts } from '../../../gre/type-extensions' +import GraphChain from '../../../gre/helpers/network' describe('Controller configuration', () => { - const { contracts, getNamedAccounts } = hre.graph() - const { Controller } = contracts + const graph = hre.graph() + const { Controller } = graph.contracts - const proxyContracts = [ + const l1ProxyContracts = [ 'Curation', 'GNS', 'DisputeManager', @@ -14,21 +15,41 @@ describe('Controller configuration', () => { 'RewardsManager', 'Staking', 'GraphToken', + 'L1GraphTokenGateway', + ] + + const l2ProxyContracts = [ + 'Curation', + 'GNS', + 'DisputeManager', + 'EpochManager', + 'RewardsManager', + 'Staking', + 'L2GraphToken', + 'L2GraphTokenGateway', ] let namedAccounts: NamedAccounts before(async () => { - namedAccounts = await getNamedAccounts() + namedAccounts = await graph.getNamedAccounts() }) const proxyShouldMatchDeployed = async (contractName: string) => { - const curationAddress = await Controller.getContractProxy( - ethers.utils.solidityKeccak256(['string'], [contractName]), + // remove L1/L2 prefix, contracts are not registered as L1/L2 on controller + const name = contractName.replace(/(^L1|L2)/gi, '') + + const address = await Controller.getContractProxy( + ethers.utils.solidityKeccak256(['string'], [name]), ) - expect(curationAddress).eq(contracts[contractName].address) + expect(address).eq(graph.contracts[contractName].address) } + it('protocol should be unpaused', async function () { + const paused = await Controller.paused() + expect(paused).eq(false) + }) + it('should be owned by governor', async function () { const owner = await Controller.governor() expect(owner).eq(namedAccounts.governor.address) @@ -40,6 +61,7 @@ describe('Controller configuration', () => { }) describe('proxy contract', async function () { + const proxyContracts = GraphChain.isL1(graph.chainId) ? l1ProxyContracts : l2ProxyContracts for (const contract of proxyContracts) { it(`${contract} should match deployed`, async function () { await proxyShouldMatchDeployed(contract) diff --git a/e2e/deployment/config/graphToken.test.ts b/e2e/deployment/config/graphToken.test.ts index cc5e60618..8b84208f3 100644 --- a/e2e/deployment/config/graphToken.test.ts +++ b/e2e/deployment/config/graphToken.test.ts @@ -23,7 +23,7 @@ describe('GraphToken configuration', () => { it('deployer should not be minter', async function () { const deployer = await getDeployer() const deployerIsMinter = await GraphToken.isMinter(deployer.address) - hre.network.config.chainId === 1337 ? this.skip() : expect(deployerIsMinter).eq(false) + expect(deployerIsMinter).eq(false) }) it('RewardsManager should be minter', async function () { diff --git a/e2e/deployment/config/l1/bridgeEscrow.test.ts b/e2e/deployment/config/l1/bridgeEscrow.test.ts new file mode 100644 index 000000000..2304a2063 --- /dev/null +++ b/e2e/deployment/config/l1/bridgeEscrow.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' + +describe('[L1] BridgeEscrow configuration', function () { + const graph = hre.graph() + const { Controller, BridgeEscrow } = graph.contracts + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + }) + + it('should be controlled by Controller', async function () { + const controller = await BridgeEscrow.controller() + expect(controller).eq(Controller.address) + }) +}) diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts new file mode 100644 index 000000000..d2424d0ce --- /dev/null +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -0,0 +1,83 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' + +describe('[L1] L1GraphTokenGateway configuration', function () { + const graph = hre.graph() + const { Controller, L1GraphTokenGateway } = graph.contracts + + let unauthorized: SignerWithAddress + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + unauthorized = (await graph.getTestAccounts())[0] + }) + + it('bridge should be unpaused', async function () { + const paused = await L1GraphTokenGateway.paused() + expect(paused).eq(false) + }) + + it('should be controlled by Controller', async function () { + const controller = await L1GraphTokenGateway.controller() + expect(controller).eq(Controller.address) + }) + + describe('calls with unauthorized user', () => { + it('initialize should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).initialize(unauthorized.address) + await expect(tx).revertedWith('Caller must be the implementation') + }) + + it('setArbitrumAddresses should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).setArbitrumAddresses( + unauthorized.address, + unauthorized.address, + ) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('setL2TokenAddress should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).setL2TokenAddress(unauthorized.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('setL2CounterpartAddress should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).setL2CounterpartAddress( + unauthorized.address, + ) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('setEscrowAddress should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).setEscrowAddress(unauthorized.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('addToCallhookWhitelist should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).addToCallhookWhitelist( + unauthorized.address, + ) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('removeFromCallhookWhitelist should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).removeFromCallhookWhitelist( + unauthorized.address, + ) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('finalizeInboundTransfer should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).finalizeInboundTransfer( + unauthorized.address, + unauthorized.address, + unauthorized.address, + '100', + '0x00', + ) + + await expect(tx).revertedWith('INBOX_NOT_SET') + }) + }) +}) diff --git a/e2e/deployment/config/l1/rewardsManager.test.ts b/e2e/deployment/config/l1/rewardsManager.test.ts new file mode 100644 index 000000000..4cc990161 --- /dev/null +++ b/e2e/deployment/config/l1/rewardsManager.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' + +describe('[L1] RewardsManager configuration', () => { + const graph = hre.graph() + const { RewardsManager } = graph.contracts + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + }) + + it('issuanceRate should match "issuanceRate" in the config file', async function () { + const value = await RewardsManager.issuanceRate() + expect(value).eq('1000000012184945188') // hardcoded as it's set with a function call rather than init parameter + }) +}) diff --git a/e2e/deployment/config/l2/l2GraphToken.test.ts b/e2e/deployment/config/l2/l2GraphToken.test.ts new file mode 100644 index 000000000..0d9f4b025 --- /dev/null +++ b/e2e/deployment/config/l2/l2GraphToken.test.ts @@ -0,0 +1,39 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' + +describe('[L2] L2GraphToken', () => { + const graph = hre.graph() + const { L2GraphToken } = graph.contracts + + let unauthorized: SignerWithAddress + + before(async function () { + if (GraphChain.isL1(graph.chainId)) this.skip() + unauthorized = (await graph.getTestAccounts())[0] + }) + + describe('calls with unauthorized user', () => { + it('mint should revert', async function () { + const tx = L2GraphToken.connect(unauthorized).mint( + unauthorized.address, + '1000000000000000000000', + ) + await expect(tx).revertedWith('Only minter can call') + }) + + it('bridgeMint should revert', async function () { + const tx = L2GraphToken.connect(unauthorized).bridgeMint( + unauthorized.address, + '1000000000000000000000', + ) + await expect(tx).revertedWith('NOT_GATEWAY') + }) + + it('setGateway should revert', async function () { + const tx = L2GraphToken.connect(unauthorized).setGateway(unauthorized.address) + await expect(tx).revertedWith('Only Governor can call') + }) + }) +}) diff --git a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts new file mode 100644 index 000000000..04732498b --- /dev/null +++ b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts @@ -0,0 +1,61 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' + +describe('[L2] L2GraphTokenGateway configuration', function () { + const graph = hre.graph() + const { Controller, L2GraphTokenGateway } = graph.contracts + + let unauthorized: SignerWithAddress + before(async function () { + if (GraphChain.isL1(graph.chainId)) this.skip() + unauthorized = (await graph.getTestAccounts())[0] + }) + + it('bridge should be unpaused', async function () { + const paused = await L2GraphTokenGateway.paused() + expect(paused).eq(false) + }) + + it('should be controlled by Controller', async function () { + const controller = await L2GraphTokenGateway.controller() + expect(controller).eq(Controller.address) + }) + + describe('calls with unauthorized user', () => { + it('initialize should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).initialize(unauthorized.address) + await expect(tx).revertedWith('Caller must be the implementation') + }) + + it('setL2Router should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).setL2Router(unauthorized.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('setL1TokenAddress should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).setL1TokenAddress(unauthorized.address) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('setL1CounterpartAddress should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).setL1CounterpartAddress( + unauthorized.address, + ) + await expect(tx).revertedWith('Caller must be Controller governor') + }) + + it('finalizeInboundTransfer should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).finalizeInboundTransfer( + unauthorized.address, + unauthorized.address, + unauthorized.address, + '1000000000000', + '0x00', + ) + + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + }) + }) +}) diff --git a/e2e/deployment/config/l2/rewardsManager.test.ts b/e2e/deployment/config/l2/rewardsManager.test.ts new file mode 100644 index 000000000..29ad83c5f --- /dev/null +++ b/e2e/deployment/config/l2/rewardsManager.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' + +describe('[L2] RewardsManager configuration', () => { + const graph = hre.graph() + const { RewardsManager } = graph.contracts + + before(async function () { + if (GraphChain.isL1(graph.chainId)) this.skip() + }) + + it('issuanceRate should be zero', async function () { + const value = await RewardsManager.issuanceRate() + expect(value).eq('0') + }) +}) diff --git a/e2e/deployment/config/protocol.test.ts b/e2e/deployment/config/protocol.test.ts deleted file mode 100644 index 4e12f5088..000000000 --- a/e2e/deployment/config/protocol.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { expect } from 'chai' -import hre from 'hardhat' - -describe('Protocol configuration', () => { - const { contracts } = hre.graph() - - it('should be unpaused', async function () { - const paused = await contracts.Controller.paused() - expect(paused).eq(false) - }) -}) diff --git a/e2e/deployment/config/rewardsManager.test.ts b/e2e/deployment/config/rewardsManager.test.ts index 475abd9f4..ddbcbb835 100644 --- a/e2e/deployment/config/rewardsManager.test.ts +++ b/e2e/deployment/config/rewardsManager.test.ts @@ -19,11 +19,6 @@ describe('RewardsManager configuration', () => { expect(controller).eq(Controller.address) }) - it('issuanceRate should match "issuanceRate" in the config file', async function () { - const value = await RewardsManager.issuanceRate() - expect(value).eq('1000000012184945188') // hardcoded as it's set with a function call rather than init parameter - }) - it('should allow subgraph availability oracle to deny rewards', async function () { const availabilityOracle = await RewardsManager.subgraphAvailabilityOracle() expect(availabilityOracle).eq(namedAccounts.availabilityOracle.address) diff --git a/e2e/deployment/init/graphToken.test.ts b/e2e/deployment/init/graphToken.test.ts deleted file mode 100644 index 8c2230adb..000000000 --- a/e2e/deployment/init/graphToken.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { expect } from 'chai' -import hre from 'hardhat' -import { getItemValue } from '../../../cli/config' - -describe('GraphToken initialization', () => { - const { - graphConfig, - contracts: { GraphToken }, - } = hre.graph() - - it('total supply should match "initialSupply" on the config file', async function () { - const value = await GraphToken.totalSupply() - const expected = getItemValue(graphConfig, 'contracts/GraphToken/init/initialSupply') - hre.network.config.chainId === 1337 ? expect(value).eq(expected) : expect(value).gte(expected) - }) -}) diff --git a/e2e/deployment/init/l1/graphToken.test.ts b/e2e/deployment/init/l1/graphToken.test.ts new file mode 100644 index 000000000..aa0cc04e3 --- /dev/null +++ b/e2e/deployment/init/l1/graphToken.test.ts @@ -0,0 +1,19 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import { getItemValue } from '../../../../cli/config' +import GraphChain from '../../../../gre/helpers/network' + +describe('[L1] GraphToken initialization', () => { + const graph = hre.graph() + const { GraphToken } = graph.contracts + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + }) + + it('total supply should match "initialSupply" on the config file', async function () { + const value = await GraphToken.totalSupply() + const expected = getItemValue(graph.graphConfig, 'contracts/GraphToken/init/initialSupply') + expect(value).eq(expected) + }) +}) diff --git a/e2e/deployment/init/l2/graphToken.test.ts b/e2e/deployment/init/l2/graphToken.test.ts new file mode 100644 index 000000000..90b232531 --- /dev/null +++ b/e2e/deployment/init/l2/graphToken.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' + +describe('[L2] GraphToken initialization', () => { + const graph = hre.graph() + const { GraphToken } = graph.contracts + + before(async function () { + if (GraphChain.isL1(graph.chainId)) this.skip() + }) + + it('total supply should be zero', async function () { + const value = await GraphToken.totalSupply() + expect(value).eq(0) + }) +}) diff --git a/e2e/scenarios/lib/accounts.ts b/e2e/scenarios/lib/accounts.ts index 6796a7aea..45c1a0e5b 100644 --- a/e2e/scenarios/lib/accounts.ts +++ b/e2e/scenarios/lib/accounts.ts @@ -12,7 +12,9 @@ const checkBalance = async ( const balance = await getBalanceFn(address) if (balance.lt(amount)) { throw new Error( - `Sender does not have enough funds to distribute! Required ${amount} - Balance ${balance}`, + `Sender does not have enough funds to distribute! Required ${amount} - Balance ${ethers.utils.formatEther( + balance, + )}`, ) } } @@ -20,6 +22,7 @@ const checkBalance = async ( const ensureBalance = async ( beneficiary: string, amount: BigNumberish, + symbol: string, getBalanceFn: (address: string) => Promise, transferFn: ( address: string, @@ -30,7 +33,7 @@ const ensureBalance = async ( const balanceDif = BigNumber.from(amount).sub(balance) if (balanceDif.gt(0)) { - console.log(`Funding ${beneficiary} with ${balanceDif}...`) + console.log(`Funding ${beneficiary} with ${ethers.utils.formatEther(balanceDif)} ${symbol}...`) const tx = await transferFn(beneficiary, balanceDif) await tx.wait() } @@ -48,6 +51,7 @@ export const ensureETHBalance = async ( await ensureBalance( beneficiaries[index], amounts[index], + 'ETH', ethers.provider.getBalance, (address: string, amount: BigNumber) => { return sender.sendTransaction({ to: address, value: amount }) @@ -66,9 +70,12 @@ export const ensureGRTAllowance = async ( const allowTokens = BigNumber.from(amount).sub(allowance) if (allowTokens.gt(0)) { console.log( - `\nApproving ${spender} to spend ${allowTokens} tokens on ${owner.address} behalf...`, + `\nApproving ${spender} to spend ${ethers.utils.formatEther(allowTokens)} GRT on ${ + owner.address + } behalf...`, ) - await grt.connect(owner).approve(spender, amount) + const tx = await grt.connect(owner).approve(spender, amount) + await tx.wait() } } @@ -112,6 +119,7 @@ export const fundAccountsGRT = async ( await ensureBalance( beneficiaries[index], amounts[index], + 'GRT', grt.balanceOf, grt.connect(sender).transfer, ) diff --git a/e2e/scenarios/lib/staking.ts b/e2e/scenarios/lib/staking.ts index 234d6b1ce..d221afccd 100644 --- a/e2e/scenarios/lib/staking.ts +++ b/e2e/scenarios/lib/staking.ts @@ -11,9 +11,11 @@ export const stake = async ( ): Promise => { // Approve await ensureGRTAllowance(indexer, contracts.Staking.address, amount, contracts.GraphToken) + const allowance = await contracts.GraphToken.allowance(indexer.address, contracts.Staking.address) + console.log(`Allowance: ${ethers.utils.formatEther(allowance)}`) // Stake - console.log(`\nStaking ${amount} tokens...`) + console.log(`\nStaking ${ethers.utils.formatEther(amount)} tokens...`) await sendTransaction(indexer, contracts.Staking, 'stake', [amount]) } diff --git a/hardhat.config.ts b/hardhat.config.ts index 3502efd6c..aa89f8715 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -152,6 +152,16 @@ const config: HardhatUserConfig = { accounts: process.env.FORK === 'true' ? getAccountsKeys() : { mnemonic: DEFAULT_TEST_MNEMONIC }, }, + localnitrol1: { + chainId: 1337, + url: 'http://localhost:8545', + accounts: { mnemonic: DEFAULT_TEST_MNEMONIC }, + }, + localnitrol2: { + chainId: 412346, + url: 'http://localhost:8547', + accounts: { mnemonic: DEFAULT_TEST_MNEMONIC }, + }, }, graph: { addressBook: process.env.ADDRESS_BOOK ?? 'addresses.json', diff --git a/package.json b/package.json index 80ca7fa55..63fab4bd3 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "ethers": "^5.6.0" }, "devDependencies": { - "@arbitrum/sdk": "^3.0.0-beta.5", + "@arbitrum/sdk": "^3.0.0-beta.6", "@commitlint/cli": "^13.2.1", "@commitlint/config-conventional": "^13.2.0", "@defi-wonderland/smock": "^2.0.7", @@ -91,6 +91,7 @@ "deploy-localhost": "yarn deploy --force --network localhost --graph-config config/graph.localhost.yml", "deploy-rinkeby": "yarn deploy --force --network rinkeby --graph-config config/graph.rinkeby.yml", "deploy-goerli": "yarn deploy --force --network goerli --graph-config config/graph.goerli.yml", + "deploy-arbitrum-goerli": "yarn deploy --force --network arbitrum-goerli --graph-config config/graph.arbitrum-goerli.yml", "predeploy": "scripts/predeploy", "test": "scripts/test", "test:e2e": "scripts/e2e", diff --git a/scripts/e2e b/scripts/e2e index 049579d9c..3690cd6ac 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -4,44 +4,70 @@ set -eo pipefail source $(pwd)/scripts/evm # Allow overriding config -GRAPH_CONFIG=${1:-"config/graph.localhost.yml"} -ADDRESS_BOOK=${2:-"addresses.json"} +GRAPH_CONFIG=${GRAPH_CONFIG:-"config/graph.localhost.yml"} +ADDRESS_BOOK=${ADDRESS_BOOK:-"addresses.json"} +NETWORK=${NETWORK:-"localhost"} + +echo "Running e2e tests" +echo "- Using config: $GRAPH_CONFIG" +echo "- Using address book: $ADDRESS_BOOK" +echo "- Using network: $NETWORK" ### Setup # Compile contracts yarn build # Start evm -evm_kill -evm_start -sleep 5 +if [[ "$NETWORK" == "localhost" ]]; then + evm_kill + evm_start + sleep 5 +fi + +# Create address book if needed +if [[ ! -f "$ADDRESS_BOOK" ]]; then + echo '{}' > "$ADDRESS_BOOK" +fi # Pre-deploy actions -npx hardhat migrate:accounts --network localhost --graph-config "$GRAPH_CONFIG" +npx hardhat migrate:accounts --network "$NETWORK" --graph-config "$GRAPH_CONFIG" +if [[ "$NETWORK" == *"localnitro"* ]]; then + npx hardhat migrate:accounts:nitro --network "$NETWORK" --graph-config "$GRAPH_CONFIG" +fi # Deploy protocol npx hardhat migrate \ - --network localhost \ + --network "$NETWORK" \ --skip-confirmation \ --auto-mine \ --graph-config "$GRAPH_CONFIG" \ --address-book "$ADDRESS_BOOK" # Post deploy actions -npx hardhat migrate:ownership --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" -npx hardhat migrate:unpause --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +npx hardhat migrate:ownership --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +npx hardhat migrate:unpause --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" ### Test -# Run tests using the localhost evm instance and the localhost graph config -npx hardhat e2e --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" -npx hardhat e2e:scenario create-subgraphs --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" -npx hardhat e2e:scenario open-allocations --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" -npx hardhat e2e:scenario close-allocations --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +# Run tests +npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + +# Skip GRT scenarios in L2 as we don't have bridged GRT yet +if [[ "$NETWORK" != "localnitrol2" ]]; then + npx hardhat e2e:scenario create-subgraphs --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + npx hardhat e2e:scenario open-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +fi + +# skip close-allocations for arbitrum testnodes as we can't advance epoch +if [[ "$NETWORK" != *"localnitro"* ]]; then + npx hardhat e2e:scenario close-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +fi ### Cleanup # Exit error mode so the evm instance always gets killed -set +e -result=0 +if [[ "$NETWORK" == "localhost" ]]; then + set +e + result=0 -evm_kill -exit $result + evm_kill + exit $result +fi diff --git a/tasks/deployment/accounts.ts b/tasks/deployment/accounts.ts index e5be935ea..c09ff2a2f 100644 --- a/tasks/deployment/accounts.ts +++ b/tasks/deployment/accounts.ts @@ -2,6 +2,7 @@ import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' import { updateItemValue, writeConfig } from '../../cli/config' +import { BigNumber, ContractTransaction } from 'ethers' task('migrate:accounts', 'Creates protocol accounts and saves them in graph config') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) @@ -38,3 +39,69 @@ task('migrate:accounts', 'Creates protocol accounts and saves them in graph conf writeConfig(taskArgs.graphConfig, graphConfig.toString()) }) + +task('migrate:accounts:nitro', 'Funds protocol accounts on Arbitrum Nitro testnodes') + .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('privateKey', 'The private key for Arbitrum testnode genesis account') + .addOptionalParam('amount', 'The amount to fund each account with') + .setAction(async (taskArgs, hre) => { + // Arbitrum Nitro testnodes have a pre-funded genesis account whose private key is hardcoded here: + // - L1 > https://github.com/OffchainLabs/nitro/blob/01c558c06ad9cbaa083bebe3e51960e195c3fd6b/test-node.bash#L136 + // - L2 > https://github.com/OffchainLabs/nitro/blob/01c558c06ad9cbaa083bebe3e51960e195c3fd6b/testnode-scripts/config.ts#L22 + const genesisAccountPrivateKey = + taskArgs.privateKey ?? 'e887f7d17d07cc7b8004053fb8826f6657084e88904bb61590e498ca04704cf2' + const genesisAccount = new hre.ethers.Wallet(genesisAccountPrivateKey) + + // Get protocol accounts + const { getDeployer, getNamedAccounts, getTestAccounts, provider } = hre.graph(taskArgs) + const deployer = await getDeployer() + const testAccounts = await getTestAccounts() + const namedAccounts = await getNamedAccounts() + const accounts = [ + deployer, + ...testAccounts, + ...Object.keys(namedAccounts).map((k) => namedAccounts[k]), + ] + + // Amount to fund + // - If amount is specified, use that + // - Otherwise, use 95% of genesis account balance with a maximum of 100 Eth + let amount: BigNumber + const maxAmount = hre.ethers.utils.parseEther('100') + const genesisAccountBalance = await provider.getBalance(genesisAccount.address) + + if (taskArgs.amount) { + amount = hre.ethers.BigNumber.from(taskArgs.amount) + } else { + const splitGenesisBalance = genesisAccountBalance.mul(95).div(100).div(accounts.length) + if (splitGenesisBalance.gt(maxAmount)) { + amount = maxAmount + } else { + amount = splitGenesisBalance + } + } + + // Check genesis account balance + const requiredFunds = amount.mul(accounts.length) + if (genesisAccountBalance.lt(requiredFunds)) { + throw new Error('Insufficient funds in genesis account') + } + + // Fund accounts + console.log('> Funding protocol addresses') + console.log(`Genesis account: ${genesisAccount.address}`) + console.log(`Total accounts: ${accounts.length}`) + console.log(`Amount per account: ${hre.ethers.utils.formatEther(amount)}`) + console.log(`Required funds: ${hre.ethers.utils.formatEther(requiredFunds)}`) + + const txs: ContractTransaction[] = [] + for (const account of accounts) { + const tx = await genesisAccount.connect(provider).sendTransaction({ + value: amount, + to: account.address, + }) + txs.push(tx) + } + await Promise.all(txs.map((tx) => tx.wait())) + console.log('Done!') + }) diff --git a/tasks/deployment/ownership.ts b/tasks/deployment/ownership.ts index 68f5561b0..86591c974 100644 --- a/tasks/deployment/ownership.ts +++ b/tasks/deployment/ownership.ts @@ -6,17 +6,18 @@ task('migrate:ownership', 'Accepts ownership of protocol contracts on behalf of .addOptionalParam('addressBook', cliOpts.addressBook.description) .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .setAction(async (taskArgs, hre) => { - const { contracts, getNamedAccounts } = hre.graph(taskArgs) - const { governor } = await getNamedAccounts() + const graph = hre.graph(taskArgs) + const { GraphToken, Controller, GraphProxyAdmin, SubgraphNFT } = graph.contracts + const { governor } = await graph.getNamedAccounts() console.log('> Accepting ownership of contracts') console.log(`- Governor: ${governor.address}`) const txs: ContractTransaction[] = [] - txs.push(await contracts.GraphToken.connect(governor).acceptOwnership()) - txs.push(await contracts.Controller.connect(governor).acceptOwnership()) - txs.push(await contracts.GraphProxyAdmin.connect(governor).acceptOwnership()) - txs.push(await contracts.SubgraphNFT.connect(governor).acceptOwnership()) + txs.push(await GraphToken.connect(governor).acceptOwnership()) + txs.push(await Controller.connect(governor).acceptOwnership()) + txs.push(await GraphProxyAdmin.connect(governor).acceptOwnership()) + txs.push(await SubgraphNFT.connect(governor).acceptOwnership()) await Promise.all(txs.map((tx) => tx.wait())) console.log('Done!') diff --git a/tasks/deployment/sync.ts b/tasks/deployment/sync.ts new file mode 100644 index 000000000..429f5aba6 --- /dev/null +++ b/tasks/deployment/sync.ts @@ -0,0 +1,71 @@ +import { ContractTransaction } from 'ethers' +import { task } from 'hardhat/config' +import { cliOpts } from '../../cli/defaults' +import { chainIdIsL2 } from '../../cli/cross-chain' + +task('migrate:sync', 'Sync controller contracts') + .addParam('addressBook', cliOpts.addressBook.description, cliOpts.addressBook.default) + .addParam('graphConfig', cliOpts.graphConfig.description, cliOpts.graphConfig.default) + .setAction(async (taskArgs, hre) => { + const { contracts, getDeployer } = hre.graph({ + addressBook: taskArgs.addressBook, + graphConfig: taskArgs.graphConfig, + }) + const deployer = await getDeployer() + + const chainId = hre.network.config.chainId?.toString() ?? '1337' + const isL2 = chainIdIsL2(chainId) + + // Sync contracts + console.log( + `Syncing cache for contract addresses on chainId ${chainId} (${isL2 ? 'L2' : 'L1'})`, + ) + const txs: ContractTransaction[] = [] + console.log('> Syncing cache on Curation') + txs.push(await contracts['Curation'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on GNS') + txs.push(await contracts['GNS'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on ServiceRegistry') + txs.push(await contracts['ServiceRegistry'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on DisputeManager') + txs.push(await contracts['DisputeManager'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on RewardsManager') + txs.push(await contracts['RewardsManager'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on Staking') + txs.push(await contracts['Staking'].connect(deployer).syncAllContracts()) + if (isL2) { + console.log('> Syncing cache on L2GraphTokenGateway') + txs.push(await contracts['L2GraphTokenGateway'].connect(deployer).syncAllContracts()) + if (contracts['L2Reservoir']) { + console.log('> Syncing cache on L2Reservoir') + txs.push(await contracts['L2Reservoir'].connect(deployer).syncAllContracts()) + } + } else { + // L1 chains might not have these contracts deployed yet... + if (contracts['L1GraphTokenGateway']) { + console.log('> Syncing cache on L1GraphTokenGateway') + txs.push(await contracts['L1GraphTokenGateway'].connect(deployer).syncAllContracts()) + } else { + console.log('Skipping L1GraphTokenGateway as it does not seem to be deployed yet') + } + if (contracts['BridgeEscrow']) { + console.log('> Syncing cache on BridgeEscrow') + txs.push(await contracts['BridgeEscrow'].connect(deployer).syncAllContracts()) + } else { + console.log('Skipping BridgeEscrow as it does not seem to be deployed yet') + } + if (contracts['L1Reservoir']) { + console.log('> Syncing cache on L1Reservoir') + txs.push(await contracts['L1Reservoir'].connect(deployer).syncAllContracts()) + } else { + console.log('Skipping L1Reservoir as it does not seem to be deployed yet') + } + } + await Promise.all( + txs.map((tx) => { + console.log(tx.hash) + return tx.wait() + }), + ) + console.log('Done!') + }) diff --git a/tasks/deployment/unpause.ts b/tasks/deployment/unpause.ts index 2065c5573..fc10c766f 100644 --- a/tasks/deployment/unpause.ts +++ b/tasks/deployment/unpause.ts @@ -1,15 +1,25 @@ import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' +import GraphChain from '../../gre/helpers/network' -task('migrate:unpause', 'Unpause protocol') +task('migrate:unpause', 'Unpause protocol and bridge') .addOptionalParam('addressBook', cliOpts.addressBook.description) .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .setAction(async (taskArgs, hre) => { - const { contracts, getNamedAccounts } = hre.graph(taskArgs) - const { governor } = await getNamedAccounts() + const graph = hre.graph(taskArgs) + const { governor } = await graph.getNamedAccounts() + const { Controller, L1GraphTokenGateway, L2GraphTokenGateway } = graph.contracts console.log('> Unpausing protocol') - const tx = await contracts.Controller.connect(governor).setPaused(false) + const tx = await Controller.connect(governor).setPaused(false) await tx.wait() + + console.log('> Unpausing bridge') + const GraphTokenGateway = GraphChain.isL2(graph.chainId) + ? L2GraphTokenGateway + : L1GraphTokenGateway + const tx2 = await GraphTokenGateway.connect(governor).setPaused(false) + await tx2.wait() + console.log('Done!') }) diff --git a/yarn.lock b/yarn.lock index c9086728b..7b859117b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@arbitrum/sdk@^3.0.0-beta.5": - version "3.0.0-beta.5" - resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.0.0-beta.5.tgz#ef1c81de58db9e76defb4a1971274316a375133b" - integrity sha512-qeNdK7es4uKRFciz4zznPEnGRZaAHkrwNqUN1F4U6d4i8olhK0KMdSodx2ZjajBvVVwOo5kFsw5ocAaTvkf28g== +"@arbitrum/sdk@^3.0.0-beta.6": + version "3.0.0-beta.6" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.0.0-beta.6.tgz#a36c3e39a7358396b5533f3288125107da6ae59e" + integrity sha512-kPCfgj72MeyVcIXQKoztLO29UTcpSbXFzc/S0oDgVNNcHcXp1hWUJqqkVRg0O43P2yKjZRT/I94K0Nj2nZNiiQ== dependencies: "@ethersproject/address" "^5.0.8" "@ethersproject/bignumber" "^5.1.1" From e34d54b2890920aaa384cac749e43a65611a7b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Thu, 11 Aug 2022 14:30:54 +0200 Subject: [PATCH 005/100] chore: syncAllContracts calls in all config files --- config/graph.arbitrum-goerli.yml | 13 ++++++++++++- config/graph.arbitrum-localhost.yml | 11 +++++++++++ config/graph.arbitrum-one.yml | 11 +++++++++++ config/graph.goerli.yml | 15 ++++++++++++++- config/graph.localhost.yml | 13 +++++++++++++ config/graph.mainnet.yml | 13 +++++++++++++ 6 files changed, 74 insertions(+), 2 deletions(-) diff --git a/config/graph.arbitrum-goerli.yml b/config/graph.arbitrum-goerli.yml index 73021f042..2025181f1 100644 --- a/config/graph.arbitrum-goerli.yml +++ b/config/graph.arbitrum-goerli.yml @@ -45,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -53,7 +55,7 @@ contracts: L2GraphToken: proxy: true init: - governor: "${{Env.deployer}}" + owner: "${{Env.deployer}}" calls: - fn: "addMinter" minter: "${{RewardsManager.address}}" @@ -69,6 +71,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -78,6 +82,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -86,6 +92,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -119,6 +126,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -126,6 +134,7 @@ contracts: calls: - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{L2GraphToken.address}}" @@ -138,3 +147,5 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" diff --git a/config/graph.arbitrum-localhost.yml b/config/graph.arbitrum-localhost.yml index 2e185a14a..e929b81ee 100644 --- a/config/graph.arbitrum-localhost.yml +++ b/config/graph.arbitrum-localhost.yml @@ -45,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -69,6 +71,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -78,6 +82,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -86,6 +92,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -119,6 +126,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -126,6 +134,7 @@ contracts: calls: - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{L2GraphToken.address}}" @@ -138,3 +147,5 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml index 8fb876d8f..2708b7254 100644 --- a/config/graph.arbitrum-one.yml +++ b/config/graph.arbitrum-one.yml @@ -45,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -69,6 +71,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -78,6 +82,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -86,6 +92,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -119,6 +126,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -126,6 +134,7 @@ contracts: calls: - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{L2GraphToken.address}}" @@ -138,3 +147,5 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" diff --git a/config/graph.goerli.yml b/config/graph.goerli.yml index d24e3c23d..05a53e058 100644 --- a/config/graph.goerli.yml +++ b/config/graph.goerli.yml @@ -45,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -68,6 +70,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -77,6 +81,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -85,6 +91,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -118,6 +125,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -127,6 +135,7 @@ contracts: issuanceRate: "1000000012184945188" # per block increase of total supply, blocks in a year = 365*60*60*24/13 - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{GraphToken.address}}" @@ -139,7 +148,11 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" BridgeEscrow: proxy: true init: - controller: "${{Controller.address}}" \ No newline at end of file + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" diff --git a/config/graph.localhost.yml b/config/graph.localhost.yml index d6268c269..1fd885fb7 100644 --- a/config/graph.localhost.yml +++ b/config/graph.localhost.yml @@ -45,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -68,6 +70,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -77,6 +81,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -85,6 +91,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -118,6 +125,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -127,6 +135,7 @@ contracts: issuanceRate: "1000000012184945188" # per block increase of total supply, blocks in a year = 365*60*60*24/13 - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{GraphToken.address}}" @@ -139,7 +148,11 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" BridgeEscrow: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" diff --git a/config/graph.mainnet.yml b/config/graph.mainnet.yml index 88e32d38b..36b3669de 100644 --- a/config/graph.mainnet.yml +++ b/config/graph.mainnet.yml @@ -45,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -68,6 +70,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -77,6 +81,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -85,6 +91,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -118,6 +125,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -127,6 +135,7 @@ contracts: issuanceRate: "1000000012184945188" # per block increase of total supply, blocks in a year = 365*60*60*24/13 - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{GraphToken.address}}" @@ -139,7 +148,11 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" BridgeEscrow: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" From 41683ec72275123662b028a0259ef28977c70b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Thu, 11 Aug 2022 16:26:04 +0200 Subject: [PATCH 006/100] chore: set gateway pause guardians in config, remove addMinter in fixtures --- config/graph.arbitrum-goerli.yml | 2 ++ config/graph.arbitrum-localhost.yml | 2 ++ config/graph.arbitrum-one.yml | 2 ++ config/graph.goerli.yml | 2 ++ config/graph.localhost.yml | 2 ++ config/graph.mainnet.yml | 2 ++ test/lib/fixtures.ts | 4 +++- 7 files changed, 15 insertions(+), 1 deletion(-) diff --git a/config/graph.arbitrum-goerli.yml b/config/graph.arbitrum-goerli.yml index 2025181f1..68a656a31 100644 --- a/config/graph.arbitrum-goerli.yml +++ b/config/graph.arbitrum-goerli.yml @@ -149,3 +149,5 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian diff --git a/config/graph.arbitrum-localhost.yml b/config/graph.arbitrum-localhost.yml index e929b81ee..da29ab4b6 100644 --- a/config/graph.arbitrum-localhost.yml +++ b/config/graph.arbitrum-localhost.yml @@ -149,3 +149,5 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml index 2708b7254..1b33edacc 100644 --- a/config/graph.arbitrum-one.yml +++ b/config/graph.arbitrum-one.yml @@ -149,3 +149,5 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian diff --git a/config/graph.goerli.yml b/config/graph.goerli.yml index 05a53e058..b4b735b4e 100644 --- a/config/graph.goerli.yml +++ b/config/graph.goerli.yml @@ -150,6 +150,8 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian BridgeEscrow: proxy: true init: diff --git a/config/graph.localhost.yml b/config/graph.localhost.yml index 1fd885fb7..4a90dfd20 100644 --- a/config/graph.localhost.yml +++ b/config/graph.localhost.yml @@ -150,6 +150,8 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian BridgeEscrow: proxy: true init: diff --git a/config/graph.mainnet.yml b/config/graph.mainnet.yml index 36b3669de..6eb08b233 100644 --- a/config/graph.mainnet.yml +++ b/config/graph.mainnet.yml @@ -150,6 +150,8 @@ contracts: controller: "${{Controller.address}}" calls: - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian BridgeEscrow: proxy: true init: diff --git a/test/lib/fixtures.ts b/test/lib/fixtures.ts index ae6832938..8375a86a4 100644 --- a/test/lib/fixtures.ts +++ b/test/lib/fixtures.ts @@ -159,7 +159,9 @@ export class NetworkFixture { await staking.connect(deployer).setSlasher(slasherAddress, true) await gns.connect(deployer).approveAll() - await grt.connect(deployer).addMinter(rewardsManager.address) + if (!isL2) { + await grt.connect(deployer).addMinter(rewardsManager.address) + } // Unpause the protocol await controller.connect(deployer).setPaused(false) From bd35a9ba074401269a8f9251db7f2995373bae6a Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 23 Sep 2022 13:09:45 -0300 Subject: [PATCH 007/100] fix(config): param name in L2GraphToken initializer --- config/graph.arbitrum-localhost.yml | 2 +- config/graph.arbitrum-one.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/graph.arbitrum-localhost.yml b/config/graph.arbitrum-localhost.yml index da29ab4b6..c58aabf44 100644 --- a/config/graph.arbitrum-localhost.yml +++ b/config/graph.arbitrum-localhost.yml @@ -55,7 +55,7 @@ contracts: L2GraphToken: proxy: true init: - governor: "${{Env.deployer}}" + owner: "${{Env.deployer}}" calls: - fn: "addMinter" minter: "${{RewardsManager.address}}" diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml index 1b33edacc..9588d5f32 100644 --- a/config/graph.arbitrum-one.yml +++ b/config/graph.arbitrum-one.yml @@ -55,7 +55,7 @@ contracts: L2GraphToken: proxy: true init: - governor: "${{Env.deployer}}" + owner: "${{Env.deployer}}" calls: - fn: "addMinter" minter: "${{RewardsManager.address}}" From 80ef55a578c33161a8d54394b13d56962e9d356d Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 23 Sep 2022 13:26:53 -0300 Subject: [PATCH 008/100] fix: no minters for L2GraphToken yet --- config/graph.arbitrum-goerli.yml | 2 -- config/graph.arbitrum-localhost.yml | 2 -- config/graph.arbitrum-one.yml | 2 -- 3 files changed, 6 deletions(-) diff --git a/config/graph.arbitrum-goerli.yml b/config/graph.arbitrum-goerli.yml index 68a656a31..fa1a66ad3 100644 --- a/config/graph.arbitrum-goerli.yml +++ b/config/graph.arbitrum-goerli.yml @@ -57,8 +57,6 @@ contracts: init: owner: "${{Env.deployer}}" calls: - - fn: "addMinter" - minter: "${{RewardsManager.address}}" - fn: "renounceMinter" - fn: "transferOwnership" owner: *governor diff --git a/config/graph.arbitrum-localhost.yml b/config/graph.arbitrum-localhost.yml index c58aabf44..1061044be 100644 --- a/config/graph.arbitrum-localhost.yml +++ b/config/graph.arbitrum-localhost.yml @@ -57,8 +57,6 @@ contracts: init: owner: "${{Env.deployer}}" calls: - - fn: "addMinter" - minter: "${{RewardsManager.address}}" - fn: "renounceMinter" - fn: "transferOwnership" owner: *governor diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml index 9588d5f32..e68d67521 100644 --- a/config/graph.arbitrum-one.yml +++ b/config/graph.arbitrum-one.yml @@ -57,8 +57,6 @@ contracts: init: owner: "${{Env.deployer}}" calls: - - fn: "addMinter" - minter: "${{RewardsManager.address}}" - fn: "renounceMinter" - fn: "transferOwnership" owner: *governor From b13a85f90f4f7b5e246c4fcdaaa6e4ce34f6a86f Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 26 Sep 2022 16:26:14 -0300 Subject: [PATCH 009/100] test: ping local node before continuing --- scripts/e2e | 1 - scripts/evm | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/e2e b/scripts/e2e index 3690cd6ac..c5008f25e 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -21,7 +21,6 @@ yarn build if [[ "$NETWORK" == "localhost" ]]; then evm_kill evm_start - sleep 5 fi # Create address book if needed diff --git a/scripts/evm b/scripts/evm index cead9acc3..77b1b679f 100644 --- a/scripts/evm +++ b/scripts/evm @@ -6,9 +6,24 @@ evm_running() { nc -z localhost "$TESTRPC_PORT" > /dev/null } +ping_evm() { + PORT=$1 + curl --location --request POST "localhost:$PORT/" \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "jsonrpc":"2.0", + "method":"web3_clientVersion", + "params":[], + "id":1 + }' +} + evm_start() { echo "Starting our own evm instance at port $TESTRPC_PORT" npx hardhat node --port "$TESTRPC_PORT" > /dev/null & + while ! ping_evm $TESTRPC_PORT; do + sleep 1 + done } evm_kill() { @@ -16,4 +31,4 @@ evm_kill() { echo "Killing evm instance at port $TESTRPC_PORT" kill -9 $(lsof -i:$TESTRPC_PORT -t) fi -} \ No newline at end of file +} From 554e01059a7ee932965204211f86ce7d5f2e4250 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 26 Sep 2022 16:32:42 -0300 Subject: [PATCH 010/100] test: add timeout for evm instance --- scripts/evm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/evm b/scripts/evm index 77b1b679f..e63e2e9dc 100644 --- a/scripts/evm +++ b/scripts/evm @@ -1,12 +1,13 @@ #!/bin/bash TESTRPC_PORT=${TESTRPC_PORT:-8545} +MAX_RETRIES=120 evm_running() { nc -z localhost "$TESTRPC_PORT" > /dev/null } -ping_evm() { +evm_ping() { PORT=$1 curl --location --request POST "localhost:$PORT/" \ --header 'Content-Type: application/json' \ @@ -21,7 +22,13 @@ ping_evm() { evm_start() { echo "Starting our own evm instance at port $TESTRPC_PORT" npx hardhat node --port "$TESTRPC_PORT" > /dev/null & - while ! ping_evm $TESTRPC_PORT; do + retries=0 + while ! evm_ping $TESTRPC_PORT; do + ((retries=retries+1)) + if [ $retries -gt $MAX_RETRIES ]; then + echo "Timeout waiting for evm instance" + exit 1 + fi sleep 1 done } From 334a82e3e1ef8e159b82d67017f7b46d33ec56e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Fri, 9 Sep 2022 09:48:26 -0300 Subject: [PATCH 011/100] ci: run e2e on local nitro node --- .github/workflows/e2e.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index e381e7906..00ea89986 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -22,4 +22,10 @@ jobs: - name: Install packages run: yarn install --non-interactive --frozen-lockfile - name: Run e2e tests - run: ADDRESS_BOOK=addresses.json yarn test:e2e + run: | + git clone https://github.com/offchainlabs/nitro + pushd nitro + git submodule update --init --recursive + ./test-node.bash --init --detach + popd + NETWORK=localnitro ADDRESS_BOOK=addresses.json yarn test:e2e From 30784c8a9c37db0e5d1fc88276235720a17570a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Fri, 9 Sep 2022 10:08:55 -0300 Subject: [PATCH 012/100] fix: use localnitrol1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomás Migone --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 00ea89986..c9b1a5a5b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -28,4 +28,4 @@ jobs: git submodule update --init --recursive ./test-node.bash --init --detach popd - NETWORK=localnitro ADDRESS_BOOK=addresses.json yarn test:e2e + NETWORK=localnitrol1 ADDRESS_BOOK=addresses.json yarn test:e2e From 48df69465e7e417a49535367796cb3b5f7907b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Fri, 9 Sep 2022 16:09:51 -0300 Subject: [PATCH 013/100] fix(e2e): use no-blockscout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomás Migone --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c9b1a5a5b..bf3e32d00 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -26,6 +26,6 @@ jobs: git clone https://github.com/offchainlabs/nitro pushd nitro git submodule update --init --recursive - ./test-node.bash --init --detach + ./test-node.bash --init --no-blockscout --detach popd NETWORK=localnitrol1 ADDRESS_BOOK=addresses.json yarn test:e2e From b44d3d07850ff55121a7b23667059e7e55cc35ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 12 Sep 2022 11:54:05 +0200 Subject: [PATCH 014/100] ci: use pre-built images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- .github/workflows/e2e.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index bf3e32d00..df90a7809 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -23,8 +23,9 @@ jobs: run: yarn install --non-interactive --frozen-lockfile - name: Run e2e tests run: | - git clone https://github.com/offchainlabs/nitro + git clone https://github.com/tmigone/nitro pushd nitro + git checkout ci git submodule update --init --recursive ./test-node.bash --init --no-blockscout --detach popd From 309d660aaacc2095e34a6274b3d5d18e4f3b09d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 12 Sep 2022 12:22:09 +0200 Subject: [PATCH 015/100] ci: add L2 e2e tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- .github/workflows/e2e.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index df90a7809..802a1933c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -29,4 +29,5 @@ jobs: git submodule update --init --recursive ./test-node.bash --init --no-blockscout --detach popd - NETWORK=localnitrol1 ADDRESS_BOOK=addresses.json yarn test:e2e + NETWORK=localnitrol1 ADDRESS_BOOK=addresses.json GRAPH_CONFIG=config/graph.localhost.yml yarn test:e2e + NETWORK=localnitrol2 ADDRESS_BOOK=addresses.json GRAPH_CONFIG=config/graph.arbitrum-localhost.yml yarn test:e2e From 78691c2e97633e031330f07be7d9cedbee413fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 26 Sep 2022 15:41:57 -0400 Subject: [PATCH 016/100] Update .github/workflows/e2e.yml --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 802a1933c..7f7979a86 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -23,7 +23,7 @@ jobs: run: yarn install --non-interactive --frozen-lockfile - name: Run e2e tests run: | - git clone https://github.com/tmigone/nitro + git clone https://github.com/edgeandnode/nitro pushd nitro git checkout ci git submodule update --init --recursive From 105b4f0cb5601611d95ef30e23f480b35f67ee64 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 26 Sep 2022 17:05:58 -0300 Subject: [PATCH 017/100] test(e2e): in L2, GraphToken has no minters for now --- e2e/deployment/config/graphToken.test.ts | 7 +---- e2e/deployment/config/l1/graphToken.test.ts | 31 +++++++++++++++++++ e2e/deployment/config/l2/l2GraphToken.test.ts | 7 ++++- 3 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 e2e/deployment/config/l1/graphToken.test.ts diff --git a/e2e/deployment/config/graphToken.test.ts b/e2e/deployment/config/graphToken.test.ts index 8b84208f3..5ae1e3207 100644 --- a/e2e/deployment/config/graphToken.test.ts +++ b/e2e/deployment/config/graphToken.test.ts @@ -5,7 +5,7 @@ import { NamedAccounts } from '../../../gre/type-extensions' describe('GraphToken configuration', () => { const { getNamedAccounts, - contracts: { GraphToken, RewardsManager }, + contracts: { GraphToken }, getDeployer, } = hre.graph() @@ -25,9 +25,4 @@ describe('GraphToken configuration', () => { const deployerIsMinter = await GraphToken.isMinter(deployer.address) expect(deployerIsMinter).eq(false) }) - - it('RewardsManager should be minter', async function () { - const deployerIsMinter = await GraphToken.isMinter(RewardsManager.address) - expect(deployerIsMinter).eq(true) - }) }) diff --git a/e2e/deployment/config/l1/graphToken.test.ts b/e2e/deployment/config/l1/graphToken.test.ts new file mode 100644 index 000000000..41e6322d0 --- /dev/null +++ b/e2e/deployment/config/l1/graphToken.test.ts @@ -0,0 +1,31 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' + +describe('[L1] GraphToken', () => { + const graph = hre.graph() + const { GraphToken, RewardsManager } = graph.contracts + + let unauthorized: SignerWithAddress + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + unauthorized = (await graph.getTestAccounts())[0] + }) + + describe('calls with unauthorized user', () => { + it('mint should revert', async function () { + const tx = GraphToken.connect(unauthorized).mint( + unauthorized.address, + '1000000000000000000000', + ) + await expect(tx).revertedWith('Only minter can call') + }) + + it('RewardsManager should be minter', async function () { + const rewardsMgrIsMinter = await GraphToken.isMinter(RewardsManager.address) + expect(rewardsMgrIsMinter).eq(true) + }) + }) +}) diff --git a/e2e/deployment/config/l2/l2GraphToken.test.ts b/e2e/deployment/config/l2/l2GraphToken.test.ts index 0d9f4b025..7b87253e0 100644 --- a/e2e/deployment/config/l2/l2GraphToken.test.ts +++ b/e2e/deployment/config/l2/l2GraphToken.test.ts @@ -5,7 +5,7 @@ import GraphChain from '../../../../gre/helpers/network' describe('[L2] L2GraphToken', () => { const graph = hre.graph() - const { L2GraphToken } = graph.contracts + const { L2GraphToken, RewardsManager } = graph.contracts let unauthorized: SignerWithAddress @@ -35,5 +35,10 @@ describe('[L2] L2GraphToken', () => { const tx = L2GraphToken.connect(unauthorized).setGateway(unauthorized.address) await expect(tx).revertedWith('Only Governor can call') }) + + it('RewardsManager should not be minter (for now)', async function () { + const rewardsMgrIsMinter = await L2GraphToken.isMinter(RewardsManager.address) + expect(rewardsMgrIsMinter).eq(false) + }) }) }) From 7bd0ab2ff80b3d44c4cfdef2f07915dc5b3bd166 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Tue, 27 Sep 2022 14:03:15 -0300 Subject: [PATCH 018/100] fix: add reentrancy guard to L2GraphTokenGateway --- contracts/l2/gateway/L2GraphTokenGateway.sol | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index 593e0e228..afa11b13c 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -3,6 +3,7 @@ pragma solidity ^0.7.6; pragma abicoder v2; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../arbitrum/L2ArbitrumMessenger.sol"; @@ -18,7 +19,7 @@ import "../token/L2GraphToken.sol"; * (See: https://github.com/OffchainLabs/arbitrum/tree/master/packages/arb-bridge-peripherals/contracts/tokenbridge * and https://github.com/livepeer/arbitrum-lpt-bridge) */ -contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger { +contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, ReentrancyGuardUpgradeable { using SafeMath for uint256; // Address of the Graph Token contract on L1 @@ -85,6 +86,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger { function initialize(address _controller) external onlyImpl { Managed._initialize(_controller); _paused = true; + __ReentrancyGuard_init(); } /** @@ -138,7 +140,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger { uint256, // unused on L2 uint256, // unused on L2 bytes calldata _data - ) public payable override notPaused returns (bytes memory) { + ) public payable override notPaused nonReentrant returns (bytes memory) { require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); require(_amount > 0, "INVALID_ZERO_AMOUNT"); require(msg.value == 0, "INVALID_NONZERO_VALUE"); @@ -226,7 +228,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger { address _to, uint256 _amount, bytes calldata _data - ) external payable override notPaused onlyL1Counterpart { + ) external payable override notPaused onlyL1Counterpart nonReentrant { require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); require(msg.value == 0, "INVALID_NONZERO_VALUE"); From 1f3217673dae7308859f2e0f97128d9cb7cc6c8a Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 28 Sep 2022 11:58:49 -0300 Subject: [PATCH 019/100] fix(L2GraphTokenGateway): only call a specific onTokenTransfer function in L1-L2 callhooks --- contracts/gateway/ICallhookReceiver.sol | 23 +++++++ contracts/l2/gateway/L2GraphTokenGateway.sol | 9 +-- contracts/tests/CallhookReceiverMock.sol | 32 +++++++++ test/l2/l2GraphTokenGateway.test.ts | 68 +++++++++++--------- 4 files changed, 93 insertions(+), 39 deletions(-) create mode 100644 contracts/gateway/ICallhookReceiver.sol create mode 100644 contracts/tests/CallhookReceiverMock.sol diff --git a/contracts/gateway/ICallhookReceiver.sol b/contracts/gateway/ICallhookReceiver.sol new file mode 100644 index 000000000..b0ba0fa43 --- /dev/null +++ b/contracts/gateway/ICallhookReceiver.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/** + * @title Interface for contracts that can receive callhooks through the Arbitrum GRT bridge + * @dev Any contract that can receive a callhook on L2, send through the bridge from L1, must + * be whitelisted by the governor, but also implement this interface that contains + * the function that will actually be called by the L2GraphTokenGateway. + */ +pragma solidity ^0.7.6; + +interface ICallhookReceiver { + /** + * @dev Receive tokens with a callhook from the bridge + * @param _from Token sender in L1 + * @param _amount Amount of tokens that were transferred + * @param _data ABI-encoded callhook data + */ + function onTokenTransfer( + address _from, + uint256 _amount, + bytes calldata _data + ) external; +} diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index afa11b13c..fa75e3af6 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -9,6 +9,7 @@ import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../arbitrum/L2ArbitrumMessenger.sol"; import "../../arbitrum/AddressAliasHelper.sol"; import "../../gateway/GraphTokenGateway.sol"; +import "../../gateway/ICallhookReceiver.sol"; import "../token/L2GraphToken.sol"; /** @@ -240,15 +241,9 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran bytes memory gatewayData; (gatewayData, callhookData) = abi.decode(_data, (bytes, bytes)); } - bool success; - // solhint-disable-next-line avoid-low-level-calls - (success, ) = _to.call(callhookData); // Callhooks shouldn't revert, but if they do: // we revert, so that the retryable ticket can be re-attempted - // later. - if (!success) { - revert("CALLHOOK_FAILED"); - } + ICallhookReceiver(_to).onTokenTransfer(_from, _amount, callhookData); } emit DepositFinalized(_l1Token, _from, _to, _amount); diff --git a/contracts/tests/CallhookReceiverMock.sol b/contracts/tests/CallhookReceiverMock.sol new file mode 100644 index 000000000..1e71cf86f --- /dev/null +++ b/contracts/tests/CallhookReceiverMock.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../gateway/ICallhookReceiver.sol"; + +/** + * @title GovernedMock contract + */ +contract CallhookReceiverMock is ICallhookReceiver { + event TransferReceived(address from, uint256 amount, uint256 foo, uint256 bar); + + /** + * @dev Receive tokens with a callhook from the bridge + * Expects two uint256 values encoded in _data. + * Reverts if the first of these values is zero. + * @param _from Token sender in L1 + * @param _amount Amount of tokens that were transferred + * @param _data ABI-encoded callhook data + */ + function onTokenTransfer( + address _from, + uint256 _amount, + bytes calldata _data + ) external override { + uint256 foo; + uint256 bar; + (foo, bar) = abi.decode(_data, (uint256, uint256)); + require(foo != 0, "FOO_IS_ZERO"); + emit TransferReceived(_from, _amount, foo, bar); + } +} diff --git a/test/l2/l2GraphTokenGateway.test.ts b/test/l2/l2GraphTokenGateway.test.ts index f4d57dbab..c1521e12e 100644 --- a/test/l2/l2GraphTokenGateway.test.ts +++ b/test/l2/l2GraphTokenGateway.test.ts @@ -3,21 +3,17 @@ import { constants, ContractTransaction, Signer, utils } from 'ethers' import { L2GraphToken } from '../../build/types/L2GraphToken' import { L2GraphTokenGateway } from '../../build/types/L2GraphTokenGateway' +import { CallhookReceiverMock } from '../../build/types/CallhookReceiverMock' import { L2FixtureContracts, NetworkFixture } from '../lib/fixtures' import { FakeContract, smock } from '@defi-wonderland/smock' -import path from 'path' -import { Artifacts } from 'hardhat/internal/artifacts' -const ARTIFACTS_PATH = path.resolve('build/contracts') -const artifacts = new Artifacts(ARTIFACTS_PATH) -const rewardsManagerMockAbi = artifacts.readArtifactSync('RewardsManagerMock').abi - use(smock.matchers) import { getAccounts, toGRT, Account, toBN, getL2SignerFromL1 } from '../lib/testHelpers' import { Interface } from 'ethers/lib/utils' +import { deployContract } from '../lib/deployment' const { AddressZero } = constants @@ -37,11 +33,14 @@ describe('L2GraphTokenGateway', () => { let fixtureContracts: L2FixtureContracts let grt: L2GraphToken let l2GraphTokenGateway: L2GraphTokenGateway + let callhookReceiverMock: CallhookReceiverMock const senderTokens = toGRT('1000') const defaultData = '0x' - const mockIface = new Interface(rewardsManagerMockAbi) - const notEmptyCallHookData = mockIface.encodeFunctionData('pow', [toBN(1), toBN(2), toBN(3)]) + const notEmptyCallHookData = utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [toBN('1337'), toBN('42')], + ) const defaultDataWithNotEmptyCallHookData = utils.defaultAbiCoder.encode( ['bytes', 'bytes'], ['0x', notEmptyCallHookData], @@ -64,6 +63,11 @@ describe('L2GraphTokenGateway', () => { fixtureContracts = await fixture.loadL2(governor.signer) ;({ grt, l2GraphTokenGateway } = fixtureContracts) + callhookReceiverMock = (await deployContract( + 'CallhookReceiverMock', + governor.signer, + )) as unknown as CallhookReceiverMock + // Give some funds to the token sender await grt.connect(governor.signer).mint(tokenSender.address, senderTokens) }) @@ -319,7 +323,9 @@ describe('L2GraphTokenGateway', () => { describe('finalizeInboundTransfer', function () { const testValidFinalizeTransfer = async function ( data: string, + to?: string, ): Promise { + to = to ?? l2Receiver.address const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) await me.signer.sendTransaction({ to: await mockL1GatewayL2Alias.getAddress(), @@ -327,24 +333,18 @@ describe('L2GraphTokenGateway', () => { }) const tx = l2GraphTokenGateway .connect(mockL1GatewayL2Alias) - .finalizeInboundTransfer( - mockL1GRT.address, - tokenSender.address, - l2Receiver.address, - toGRT('10'), - data, - ) + .finalizeInboundTransfer(mockL1GRT.address, tokenSender.address, to, toGRT('10'), data) await expect(tx) .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(mockL1GRT.address, tokenSender.address, l2Receiver.address, toGRT('10')) + .withArgs(mockL1GRT.address, tokenSender.address, to, toGRT('10')) - await expect(tx).emit(grt, 'BridgeMinted').withArgs(l2Receiver.address, toGRT('10')) + await expect(tx).emit(grt, 'BridgeMinted').withArgs(to, toGRT('10')) // Unchanged const senderBalance = await grt.balanceOf(tokenSender.address) await expect(senderBalance).eq(toGRT('1000')) // 10 newly minted GRT - const receiverBalance = await grt.balanceOf(l2Receiver.address) + const receiverBalance = await grt.balanceOf(to) await expect(receiverBalance).eq(toGRT('10')) return tx } @@ -375,19 +375,23 @@ describe('L2GraphTokenGateway', () => { it('mints and sends tokens when called by the aliased gateway', async function () { await testValidFinalizeTransfer(defaultData) }) - it('calls a callhook if the sender is whitelisted', async function () { - const rewardsManagerMock = await smock.fake('RewardsManagerMock', { - address: l2Receiver.address, - }) - rewardsManagerMock.pow.returns(1) - await testValidFinalizeTransfer(defaultDataWithNotEmptyCallHookData) - expect(rewardsManagerMock.pow).to.have.been.calledWith(toBN(1), toBN(2), toBN(3)) + it('calls a callhook if the transfer includes calldata', async function () { + const tx = await testValidFinalizeTransfer( + defaultDataWithNotEmptyCallHookData, + callhookReceiverMock.address, + ) + // Emitted by the callhook: + await expect(tx) + .emit(callhookReceiverMock, 'TransferReceived') + .withArgs(tokenSender.address, toGRT('10'), toBN('1337'), toBN('42')) }) it('reverts if a callhook reverts', async function () { - const rewardsManagerMock = await smock.fake('RewardsManagerMock', { - address: l2Receiver.address, - }) - rewardsManagerMock.pow.reverts() + // The 0 will make the callhook revert (see CallhookReceiverMock.sol) + const callHookData = utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [toBN('0'), toBN('42')], + ) + const data = utils.defaultAbiCoder.encode(['bytes', 'bytes'], ['0x', callHookData]) const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) await me.signer.sendTransaction({ to: await mockL1GatewayL2Alias.getAddress(), @@ -398,11 +402,11 @@ describe('L2GraphTokenGateway', () => { .finalizeInboundTransfer( mockL1GRT.address, tokenSender.address, - l2Receiver.address, + callhookReceiverMock.address, toGRT('10'), - defaultDataWithNotEmptyCallHookData, + data, ) - await expect(tx).revertedWith('CALLHOOK_FAILED') + await expect(tx).revertedWith('FOO_IS_ZERO') }) }) }) From 394458d040685278f2fdd3cc135487403e7aa8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Wed, 28 Sep 2022 12:57:28 -0300 Subject: [PATCH 020/100] fix: apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomás Migone --- contracts/gateway/ICallhookReceiver.sol | 2 +- contracts/l2/gateway/L2GraphTokenGateway.sol | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/gateway/ICallhookReceiver.sol b/contracts/gateway/ICallhookReceiver.sol index b0ba0fa43..fb7492bb7 100644 --- a/contracts/gateway/ICallhookReceiver.sol +++ b/contracts/gateway/ICallhookReceiver.sol @@ -2,7 +2,7 @@ /** * @title Interface for contracts that can receive callhooks through the Arbitrum GRT bridge - * @dev Any contract that can receive a callhook on L2, send through the bridge from L1, must + * @dev Any contract that can receive a callhook on L2, sent through the bridge from L1, must * be whitelisted by the governor, but also implement this interface that contains * the function that will actually be called by the L2GraphTokenGateway. */ diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index fa75e3af6..8757a06ca 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -241,8 +241,6 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran bytes memory gatewayData; (gatewayData, callhookData) = abi.decode(_data, (bytes, bytes)); } - // Callhooks shouldn't revert, but if they do: - // we revert, so that the retryable ticket can be re-attempted ICallhookReceiver(_to).onTokenTransfer(_from, _amount, callhookData); } From 126d12406b6b2326fb6009e378579037af5e499a Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 28 Sep 2022 13:06:57 -0300 Subject: [PATCH 021/100] test: add test for calling contracts that don't implement onTokenTransfer --- test/l2/l2GraphTokenGateway.test.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/test/l2/l2GraphTokenGateway.test.ts b/test/l2/l2GraphTokenGateway.test.ts index c1521e12e..f283aabe1 100644 --- a/test/l2/l2GraphTokenGateway.test.ts +++ b/test/l2/l2GraphTokenGateway.test.ts @@ -14,6 +14,7 @@ use(smock.matchers) import { getAccounts, toGRT, Account, toBN, getL2SignerFromL1 } from '../lib/testHelpers' import { Interface } from 'ethers/lib/utils' import { deployContract } from '../lib/deployment' +import { RewardsManager } from '../../build/types/RewardsManager' const { AddressZero } = constants @@ -34,6 +35,7 @@ describe('L2GraphTokenGateway', () => { let grt: L2GraphToken let l2GraphTokenGateway: L2GraphTokenGateway let callhookReceiverMock: CallhookReceiverMock + let rewardsManager: RewardsManager const senderTokens = toGRT('1000') const defaultData = '0x' @@ -61,7 +63,7 @@ describe('L2GraphTokenGateway', () => { fixture = new NetworkFixture() fixtureContracts = await fixture.loadL2(governor.signer) - ;({ grt, l2GraphTokenGateway } = fixtureContracts) + ;({ grt, l2GraphTokenGateway, rewardsManager } = fixtureContracts) callhookReceiverMock = (await deployContract( 'CallhookReceiverMock', @@ -408,6 +410,28 @@ describe('L2GraphTokenGateway', () => { ) await expect(tx).revertedWith('FOO_IS_ZERO') }) + it('reverts if trying to call a callhook in a contract that does not implement onTokenTransfer', async function () { + const callHookData = utils.defaultAbiCoder.encode(['uint256'], [toBN('0')]) + const data = utils.defaultAbiCoder.encode(['bytes', 'bytes'], ['0x', callHookData]) + const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) + await me.signer.sendTransaction({ + to: await mockL1GatewayL2Alias.getAddress(), + value: utils.parseUnits('1', 'ether'), + }) + // RewardsManager does not implement onTokenTransfer, so this will fail + const tx = l2GraphTokenGateway + .connect(mockL1GatewayL2Alias) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + rewardsManager.address, + toGRT('10'), + data, + ) + await expect(tx).revertedWith( + "function selector was not recognized and there's no fallback function", + ) + }) }) }) }) From 8aa3bd650d810398deb26f3c1fac78f5f9795328 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 28 Sep 2022 13:30:47 -0300 Subject: [PATCH 022/100] test: use codecov action v3 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4ed99cf7..55b984f12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Run tests run: yarn test:coverage - name: Upload coverage report - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.json From 323bdf8131e44183ba2491086dc3c97804f53069 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Tue, 4 Oct 2022 16:38:01 -0300 Subject: [PATCH 023/100] test: fix race condition in BridgeEscrow tests --- test/gateway/bridgeEscrow.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/gateway/bridgeEscrow.test.ts b/test/gateway/bridgeEscrow.test.ts index aea195159..ec3eedb73 100644 --- a/test/gateway/bridgeEscrow.test.ts +++ b/test/gateway/bridgeEscrow.test.ts @@ -41,17 +41,17 @@ describe('BridgeEscrow', () => { describe('approveAll', function () { it('cannot be called by someone other than the governor', async function () { const tx = bridgeEscrow.connect(tokenReceiver.signer).approveAll(spender.address) - expect(tx).to.be.revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Caller must be Controller governor') }) it('allows a spender to transfer GRT held by the contract', async function () { expect(await grt.allowance(bridgeEscrow.address, spender.address)).eq(0) const tx = grt .connect(spender.signer) .transferFrom(bridgeEscrow.address, tokenReceiver.address, nTokens) - expect(tx).to.be.revertedWith('ERC20: transfer amount exceeds allowance') + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') await bridgeEscrow.connect(governor.signer).approveAll(spender.address) - expect( - await grt + await expect( + grt .connect(spender.signer) .transferFrom(bridgeEscrow.address, tokenReceiver.address, nTokens), ).to.emit(grt, 'Transfer') @@ -62,7 +62,7 @@ describe('BridgeEscrow', () => { describe('revokeAll', function () { it('cannot be called by someone other than the governor', async function () { const tx = bridgeEscrow.connect(tokenReceiver.signer).revokeAll(spender.address) - expect(tx).to.be.revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Caller must be Controller governor') }) it("revokes a spender's permission to transfer GRT held by the contract", async function () { await bridgeEscrow.connect(governor.signer).approveAll(spender.address) @@ -71,7 +71,7 @@ describe('BridgeEscrow', () => { const tx = grt .connect(spender.signer) .transferFrom(bridgeEscrow.address, tokenReceiver.address, BigNumber.from('1')) - expect(tx).to.be.revertedWith('ERC20: transfer amount exceeds allowance') + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') }) }) }) From 08cddfabb796b086262c392df188f942ba50423f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Tue, 4 Oct 2022 16:11:17 -0400 Subject: [PATCH 024/100] chore: provide sane defaults for graph config (#727) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- hardhat.config.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index aa89f8715..1a1c2758b 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -59,15 +59,21 @@ interface NetworkConfig { const networkConfigs: NetworkConfig[] = [ { network: 'mainnet', chainId: 1, graphConfig: 'config/graph.mainnet.yml' }, - { network: 'rinkeby', chainId: 4 }, - { network: 'goerli', chainId: 5 }, + { network: 'rinkeby', chainId: 4, graphConfig: 'config/graph.rinkeby.yml' }, + { network: 'goerli', chainId: 5, graphConfig: 'config/graph.goerli.yml' }, { network: 'kovan', chainId: 42 }, { network: 'arbitrum-rinkeby', chainId: 421611, url: 'https://rinkeby.arbitrum.io/rpc' }, - { network: 'arbitrum-one', chainId: 42161, url: 'https://arb1.arbitrum.io/rpc' }, + { + network: 'arbitrum-one', + chainId: 42161, + url: 'https://arb1.arbitrum.io/rpc', + graphConfig: 'config/graph.arbitrum-one.yml', + }, { network: 'arbitrum-goerli', chainId: 421613, url: 'https://goerli-rollup.arbitrum.io/rpc', + graphConfig: 'config/graph.arbitrum-goerli.yml', }, ] @@ -145,28 +151,32 @@ const config: HardhatUserConfig = { interval: 13000, }, hardfork: 'london', + graphConfig: 'config/graph.localhost.yml', }, localhost: { chainId: 1337, url: 'http://localhost:8545', accounts: process.env.FORK === 'true' ? getAccountsKeys() : { mnemonic: DEFAULT_TEST_MNEMONIC }, + graphConfig: 'config/graph.localhost.yml', }, localnitrol1: { chainId: 1337, url: 'http://localhost:8545', accounts: { mnemonic: DEFAULT_TEST_MNEMONIC }, + graphConfig: 'config/graph.localhost.yml', }, localnitrol2: { chainId: 412346, url: 'http://localhost:8547', accounts: { mnemonic: DEFAULT_TEST_MNEMONIC }, + graphConfig: 'config/graph.arbitrum-localhost.yml', }, }, graph: { addressBook: process.env.ADDRESS_BOOK ?? 'addresses.json', - l1GraphConfig: process.env.GRAPH_CONFIG ?? 'config/graph.localhost.yml', - l2GraphConfig: process.env.L2_GRAPH_CONFIG, + l1GraphConfig: process.env.L1_GRAPH_CONFIG ?? 'config/graph.localhost.yml', + l2GraphConfig: process.env.L2_GRAPH_CONFIG ?? 'config/graph.arbitrum-localhost.yml', }, etherscan: { apiKey: process.env.ETHERSCAN_API_KEY, From 7b383140ba5430a564d3caecb1074b31f5497bab Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 5 Oct 2022 13:06:23 -0300 Subject: [PATCH 025/100] test: don't use nc to check if evm is running --- scripts/evm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/evm b/scripts/evm index e63e2e9dc..62bab58fb 100644 --- a/scripts/evm +++ b/scripts/evm @@ -4,7 +4,7 @@ TESTRPC_PORT=${TESTRPC_PORT:-8545} MAX_RETRIES=120 evm_running() { - nc -z localhost "$TESTRPC_PORT" > /dev/null + lsof -i:$TESTRPC_PORT -t > /dev/null } evm_ping() { From 1d5dde2553267614f8238e60ef044c581c16d693 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 20 Oct 2022 16:13:20 -0300 Subject: [PATCH 026/100] fix: prevent unpausing gateways if they're not configured (OZ M-01 from #700) --- contracts/gateway/GraphTokenGateway.sol | 9 ++++ contracts/gateway/L1GraphTokenGateway.sol | 11 +++++ contracts/l2/gateway/L2GraphTokenGateway.sol | 10 +++++ test/gateway/l1GraphTokenGateway.test.ts | 46 ++++++++++++++++---- test/l2/l2GraphTokenGateway.test.ts | 42 ++++++++++++++---- 5 files changed, 102 insertions(+), 16 deletions(-) diff --git a/contracts/gateway/GraphTokenGateway.sol b/contracts/gateway/GraphTokenGateway.sol index 00e8441f5..7f3720163 100644 --- a/contracts/gateway/GraphTokenGateway.sol +++ b/contracts/gateway/GraphTokenGateway.sol @@ -45,6 +45,9 @@ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITok * @param _newPaused New value for the pause state (true means the transfers will be paused) */ function setPaused(bool _newPaused) external onlyGovernorOrGuardian { + if (!_newPaused) { + _checksBeforeUnpause(); + } _setPaused(_newPaused); } @@ -54,4 +57,10 @@ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITok function paused() external view returns (bool) { return _paused; } + + /** + * @dev Runs state validation before unpausing, reverts if + * something is not set properly + */ + function _checksBeforeUnpause() internal view virtual; } diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index e1f34efe5..06511420b 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -356,4 +356,15 @@ contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { } return l2GRT; } + + /** + * @dev Runs state validation before unpausing, reverts if + * something is not set properly + */ + function _checksBeforeUnpause() internal view override { + require(inbox != address(0), "INBOX_NOT_SET"); + require(l1Router != address(0), "ROUTER_NOT_SET"); + require(l2Counterpart != address(0), "L2_COUNTERPART_NOT_SET"); + require(escrow != address(0), "ESCROW_NOT_SET"); + } } diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index 8757a06ca..e24251960 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -294,4 +294,14 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran } return (from, extraData); } + + /** + * @dev Runs state validation before unpausing, reverts if + * something is not set properly + */ + function _checksBeforeUnpause() internal view override { + require(l2Router != address(0), "ROUTER_NOT_SET"); + require(l1Counterpart != address(0), "L1_COUNTERPART_NOT_SET"); + require(l1GRT != address(0), "L1GRT_NOT_SET"); + } } diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts index a5a473d86..7a51bc053 100644 --- a/test/gateway/l1GraphTokenGateway.test.ts +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -237,13 +237,35 @@ describe('L1GraphTokenGateway', () => { tx = l1GraphTokenGateway.connect(tokenSender.signer).setPaused(true) await expect(tx).revertedWith('Only Governor or Guardian can call') }) - it('can be paused and unpaused by the governor', async function () { + it('cannot be unpaused if some state variables are not set', async function () { let tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) - await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(false) - await expect(await l1GraphTokenGateway.paused()).eq(false) - tx = l1GraphTokenGateway.connect(governor.signer).setPaused(true) + await expect(tx).revertedWith('INBOX_NOT_SET') + await l1GraphTokenGateway + .connect(governor.signer) + .setArbitrumAddresses(arbitrumMocks.inboxMock.address, mockRouter.address) + tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('L2_COUNTERPART_NOT_SET') + await l1GraphTokenGateway + .connect(governor.signer) + .setL2CounterpartAddress(mockL2Gateway.address) + tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('ESCROW_NOT_SET') + }) + it('can be paused and unpaused by the governor', async function () { + await fixture.configureL1Bridge( + governor.signer, + arbitrumMocks, + fixtureContracts, + mockRouter.address, + mockL2GRT.address, + mockL2Gateway.address, + ) + let tx = l1GraphTokenGateway.connect(governor.signer).setPaused(true) await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(true) await expect(await l1GraphTokenGateway.paused()).eq(true) + tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l1GraphTokenGateway.paused()).eq(false) }) describe('setPauseGuardian', function () { it('cannot be called by someone other than governor', async function () { @@ -261,13 +283,21 @@ describe('L1GraphTokenGateway', () => { .withArgs(AddressZero, pauseGuardian.address) }) it('allows a pause guardian to pause and unpause', async function () { + await fixture.configureL1Bridge( + governor.signer, + arbitrumMocks, + fixtureContracts, + mockRouter.address, + mockL2GRT.address, + mockL2Gateway.address, + ) await l1GraphTokenGateway.connect(governor.signer).setPauseGuardian(pauseGuardian.address) - let tx = l1GraphTokenGateway.connect(pauseGuardian.signer).setPaused(false) - await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(false) - await expect(await l1GraphTokenGateway.paused()).eq(false) - tx = l1GraphTokenGateway.connect(pauseGuardian.signer).setPaused(true) + let tx = l1GraphTokenGateway.connect(pauseGuardian.signer).setPaused(true) await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(true) await expect(await l1GraphTokenGateway.paused()).eq(true) + tx = l1GraphTokenGateway.connect(pauseGuardian.signer).setPaused(false) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l1GraphTokenGateway.paused()).eq(false) }) }) }) diff --git a/test/l2/l2GraphTokenGateway.test.ts b/test/l2/l2GraphTokenGateway.test.ts index f283aabe1..c987fc281 100644 --- a/test/l2/l2GraphTokenGateway.test.ts +++ b/test/l2/l2GraphTokenGateway.test.ts @@ -173,13 +173,32 @@ describe('L2GraphTokenGateway', () => { tx = l2GraphTokenGateway.connect(tokenSender.signer).setPaused(true) await expect(tx).revertedWith('Only Governor or Guardian can call') }) - it('can be paused and unpaused by the governor', async function () { + it('cannot be paused if some state variables are not set', async function () { let tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) - await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(false) - await expect(await l2GraphTokenGateway.paused()).eq(false) - tx = l2GraphTokenGateway.connect(governor.signer).setPaused(true) + await expect(tx).revertedWith('ROUTER_NOT_SET') + await l2GraphTokenGateway.connect(governor.signer).setL2Router(mockRouter.address) + tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('L1_COUNTERPART_NOT_SET') + await l2GraphTokenGateway + .connect(governor.signer) + .setL1CounterpartAddress(mockL1Gateway.address) + tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('L1GRT_NOT_SET') + }) + it('can be paused and unpaused by the governor', async function () { + await fixture.configureL2Bridge( + governor.signer, + fixtureContracts, + mockRouter.address, + mockL1GRT.address, + mockL1Gateway.address, + ) + let tx = l2GraphTokenGateway.connect(governor.signer).setPaused(true) await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(true) await expect(await l2GraphTokenGateway.paused()).eq(true) + tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l2GraphTokenGateway.paused()).eq(false) }) describe('setPauseGuardian', function () { it('cannot be called by someone other than governor', async function () { @@ -197,13 +216,20 @@ describe('L2GraphTokenGateway', () => { .withArgs(AddressZero, pauseGuardian.address) }) it('allows a pause guardian to pause and unpause', async function () { + await fixture.configureL2Bridge( + governor.signer, + fixtureContracts, + mockRouter.address, + mockL1GRT.address, + mockL1Gateway.address, + ) await l2GraphTokenGateway.connect(governor.signer).setPauseGuardian(pauseGuardian.address) - let tx = l2GraphTokenGateway.connect(pauseGuardian.signer).setPaused(false) - await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(false) - await expect(await l2GraphTokenGateway.paused()).eq(false) - tx = l2GraphTokenGateway.connect(pauseGuardian.signer).setPaused(true) + let tx = l2GraphTokenGateway.connect(pauseGuardian.signer).setPaused(true) await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(true) await expect(await l2GraphTokenGateway.paused()).eq(true) + tx = l2GraphTokenGateway.connect(pauseGuardian.signer).setPaused(false) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l2GraphTokenGateway.paused()).eq(false) }) }) }) From ff11ec2e42b57f2459c2a8b9aa2b5a89a4e34eb7 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 20 Oct 2022 16:43:19 -0300 Subject: [PATCH 027/100] fix: use separate task to unpause bridge --- tasks/deployment/unpause.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tasks/deployment/unpause.ts b/tasks/deployment/unpause.ts index fc10c766f..afb85e0ea 100644 --- a/tasks/deployment/unpause.ts +++ b/tasks/deployment/unpause.ts @@ -2,18 +2,29 @@ import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' import GraphChain from '../../gre/helpers/network' -task('migrate:unpause', 'Unpause protocol and bridge') +task('migrate:unpause', 'Unpause protocol (except bridge)') .addOptionalParam('addressBook', cliOpts.addressBook.description) .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .setAction(async (taskArgs, hre) => { const graph = hre.graph(taskArgs) const { governor } = await graph.getNamedAccounts() - const { Controller, L1GraphTokenGateway, L2GraphTokenGateway } = graph.contracts + const { Controller } = graph.contracts console.log('> Unpausing protocol') const tx = await Controller.connect(governor).setPaused(false) await tx.wait() + console.log('Done!') + }) + +task('migrate:unpause-bridge', 'Unpause bridge') + .addOptionalParam('addressBook', cliOpts.addressBook.description) + .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .setAction(async (taskArgs, hre) => { + const graph = hre.graph(taskArgs) + const { governor } = await graph.getNamedAccounts() + const { L1GraphTokenGateway, L2GraphTokenGateway } = graph.contracts + console.log('> Unpausing bridge') const GraphTokenGateway = GraphChain.isL2(graph.chainId) ? L2GraphTokenGateway From 6d4a0d72c6cc6ed834a9427c0f09e6cd55571b2b Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 21 Oct 2022 11:27:28 -0300 Subject: [PATCH 028/100] fix(e2e): gateways start paused --- e2e/deployment/config/l1/l1GraphTokenGateway.test.ts | 6 +++--- e2e/deployment/config/l2/l2GraphTokenGateway.test.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts index d2424d0ce..1f96bd946 100644 --- a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -13,9 +13,9 @@ describe('[L1] L1GraphTokenGateway configuration', function () { unauthorized = (await graph.getTestAccounts())[0] }) - it('bridge should be unpaused', async function () { + it('bridge should be paused', async function () { const paused = await L1GraphTokenGateway.paused() - expect(paused).eq(false) + expect(paused).eq(true) }) it('should be controlled by Controller', async function () { @@ -77,7 +77,7 @@ describe('[L1] L1GraphTokenGateway configuration', function () { '0x00', ) - await expect(tx).revertedWith('INBOX_NOT_SET') + await expect(tx).revertedWith('Paused (contract)') }) }) }) diff --git a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts index 04732498b..f572a37be 100644 --- a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts @@ -13,9 +13,9 @@ describe('[L2] L2GraphTokenGateway configuration', function () { unauthorized = (await graph.getTestAccounts())[0] }) - it('bridge should be unpaused', async function () { + it('bridge should be paused', async function () { const paused = await L2GraphTokenGateway.paused() - expect(paused).eq(false) + expect(paused).eq(true) }) it('should be controlled by Controller', async function () { @@ -55,7 +55,7 @@ describe('[L2] L2GraphTokenGateway configuration', function () { '0x00', ) - await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + await expect(tx).revertedWith('Paused (contract)') }) }) }) From 16dc44e47157510c330b97eebedcbf50eb7ec2b5 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 20 Oct 2022 16:38:01 -0300 Subject: [PATCH 029/100] fix: validate Arbitrum addresses and callhook senders are contracts (OZ L-01 from #700) --- contracts/gateway/L1GraphTokenGateway.sol | 3 ++ test/gateway/l1GraphTokenGateway.test.ts | 53 ++++++++++++++++++----- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 06511420b..e84f6c207 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -109,6 +109,8 @@ contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { function setArbitrumAddresses(address _inbox, address _l1Router) external onlyGovernor { require(_inbox != address(0), "INVALID_INBOX"); require(_l1Router != address(0), "INVALID_L1_ROUTER"); + require(Address.isContract(_inbox), "INBOX_MUST_BE_CONTRACT"); + require(Address.isContract(_l1Router), "ROUTER_MUST_BE_CONTRACT"); inbox = _inbox; l1Router = _l1Router; emit ArbitrumAddressesSet(_inbox, _l1Router); @@ -151,6 +153,7 @@ contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { */ function addToCallhookWhitelist(address _newWhitelisted) external onlyGovernor { require(_newWhitelisted != address(0), "INVALID_ADDRESS"); + require(Address.isContract(_newWhitelisted), "MUST_BE_CONTRACT"); require(!callhookWhitelist[_newWhitelisted], "ALREADY_WHITELISTED"); callhookWhitelist[_newWhitelisted] = true; emit AddedToCallhookWhitelist(_newWhitelisted); diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts index 7a51bc053..4a022e88a 100644 --- a/test/gateway/l1GraphTokenGateway.test.ts +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -16,6 +16,7 @@ import { toGRT, Account, applyL1ToL2Alias, + provider, } from '../lib/testHelpers' import { BridgeEscrow } from '../../build/types/BridgeEscrow' @@ -61,6 +62,8 @@ describe('L1GraphTokenGateway', () => { ;[governor, tokenSender, l2Receiver, mockRouter, mockL2GRT, mockL2Gateway, pauseGuardian] = await getAccounts() + // Dummy code on the mock router so that it appears as a contract + await provider().send('hardhat_setCode', [mockRouter.address, '0x1234']) fixture = new NetworkFixture() fixtureContracts = await fixture.load(governor.signer) ;({ grt, l1GraphTokenGateway, bridgeEscrow } = fixtureContracts) @@ -128,6 +131,16 @@ describe('L1GraphTokenGateway', () => { .setArbitrumAddresses(inboxMock.address, mockRouter.address) await expect(tx).revertedWith('Caller must be Controller governor') }) + it('rejects setting an EOA as router or inbox', async function () { + let tx = l1GraphTokenGateway + .connect(governor.signer) + .setArbitrumAddresses(tokenSender.address, mockRouter.address) + await expect(tx).revertedWith('INBOX_MUST_BE_CONTRACT') + tx = l1GraphTokenGateway + .connect(governor.signer) + .setArbitrumAddresses(inboxMock.address, tokenSender.address) + await expect(tx).revertedWith('ROUTER_MUST_BE_CONTRACT') + }) it('sets inbox and router address', async function () { const tx = l1GraphTokenGateway .connect(governor.signer) @@ -192,42 +205,56 @@ describe('L1GraphTokenGateway', () => { it('is not callable by addreses that are not the governor', async function () { const tx = l1GraphTokenGateway .connect(tokenSender.signer) - .addToCallhookWhitelist(tokenSender.address) + .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) await expect(tx).revertedWith('Caller must be Controller governor') - expect(await l1GraphTokenGateway.callhookWhitelist(tokenSender.address)).eq(false) + expect( + await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), + ).eq(false) }) - it('adds an address to the callhook whitelist', async function () { + it('rejects adding an EOA to the callhook whitelist', async function () { const tx = l1GraphTokenGateway .connect(governor.signer) .addToCallhookWhitelist(tokenSender.address) + await expect(tx).revertedWith('MUST_BE_CONTRACT') + }) + it('adds an address to the callhook whitelist', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) await expect(tx) .emit(l1GraphTokenGateway, 'AddedToCallhookWhitelist') - .withArgs(tokenSender.address) - expect(await l1GraphTokenGateway.callhookWhitelist(tokenSender.address)).eq(true) + .withArgs(fixtureContracts.rewardsManager.address) + expect( + await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), + ).eq(true) }) }) describe('removeFromCallhookWhitelist', function () { it('is not callable by addreses that are not the governor', async function () { await l1GraphTokenGateway .connect(governor.signer) - .addToCallhookWhitelist(tokenSender.address) + .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) const tx = l1GraphTokenGateway .connect(tokenSender.signer) - .removeFromCallhookWhitelist(tokenSender.address) + .removeFromCallhookWhitelist(fixtureContracts.rewardsManager.address) await expect(tx).revertedWith('Caller must be Controller governor') - expect(await l1GraphTokenGateway.callhookWhitelist(tokenSender.address)).eq(true) + expect( + await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), + ).eq(true) }) it('removes an address from the callhook whitelist', async function () { await l1GraphTokenGateway .connect(governor.signer) - .addToCallhookWhitelist(tokenSender.address) + .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) const tx = l1GraphTokenGateway .connect(governor.signer) - .removeFromCallhookWhitelist(tokenSender.address) + .removeFromCallhookWhitelist(fixtureContracts.rewardsManager.address) await expect(tx) .emit(l1GraphTokenGateway, 'RemovedFromCallhookWhitelist') - .withArgs(tokenSender.address) - expect(await l1GraphTokenGateway.callhookWhitelist(tokenSender.address)).eq(false) + .withArgs(fixtureContracts.rewardsManager.address) + expect( + await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), + ).eq(false) }) }) describe('Pausable behavior', () => { @@ -479,6 +506,8 @@ describe('L1GraphTokenGateway', () => { await expect(tx).revertedWith('CALL_HOOK_DATA_NOT_ALLOWED') }) it('allows sending nonempty calldata, if the sender is whitelisted', async function () { + // Make the sender a contract so that it can be allowed to send callhooks + await provider().send('hardhat_setCode', [tokenSender.address, '0x1234']) await l1GraphTokenGateway .connect(governor.signer) .addToCallhookWhitelist(tokenSender.address) From 2dfacb6322627d58a883803035f7b3b0c57f22ea Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 21 Oct 2022 11:51:49 -0300 Subject: [PATCH 030/100] fix: remove unnecessary L2 gas check (OZ N-01 from #700) --- contracts/gateway/L1GraphTokenGateway.sol | 10 +--------- test/gateway/l1GraphTokenGateway.test.ts | 12 ++++++++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index e84f6c207..fbcbcf724 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -217,15 +217,7 @@ contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { extraData.length == 0 || callhookWhitelist[msg.sender] == true, "CALL_HOOK_DATA_NOT_ALLOWED" ); - require(maxSubmissionCost > 0, "NO_SUBMISSION_COST"); - - { - // makes sure only sufficient ETH is supplied as required for successful redemption on L2 - // if a user does not desire immediate redemption they should provide - // a msg.value of AT LEAST maxSubmissionCost - uint256 expectedEth = maxSubmissionCost.add(_maxGas.mul(_gasPriceBid)); - require(msg.value >= expectedEth, "WRONG_ETH_VALUE"); - } + require(maxSubmissionCost != 0, "NO_SUBMISSION_COST"); outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData); } { diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts index 4a022e88a..70984717a 100644 --- a/test/gateway/l1GraphTokenGateway.test.ts +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -52,6 +52,10 @@ describe('L1GraphTokenGateway', () => { ['uint256', 'bytes'], [maxSubmissionCost, emptyCallHookData], ) + const defaultDataNoSubmissionCost = utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [toBN(0), emptyCallHookData], + ) const notEmptyCallHookData = '0x12' const defaultDataWithNotEmptyCallHookData = utils.defaultAbiCoder.encode( ['uint256', 'bytes'], @@ -471,7 +475,7 @@ describe('L1GraphTokenGateway', () => { ) await testValidOutboundTransfer(mockRouter.signer, routerEncodedData, emptyCallHookData) }) - it('reverts when called with the wrong value', async function () { + it('reverts when called with no submission cost', async function () { await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) const tx = l1GraphTokenGateway .connect(tokenSender.signer) @@ -481,12 +485,12 @@ describe('L1GraphTokenGateway', () => { toGRT('10'), maxGas, gasPriceBid, - defaultData, + defaultDataNoSubmissionCost, { - value: defaultEthValue.sub(1), + value: defaultEthValue, }, ) - await expect(tx).revertedWith('WRONG_ETH_VALUE') + await expect(tx).revertedWith('NO_SUBMISSION_COST') }) it('reverts when called with nonempty calldata, if the sender is not whitelisted', async function () { await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) From fa52952b4421ac6e948c75217ebd0c1222e29b54 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 21 Oct 2022 13:46:02 -0300 Subject: [PATCH 031/100] fix: add storage gaps to GraphTokenGateway and GraphTokenUpgradeable (C4 M-244) --- contracts/gateway/GraphTokenGateway.sol | 3 +++ contracts/l2/token/GraphTokenUpgradeable.sol | 2 ++ 2 files changed, 5 insertions(+) diff --git a/contracts/gateway/GraphTokenGateway.sol b/contracts/gateway/GraphTokenGateway.sol index 7f3720163..b239af00a 100644 --- a/contracts/gateway/GraphTokenGateway.sol +++ b/contracts/gateway/GraphTokenGateway.sol @@ -12,6 +12,9 @@ import "../governance/Managed.sol"; * @dev This includes everything that's shared between the L1 and L2 sides of the bridge. */ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITokenGateway { + // Storage gap added in case we need to add state variables to this contract + uint256[50] private __gap; + /** * @dev Check if the caller is the Controller's governor or this contract's pause guardian. */ diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index 6d003c1c3..4ecfb9ffa 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -50,6 +50,8 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra bytes32 private DOMAIN_SEPARATOR; mapping(address => bool) private _minters; mapping(address => uint256) public nonces; + // Storage gap added in case we need to add state variables to this contract + uint256[47] private __gap; // -- Events -- From bcb4802127f075b01187f92ff01f3695a9ea79d6 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 21 Oct 2022 13:51:53 -0300 Subject: [PATCH 032/100] fix: ensure l2Counterpart is nonzero (C4 M-201) --- contracts/gateway/L1GraphTokenGateway.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index fbcbcf724..3fcb08a94 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -72,6 +72,7 @@ contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { */ modifier onlyL2Counterpart() { require(inbox != address(0), "INBOX_NOT_SET"); + require(l2Counterpart != address(0), "L2_COUNTERPART_NOT_SET"); // a message coming from the counterpart gateway was executed by the bridge IBridge bridge = IInbox(inbox).bridge(); From 85312a0a897d54f8a285a7b61c4fcbc85598b33f Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 21 Oct 2022 14:08:00 -0300 Subject: [PATCH 033/100] fix: ensure contracts are only initialized once (C4 M-149) --- contracts/gateway/BridgeEscrow.sol | 6 ++++-- contracts/gateway/L1GraphTokenGateway.sol | 5 +++-- contracts/l2/gateway/L2GraphTokenGateway.sol | 2 +- contracts/l2/token/L2GraphToken.sol | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/contracts/gateway/BridgeEscrow.sol b/contracts/gateway/BridgeEscrow.sol index 605f13a50..1c1d85f54 100644 --- a/contracts/gateway/BridgeEscrow.sol +++ b/contracts/gateway/BridgeEscrow.sol @@ -2,6 +2,8 @@ pragma solidity ^0.7.6; +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + import "../upgrades/GraphUpgradeable.sol"; import "../governance/Managed.sol"; import "../token/IGraphToken.sol"; @@ -12,12 +14,12 @@ import "../token/IGraphToken.sol"; * a set of spenders that can transfer the tokens; the L1 side of each L2 bridge has to be * approved as a spender. */ -contract BridgeEscrow is GraphUpgradeable, Managed { +contract BridgeEscrow is Initializable, GraphUpgradeable, Managed { /** * @dev Initialize this contract. * @param _controller Address of the Controller that manages this contract */ - function initialize(address _controller) external onlyImpl { + function initialize(address _controller) external onlyImpl initializer { Managed._initialize(_controller); } diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 3fcb08a94..d1cbd417a 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -3,6 +3,7 @@ pragma solidity ^0.7.6; pragma abicoder v2; +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; @@ -18,7 +19,7 @@ import "./GraphTokenGateway.sol"; * (See: https://github.com/OffchainLabs/arbitrum/tree/master/packages/arb-bridge-peripherals/contracts/tokenbridge * and https://github.com/livepeer/arbitrum-lpt-bridge) */ -contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { +contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMessenger { using SafeMath for uint256; // Address of the Graph Token contract on L2 @@ -97,7 +98,7 @@ contract L1GraphTokenGateway is GraphTokenGateway, L1ArbitrumMessenger { * - pauseGuardian using setPauseGuardian * @param _controller Address of the Controller that manages this contract */ - function initialize(address _controller) external onlyImpl { + function initialize(address _controller) external onlyImpl initializer { Managed._initialize(_controller); _paused = true; } diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index e24251960..251897f6f 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -84,7 +84,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran * - pauseGuardian using setPauseGuardian * @param _controller Address of the Controller that manages this contract */ - function initialize(address _controller) external onlyImpl { + function initialize(address _controller) external onlyImpl initializer { Managed._initialize(_controller); _paused = true; __ReentrancyGuard_init(); diff --git a/contracts/l2/token/L2GraphToken.sol b/contracts/l2/token/L2GraphToken.sol index ec6ca4eb8..0e1d942c9 100644 --- a/contracts/l2/token/L2GraphToken.sol +++ b/contracts/l2/token/L2GraphToken.sol @@ -45,7 +45,7 @@ contract L2GraphToken is GraphTokenUpgradeable, IArbToken { * - l1Address using setL1Address * @param _owner Governance address that owns this contract */ - function initialize(address _owner) external onlyImpl { + function initialize(address _owner) external onlyImpl initializer { require(_owner != address(0), "Owner must be set"); // Initial supply hard coded to 0 as tokens are only supposed // to be minted through the bridge. From 2c4ac9d8a879557f08bc448f243998282d9f8215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 12:22:34 -0300 Subject: [PATCH 034/100] fix(c4-117-g03): state variables should be cached in stack variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/governance/Governed.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts/governance/Governed.sol b/contracts/governance/Governed.sol index 2a87389d9..36d96d13a 100644 --- a/contracts/governance/Governed.sol +++ b/contracts/governance/Governed.sol @@ -51,15 +51,16 @@ contract Governed { * This function must called by the pending governor. */ function acceptOwnership() external { + address _pendingGovernor = pendingGovernor; require( - pendingGovernor != address(0) && msg.sender == pendingGovernor, + _pendingGovernor != address(0) && msg.sender == _pendingGovernor, "Caller must be pending governor" ); address oldGovernor = governor; - address oldPendingGovernor = pendingGovernor; + address oldPendingGovernor = _pendingGovernor; - governor = pendingGovernor; + governor = _pendingGovernor; pendingGovernor = address(0); emit NewOwnership(oldGovernor, governor); From 76865a492dc209bdda31bfed32a71d9b7a445517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 13:31:12 -0300 Subject: [PATCH 035/100] =?UTF-8?q?fix(c4-117-g05):=20require()/revert()?= =?UTF-8?q?=C2=A0strings=20longer=20than=2032=20bytes=20cost=20extra=20gas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/gateway/GraphTokenGateway.sol | 2 +- contracts/governance/Managed.sol | 2 +- contracts/upgrades/GraphProxy.sol | 6 +++--- contracts/upgrades/GraphUpgradeable.sol | 2 +- .../config/l1/l1GraphTokenGateway.test.ts | 12 ++++++------ .../config/l2/l2GraphTokenGateway.test.ts | 6 +++--- test/curation/configuration.test.ts | 8 ++++---- test/disputes/configuration.test.ts | 8 ++++---- test/gateway/bridgeEscrow.test.ts | 4 ++-- test/gateway/l1GraphTokenGateway.test.ts | 18 +++++++++--------- test/gns.test.ts | 6 +++--- test/l2/l2GraphTokenGateway.test.ts | 12 ++++++------ test/rewards/rewards.test.ts | 4 ++-- test/staking/configuration.test.ts | 18 +++++++++--------- test/staking/delegation.test.ts | 6 +++--- 15 files changed, 57 insertions(+), 57 deletions(-) diff --git a/contracts/gateway/GraphTokenGateway.sol b/contracts/gateway/GraphTokenGateway.sol index b239af00a..ed59b149a 100644 --- a/contracts/gateway/GraphTokenGateway.sol +++ b/contracts/gateway/GraphTokenGateway.sol @@ -21,7 +21,7 @@ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITok modifier onlyGovernorOrGuardian() { require( msg.sender == controller.getGovernor() || msg.sender == pauseGuardian, - "Only Governor or Guardian can call" + "Only Governor or Guardian" ); _; } diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index ce18fb009..fcdf73c36 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -50,7 +50,7 @@ contract Managed { } function _onlyGovernor() internal view { - require(msg.sender == controller.getGovernor(), "Caller must be Controller governor"); + require(msg.sender == controller.getGovernor(), "Only Controller governor"); } function _onlyController() internal view { diff --git a/contracts/upgrades/GraphProxy.sol b/contracts/upgrades/GraphProxy.sol index 69c88fd28..6e29396db 100644 --- a/contracts/upgrades/GraphProxy.sol +++ b/contracts/upgrades/GraphProxy.sol @@ -102,7 +102,7 @@ contract GraphProxy is GraphProxyStorage { * NOTE: Only the admin can call this function. */ function setAdmin(address _newAdmin) external ifAdmin { - require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); + require(_newAdmin != address(0), "Admin cant be the zero address"); _setAdmin(_newAdmin); } @@ -138,10 +138,10 @@ contract GraphProxy is GraphProxyStorage { */ function _acceptUpgrade() internal { address _pendingImplementation = _pendingImplementation(); - require(Address.isContract(_pendingImplementation), "Implementation must be a contract"); + require(Address.isContract(_pendingImplementation), "Impl must be a contract"); require( _pendingImplementation != address(0) && msg.sender == _pendingImplementation, - "Caller must be the pending implementation" + "Only pending implementation" ); _setImplementation(_pendingImplementation); diff --git a/contracts/upgrades/GraphUpgradeable.sol b/contracts/upgrades/GraphUpgradeable.sol index 2331cae27..3f3b505ba 100644 --- a/contracts/upgrades/GraphUpgradeable.sol +++ b/contracts/upgrades/GraphUpgradeable.sol @@ -29,7 +29,7 @@ contract GraphUpgradeable { * @dev Check if the caller is the implementation. */ modifier onlyImpl() { - require(msg.sender == _implementation(), "Caller must be the implementation"); + require(msg.sender == _implementation(), "Only implementation"); _; } diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts index 1f96bd946..a2d8edbae 100644 --- a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -34,38 +34,38 @@ describe('[L1] L1GraphTokenGateway configuration', function () { unauthorized.address, unauthorized.address, ) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('setL2TokenAddress should revert', async function () { const tx = L1GraphTokenGateway.connect(unauthorized).setL2TokenAddress(unauthorized.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('setL2CounterpartAddress should revert', async function () { const tx = L1GraphTokenGateway.connect(unauthorized).setL2CounterpartAddress( unauthorized.address, ) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('setEscrowAddress should revert', async function () { const tx = L1GraphTokenGateway.connect(unauthorized).setEscrowAddress(unauthorized.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('addToCallhookWhitelist should revert', async function () { const tx = L1GraphTokenGateway.connect(unauthorized).addToCallhookWhitelist( unauthorized.address, ) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('removeFromCallhookWhitelist should revert', async function () { const tx = L1GraphTokenGateway.connect(unauthorized).removeFromCallhookWhitelist( unauthorized.address, ) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('finalizeInboundTransfer should revert', async function () { diff --git a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts index f572a37be..e4626b59b 100644 --- a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts @@ -31,19 +31,19 @@ describe('[L2] L2GraphTokenGateway configuration', function () { it('setL2Router should revert', async function () { const tx = L2GraphTokenGateway.connect(unauthorized).setL2Router(unauthorized.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('setL1TokenAddress should revert', async function () { const tx = L2GraphTokenGateway.connect(unauthorized).setL1TokenAddress(unauthorized.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('setL1CounterpartAddress should revert', async function () { const tx = L2GraphTokenGateway.connect(unauthorized).setL1CounterpartAddress( unauthorized.address, ) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('finalizeInboundTransfer should revert', async function () { diff --git a/test/curation/configuration.test.ts b/test/curation/configuration.test.ts index b2424c784..f475d8f30 100644 --- a/test/curation/configuration.test.ts +++ b/test/curation/configuration.test.ts @@ -55,7 +55,7 @@ describe('Curation:Config', () => { it('reject set `defaultReserveRatio` if not allowed', async function () { const tx = curation.connect(me.signer).setDefaultReserveRatio(defaults.curation.reserveRatio) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -79,7 +79,7 @@ describe('Curation:Config', () => { const tx = curation .connect(me.signer) .setMinimumCurationDeposit(defaults.curation.minimumCurationDeposit) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -99,7 +99,7 @@ describe('Curation:Config', () => { it('reject set `curationTaxPercentage` if not allowed', async function () { const tx = curation.connect(me.signer).setCurationTaxPercentage(0) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -124,7 +124,7 @@ describe('Curation:Config', () => { it('reject set `curationTokenMaster` if not allowed', async function () { const newCurationTokenMaster = curation.address const tx = curation.connect(me.signer).setCurationTokenMaster(newCurationTokenMaster) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) }) diff --git a/test/disputes/configuration.test.ts b/test/disputes/configuration.test.ts index 28bc92ed5..c663c31d7 100644 --- a/test/disputes/configuration.test.ts +++ b/test/disputes/configuration.test.ts @@ -49,7 +49,7 @@ describe('DisputeManager:Config', () => { it('reject set `arbitrator` if not allowed', async function () { const tx = disputeManager.connect(me.signer).setArbitrator(arbitrator.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `arbitrator` to address zero', async function () { @@ -74,7 +74,7 @@ describe('DisputeManager:Config', () => { it('reject set `minimumDeposit` if not allowed', async function () { const newValue = toBN('1') const tx = disputeManager.connect(me.signer).setMinimumDeposit(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -97,7 +97,7 @@ describe('DisputeManager:Config', () => { it('reject set `fishermanRewardPercentage` if not allowed', async function () { const tx = disputeManager.connect(me.signer).setFishermanRewardPercentage(50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -127,7 +127,7 @@ describe('DisputeManager:Config', () => { it('reject set `slashingPercentage` if not allowed', async function () { const tx = disputeManager.connect(me.signer).setSlashingPercentage(50, 50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) }) diff --git a/test/gateway/bridgeEscrow.test.ts b/test/gateway/bridgeEscrow.test.ts index ec3eedb73..fabe9de27 100644 --- a/test/gateway/bridgeEscrow.test.ts +++ b/test/gateway/bridgeEscrow.test.ts @@ -41,7 +41,7 @@ describe('BridgeEscrow', () => { describe('approveAll', function () { it('cannot be called by someone other than the governor', async function () { const tx = bridgeEscrow.connect(tokenReceiver.signer).approveAll(spender.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('allows a spender to transfer GRT held by the contract', async function () { expect(await grt.allowance(bridgeEscrow.address, spender.address)).eq(0) @@ -62,7 +62,7 @@ describe('BridgeEscrow', () => { describe('revokeAll', function () { it('cannot be called by someone other than the governor', async function () { const tx = bridgeEscrow.connect(tokenReceiver.signer).revokeAll(spender.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it("revokes a spender's permission to transfer GRT held by the contract", async function () { await bridgeEscrow.connect(governor.signer).approveAll(spender.address) diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts index 70984717a..7c2d3db5e 100644 --- a/test/gateway/l1GraphTokenGateway.test.ts +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -133,7 +133,7 @@ describe('L1GraphTokenGateway', () => { const tx = l1GraphTokenGateway .connect(tokenSender.signer) .setArbitrumAddresses(inboxMock.address, mockRouter.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('rejects setting an EOA as router or inbox', async function () { let tx = l1GraphTokenGateway @@ -162,7 +162,7 @@ describe('L1GraphTokenGateway', () => { const tx = l1GraphTokenGateway .connect(tokenSender.signer) .setL2TokenAddress(mockL2GRT.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('sets l2GRT', async function () { const tx = l1GraphTokenGateway.connect(governor.signer).setL2TokenAddress(mockL2GRT.address) @@ -176,7 +176,7 @@ describe('L1GraphTokenGateway', () => { const tx = l1GraphTokenGateway .connect(tokenSender.signer) .setL2CounterpartAddress(mockL2Gateway.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('sets L2Counterpart', async function () { const tx = l1GraphTokenGateway @@ -193,7 +193,7 @@ describe('L1GraphTokenGateway', () => { const tx = l1GraphTokenGateway .connect(tokenSender.signer) .setEscrowAddress(bridgeEscrow.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('sets escrow', async function () { const tx = l1GraphTokenGateway @@ -210,7 +210,7 @@ describe('L1GraphTokenGateway', () => { const tx = l1GraphTokenGateway .connect(tokenSender.signer) .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') expect( await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), ).eq(false) @@ -241,7 +241,7 @@ describe('L1GraphTokenGateway', () => { const tx = l1GraphTokenGateway .connect(tokenSender.signer) .removeFromCallhookWhitelist(fixtureContracts.rewardsManager.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') expect( await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), ).eq(true) @@ -264,9 +264,9 @@ describe('L1GraphTokenGateway', () => { describe('Pausable behavior', () => { it('cannot be paused or unpaused by someone other than governor or pauseGuardian', async () => { let tx = l1GraphTokenGateway.connect(tokenSender.signer).setPaused(false) - await expect(tx).revertedWith('Only Governor or Guardian can call') + await expect(tx).revertedWith('Only Governor or Guardian') tx = l1GraphTokenGateway.connect(tokenSender.signer).setPaused(true) - await expect(tx).revertedWith('Only Governor or Guardian can call') + await expect(tx).revertedWith('Only Governor or Guardian') }) it('cannot be unpaused if some state variables are not set', async function () { let tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) @@ -303,7 +303,7 @@ describe('L1GraphTokenGateway', () => { const tx = l1GraphTokenGateway .connect(tokenSender.signer) .setPauseGuardian(pauseGuardian.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('sets a new pause guardian', async function () { const tx = l1GraphTokenGateway diff --git a/test/gns.test.ts b/test/gns.test.ts index 298d95ab0..1ee46002a 100644 --- a/test/gns.test.ts +++ b/test/gns.test.ts @@ -546,7 +546,7 @@ describe('GNS', () => { it('reject set `ownerTaxPercentage` if not allowed', async function () { const tx = gns.connect(me.signer).setOwnerTaxPercentage(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -1034,7 +1034,7 @@ describe('GNS', () => { // Batch send transaction const tx = gns.connect(me.signer).multicall([tx1.data, tx2.data]) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('should revert if batching a call to initialize', async function () { @@ -1050,7 +1050,7 @@ describe('GNS', () => { // Batch send transaction const tx = gns.connect(me.signer).multicall([tx1.data, tx2.data]) - await expect(tx).revertedWith('Caller must be the implementation') + await expect(tx).revertedWith('Only implementation') }) it('should revert if trying to call a private function', async function () { diff --git a/test/l2/l2GraphTokenGateway.test.ts b/test/l2/l2GraphTokenGateway.test.ts index c987fc281..7c222b73d 100644 --- a/test/l2/l2GraphTokenGateway.test.ts +++ b/test/l2/l2GraphTokenGateway.test.ts @@ -126,7 +126,7 @@ describe('L2GraphTokenGateway', () => { describe('setL2Router', function () { it('is not callable by addreses that are not the governor', async function () { const tx = l2GraphTokenGateway.connect(tokenSender.signer).setL2Router(mockRouter.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('sets router address', async function () { const tx = l2GraphTokenGateway.connect(governor.signer).setL2Router(mockRouter.address) @@ -140,7 +140,7 @@ describe('L2GraphTokenGateway', () => { const tx = l2GraphTokenGateway .connect(tokenSender.signer) .setL1TokenAddress(mockL1GRT.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('sets l2GRT', async function () { const tx = l2GraphTokenGateway.connect(governor.signer).setL1TokenAddress(mockL1GRT.address) @@ -154,7 +154,7 @@ describe('L2GraphTokenGateway', () => { const tx = l2GraphTokenGateway .connect(tokenSender.signer) .setL1CounterpartAddress(mockL1Gateway.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('sets L1Counterpart', async function () { const tx = l2GraphTokenGateway @@ -169,9 +169,9 @@ describe('L2GraphTokenGateway', () => { describe('Pausable behavior', () => { it('cannot be paused or unpaused by someone other than governor or pauseGuardian', async () => { let tx = l2GraphTokenGateway.connect(tokenSender.signer).setPaused(false) - await expect(tx).revertedWith('Only Governor or Guardian can call') + await expect(tx).revertedWith('Only Governor or Guardian') tx = l2GraphTokenGateway.connect(tokenSender.signer).setPaused(true) - await expect(tx).revertedWith('Only Governor or Guardian can call') + await expect(tx).revertedWith('Only Governor or Guardian') }) it('cannot be paused if some state variables are not set', async function () { let tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) @@ -205,7 +205,7 @@ describe('L2GraphTokenGateway', () => { const tx = l2GraphTokenGateway .connect(tokenSender.signer) .setPauseGuardian(pauseGuardian.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('sets a new pause guardian', async function () { const tx = l2GraphTokenGateway diff --git a/test/rewards/rewards.test.ts b/test/rewards/rewards.test.ts index 23aee0dfa..beed73a73 100644 --- a/test/rewards/rewards.test.ts +++ b/test/rewards/rewards.test.ts @@ -171,7 +171,7 @@ describe('Rewards', () => { describe('issuance rate update', function () { it('reject set issuance rate if unauthorized', async function () { const tx = rewardsManager.connect(indexer1.signer).setIssuanceRate(toGRT('1.025')) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set issuance rate to less than minimum allowed', async function () { @@ -199,7 +199,7 @@ describe('Rewards', () => { const tx = rewardsManager .connect(indexer1.signer) .setSubgraphAvailabilityOracle(oracle.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('should set subgraph oracle if governor', async function () { diff --git a/test/staking/configuration.test.ts b/test/staking/configuration.test.ts index 39ed30502..52aeaa843 100644 --- a/test/staking/configuration.test.ts +++ b/test/staking/configuration.test.ts @@ -52,7 +52,7 @@ describe('Staking:Config', () => { it('reject set `minimumIndexerStake` if not allowed', async function () { const newValue = toGRT('100') const tx = staking.connect(me.signer).setMinimumIndexerStake(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `minimumIndexerStake` to zero', async function () { @@ -74,7 +74,7 @@ describe('Staking:Config', () => { it('reject set `slasher` if not allowed', async function () { const tx = staking.connect(other.signer).setSlasher(me.address, true) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `slasher` for zero', async function () { @@ -103,7 +103,7 @@ describe('Staking:Config', () => { it('reject set `assetHolder` if not allowed', async function () { const tx = staking.connect(other.signer).setAssetHolder(me.address, true) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `assetHolder` to address zero', async function () { @@ -122,7 +122,7 @@ describe('Staking:Config', () => { it('reject set `channelDisputeEpochs` if not allowed', async function () { const newValue = toBN('5') const tx = staking.connect(other.signer).setChannelDisputeEpochs(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `channelDisputeEpochs` to zero', async function () { @@ -146,7 +146,7 @@ describe('Staking:Config', () => { it('reject set `curationPercentage` if not allowed', async function () { const tx = staking.connect(other.signer).setCurationPercentage(50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -166,7 +166,7 @@ describe('Staking:Config', () => { it('reject set `protocolPercentage` if not allowed', async function () { const tx = staking.connect(other.signer).setProtocolPercentage(50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -180,7 +180,7 @@ describe('Staking:Config', () => { it('reject set `maxAllocationEpochs` if not allowed', async function () { const newValue = toBN('5') const tx = staking.connect(other.signer).setMaxAllocationEpochs(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -194,7 +194,7 @@ describe('Staking:Config', () => { it('reject set `thawingPeriod` if not allowed', async function () { const newValue = toBN('5') const tx = staking.connect(other.signer).setThawingPeriod(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `thawingPeriod` to zero', async function () { @@ -225,7 +225,7 @@ describe('Staking:Config', () => { it('reject set `rebateRatio` if not allowed', async function () { const tx = staking.connect(other.signer).setRebateRatio(1, 1) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) }) diff --git a/test/staking/delegation.test.ts b/test/staking/delegation.test.ts index cad46a063..e097dfd9f 100644 --- a/test/staking/delegation.test.ts +++ b/test/staking/delegation.test.ts @@ -217,7 +217,7 @@ describe('Staking::Delegation', () => { it('reject set `delegationRatio` if not allowed', async function () { const tx = staking.connect(me.signer).setDelegationRatio(delegationRatio) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -231,7 +231,7 @@ describe('Staking::Delegation', () => { it('reject set `delegationParametersCooldown` if not allowed', async function () { const tx = staking.connect(me.signer).setDelegationParametersCooldown(cooldown) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -251,7 +251,7 @@ describe('Staking::Delegation', () => { it('reject set `delegationTaxPercentage` if not allowed', async function () { const tx = staking.connect(me.signer).setDelegationTaxPercentage(50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) From 18afb3add07b752adfde9a775115b0438415e4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 14:08:08 -0300 Subject: [PATCH 036/100] =?UTF-8?q?fix(c4-117-g06):=20keccak256()=C2=A0sho?= =?UTF-8?q?uld=20only=20need=20to=20be=20called=20on=20a=20specific=20stri?= =?UTF-8?q?ng=20literal=20once?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/governance/Managed.sol | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index fcdf73c36..fcec6e47a 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -28,6 +28,14 @@ contract Managed { mapping(bytes32 => address) private addressCache; uint256[10] private __gap; + // Immutables + bytes32 immutable CURATION = keccak256("Curation"); + bytes32 immutable EPOCH_MANAGER = keccak256("EpochManager"); + bytes32 immutable REWARDS_MANAGER = keccak256("RewardsManager"); + bytes32 immutable STAKING = keccak256("Staking"); + bytes32 immutable GRAPH_TOKEN = keccak256("GraphToken"); + bytes32 immutable GRAPH_TOKEN_GATEWAY = keccak256("GraphTokenGateway"); + // -- Events -- event ParameterUpdated(string param); @@ -111,7 +119,7 @@ contract Managed { * @return Curation contract registered with Controller */ function curation() internal view returns (ICuration) { - return ICuration(_resolveContract(keccak256("Curation"))); + return ICuration(_resolveContract(CURATION)); } /** @@ -119,7 +127,7 @@ contract Managed { * @return Epoch manager contract registered with Controller */ function epochManager() internal view returns (IEpochManager) { - return IEpochManager(_resolveContract(keccak256("EpochManager"))); + return IEpochManager(_resolveContract(EPOCH_MANAGER)); } /** @@ -127,7 +135,7 @@ contract Managed { * @return Rewards manager contract registered with Controller */ function rewardsManager() internal view returns (IRewardsManager) { - return IRewardsManager(_resolveContract(keccak256("RewardsManager"))); + return IRewardsManager(_resolveContract(REWARDS_MANAGER)); } /** @@ -135,7 +143,7 @@ contract Managed { * @return Staking contract registered with Controller */ function staking() internal view returns (IStaking) { - return IStaking(_resolveContract(keccak256("Staking"))); + return IStaking(_resolveContract(STAKING)); } /** @@ -143,7 +151,7 @@ contract Managed { * @return Graph token contract registered with Controller */ function graphToken() internal view returns (IGraphToken) { - return IGraphToken(_resolveContract(keccak256("GraphToken"))); + return IGraphToken(_resolveContract(GRAPH_TOKEN)); } /** @@ -151,7 +159,7 @@ contract Managed { * @return Graph token gateway contract registered with Controller */ function graphTokenGateway() internal view returns (ITokenGateway) { - return ITokenGateway(_resolveContract(keccak256("GraphTokenGateway"))); + return ITokenGateway(_resolveContract(GRAPH_TOKEN_GATEWAY)); } /** From ee5d3cb049be94297ee88e0ebf792623477044de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 14:13:01 -0300 Subject: [PATCH 037/100] =?UTF-8?q?fix(c4-117-g11):=20using=C2=A0>=200?= =?UTF-8?q?=C2=A0costs=20more=20gas=20than=C2=A0!=3D=200=C2=A0when=20used?= =?UTF-8?q?=20on=20a=C2=A0uint=C2=A0in=20a=C2=A0require()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/gateway/L1GraphTokenGateway.sol | 2 +- contracts/l2/gateway/L2GraphTokenGateway.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index d1cbd417a..5dda7ca31 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -203,7 +203,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess ) external payable override notPaused returns (bytes memory) { IGraphToken token = graphToken(); require(_l1Token == address(token), "TOKEN_NOT_GRT"); - require(_amount > 0, "INVALID_ZERO_AMOUNT"); + require(_amount != 0, "INVALID_ZERO_AMOUNT"); require(_to != address(0), "INVALID_DESTINATION"); // nested scopes to avoid stack too deep errors diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index 251897f6f..f220d2a2c 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -143,7 +143,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran bytes calldata _data ) public payable override notPaused nonReentrant returns (bytes memory) { require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); - require(_amount > 0, "INVALID_ZERO_AMOUNT"); + require(_amount != 0, "INVALID_ZERO_AMOUNT"); require(msg.value == 0, "INVALID_NONZERO_VALUE"); require(_to != address(0), "INVALID_DESTINATION"); From 71314dd94f8abc192327e001dfd76de8f949c3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 15:10:24 -0300 Subject: [PATCH 038/100] =?UTF-8?q?fix(c4-117-g12):=20splitting=C2=A0requi?= =?UTF-8?q?re()=C2=A0statements=20that=20use=C2=A0&&=C2=A0saves=20gas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/gateway/L1GraphTokenGateway.sol | 3 ++- contracts/upgrades/GraphProxy.sol | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 5dda7ca31..4856ac452 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -143,7 +143,8 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess * @param _escrow Address of the BridgeEscrow */ function setEscrowAddress(address _escrow) external onlyGovernor { - require(_escrow != address(0) && Address.isContract(_escrow), "INVALID_ESCROW"); + require(_escrow != address(0), "INVALID_ESCROW"); + require(Address.isContract(_escrow), "MUST_BE_CONTRACT"); escrow = _escrow; emit EscrowAddressSet(_escrow); } diff --git a/contracts/upgrades/GraphProxy.sol b/contracts/upgrades/GraphProxy.sol index 6e29396db..cf7ad7812 100644 --- a/contracts/upgrades/GraphProxy.sol +++ b/contracts/upgrades/GraphProxy.sol @@ -139,10 +139,8 @@ contract GraphProxy is GraphProxyStorage { function _acceptUpgrade() internal { address _pendingImplementation = _pendingImplementation(); require(Address.isContract(_pendingImplementation), "Impl must be a contract"); - require( - _pendingImplementation != address(0) && msg.sender == _pendingImplementation, - "Only pending implementation" - ); + require(_pendingImplementation != address(0), "Impl cannot be zero address"); + require(msg.sender == _pendingImplementation, "Only pending implementation"); _setImplementation(_pendingImplementation); _setPendingImplementation(address(0)); From d5aece460b3ebd3da366a6d18de9667738ff5208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 15:33:56 -0300 Subject: [PATCH 039/100] =?UTF-8?q?fix(c4-117-g15):=20require()=C2=A0or?= =?UTF-8?q?=C2=A0revert()=C2=A0statements=20that=20check=20input=20argumen?= =?UTF-8?q?ts=20should=20be=20at=20the=20top=20of=20the=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/gateway/L1GraphTokenGateway.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 4856ac452..5527856c7 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -203,8 +203,8 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess bytes calldata _data ) external payable override notPaused returns (bytes memory) { IGraphToken token = graphToken(); - require(_l1Token == address(token), "TOKEN_NOT_GRT"); require(_amount != 0, "INVALID_ZERO_AMOUNT"); + require(_l1Token == address(token), "TOKEN_NOT_GRT"); require(_to != address(0), "INVALID_DESTINATION"); // nested scopes to avoid stack too deep errors From 2ad9f4e3e4c8ad96a108120adba8a3dd4c97a52d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 15:44:54 -0300 Subject: [PATCH 040/100] =?UTF-8?q?fix(c4-6-g07):=20expressions=20for=20co?= =?UTF-8?q?nstant=20values=20such=20as=20a=20call=20to=C2=A0keccak256(),?= =?UTF-8?q?=20should=20use=C2=A0=20immutable=C2=A0rather=20than=C2=A0const?= =?UTF-8?q?ant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/l2/token/GraphTokenUpgradeable.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index 4ecfb9ffa..e9438cb3c 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -31,15 +31,15 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra // -- EIP712 -- // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator - bytes32 private constant DOMAIN_TYPE_HASH = + bytes32 private immutable DOMAIN_TYPE_HASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" ); - bytes32 private constant DOMAIN_NAME_HASH = keccak256("Graph Token"); - bytes32 private constant DOMAIN_VERSION_HASH = keccak256("0"); - bytes32 private constant DOMAIN_SALT = + bytes32 private immutable DOMAIN_NAME_HASH = keccak256("Graph Token"); + bytes32 private immutable DOMAIN_VERSION_HASH = keccak256("0"); + bytes32 private immutable DOMAIN_SALT = 0xe33842a7acd1d5a1d28f25a931703e5605152dc48d64dc4716efdae1f5659591; // Randomly generated salt - bytes32 private constant PERMIT_TYPEHASH = + bytes32 private immutable PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); From e0440a85c1e8ae313dd3d230fd075b7fdb8927ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 15:56:23 -0300 Subject: [PATCH 041/100] =?UTF-8?q?fix(c4-6-g08):=20duplicated=C2=A0requir?= =?UTF-8?q?e()/revert()=C2=A0checks=20should=20be=20refactored=20to=20a=20?= =?UTF-8?q?modifier=20or=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/l2/token/GraphTokenUpgradeable.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index e9438cb3c..ce22ba538 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -114,7 +114,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra * @param _account Address of the minter */ function removeMinter(address _account) external onlyGovernor { - require(_minters[_account], "NOT_A_MINTER"); + require(isMinter(_account), "NOT_A_MINTER"); _removeMinter(_account); } @@ -122,7 +122,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra * @dev Renounce to be a minter. */ function renounceMinter() external { - require(_minters[msg.sender], "NOT_A_MINTER"); + require(isMinter(msg.sender), "NOT_A_MINTER"); _removeMinter(msg.sender); } From 3805ca9a2d5595dc3edb0ae1ed8fe111c4f63769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 15:59:13 -0300 Subject: [PATCH 042/100] =?UTF-8?q?fix(c4-6-13):=20use=C2=A0calldata=C2=A0?= =?UTF-8?q?instead=20of=C2=A0memory=C2=A0for=20function=20parameters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/gateway/L1GraphTokenGateway.sol | 2 +- contracts/l2/gateway/L2GraphTokenGateway.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 5527856c7..7688a5e29 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -285,7 +285,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess * @return Base ether value required to keep retryable ticket alive * @return Additional data sent to L2 */ - function parseOutboundData(bytes memory _data) + function parseOutboundData(bytes calldata _data) private view returns ( diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index f220d2a2c..edd7ff610 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -283,7 +283,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran * @return Sender of the tx * @return Any other data sent to L1 */ - function parseOutboundData(bytes memory _data) private view returns (address, bytes memory) { + function parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) { address from; bytes memory extraData; if (msg.sender == l2Router) { From 54157a76e46a0704bc63b8cb29ddacd1c8d3e259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 16:15:57 -0300 Subject: [PATCH 043/100] fix(c4-86-g07): emitting or returning storage values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/l2/token/L2GraphToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/l2/token/L2GraphToken.sol b/contracts/l2/token/L2GraphToken.sol index 0e1d942c9..7a5abf78c 100644 --- a/contracts/l2/token/L2GraphToken.sol +++ b/contracts/l2/token/L2GraphToken.sol @@ -59,7 +59,7 @@ contract L2GraphToken is GraphTokenUpgradeable, IArbToken { function setGateway(address _gw) external onlyGovernor { require(_gw != address(0), "INVALID_GATEWAY"); gateway = _gw; - emit GatewaySet(gateway); + emit GatewaySet(_gw); } /** From 7d29d6d57892e99ed5702df19b91c7920cc40cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 16:25:18 -0300 Subject: [PATCH 044/100] fix(c4-172-g02): set allowance to 0 instead of decreasing allowance by approved amount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/gateway/BridgeEscrow.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/gateway/BridgeEscrow.sol b/contracts/gateway/BridgeEscrow.sol index 1c1d85f54..4d9c11e0d 100644 --- a/contracts/gateway/BridgeEscrow.sol +++ b/contracts/gateway/BridgeEscrow.sol @@ -36,7 +36,6 @@ contract BridgeEscrow is Initializable, GraphUpgradeable, Managed { * @param _spender Address of the spender that will be revoked */ function revokeAll(address _spender) external onlyGovernor { - IGraphToken grt = graphToken(); - grt.decreaseAllowance(_spender, grt.allowance(address(this), _spender)); + graphToken().approve(_spender, 0); } } From 534d224f75895160f0d989a540164e811e906fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 16:28:03 -0300 Subject: [PATCH 045/100] fix(c4-287-g01): remove unused local variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/l2/gateway/L2GraphTokenGateway.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index edd7ff610..371db711f 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -238,8 +238,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran if (_data.length > 0) { bytes memory callhookData; { - bytes memory gatewayData; - (gatewayData, callhookData) = abi.decode(_data, (bytes, bytes)); + (, callhookData) = abi.decode(_data, (bytes, bytes)); } ICallhookReceiver(_to).onTokenTransfer(_from, _amount, callhookData); } From 0b7ad55d821bcdffed3ed236668be841f22d14fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 16:30:29 -0300 Subject: [PATCH 046/100] fix(c4-272): remove unnecessary use of safe math MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/l2/token/GraphTokenUpgradeable.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index ce22ba538..10a77556a 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -4,7 +4,6 @@ pragma solidity ^0.7.6; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol"; import "@openzeppelin/contracts/cryptography/ECDSA.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; import "../../upgrades/GraphUpgradeable.sol"; import "../../governance/Governed.sol"; @@ -26,7 +25,6 @@ import "../../governance/Governed.sol"; * the original's constructor + non-upgradeable approach. */ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable { - using SafeMath for uint256; // -- EIP712 -- // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator @@ -96,7 +94,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra require(_owner == recoveredAddress, "GRT: invalid permit"); require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit"); - nonces[_owner] = nonces[_owner].add(1); + nonces[_owner] = nonces[_owner] + 1; _approve(_owner, _spender, _value); } From d091781d6468523c6f1aaca5fd16f137e33f60b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 16:34:11 -0300 Subject: [PATCH 047/100] fix(c4-272): remove unnecessary usage of safemath MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/l2/token/GraphTokenUpgradeable.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index 10a77556a..ac6ce63f0 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -25,7 +25,6 @@ import "../../governance/Governed.sol"; * the original's constructor + non-upgradeable approach. */ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable { - // -- EIP712 -- // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator From 1f85e126bb51423d6cfd3a6f12aa8a21199cd7d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 16:38:35 -0300 Subject: [PATCH 048/100] fix: further optimize governed contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/governance/Governed.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/governance/Governed.sol b/contracts/governance/Governed.sol index 36d96d13a..697f2fa12 100644 --- a/contracts/governance/Governed.sol +++ b/contracts/governance/Governed.sol @@ -51,16 +51,16 @@ contract Governed { * This function must called by the pending governor. */ function acceptOwnership() external { - address _pendingGovernor = pendingGovernor; + address oldPendingGovernor = pendingGovernor; + require( - _pendingGovernor != address(0) && msg.sender == _pendingGovernor, + oldPendingGovernor != address(0) && msg.sender == oldPendingGovernor, "Caller must be pending governor" ); address oldGovernor = governor; - address oldPendingGovernor = _pendingGovernor; - governor = _pendingGovernor; + governor = oldPendingGovernor; pendingGovernor = address(0); emit NewOwnership(oldGovernor, governor); From 27902ccec83688bc895a83c21aebe5ae511a6086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 16:39:30 -0300 Subject: [PATCH 049/100] fix: add visibility to managed immutable vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/governance/Managed.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index fcec6e47a..562028c15 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -29,12 +29,12 @@ contract Managed { uint256[10] private __gap; // Immutables - bytes32 immutable CURATION = keccak256("Curation"); - bytes32 immutable EPOCH_MANAGER = keccak256("EpochManager"); - bytes32 immutable REWARDS_MANAGER = keccak256("RewardsManager"); - bytes32 immutable STAKING = keccak256("Staking"); - bytes32 immutable GRAPH_TOKEN = keccak256("GraphToken"); - bytes32 immutable GRAPH_TOKEN_GATEWAY = keccak256("GraphTokenGateway"); + bytes32 private immutable CURATION = keccak256("Curation"); + bytes32 private immutable EPOCH_MANAGER = keccak256("EpochManager"); + bytes32 private immutable REWARDS_MANAGER = keccak256("RewardsManager"); + bytes32 private immutable STAKING = keccak256("Staking"); + bytes32 private immutable GRAPH_TOKEN = keccak256("GraphToken"); + bytes32 private immutable GRAPH_TOKEN_GATEWAY = keccak256("GraphTokenGateway"); // -- Events -- From e3a668ec5939cfe3a926b871fb5d1337def17934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 24 Oct 2022 16:54:18 -0300 Subject: [PATCH 050/100] fix: update e2e tests to new error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- e2e/deployment/config/l1/l1GraphTokenGateway.test.ts | 2 +- e2e/deployment/config/l2/l2GraphTokenGateway.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts index a2d8edbae..cce20c735 100644 --- a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -26,7 +26,7 @@ describe('[L1] L1GraphTokenGateway configuration', function () { describe('calls with unauthorized user', () => { it('initialize should revert', async function () { const tx = L1GraphTokenGateway.connect(unauthorized).initialize(unauthorized.address) - await expect(tx).revertedWith('Caller must be the implementation') + await expect(tx).revertedWith('Only implementation') }) it('setArbitrumAddresses should revert', async function () { diff --git a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts index e4626b59b..c8944927c 100644 --- a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts @@ -26,7 +26,7 @@ describe('[L2] L2GraphTokenGateway configuration', function () { describe('calls with unauthorized user', () => { it('initialize should revert', async function () { const tx = L2GraphTokenGateway.connect(unauthorized).initialize(unauthorized.address) - await expect(tx).revertedWith('Caller must be the implementation') + await expect(tx).revertedWith('Only implementation') }) it('setL2Router should revert', async function () { From 23f9dad29a0f1848d8266b3fe2ead2279f4826f6 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 13:19:32 -0300 Subject: [PATCH 051/100] fix: avoid variable shadowing (C4 QA) --- contracts/governance/Controller.sol | 10 ++++++---- contracts/upgrades/GraphProxy.sol | 16 ++++++++-------- contracts/upgrades/GraphProxyStorage.sol | 14 +++++++------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/contracts/governance/Controller.sol b/contracts/governance/Controller.sol index 2e4418a59..2f0052b89 100644 --- a/contracts/governance/Controller.sol +++ b/contracts/governance/Controller.sol @@ -94,17 +94,19 @@ contract Controller is Governed, Pausable, IController { /** * @notice Change the partial paused state of the contract * Partial pause is intended as a partial pause of the protocol + * @param _toPause True if the contracts should be (partially) paused, false otherwise */ - function setPartialPaused(bool _partialPaused) external override onlyGovernorOrGuardian { - _setPartialPaused(_partialPaused); + function setPartialPaused(bool _toPause) external override onlyGovernorOrGuardian { + _setPartialPaused(_toPause); } /** * @notice Change the paused state of the contract * Full pause most of protocol functions + * @param _toPause True if the contracts should be paused, false otherwise */ - function setPaused(bool _paused) external override onlyGovernorOrGuardian { - _setPaused(_paused); + function setPaused(bool _toPause) external override onlyGovernorOrGuardian { + _setPaused(_toPause); } /** diff --git a/contracts/upgrades/GraphProxy.sol b/contracts/upgrades/GraphProxy.sol index cf7ad7812..7d62cdafb 100644 --- a/contracts/upgrades/GraphProxy.sol +++ b/contracts/upgrades/GraphProxy.sol @@ -19,7 +19,7 @@ contract GraphProxy is GraphProxyStorage { * the sender is the admin. */ modifier ifAdmin() { - if (msg.sender == _admin()) { + if (msg.sender == _getAdmin()) { _; } else { _fallback(); @@ -31,7 +31,7 @@ contract GraphProxy is GraphProxyStorage { * the sender is the admin or pending implementation. */ modifier ifAdminOrPendingImpl() { - if (msg.sender == _admin() || msg.sender == _pendingImplementation()) { + if (msg.sender == _getAdmin() || msg.sender == _getPendingImplementation()) { _; } else { _fallback(); @@ -67,7 +67,7 @@ contract GraphProxy is GraphProxyStorage { * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function admin() external ifAdminOrPendingImpl returns (address) { - return _admin(); + return _getAdmin(); } /** @@ -80,7 +80,7 @@ contract GraphProxy is GraphProxyStorage { * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` */ function implementation() external ifAdminOrPendingImpl returns (address) { - return _implementation(); + return _getImplementation(); } /** @@ -93,7 +93,7 @@ contract GraphProxy is GraphProxyStorage { * `0x9e5eddc59e0b171f57125ab86bee043d9128098c3a6b9adb4f2e86333c2f6f8c` */ function pendingImplementation() external ifAdminOrPendingImpl returns (address) { - return _pendingImplementation(); + return _getPendingImplementation(); } /** @@ -129,7 +129,7 @@ contract GraphProxy is GraphProxyStorage { function acceptUpgradeAndCall(bytes calldata data) external ifAdminOrPendingImpl { _acceptUpgrade(); // solhint-disable-next-line avoid-low-level-calls - (bool success, ) = _implementation().delegatecall(data); + (bool success, ) = _getImplementation().delegatecall(data); require(success); } @@ -137,7 +137,7 @@ contract GraphProxy is GraphProxyStorage { * @dev Admin function for new implementation to accept its role as implementation. */ function _acceptUpgrade() internal { - address _pendingImplementation = _pendingImplementation(); + address _pendingImplementation = _getPendingImplementation(); require(Address.isContract(_pendingImplementation), "Impl must be a contract"); require(_pendingImplementation != address(0), "Impl cannot be zero address"); require(msg.sender == _pendingImplementation, "Only pending implementation"); @@ -152,7 +152,7 @@ contract GraphProxy is GraphProxyStorage { * external caller. */ function _fallback() internal { - require(msg.sender != _admin(), "Cannot fallback to proxy target"); + require(msg.sender != _getAdmin(), "Cannot fallback to proxy target"); assembly { // (a) get free memory pointer diff --git a/contracts/upgrades/GraphProxyStorage.sol b/contracts/upgrades/GraphProxyStorage.sol index 950f9776b..0911ce2ee 100644 --- a/contracts/upgrades/GraphProxyStorage.sol +++ b/contracts/upgrades/GraphProxyStorage.sol @@ -59,14 +59,14 @@ contract GraphProxyStorage { * @dev Modifier to check whether the `msg.sender` is the admin. */ modifier onlyAdmin() { - require(msg.sender == _admin(), "Caller must be admin"); + require(msg.sender == _getAdmin(), "Caller must be admin"); _; } /** * @return adm The admin slot. */ - function _admin() internal view returns (address adm) { + function _getAdmin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) @@ -83,14 +83,14 @@ contract GraphProxyStorage { sstore(slot, _newAdmin) } - emit AdminUpdated(_admin(), _newAdmin); + emit AdminUpdated(_getAdmin(), _newAdmin); } /** * @dev Returns the current implementation. * @return impl Address of the current implementation */ - function _implementation() internal view returns (address impl) { + function _getImplementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) @@ -101,7 +101,7 @@ contract GraphProxyStorage { * @dev Returns the current pending implementation. * @return impl Address of the current pending implementation */ - function _pendingImplementation() internal view returns (address impl) { + function _getPendingImplementation() internal view returns (address impl) { bytes32 slot = PENDING_IMPLEMENTATION_SLOT; assembly { impl := sload(slot) @@ -113,7 +113,7 @@ contract GraphProxyStorage { * @param _newImplementation Address of the new implementation */ function _setImplementation(address _newImplementation) internal { - address oldImplementation = _implementation(); + address oldImplementation = _getImplementation(); bytes32 slot = IMPLEMENTATION_SLOT; assembly { @@ -128,7 +128,7 @@ contract GraphProxyStorage { * @param _newImplementation Address of the new pending implementation */ function _setPendingImplementation(address _newImplementation) internal { - address oldPendingImplementation = _pendingImplementation(); + address oldPendingImplementation = _getPendingImplementation(); bytes32 slot = PENDING_IMPLEMENTATION_SLOT; assembly { From d90a2f37bf5caa68f7da33449524fbee4a0ecab9 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 15:18:21 -0300 Subject: [PATCH 052/100] fix: improve NatSpec and a few other details (C4 QA) --- contracts/gateway/BridgeEscrow.sol | 6 +- contracts/gateway/GraphTokenGateway.sol | 3 +- contracts/gateway/ICallhookReceiver.sol | 2 +- contracts/gateway/L1GraphTokenGateway.sol | 44 +++++++------- contracts/governance/Controller.sol | 8 ++- contracts/governance/Governed.sol | 11 +++- contracts/governance/Managed.sol | 64 ++++++++++++++------ contracts/governance/Pausable.sol | 27 ++++++--- contracts/l2/gateway/L2GraphTokenGateway.sol | 28 ++++----- contracts/l2/token/GraphTokenUpgradeable.sol | 28 ++++++--- contracts/l2/token/L2GraphToken.sol | 24 ++++---- contracts/upgrades/GraphProxy.sol | 60 ++++++++++-------- contracts/upgrades/GraphProxyAdmin.sol | 23 ++++--- contracts/upgrades/GraphProxyStorage.sol | 6 ++ contracts/upgrades/GraphUpgradeable.sol | 8 ++- 15 files changed, 213 insertions(+), 129 deletions(-) diff --git a/contracts/gateway/BridgeEscrow.sol b/contracts/gateway/BridgeEscrow.sol index 4d9c11e0d..c18b839f2 100644 --- a/contracts/gateway/BridgeEscrow.sol +++ b/contracts/gateway/BridgeEscrow.sol @@ -16,7 +16,7 @@ import "../token/IGraphToken.sol"; */ contract BridgeEscrow is Initializable, GraphUpgradeable, Managed { /** - * @dev Initialize this contract. + * @notice Initialize the BridgeEscrow contract. * @param _controller Address of the Controller that manages this contract */ function initialize(address _controller) external onlyImpl initializer { @@ -24,7 +24,7 @@ contract BridgeEscrow is Initializable, GraphUpgradeable, Managed { } /** - * @dev Approve a spender (i.e. a bridge that manages the GRT funds held by the escrow) + * @notice Approve a spender (i.e. a bridge that manages the GRT funds held by the escrow) * @param _spender Address of the spender that will be approved */ function approveAll(address _spender) external onlyGovernor { @@ -32,7 +32,7 @@ contract BridgeEscrow is Initializable, GraphUpgradeable, Managed { } /** - * @dev Revoke a spender (i.e. a bridge that will no longer manage the GRT funds held by the escrow) + * @notice Revoke a spender (i.e. a bridge that will no longer manage the GRT funds held by the escrow) * @param _spender Address of the spender that will be revoked */ function revokeAll(address _spender) external onlyGovernor { diff --git a/contracts/gateway/GraphTokenGateway.sol b/contracts/gateway/GraphTokenGateway.sol index ed59b149a..66c6e7110 100644 --- a/contracts/gateway/GraphTokenGateway.sol +++ b/contracts/gateway/GraphTokenGateway.sol @@ -12,7 +12,7 @@ import "../governance/Managed.sol"; * @dev This includes everything that's shared between the L1 and L2 sides of the bridge. */ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITokenGateway { - // Storage gap added in case we need to add state variables to this contract + /// @dev Storage gap added in case we need to add state variables to this contract uint256[50] private __gap; /** @@ -56,6 +56,7 @@ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITok /** * @notice Getter to access paused state of this contract + * @return True if the contract is paused, false otherwise */ function paused() external view returns (bool) { return _paused; diff --git a/contracts/gateway/ICallhookReceiver.sol b/contracts/gateway/ICallhookReceiver.sol index fb7492bb7..2f71d180c 100644 --- a/contracts/gateway/ICallhookReceiver.sol +++ b/contracts/gateway/ICallhookReceiver.sol @@ -10,7 +10,7 @@ pragma solidity ^0.7.6; interface ICallhookReceiver { /** - * @dev Receive tokens with a callhook from the bridge + * @notice Receive tokens with a callhook from the bridge * @param _from Token sender in L1 * @param _amount Amount of tokens that were transferred * @param _data ABI-encoded callhook data diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 7688a5e29..eed07fcb8 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -22,20 +22,20 @@ import "./GraphTokenGateway.sol"; contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMessenger { using SafeMath for uint256; - // Address of the Graph Token contract on L2 + /// Address of the Graph Token contract on L2 address public l2GRT; - // Address of the Arbitrum Inbox + /// Address of the Arbitrum Inbox address public inbox; - // Address of the Arbitrum Gateway Router on L1 + /// Address of the Arbitrum Gateway Router on L1 address public l1Router; - // Address of the L2GraphTokenGateway on L2 that is the counterpart of this gateway + /// Address of the L2GraphTokenGateway on L2 that is the counterpart of this gateway address public l2Counterpart; - // Address of the BridgeEscrow contract that holds the GRT in the bridge + /// Address of the BridgeEscrow contract that holds the GRT in the bridge address public escrow; - // Addresses for which this mapping is true are allowed to send callhooks in outbound transfers + /// Addresses for which this mapping is true are allowed to send callhooks in outbound transfers mapping(address => bool) public callhookWhitelist; - // Emitted when an outbound transfer is initiated, i.e. tokens are deposited from L1 to L2 + /// Emitted when an outbound transfer is initiated, i.e. tokens are deposited from L1 to L2 event DepositInitiated( address l1Token, address indexed from, @@ -44,7 +44,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess uint256 amount ); - // Emitted when an incoming transfer is finalized, i.e tokens are withdrawn from L2 to L1 + /// Emitted when an incoming transfer is finalized, i.e tokens are withdrawn from L2 to L1 event WithdrawalFinalized( address l1Token, address indexed from, @@ -53,17 +53,17 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess uint256 amount ); - // Emitted when the Arbitrum Inbox and Gateway Router addresses have been updated + /// Emitted when the Arbitrum Inbox and Gateway Router addresses have been updated event ArbitrumAddressesSet(address inbox, address l1Router); - // Emitted when the L2 GRT address has been updated + /// Emitted when the L2 GRT address has been updated event L2TokenAddressSet(address l2GRT); - // Emitted when the counterpart L2GraphTokenGateway address has been updated + /// Emitted when the counterpart L2GraphTokenGateway address has been updated event L2CounterpartAddressSet(address l2Counterpart); - // Emitted when the escrow address has been updated + /// Emitted when the escrow address has been updated event EscrowAddressSet(address escrow); - // Emitted when an address is added to the callhook whitelist + /// Emitted when an address is added to the callhook whitelist event AddedToCallhookWhitelist(address newWhitelisted); - // Emitted when an address is removed from the callhook whitelist + /// Emitted when an address is removed from the callhook whitelist event RemovedFromCallhookWhitelist(address notWhitelisted); /** @@ -86,8 +86,8 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @dev Initialize this contract. - * The contract will be paused. + * @notice Initialize the L1GraphTokenGateway contract. + * @dev The contract will be paused. * Note some parameters have to be set separately as they are generally * not expected to be available at initialization time: * - inbox and l1Router using setArbitrumAddresses @@ -104,7 +104,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @dev Sets the addresses for L1 contracts provided by Arbitrum + * @notice Sets the addresses for L1 contracts provided by Arbitrum * @param _inbox Address of the Inbox that is part of the Arbitrum Bridge * @param _l1Router Address of the Gateway Router */ @@ -119,7 +119,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @dev Sets the address of the L2 Graph Token + * @notice Sets the address of the L2 Graph Token * @param _l2GRT Address of the GRT contract on L2 */ function setL2TokenAddress(address _l2GRT) external onlyGovernor { @@ -129,7 +129,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @dev Sets the address of the counterpart gateway on L2 + * @notice Sets the address of the counterpart gateway on L2 * @param _l2Counterpart Address of the corresponding L2GraphTokenGateway on Arbitrum */ function setL2CounterpartAddress(address _l2Counterpart) external onlyGovernor { @@ -139,7 +139,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @dev Sets the address of the escrow contract on L1 + * @notice Sets the address of the escrow contract on L1 * @param _escrow Address of the BridgeEscrow */ function setEscrowAddress(address _escrow) external onlyGovernor { @@ -150,7 +150,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @dev Adds an address to the callhook whitelist. + * @notice Adds an address to the callhook whitelist. * This address will be allowed to include callhooks when transferring tokens. * @param _newWhitelisted Address to add to the whitelist */ @@ -163,7 +163,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @dev Removes an address from the callhook whitelist. + * @notice Removes an address from the callhook whitelist. * This address will no longer be allowed to include callhooks when transferring tokens. * @param _notWhitelisted Address to remove from the whitelist */ diff --git a/contracts/governance/Controller.sol b/contracts/governance/Controller.sol index 2f0052b89..18d1a9307 100644 --- a/contracts/governance/Controller.sol +++ b/contracts/governance/Controller.sol @@ -13,13 +13,14 @@ import "./Pausable.sol"; * https://github.com/livepeer/protocol/blob/streamflow/contracts/Controller.sol */ contract Controller is Governed, Pausable, IController { - // Track contract ids to contract proxy address + /// @dev Track contract ids to contract proxy address mapping(bytes32 => address) private registry; + /// Emitted when the proxy address for a protocol contract has been set event SetContractProxy(bytes32 indexed id, address contractAddress); /** - * @dev Contract constructor. + * @notice Controller contract constructor. */ constructor() { Governed._initialize(msg.sender); @@ -74,6 +75,7 @@ contract Controller is Governed, Pausable, IController { /** * @notice Get contract proxy address by its id * @param _id Contract id + * @return Address of the proxy contract for the provided id */ function getContractProxy(bytes32 _id) public view override returns (address) { return registry[_id]; @@ -120,6 +122,7 @@ contract Controller is Governed, Pausable, IController { /** * @notice Getter to access paused + * @return True if the contracts are paused, false otherwise */ function paused() external view override returns (bool) { return _paused; @@ -127,6 +130,7 @@ contract Controller is Governed, Pausable, IController { /** * @notice Getter to access partial pause status + * @return True if the contracts are partially paused, false otherwise */ function partialPaused() external view override returns (bool) { return _partialPaused; diff --git a/contracts/governance/Governed.sol b/contracts/governance/Governed.sol index 697f2fa12..d194992ea 100644 --- a/contracts/governance/Governed.sol +++ b/contracts/governance/Governed.sol @@ -9,12 +9,16 @@ pragma solidity ^0.7.6; contract Governed { // -- State -- + /// Address of the governor address public governor; + /// Address of the new governor that is pending acceptance address public pendingGovernor; // -- Events -- + /// Emitted when a new owner/governor has been set, but is pending acceptance event NewPendingOwnership(address indexed from, address indexed to); + /// Emitted when a new owner/governor has accepted their role event NewOwnership(address indexed from, address indexed to); /** @@ -26,14 +30,15 @@ contract Governed { } /** - * @dev Initialize the governor to the contract caller. + * @dev Initialize the governor for this contract + * @param _initGovernor Address of the governor */ function _initialize(address _initGovernor) internal { governor = _initGovernor; } /** - * @dev Admin function to begin change of governor. The `_newGovernor` must call + * @notice Admin function to begin change of governor. The `_newGovernor` must call * `acceptOwnership` to finalize the transfer. * @param _newGovernor Address of new `governor` */ @@ -47,7 +52,7 @@ contract Governed { } /** - * @dev Admin function for pending governor to accept role and update governor. + * @notice Admin function for pending governor to accept role and update governor. * This function must called by the pending governor. */ function acceptOwnership() external { diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index 562028c15..220132018 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -11,6 +11,8 @@ import "../staking/IStaking.sol"; import "../token/IGraphToken.sol"; import "../arbitrum/ITokenGateway.sol"; +import "./IManaged.sol"; + /** * @title Graph Managed contract * @dev The Managed contract provides an interface to interact with the Controller. @@ -20,12 +22,14 @@ import "../arbitrum/ITokenGateway.sol"; * Inspired by Livepeer: * https://github.com/livepeer/protocol/blob/streamflow/contracts/Controller.sol */ -contract Managed { +contract Managed is IManaged { // -- State -- - // Controller that contract is registered with + /// Controller that contract is registered with IController public controller; + /// @dev Cache for the addresses of the contracts retrieved from the controller mapping(bytes32 => address) private addressCache; + /// @dev Gap for future storage variables uint256[10] private __gap; // Immutables @@ -38,50 +42,72 @@ contract Managed { // -- Events -- + /// Emitted when a contract parameter has been updated event ParameterUpdated(string param); + /// Emitted when the controller address has been set event SetController(address controller); - /** - * @dev Emitted when contract with `nameHash` is synced to `contractAddress`. - */ + /// Emitted when contract with `nameHash` is synced to `contractAddress`. event ContractSynced(bytes32 indexed nameHash, address contractAddress); // -- Modifiers -- + /** + * @dev Revert if the controller is paused or partially paused + */ function _notPartialPaused() internal view { require(!controller.paused(), "Paused"); require(!controller.partialPaused(), "Partial-paused"); } + /** + * @dev Revert if the controller is paused + */ function _notPaused() internal view virtual { require(!controller.paused(), "Paused"); } + /** + * @dev Revert if the caller is not the governor + */ function _onlyGovernor() internal view { require(msg.sender == controller.getGovernor(), "Only Controller governor"); } + /** + * @dev Revert if the caller is not the Controller + */ function _onlyController() internal view { require(msg.sender == address(controller), "Caller must be Controller"); } + /** + * @dev Revert if the controller is paused or partially paused + */ modifier notPartialPaused() { _notPartialPaused(); _; } + /** + * @dev Revert if the controller is paused + */ modifier notPaused() { _notPaused(); _; } - // Check if sender is controller. + /** + * @dev Revert if the caller is not the Controller + */ modifier onlyController() { _onlyController(); _; } - // Check if sender is the governor. + /** + * @dev Revert if the caller is not the governor + */ modifier onlyGovernor() { _onlyGovernor(); _; @@ -90,7 +116,8 @@ contract Managed { // -- Functions -- /** - * @dev Initialize the controller. + * @dev Initialize a Managed contract + * @param _controller Address for the Controller that manages this contract */ function _initialize(address _controller) internal { _setController(_controller); @@ -100,7 +127,7 @@ contract Managed { * @notice Set Controller. Only callable by current controller. * @param _controller Controller contract address */ - function setController(address _controller) external onlyController { + function setController(address _controller) external override onlyController { _setController(_controller); } @@ -115,7 +142,7 @@ contract Managed { } /** - * @dev Return Curation interface. + * @dev Return Curation interface * @return Curation contract registered with Controller */ function curation() internal view returns (ICuration) { @@ -123,7 +150,7 @@ contract Managed { } /** - * @dev Return EpochManager interface. + * @dev Return EpochManager interface * @return Epoch manager contract registered with Controller */ function epochManager() internal view returns (IEpochManager) { @@ -131,7 +158,7 @@ contract Managed { } /** - * @dev Return RewardsManager interface. + * @dev Return RewardsManager interface * @return Rewards manager contract registered with Controller */ function rewardsManager() internal view returns (IRewardsManager) { @@ -139,7 +166,7 @@ contract Managed { } /** - * @dev Return Staking interface. + * @dev Return Staking interface * @return Staking contract registered with Controller */ function staking() internal view returns (IStaking) { @@ -147,7 +174,7 @@ contract Managed { } /** - * @dev Return GraphToken interface. + * @dev Return GraphToken interface * @return Graph token contract registered with Controller */ function graphToken() internal view returns (IGraphToken) { @@ -155,7 +182,7 @@ contract Managed { } /** - * @dev Return GraphTokenGateway (L1 or L2) interface. + * @dev Return GraphTokenGateway (L1 or L2) interface * @return Graph token gateway contract registered with Controller */ function graphTokenGateway() internal view returns (ITokenGateway) { @@ -163,7 +190,8 @@ contract Managed { } /** - * @dev Resolve a contract address from the cache or the Controller if not found. + * @dev Resolve a contract address from the cache or the Controller if not found + * @param _nameHash keccak256 hash of the contract name * @return Address of the contract */ function _resolveContract(bytes32 _nameHash) internal view returns (address) { @@ -188,8 +216,8 @@ contract Managed { } /** - * @dev Sync protocol contract addresses from the Controller registry. - * This function will cache all the contracts using the latest addresses + * @notice Sync protocol contract addresses from the Controller registry + * @dev This function will cache all the contracts using the latest addresses * Anyone can call the function whenever a Proxy contract change in the * controller to ensure the protocol is using the latest version */ diff --git a/contracts/governance/Pausable.sol b/contracts/governance/Pausable.sol index b1ecbdf30..4b2c18459 100644 --- a/contracts/governance/Pausable.sol +++ b/contracts/governance/Pausable.sol @@ -3,25 +3,35 @@ pragma solidity ^0.7.6; contract Pausable { - // Partial paused paused exit and enter functions for GRT, but not internal - // functions, such as allocating + /** + * @dev "Partial paused" pauses exit and enter functions for GRT, but not internal + * functions, such as allocating + */ bool internal _partialPaused; - // Paused will pause all major protocol functions + /** + * @dev Paused will pause all major protocol functions + */ bool internal _paused; - // Time last paused for both pauses + /// Timestamp for the last time the partial pause was set uint256 public lastPausePartialTime; + /// Timestamp for the last time the full pause was set uint256 public lastPauseTime; - // Pause guardian is a separate entity from the governor that can pause + /// Pause guardian is a separate entity from the governor that can + /// pause and unpause the protocol, fully or partially address public pauseGuardian; + /// Emitted when the partial pause state changed event PartialPauseChanged(bool isPaused); + /// Emitted when the full pause state changed event PauseChanged(bool isPaused); + /// Emitted when the pause guardian is changed event NewPauseGuardian(address indexed oldPauseGuardian, address indexed pauseGuardian); /** - * @notice Change the partial paused state of the contract + * @dev Change the partial paused state of the contract + * @param _toPause New value for the partial pause state (true means the contracts will be partially paused) */ function _setPartialPaused(bool _toPause) internal { if (_toPause == _partialPaused) { @@ -35,7 +45,8 @@ contract Pausable { } /** - * @notice Change the paused state of the contract + * @dev Change the paused state of the contract + * @param _toPause New value for the pause state (true means the contracts will be paused) */ function _setPaused(bool _toPause) internal { if (_toPause == _paused) { @@ -49,7 +60,7 @@ contract Pausable { } /** - * @notice Change the Pause Guardian + * @dev Change the Pause Guardian * @param newPauseGuardian The address of the new Pause Guardian */ function _setPauseGuardian(address newPauseGuardian) internal { diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index 371db711f..3ff95a393 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -23,20 +23,20 @@ import "../token/L2GraphToken.sol"; contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, ReentrancyGuardUpgradeable { using SafeMath for uint256; - // Address of the Graph Token contract on L1 + /// Address of the Graph Token contract on L1 address public l1GRT; - // Address of the L1GraphTokenGateway that is the counterpart of this gateway on L1 + /// Address of the L1GraphTokenGateway that is the counterpart of this gateway on L1 address public l1Counterpart; - // Address of the Arbitrum Gateway Router on L2 + /// Address of the Arbitrum Gateway Router on L2 address public l2Router; - // Calldata included in an outbound transfer, stored as a structure for convenience and stack depth + /// @dev Calldata included in an outbound transfer, stored as a structure for convenience and stack depth struct OutboundCalldata { address from; bytes extraData; } - // Emitted when an incoming transfer is finalized, i.e. tokens were deposited from L1 to L2 + /// Emitted when an incoming transfer is finalized, i.e. tokens were deposited from L1 to L2 event DepositFinalized( address indexed l1Token, address indexed from, @@ -44,7 +44,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran uint256 amount ); - // Emitted when an outbound transfer is initiated, i.e. tokens are being withdrawn from L2 back to L1 + /// Emitted when an outbound transfer is initiated, i.e. tokens are being withdrawn from L2 back to L1 event WithdrawalInitiated( address l1Token, address indexed from, @@ -54,11 +54,11 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran uint256 amount ); - // Emitted when the Arbitrum Gateway Router address on L2 has been updated + /// Emitted when the Arbitrum Gateway Router address on L2 has been updated event L2RouterSet(address l2Router); - // Emitted when the L1 Graph Token address has been updated + /// Emitted when the L1 Graph Token address has been updated event L1TokenAddressSet(address l1GRT); - // Emitted when the address of the counterpart gateway on L1 has been updated + /// Emitted when the address of the counterpart gateway on L1 has been updated event L1CounterpartAddressSet(address l1Counterpart); /** @@ -74,8 +74,8 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran } /** - * @dev Initialize this contract. - * The contract will be paused. + * @notice Initialize the L2GraphTokenGateway contract. + * @dev The contract will be paused. * Note some parameters have to be set separately as they are generally * not expected to be available at initialization time: * - l2Router using setL2Router @@ -91,7 +91,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran } /** - * @dev Sets the address of the Arbitrum Gateway Router on L2 + * @notice Sets the address of the Arbitrum Gateway Router on L2 * @param _l2Router Address of the L2 Router (provided by Arbitrum) */ function setL2Router(address _l2Router) external onlyGovernor { @@ -101,7 +101,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran } /** - * @dev Sets the address of the Graph Token on L1 + * @notice Sets the address of the Graph Token on L1 * @param _l1GRT L1 address of the Graph Token contract */ function setL1TokenAddress(address _l1GRT) external onlyGovernor { @@ -111,7 +111,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran } /** - * @dev Sets the address of the counterpart gateway on L1 + * @notice Sets the address of the counterpart gateway on L1 * @param _l1Counterpart Address of the L1GraphTokenGateway on L1 */ function setL1CounterpartAddress(address _l1Counterpart) external onlyGovernor { diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index ac6ce63f0..241041635 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -28,14 +28,19 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra // -- EIP712 -- // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator + /// @dev Hash of the EIP-712 Domain type bytes32 private immutable DOMAIN_TYPE_HASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" ); + /// @dev Hash of the EIP-712 Domain name bytes32 private immutable DOMAIN_NAME_HASH = keccak256("Graph Token"); + /// @dev Hash of the EIP-712 Domain version bytes32 private immutable DOMAIN_VERSION_HASH = keccak256("0"); + /// @dev EIP-712 Domain salt bytes32 private immutable DOMAIN_SALT = 0xe33842a7acd1d5a1d28f25a931703e5605152dc48d64dc4716efdae1f5659591; // Randomly generated salt + /// @dev Hash of the EIP-712 permit type bytes32 private immutable PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" @@ -43,25 +48,30 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra // -- State -- - // solhint-disable-next-line var-name-mixedcase - bytes32 private DOMAIN_SEPARATOR; + /// @dev EIP-712 Domain separator + bytes32 private DOMAIN_SEPARATOR; // solhint-disable-line var-name-mixedcase + /// @dev Addresses for which this mapping is true are allowed to mint tokens mapping(address => bool) private _minters; + /// Nonces for permit signatures for each token holder mapping(address => uint256) public nonces; - // Storage gap added in case we need to add state variables to this contract + /// @dev Storage gap added in case we need to add state variables to this contract uint256[47] private __gap; // -- Events -- + /// Emitted when a new minter is added event MinterAdded(address indexed account); + /// Emitted when a minter is removed event MinterRemoved(address indexed account); + /// @dev Reverts if the caller is not an authorized minter modifier onlyMinter() { require(isMinter(msg.sender), "Only minter can call"); _; } /** - * @dev Approve token allowance by validating a message signed by the holder. + * @notice Approve token allowance by validating a message signed by the holder. * @param _owner Address of the token holder * @param _spender Address of the approved spender * @param _value Amount of tokens to approve the spender @@ -98,7 +108,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra } /** - * @dev Add a new minter. + * @notice Add a new minter. * @param _account Address of the minter */ function addMinter(address _account) external onlyGovernor { @@ -107,7 +117,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra } /** - * @dev Remove a minter. + * @notice Remove a minter. * @param _account Address of the minter */ function removeMinter(address _account) external onlyGovernor { @@ -116,7 +126,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra } /** - * @dev Renounce to be a minter. + * @notice Renounce being a minter. */ function renounceMinter() external { require(isMinter(msg.sender), "NOT_A_MINTER"); @@ -124,7 +134,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra } /** - * @dev Mint new tokens. + * @notice Mint new tokens. * @param _to Address to send the newly minted tokens * @param _amount Amount of tokens to mint */ @@ -133,7 +143,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra } /** - * @dev Return if the `_account` is a minter or not. + * @notice Return if the `_account` is a minter or not. * @param _account Address to check * @return True if the `_account` is minter */ diff --git a/contracts/l2/token/L2GraphToken.sol b/contracts/l2/token/L2GraphToken.sol index 7a5abf78c..bdcfa973c 100644 --- a/contracts/l2/token/L2GraphToken.sol +++ b/contracts/l2/token/L2GraphToken.sol @@ -15,18 +15,18 @@ import "../../arbitrum/IArbToken.sol"; contract L2GraphToken is GraphTokenUpgradeable, IArbToken { using SafeMath for uint256; - // Address of the gateway (on L2) that is allowed to mint tokens + /// Address of the gateway (on L2) that is allowed to mint tokens address public gateway; - // Address of the corresponding Graph Token contract on L1 + /// Address of the corresponding Graph Token contract on L1 address public override l1Address; - // Emitted when the bridge / gateway has minted new tokens, i.e. tokens were transferred to L2 + /// Emitted when the bridge / gateway has minted new tokens, i.e. tokens were transferred to L2 event BridgeMinted(address indexed account, uint256 amount); - // Emitted when the bridge / gateway has burned tokens, i.e. tokens were transferred back to L1 + /// Emitted when the bridge / gateway has burned tokens, i.e. tokens were transferred back to L1 event BridgeBurned(address indexed account, uint256 amount); - // Emitted when the address of the gateway has been updated + /// Emitted when the address of the gateway has been updated event GatewaySet(address gateway); - // Emitted when the address of the Graph Token contract on L1 has been updated + /// Emitted when the address of the Graph Token contract on L1 has been updated event L1AddressSet(address l1Address); /** @@ -38,8 +38,8 @@ contract L2GraphToken is GraphTokenUpgradeable, IArbToken { } /** - * @dev L2 Graph Token Contract initializer. - * Note some parameters have to be set separately as they are generally + * @notice L2 Graph Token Contract initializer. + * @dev Note some parameters have to be set separately as they are generally * not expected to be available at initialization time: * - gateway using setGateway * - l1Address using setL1Address @@ -53,7 +53,7 @@ contract L2GraphToken is GraphTokenUpgradeable, IArbToken { } /** - * @dev Sets the address of the L2 gateway allowed to mint tokens + * @notice Sets the address of the L2 gateway allowed to mint tokens * @param _gw Address for the L2GraphTokenGateway that will be allowed to mint tokens */ function setGateway(address _gw) external onlyGovernor { @@ -63,7 +63,7 @@ contract L2GraphToken is GraphTokenUpgradeable, IArbToken { } /** - * @dev Sets the address of the counterpart token on L1 + * @notice Sets the address of the counterpart token on L1 * @param _addr Address for the GraphToken contract on L1 */ function setL1Address(address _addr) external onlyGovernor { @@ -73,7 +73,7 @@ contract L2GraphToken is GraphTokenUpgradeable, IArbToken { } /** - * @dev Increases token supply, only callable by the L1/L2 bridge (when tokens are transferred to L2) + * @notice Increases token supply, only callable by the L1/L2 bridge (when tokens are transferred to L2) * @param _account Address to credit with the new tokens * @param _amount Number of tokens to mint */ @@ -83,7 +83,7 @@ contract L2GraphToken is GraphTokenUpgradeable, IArbToken { } /** - * @dev Decreases token supply, only callable by the L1/L2 bridge (when tokens are transferred to L1). + * @notice Decreases token supply, only callable by the L1/L2 bridge (when tokens are transferred to L1). * @param _account Address from which to extract the tokens * @param _amount Number of tokens to burn */ diff --git a/contracts/upgrades/GraphProxy.sol b/contracts/upgrades/GraphProxy.sol index 7d62cdafb..665b15bee 100644 --- a/contracts/upgrades/GraphProxy.sol +++ b/contracts/upgrades/GraphProxy.sol @@ -6,6 +6,8 @@ import "@openzeppelin/contracts/utils/Address.sol"; import "./GraphProxyStorage.sol"; +import "./IGraphProxy.sol"; + /** * @title Graph Proxy * @dev Graph Proxy contract used to delegate call implementation contracts and support upgrades. @@ -13,7 +15,7 @@ import "./GraphProxyStorage.sol"; * This contract implements a proxy that is upgradeable by an admin. * https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#transparent-proxies-and-function-clashes */ -contract GraphProxy is GraphProxyStorage { +contract GraphProxy is GraphProxyStorage, IGraphProxy { /** * @dev Modifier used internally that will delegate the call to the implementation unless * the sender is the admin. @@ -39,7 +41,7 @@ contract GraphProxy is GraphProxyStorage { } /** - * @dev Contract constructor. + * @notice GraphProxy contract constructor. * @param _impl Address of the initial implementation * @param _admin Address of the proxy admin */ @@ -58,75 +60,84 @@ contract GraphProxy is GraphProxyStorage { } /** - * @dev Returns the current admin. + * @notice Get the current admin * - * NOTE: Only the admin and implementation can call this function. + * @dev NOTE: Only the admin and implementation can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` + * + * @return The address of the current admin */ - function admin() external ifAdminOrPendingImpl returns (address) { + function admin() external override ifAdminOrPendingImpl returns (address) { return _getAdmin(); } /** - * @dev Returns the current implementation. + * @notice Get the current implementation. * - * NOTE: Only the admin can call this function. + * @dev NOTE: Only the admin can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + * + * @return The address of the current implementation for this proxy */ - function implementation() external ifAdminOrPendingImpl returns (address) { + function implementation() external override ifAdminOrPendingImpl returns (address) { return _getImplementation(); } /** - * @dev Returns the current pending implementation. + * @notice Get the current pending implementation. * - * NOTE: Only the admin can call this function. + * @dev NOTE: Only the admin can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x9e5eddc59e0b171f57125ab86bee043d9128098c3a6b9adb4f2e86333c2f6f8c` + * + * @return The address of the current pending implementation for this proxy */ - function pendingImplementation() external ifAdminOrPendingImpl returns (address) { + function pendingImplementation() external override ifAdminOrPendingImpl returns (address) { return _getPendingImplementation(); } /** - * @dev Changes the admin of the proxy. + * @notice Changes the admin of the proxy. * - * NOTE: Only the admin can call this function. + * @dev NOTE: Only the admin can call this function. + * + * @param _newAdmin Address of the new admin */ - function setAdmin(address _newAdmin) external ifAdmin { + function setAdmin(address _newAdmin) external override ifAdmin { require(_newAdmin != address(0), "Admin cant be the zero address"); _setAdmin(_newAdmin); } /** - * @dev Upgrades to a new implementation contract. + * @notice Upgrades to a new implementation contract. + * @dev NOTE: Only the admin can call this function. * @param _newImplementation Address of implementation contract - * - * NOTE: Only the admin can call this function. */ - function upgradeTo(address _newImplementation) external ifAdmin { + function upgradeTo(address _newImplementation) external override ifAdmin { _setPendingImplementation(_newImplementation); } /** - * @dev Admin function for new implementation to accept its role as implementation. + * @notice Admin function for new implementation to accept its role as implementation. */ - function acceptUpgrade() external ifAdminOrPendingImpl { + function acceptUpgrade() external override ifAdminOrPendingImpl { _acceptUpgrade(); } /** - * @dev Admin function for new implementation to accept its role as implementation. + * @notice Admin function for new implementation to accept its role as implementation, + * calling a function on the new implementation. + * @param data Calldata (including selector) for the function to delegatecall into the implementation */ - function acceptUpgradeAndCall(bytes calldata data) external ifAdminOrPendingImpl { + function acceptUpgradeAndCall(bytes calldata data) external override ifAdminOrPendingImpl { _acceptUpgrade(); // solhint-disable-next-line avoid-low-level-calls (bool success, ) = _getImplementation().delegatecall(data); @@ -154,6 +165,7 @@ contract GraphProxy is GraphProxyStorage { function _fallback() internal { require(msg.sender != _getAdmin(), "Cannot fallback to proxy target"); + // solhint-disable-next-line no-inline-assembly assembly { // (a) get free memory pointer let ptr := mload(0x40) @@ -183,7 +195,7 @@ contract GraphProxy is GraphProxyStorage { } /** - * @dev Fallback function that delegates calls to implementation. Will run if no other + * @notice Fallback function that delegates calls to implementation. Will run if no other * function in the contract matches the call data. */ fallback() external payable { @@ -191,7 +203,7 @@ contract GraphProxy is GraphProxyStorage { } /** - * @dev Fallback function that delegates calls to implementation. Will run if call data + * @notice Fallback function that delegates calls to implementation. Will run if call data * is empty. */ receive() external payable { diff --git a/contracts/upgrades/GraphProxyAdmin.sol b/contracts/upgrades/GraphProxyAdmin.sol index 3775b9df1..ff351e412 100644 --- a/contracts/upgrades/GraphProxyAdmin.sol +++ b/contracts/upgrades/GraphProxyAdmin.sol @@ -16,15 +16,16 @@ import "./GraphUpgradeable.sol"; */ contract GraphProxyAdmin is Governed { /** - * @dev Contract constructor. + * @notice Contract constructor. */ constructor() { Governed._initialize(msg.sender); } /** - * @dev Returns the current implementation of a proxy. - * This is needed because only the proxy admin can query it. + * @notice Returns the current implementation of a proxy. + * @dev This is needed because only the proxy admin can query it. + * @param _proxy Address of the proxy for which to get the implementation. * @return The address of the current implementation of the proxy. */ function getProxyImplementation(IGraphProxy _proxy) public view returns (address) { @@ -36,8 +37,9 @@ contract GraphProxyAdmin is Governed { } /** - * @dev Returns the pending implementation of a proxy. - * This is needed because only the proxy admin can query it. + * @notice Returns the pending implementation of a proxy. + * @dev This is needed because only the proxy admin can query it. + * @param _proxy Address of the proxy for which to get the pending implementation. * @return The address of the pending implementation of the proxy. */ function getProxyPendingImplementation(IGraphProxy _proxy) public view returns (address) { @@ -49,7 +51,8 @@ contract GraphProxyAdmin is Governed { } /** - * @dev Returns the admin of a proxy. Only the admin can query it. + * @notice Returns the admin of a proxy. Only the admin can query it. + * @param _proxy Address of the proxy for which to get the admin. * @return The address of the current admin of the proxy. */ function getProxyAdmin(IGraphProxy _proxy) public view returns (address) { @@ -61,7 +64,7 @@ contract GraphProxyAdmin is Governed { } /** - * @dev Changes the admin of a proxy. + * @notice Changes the admin of a proxy. * @param _proxy Proxy to change admin. * @param _newAdmin Address to transfer proxy administration to. */ @@ -70,7 +73,7 @@ contract GraphProxyAdmin is Governed { } /** - * @dev Upgrades a proxy to the newest implementation of a contract. + * @notice Upgrades a proxy to the newest implementation of a contract. * @param _proxy Proxy to be upgraded. * @param _implementation the address of the Implementation. */ @@ -79,7 +82,7 @@ contract GraphProxyAdmin is Governed { } /** - * @dev Accepts a proxy. + * @notice Accepts a proxy. * @param _implementation Address of the implementation accepting the proxy. * @param _proxy Address of the proxy being accepted. */ @@ -88,7 +91,7 @@ contract GraphProxyAdmin is Governed { } /** - * @dev Accepts a proxy and call a function on the implementation. + * @notice Accepts a proxy and call a function on the implementation. * @param _implementation Address of the implementation accepting the proxy. * @param _proxy Address of the proxy being accepted. * @param _data Encoded function to call on the implementation after accepting the proxy. diff --git a/contracts/upgrades/GraphProxyStorage.sol b/contracts/upgrades/GraphProxyStorage.sol index 0911ce2ee..371e9a315 100644 --- a/contracts/upgrades/GraphProxyStorage.sol +++ b/contracts/upgrades/GraphProxyStorage.sol @@ -68,6 +68,7 @@ contract GraphProxyStorage { */ function _getAdmin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { adm := sload(slot) } @@ -79,6 +80,7 @@ contract GraphProxyStorage { */ function _setAdmin(address _newAdmin) internal { bytes32 slot = ADMIN_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _newAdmin) } @@ -92,6 +94,7 @@ contract GraphProxyStorage { */ function _getImplementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { impl := sload(slot) } @@ -103,6 +106,7 @@ contract GraphProxyStorage { */ function _getPendingImplementation() internal view returns (address impl) { bytes32 slot = PENDING_IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { impl := sload(slot) } @@ -116,6 +120,7 @@ contract GraphProxyStorage { address oldImplementation = _getImplementation(); bytes32 slot = IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _newImplementation) } @@ -131,6 +136,7 @@ contract GraphProxyStorage { address oldPendingImplementation = _getPendingImplementation(); bytes32 slot = PENDING_IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _newImplementation) } diff --git a/contracts/upgrades/GraphUpgradeable.sol b/contracts/upgrades/GraphUpgradeable.sol index 3f3b505ba..3851ea78a 100644 --- a/contracts/upgrades/GraphUpgradeable.sol +++ b/contracts/upgrades/GraphUpgradeable.sol @@ -39,22 +39,26 @@ contract GraphUpgradeable { */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { impl := sload(slot) } } /** - * @dev Accept to be an implementation of proxy. + * @notice Accept to be an implementation of proxy. + * @param _proxy Proxy to accept */ function acceptProxy(IGraphProxy _proxy) external onlyProxyAdmin(_proxy) { _proxy.acceptUpgrade(); } /** - * @dev Accept to be an implementation of proxy and then call a function from the new + * @notice Accept to be an implementation of proxy and then call a function from the new * implementation as specified by `_data`, which should be an encoded function call. This is * useful to initialize new storage variables in the proxied contract. + * @param _proxy Proxy to accept + * @param _data Calldata for the initialization function call (including selector) */ function acceptProxyAndCall(IGraphProxy _proxy, bytes calldata _data) external From 289f328d9544ebeaa00f49af570d0757feaf9452 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 15:28:30 -0300 Subject: [PATCH 053/100] fix: typos in L1GraphTokenGateway (C4 QA) --- contracts/gateway/L1GraphTokenGateway.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index eed07fcb8..b0ee09a7d 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -188,7 +188,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess * with token transfers. * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router) * @param _to Recipient address on L2 - * @param _amount Amount of tokens to tranfer + * @param _amount Amount of tokens to transfer * @param _maxGas Gas limit for L2 execution of the ticket * @param _gasPriceBid Price per gas on L2 * @param _data Encoded maxSubmissionCost and sender address along with additional calldata @@ -255,7 +255,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess * and the encoded exitNum is assumed to be 0. * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router) * @param _from Address of the sender - * @param _to Recepient address on L1 + * @param _to Recipient address on L1 * @param _amount Amount of tokens transferred */ function finalizeInboundTransfer( From d89c7a749f146b0d2ee36930ebfdfd521bbda7cc Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 15:29:19 -0300 Subject: [PATCH 054/100] fix: make public functions external in GraphProxyAdmin (C4 QA) --- contracts/upgrades/GraphProxyAdmin.sol | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/contracts/upgrades/GraphProxyAdmin.sol b/contracts/upgrades/GraphProxyAdmin.sol index ff351e412..209eb0e7d 100644 --- a/contracts/upgrades/GraphProxyAdmin.sol +++ b/contracts/upgrades/GraphProxyAdmin.sol @@ -28,7 +28,7 @@ contract GraphProxyAdmin is Governed { * @param _proxy Address of the proxy for which to get the implementation. * @return The address of the current implementation of the proxy. */ - function getProxyImplementation(IGraphProxy _proxy) public view returns (address) { + function getProxyImplementation(IGraphProxy _proxy) external view returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("implementation()")) == 0x5c60da1b (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"5c60da1b"); @@ -42,7 +42,7 @@ contract GraphProxyAdmin is Governed { * @param _proxy Address of the proxy for which to get the pending implementation. * @return The address of the pending implementation of the proxy. */ - function getProxyPendingImplementation(IGraphProxy _proxy) public view returns (address) { + function getProxyPendingImplementation(IGraphProxy _proxy) external view returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("pendingImplementation()")) == 0x396f7b23 (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"396f7b23"); @@ -55,7 +55,7 @@ contract GraphProxyAdmin is Governed { * @param _proxy Address of the proxy for which to get the admin. * @return The address of the current admin of the proxy. */ - function getProxyAdmin(IGraphProxy _proxy) public view returns (address) { + function getProxyAdmin(IGraphProxy _proxy) external view returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("admin()")) == 0xf851a440 (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"f851a440"); @@ -68,7 +68,7 @@ contract GraphProxyAdmin is Governed { * @param _proxy Proxy to change admin. * @param _newAdmin Address to transfer proxy administration to. */ - function changeProxyAdmin(IGraphProxy _proxy, address _newAdmin) public onlyGovernor { + function changeProxyAdmin(IGraphProxy _proxy, address _newAdmin) external onlyGovernor { _proxy.setAdmin(_newAdmin); } @@ -77,7 +77,7 @@ contract GraphProxyAdmin is Governed { * @param _proxy Proxy to be upgraded. * @param _implementation the address of the Implementation. */ - function upgrade(IGraphProxy _proxy, address _implementation) public onlyGovernor { + function upgrade(IGraphProxy _proxy, address _implementation) external onlyGovernor { _proxy.upgradeTo(_implementation); } @@ -86,7 +86,10 @@ contract GraphProxyAdmin is Governed { * @param _implementation Address of the implementation accepting the proxy. * @param _proxy Address of the proxy being accepted. */ - function acceptProxy(GraphUpgradeable _implementation, IGraphProxy _proxy) public onlyGovernor { + function acceptProxy(GraphUpgradeable _implementation, IGraphProxy _proxy) + external + onlyGovernor + { _implementation.acceptProxy(_proxy); } From a42bcbc72e4fb0baa8f65c93fc68f8ca287f31cf Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 15:29:42 -0300 Subject: [PATCH 055/100] fix: make nonReentrant the first modifier (C4 QA) --- contracts/l2/gateway/L2GraphTokenGateway.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index 3ff95a393..55f4ce957 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -141,7 +141,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran uint256, // unused on L2 uint256, // unused on L2 bytes calldata _data - ) public payable override notPaused nonReentrant returns (bytes memory) { + ) public payable override nonReentrant notPaused returns (bytes memory) { require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); require(_amount != 0, "INVALID_ZERO_AMOUNT"); require(msg.value == 0, "INVALID_NONZERO_VALUE"); @@ -229,7 +229,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran address _to, uint256 _amount, bytes calldata _data - ) external payable override notPaused onlyL1Counterpart nonReentrant { + ) external payable override nonReentrant notPaused onlyL1Counterpart { require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); require(msg.value == 0, "INVALID_NONZERO_VALUE"); From ba1a870784b404546b29fd47575d3cd889e82627 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 15:33:48 -0300 Subject: [PATCH 056/100] fix: use underscore for private variables (C4 QA) --- contracts/governance/Controller.sol | 10 +++++----- contracts/governance/Managed.sol | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/governance/Controller.sol b/contracts/governance/Controller.sol index 18d1a9307..354f42c2e 100644 --- a/contracts/governance/Controller.sol +++ b/contracts/governance/Controller.sol @@ -14,7 +14,7 @@ import "./Pausable.sol"; */ contract Controller is Governed, Pausable, IController { /// @dev Track contract ids to contract proxy address - mapping(bytes32 => address) private registry; + mapping(bytes32 => address) private _registry; /// Emitted when the proxy address for a protocol contract has been set event SetContractProxy(bytes32 indexed id, address contractAddress); @@ -59,7 +59,7 @@ contract Controller is Governed, Pausable, IController { onlyGovernor { require(_contractAddress != address(0), "Contract address must be set"); - registry[_id] = _contractAddress; + _registry[_id] = _contractAddress; emit SetContractProxy(_id, _contractAddress); } @@ -68,7 +68,7 @@ contract Controller is Governed, Pausable, IController { * @param _id Contract id (keccak256 hash of contract name) */ function unsetContractProxy(bytes32 _id) external override onlyGovernor { - registry[_id] = address(0); + _registry[_id] = address(0); emit SetContractProxy(_id, address(0)); } @@ -78,7 +78,7 @@ contract Controller is Governed, Pausable, IController { * @return Address of the proxy contract for the provided id */ function getContractProxy(bytes32 _id) public view override returns (address) { - return registry[_id]; + return _registry[_id]; } /** @@ -88,7 +88,7 @@ contract Controller is Governed, Pausable, IController { */ function updateController(bytes32 _id, address _controller) external override onlyGovernor { require(_controller != address(0), "Controller must be set"); - return IManaged(registry[_id]).setController(_controller); + return IManaged(_registry[_id]).setController(_controller); } // -- Pausing -- diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index 220132018..2020992c1 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -28,7 +28,7 @@ contract Managed is IManaged { /// Controller that contract is registered with IController public controller; /// @dev Cache for the addresses of the contracts retrieved from the controller - mapping(bytes32 => address) private addressCache; + mapping(bytes32 => address) private _addressCache; /// @dev Gap for future storage variables uint256[10] private __gap; @@ -195,7 +195,7 @@ contract Managed is IManaged { * @return Address of the contract */ function _resolveContract(bytes32 _nameHash) internal view returns (address) { - address contractAddress = addressCache[_nameHash]; + address contractAddress = _addressCache[_nameHash]; if (contractAddress == address(0)) { contractAddress = controller.getContractProxy(_nameHash); } @@ -209,8 +209,8 @@ contract Managed is IManaged { function _syncContract(string memory _name) internal { bytes32 nameHash = keccak256(abi.encodePacked(_name)); address contractAddress = controller.getContractProxy(nameHash); - if (addressCache[nameHash] != contractAddress) { - addressCache[nameHash] = contractAddress; + if (_addressCache[nameHash] != contractAddress) { + _addressCache[nameHash] = contractAddress; emit ContractSynced(nameHash, contractAddress); } } From 751240dcbdf895247c4480f44a5d9031233d84e1 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 15:35:27 -0300 Subject: [PATCH 057/100] fix: emit the correct old admin in _setAdmin (C4 QA) --- contracts/upgrades/GraphProxyStorage.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/upgrades/GraphProxyStorage.sol b/contracts/upgrades/GraphProxyStorage.sol index 371e9a315..eb071b9b3 100644 --- a/contracts/upgrades/GraphProxyStorage.sol +++ b/contracts/upgrades/GraphProxyStorage.sol @@ -79,13 +79,14 @@ contract GraphProxyStorage { * @param _newAdmin Address of the new proxy admin */ function _setAdmin(address _newAdmin) internal { + address oldAdmin = _getAdmin(); bytes32 slot = ADMIN_SLOT; // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _newAdmin) } - emit AdminUpdated(_getAdmin(), _newAdmin); + emit AdminUpdated(oldAdmin, _newAdmin); } /** From e78940388638f56ea453537db1f31305009aaf52 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 17:07:43 -0300 Subject: [PATCH 058/100] fix: prefer the term 'allowlist' for callhook senders --- cli/commands/bridge/to-l2.ts | 2 +- contracts/gateway/L1GraphTokenGateway.sol | 50 +++++++++---------- contracts/l2/gateway/L2GraphTokenGateway.sol | 6 +-- .../config/l1/l1GraphTokenGateway.test.ts | 8 +-- test/gateway/l1GraphTokenGateway.test.ts | 42 ++++++++-------- 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/cli/commands/bridge/to-l2.ts b/cli/commands/bridge/to-l2.ts index 307886e30..ffacee1c8 100644 --- a/cli/commands/bridge/to-l2.ts +++ b/cli/commands/bridge/to-l2.ts @@ -163,7 +163,7 @@ export const sendToL2Command = { description: 'Receiving address in L2. Same to L1 address if empty', }) .positional('calldata', { - description: 'Calldata to pass to the recipient. Must be whitelisted in the bridge', + description: 'Calldata to pass to the recipient. Must be allowlisted in the bridge', }) .coerce({ maxGas: toBN, diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index b0ee09a7d..7c955a6fe 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -33,7 +33,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess /// Address of the BridgeEscrow contract that holds the GRT in the bridge address public escrow; /// Addresses for which this mapping is true are allowed to send callhooks in outbound transfers - mapping(address => bool) public callhookWhitelist; + mapping(address => bool) public callhookAllowlist; /// Emitted when an outbound transfer is initiated, i.e. tokens are deposited from L1 to L2 event DepositInitiated( @@ -61,10 +61,10 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess event L2CounterpartAddressSet(address l2Counterpart); /// Emitted when the escrow address has been updated event EscrowAddressSet(address escrow); - /// Emitted when an address is added to the callhook whitelist - event AddedToCallhookWhitelist(address newWhitelisted); - /// Emitted when an address is removed from the callhook whitelist - event RemovedFromCallhookWhitelist(address notWhitelisted); + /// Emitted when an address is added to the callhook allowlist + event AddedToCallhookAllowlist(address newAllowlisted); + /// Emitted when an address is removed from the callhook allowlist + event RemovedFromCallhookAllowlist(address notAllowlisted); /** * @dev Allows a function to be called only by the gateway's L2 counterpart. @@ -94,7 +94,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess * - l2GRT using setL2TokenAddress * - l2Counterpart using setL2CounterpartAddress * - escrow using setEscrowAddress - * - whitelisted callhook callers using addToCallhookWhitelist + * - allowlisted callhook callers using addToCallhookAllowlist * - pauseGuardian using setPauseGuardian * @param _controller Address of the Controller that manages this contract */ @@ -150,28 +150,28 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @notice Adds an address to the callhook whitelist. + * @notice Adds an address to the callhook allowlist. * This address will be allowed to include callhooks when transferring tokens. - * @param _newWhitelisted Address to add to the whitelist + * @param _newAllowlisted Address to add to the allowlist */ - function addToCallhookWhitelist(address _newWhitelisted) external onlyGovernor { - require(_newWhitelisted != address(0), "INVALID_ADDRESS"); - require(Address.isContract(_newWhitelisted), "MUST_BE_CONTRACT"); - require(!callhookWhitelist[_newWhitelisted], "ALREADY_WHITELISTED"); - callhookWhitelist[_newWhitelisted] = true; - emit AddedToCallhookWhitelist(_newWhitelisted); + function addToCallhookAllowlist(address _newAllowlisted) external onlyGovernor { + require(_newAllowlisted != address(0), "INVALID_ADDRESS"); + require(Address.isContract(_newAllowlisted), "MUST_BE_CONTRACT"); + require(!callhookAllowlist[_newAllowlisted], "ALREADY_ALLOWLISTED"); + callhookAllowlist[_newAllowlisted] = true; + emit AddedToCallhookAllowlist(_newAllowlisted); } /** - * @notice Removes an address from the callhook whitelist. + * @notice Removes an address from the callhook allowlist. * This address will no longer be allowed to include callhooks when transferring tokens. - * @param _notWhitelisted Address to remove from the whitelist + * @param _notAllowlisted Address to remove from the allowlist */ - function removeFromCallhookWhitelist(address _notWhitelisted) external onlyGovernor { - require(_notWhitelisted != address(0), "INVALID_ADDRESS"); - require(callhookWhitelist[_notWhitelisted], "NOT_WHITELISTED"); - callhookWhitelist[_notWhitelisted] = false; - emit RemovedFromCallhookWhitelist(_notWhitelisted); + function removeFromCallhookAllowlist(address _notAllowlisted) external onlyGovernor { + require(_notAllowlisted != address(0), "INVALID_ADDRESS"); + require(callhookAllowlist[_notAllowlisted], "NOT_ALLOWLISTED"); + callhookAllowlist[_notAllowlisted] = false; + emit RemovedFromCallhookAllowlist(_notAllowlisted); } /** @@ -180,10 +180,10 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess * The ticket must be redeemed on L2 to receive tokens at the specified address. * Note that the caller must previously allow the gateway to spend the specified amount of GRT. * @dev maxGas and gasPriceBid must be set using Arbitrum's NodeInterface.estimateRetryableTicket method. - * Also note that whitelisted senders (some protocol contracts) can include additional calldata + * Also note that allowlisted senders (some protocol contracts) can include additional calldata * for a callhook to be executed on the L2 side when the tokens are received. In this case, the L2 transaction * can revert if the callhook reverts, potentially locking the tokens on the bridge if the callhook - * never succeeds. This requires extra care when adding contracts to the whitelist, but is necessary to ensure that + * never succeeds. This requires extra care when adding contracts to the allowlist, but is necessary to ensure that * the tickets can be retried in the case of a temporary failure, and to ensure the atomicity of callhooks * with token transfers. * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router) @@ -217,7 +217,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess bytes memory extraData; (from, maxSubmissionCost, extraData) = parseOutboundData(_data); require( - extraData.length == 0 || callhookWhitelist[msg.sender] == true, + extraData.length == 0 || callhookAllowlist[msg.sender] == true, "CALL_HOOK_DATA_NOT_ALLOWED" ); require(maxSubmissionCost != 0, "NO_SUBMISSION_COST"); @@ -318,7 +318,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess * @param _from Address on L1 from which we're transferring tokens * @param _to Address on L2 to which we're transferring tokens * @param _amount Amount of GRT to transfer - * @param _data Additional call data for the L2 transaction, which must be empty unless the caller is whitelisted + * @param _data Additional call data for the L2 transaction, which must be empty unless the caller is allowlisted * @return Encoded calldata (including function selector) for the L2 transaction */ function getOutboundCalldata( diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index 55f4ce957..e534c06e7 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -211,17 +211,17 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran * @notice Receives token amount from L1 and mints the equivalent tokens to the receiving address * @dev Only accepts transactions from the L1 GRT Gateway. * The function is payable for ITokenGateway compatibility, but msg.value must be zero. - * Note that whitelisted senders (some protocol contracts) can include additional calldata + * Note that allowlisted senders (some protocol contracts) can include additional calldata * for a callhook to be executed on the L2 side when the tokens are received. In this case, the L2 transaction * can revert if the callhook reverts, potentially locking the tokens on the bridge if the callhook - * never succeeds. This requires extra care when adding contracts to the whitelist, but is necessary to ensure that + * never succeeds. This requires extra care when adding contracts to the allowlist, but is necessary to ensure that * the tickets can be retried in the case of a temporary failure, and to ensure the atomicity of callhooks * with token transfers. * @param _l1Token L1 Address of GRT * @param _from Address of the sender on L1 * @param _to Recipient address on L2 * @param _amount Amount of tokens transferred - * @param _data Extra callhook data, only used when the sender is whitelisted + * @param _data Extra callhook data, only used when the sender is allowlisted */ function finalizeInboundTransfer( address _l1Token, diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts index cce20c735..88abc6c1c 100644 --- a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -54,15 +54,15 @@ describe('[L1] L1GraphTokenGateway configuration', function () { await expect(tx).revertedWith('Only Controller governor') }) - it('addToCallhookWhitelist should revert', async function () { - const tx = L1GraphTokenGateway.connect(unauthorized).addToCallhookWhitelist( + it('addToCallhookAllowlist should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).addToCallhookAllowlist( unauthorized.address, ) await expect(tx).revertedWith('Only Controller governor') }) - it('removeFromCallhookWhitelist should revert', async function () { - const tx = L1GraphTokenGateway.connect(unauthorized).removeFromCallhookWhitelist( + it('removeFromCallhookAllowlist should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).removeFromCallhookAllowlist( unauthorized.address, ) await expect(tx).revertedWith('Only Controller governor') diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts index 7c2d3db5e..2542832ab 100644 --- a/test/gateway/l1GraphTokenGateway.test.ts +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -205,59 +205,59 @@ describe('L1GraphTokenGateway', () => { expect(await l1GraphTokenGateway.escrow()).eq(bridgeEscrow.address) }) }) - describe('addToCallhookWhitelist', function () { + describe('addToCallhookAllowlist', function () { it('is not callable by addreses that are not the governor', async function () { const tx = l1GraphTokenGateway .connect(tokenSender.signer) - .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) + .addToCallhookAllowlist(fixtureContracts.rewardsManager.address) await expect(tx).revertedWith('Only Controller governor') expect( - await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), + await l1GraphTokenGateway.callhookAllowlist(fixtureContracts.rewardsManager.address), ).eq(false) }) - it('rejects adding an EOA to the callhook whitelist', async function () { + it('rejects adding an EOA to the callhook allowlist', async function () { const tx = l1GraphTokenGateway .connect(governor.signer) - .addToCallhookWhitelist(tokenSender.address) + .addToCallhookAllowlist(tokenSender.address) await expect(tx).revertedWith('MUST_BE_CONTRACT') }) - it('adds an address to the callhook whitelist', async function () { + it('adds an address to the callhook allowlist', async function () { const tx = l1GraphTokenGateway .connect(governor.signer) - .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) + .addToCallhookAllowlist(fixtureContracts.rewardsManager.address) await expect(tx) - .emit(l1GraphTokenGateway, 'AddedToCallhookWhitelist') + .emit(l1GraphTokenGateway, 'AddedToCallhookAllowlist') .withArgs(fixtureContracts.rewardsManager.address) expect( - await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), + await l1GraphTokenGateway.callhookAllowlist(fixtureContracts.rewardsManager.address), ).eq(true) }) }) - describe('removeFromCallhookWhitelist', function () { + describe('removeFromCallhookAllowlist', function () { it('is not callable by addreses that are not the governor', async function () { await l1GraphTokenGateway .connect(governor.signer) - .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) + .addToCallhookAllowlist(fixtureContracts.rewardsManager.address) const tx = l1GraphTokenGateway .connect(tokenSender.signer) - .removeFromCallhookWhitelist(fixtureContracts.rewardsManager.address) + .removeFromCallhookAllowlist(fixtureContracts.rewardsManager.address) await expect(tx).revertedWith('Only Controller governor') expect( - await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), + await l1GraphTokenGateway.callhookAllowlist(fixtureContracts.rewardsManager.address), ).eq(true) }) - it('removes an address from the callhook whitelist', async function () { + it('removes an address from the callhook allowlist', async function () { await l1GraphTokenGateway .connect(governor.signer) - .addToCallhookWhitelist(fixtureContracts.rewardsManager.address) + .addToCallhookAllowlist(fixtureContracts.rewardsManager.address) const tx = l1GraphTokenGateway .connect(governor.signer) - .removeFromCallhookWhitelist(fixtureContracts.rewardsManager.address) + .removeFromCallhookAllowlist(fixtureContracts.rewardsManager.address) await expect(tx) - .emit(l1GraphTokenGateway, 'RemovedFromCallhookWhitelist') + .emit(l1GraphTokenGateway, 'RemovedFromCallhookAllowlist') .withArgs(fixtureContracts.rewardsManager.address) expect( - await l1GraphTokenGateway.callhookWhitelist(fixtureContracts.rewardsManager.address), + await l1GraphTokenGateway.callhookAllowlist(fixtureContracts.rewardsManager.address), ).eq(false) }) }) @@ -492,7 +492,7 @@ describe('L1GraphTokenGateway', () => { ) await expect(tx).revertedWith('NO_SUBMISSION_COST') }) - it('reverts when called with nonempty calldata, if the sender is not whitelisted', async function () { + it('reverts when called with nonempty calldata, if the sender is not allowlisted', async function () { await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) const tx = l1GraphTokenGateway .connect(tokenSender.signer) @@ -509,12 +509,12 @@ describe('L1GraphTokenGateway', () => { ) await expect(tx).revertedWith('CALL_HOOK_DATA_NOT_ALLOWED') }) - it('allows sending nonempty calldata, if the sender is whitelisted', async function () { + it('allows sending nonempty calldata, if the sender is allowlisted', async function () { // Make the sender a contract so that it can be allowed to send callhooks await provider().send('hardhat_setCode', [tokenSender.address, '0x1234']) await l1GraphTokenGateway .connect(governor.signer) - .addToCallhookWhitelist(tokenSender.address) + .addToCallhookAllowlist(tokenSender.address) await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) await testValidOutboundTransfer( tokenSender.signer, From 8d73fc35ebaa402ee85ac9a3eaf6cbfc9a33a08f Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 17:31:50 -0300 Subject: [PATCH 059/100] fix: check deadline before recovering signature (C4 QA) --- contracts/l2/token/GraphTokenUpgradeable.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index 241041635..3728f3d93 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -89,6 +89,7 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra bytes32 _r, bytes32 _s ) external { + require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit"); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", @@ -101,7 +102,6 @@ contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgra address recoveredAddress = ECDSA.recover(digest, _v, _r, _s); require(_owner == recoveredAddress, "GRT: invalid permit"); - require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit"); nonces[_owner] = nonces[_owner] + 1; _approve(_owner, _spender, _value); From c74885f47c39a811788183ebb912cfd2eaed4270 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 17:37:09 -0300 Subject: [PATCH 060/100] fix: add some missing revert strings (C4 QA) --- contracts/upgrades/GraphProxy.sol | 2 +- contracts/upgrades/GraphProxyAdmin.sol | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/upgrades/GraphProxy.sol b/contracts/upgrades/GraphProxy.sol index 665b15bee..11fb5ce3c 100644 --- a/contracts/upgrades/GraphProxy.sol +++ b/contracts/upgrades/GraphProxy.sol @@ -141,7 +141,7 @@ contract GraphProxy is GraphProxyStorage, IGraphProxy { _acceptUpgrade(); // solhint-disable-next-line avoid-low-level-calls (bool success, ) = _getImplementation().delegatecall(data); - require(success); + require(success, "Impl call failed"); } /** diff --git a/contracts/upgrades/GraphProxyAdmin.sol b/contracts/upgrades/GraphProxyAdmin.sol index 209eb0e7d..6adf9674c 100644 --- a/contracts/upgrades/GraphProxyAdmin.sol +++ b/contracts/upgrades/GraphProxyAdmin.sol @@ -32,7 +32,7 @@ contract GraphProxyAdmin is Governed { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("implementation()")) == 0x5c60da1b (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"5c60da1b"); - require(success); + require(success, "Proxy impl call failed"); return abi.decode(returndata, (address)); } @@ -46,7 +46,7 @@ contract GraphProxyAdmin is Governed { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("pendingImplementation()")) == 0x396f7b23 (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"396f7b23"); - require(success); + require(success, "Proxy pendingImpl call failed"); return abi.decode(returndata, (address)); } @@ -59,7 +59,7 @@ contract GraphProxyAdmin is Governed { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("admin()")) == 0xf851a440 (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"f851a440"); - require(success); + require(success, "Proxy admin call failed"); return abi.decode(returndata, (address)); } From cc78a5ccf2bbe51b1171f669181ce9880676da48 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 17:39:50 -0300 Subject: [PATCH 061/100] fix: dont allow the router to be allowlisted (C4 QA) --- contracts/gateway/L1GraphTokenGateway.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 7c955a6fe..ec441f2eb 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -111,6 +111,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess function setArbitrumAddresses(address _inbox, address _l1Router) external onlyGovernor { require(_inbox != address(0), "INVALID_INBOX"); require(_l1Router != address(0), "INVALID_L1_ROUTER"); + require(!callhookAllowlist[_l1Router], "ROUTER_CANT_BE_ALLOWLISTED"); require(Address.isContract(_inbox), "INBOX_MUST_BE_CONTRACT"); require(Address.isContract(_l1Router), "ROUTER_MUST_BE_CONTRACT"); inbox = _inbox; @@ -156,6 +157,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess */ function addToCallhookAllowlist(address _newAllowlisted) external onlyGovernor { require(_newAllowlisted != address(0), "INVALID_ADDRESS"); + require(_newAllowlisted != l1Router, "CANT_ALLOW_ROUTER"); require(Address.isContract(_newAllowlisted), "MUST_BE_CONTRACT"); require(!callhookAllowlist[_newAllowlisted], "ALREADY_ALLOWLISTED"); callhookAllowlist[_newAllowlisted] = true; From 38fa46874caa20d2ac4cb4d4327096d3fc128dee Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 17:46:03 -0300 Subject: [PATCH 062/100] fix: mark some non-deployable contracts as abstract (C4 QA) --- contracts/governance/Governed.sol | 2 +- contracts/governance/Managed.sol | 2 +- contracts/governance/Pausable.sol | 2 +- contracts/l2/token/GraphTokenUpgradeable.sol | 2 +- contracts/upgrades/GraphProxyStorage.sol | 2 +- contracts/upgrades/GraphUpgradeable.sol | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/governance/Governed.sol b/contracts/governance/Governed.sol index d194992ea..f692b2d19 100644 --- a/contracts/governance/Governed.sol +++ b/contracts/governance/Governed.sol @@ -6,7 +6,7 @@ pragma solidity ^0.7.6; * @title Graph Governance contract * @dev All contracts that will be owned by a Governor entity should extend this contract. */ -contract Governed { +abstract contract Governed { // -- State -- /// Address of the governor diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index 2020992c1..33a037d65 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -22,7 +22,7 @@ import "./IManaged.sol"; * Inspired by Livepeer: * https://github.com/livepeer/protocol/blob/streamflow/contracts/Controller.sol */ -contract Managed is IManaged { +abstract contract Managed is IManaged { // -- State -- /// Controller that contract is registered with diff --git a/contracts/governance/Pausable.sol b/contracts/governance/Pausable.sol index 4b2c18459..552b0aa15 100644 --- a/contracts/governance/Pausable.sol +++ b/contracts/governance/Pausable.sol @@ -2,7 +2,7 @@ pragma solidity ^0.7.6; -contract Pausable { +abstract contract Pausable { /** * @dev "Partial paused" pauses exit and enter functions for GRT, but not internal * functions, such as allocating diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index 3728f3d93..407348dd3 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -24,7 +24,7 @@ import "../../governance/Governed.sol"; * initializer functions and upgradeable OpenZeppelin contracts instead of * the original's constructor + non-upgradeable approach. */ -contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable { +abstract contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable { // -- EIP712 -- // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator diff --git a/contracts/upgrades/GraphProxyStorage.sol b/contracts/upgrades/GraphProxyStorage.sol index eb071b9b3..b308c0a0c 100644 --- a/contracts/upgrades/GraphProxyStorage.sol +++ b/contracts/upgrades/GraphProxyStorage.sol @@ -8,7 +8,7 @@ pragma solidity ^0.7.6; * This contract does not actually define state variables managed by the compiler * but uses fixed slot locations. */ -contract GraphProxyStorage { +abstract contract GraphProxyStorage { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is diff --git a/contracts/upgrades/GraphUpgradeable.sol b/contracts/upgrades/GraphUpgradeable.sol index 3851ea78a..bac95f1f5 100644 --- a/contracts/upgrades/GraphUpgradeable.sol +++ b/contracts/upgrades/GraphUpgradeable.sol @@ -8,7 +8,7 @@ import "./IGraphProxy.sol"; * @title Graph Upgradeable * @dev This contract is intended to be inherited from upgradeable contracts. */ -contract GraphUpgradeable { +abstract contract GraphUpgradeable { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is From db2a17c46d624620f6060bc5bda22133f64d1996 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 17:52:15 -0300 Subject: [PATCH 063/100] fix(Controller): mark a function that was public as external --- contracts/governance/Controller.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/Controller.sol b/contracts/governance/Controller.sol index 354f42c2e..504e1736a 100644 --- a/contracts/governance/Controller.sol +++ b/contracts/governance/Controller.sol @@ -77,7 +77,7 @@ contract Controller is Governed, Pausable, IController { * @param _id Contract id * @return Address of the proxy contract for the provided id */ - function getContractProxy(bytes32 _id) public view override returns (address) { + function getContractProxy(bytes32 _id) external view override returns (address) { return _registry[_id]; } From dfd0a24a79c9a6e32265bedc8c6e708d1978ef2d Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 18:06:08 -0300 Subject: [PATCH 064/100] fix: use upgradeable OZ contracts (C4 QA) --- contracts/gateway/L1GraphTokenGateway.sol | 24 ++++++++++++-------- contracts/l2/gateway/L2GraphTokenGateway.sol | 18 ++++++++------- contracts/l2/token/GraphTokenUpgradeable.sol | 10 ++++---- contracts/l2/token/L2GraphToken.sol | 8 ++----- 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index ec441f2eb..f88c766e3 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -4,11 +4,17 @@ pragma solidity ^0.7.6; pragma abicoder v2; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; -import "@openzeppelin/contracts/utils/Address.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; +import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; +import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; -import "../arbitrum/L1ArbitrumMessenger.sol"; -import "./GraphTokenGateway.sol"; +import { L1ArbitrumMessenger } from "../arbitrum/L1ArbitrumMessenger.sol"; +import { IBridge } from "../arbitrum/IBridge.sol"; +import { IInbox } from "../arbitrum/IInbox.sol"; +import { IOutbox } from "../arbitrum/IOutbox.sol"; +import { ITokenGateway } from "../arbitrum/ITokenGateway.sol"; +import { Managed } from "../governance/Managed.sol"; +import { GraphTokenGateway } from "./GraphTokenGateway.sol"; +import { IGraphToken } from "../token/IGraphToken.sol"; /** * @title L1 Graph Token Gateway Contract @@ -20,7 +26,7 @@ import "./GraphTokenGateway.sol"; * and https://github.com/livepeer/arbitrum-lpt-bridge) */ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMessenger { - using SafeMath for uint256; + using SafeMathUpgradeable for uint256; /// Address of the Graph Token contract on L2 address public l2GRT; @@ -112,8 +118,8 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess require(_inbox != address(0), "INVALID_INBOX"); require(_l1Router != address(0), "INVALID_L1_ROUTER"); require(!callhookAllowlist[_l1Router], "ROUTER_CANT_BE_ALLOWLISTED"); - require(Address.isContract(_inbox), "INBOX_MUST_BE_CONTRACT"); - require(Address.isContract(_l1Router), "ROUTER_MUST_BE_CONTRACT"); + require(AddressUpgradeable.isContract(_inbox), "INBOX_MUST_BE_CONTRACT"); + require(AddressUpgradeable.isContract(_l1Router), "ROUTER_MUST_BE_CONTRACT"); inbox = _inbox; l1Router = _l1Router; emit ArbitrumAddressesSet(_inbox, _l1Router); @@ -145,7 +151,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess */ function setEscrowAddress(address _escrow) external onlyGovernor { require(_escrow != address(0), "INVALID_ESCROW"); - require(Address.isContract(_escrow), "MUST_BE_CONTRACT"); + require(AddressUpgradeable.isContract(_escrow), "MUST_BE_CONTRACT"); escrow = _escrow; emit EscrowAddressSet(_escrow); } @@ -158,7 +164,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess function addToCallhookAllowlist(address _newAllowlisted) external onlyGovernor { require(_newAllowlisted != address(0), "INVALID_ADDRESS"); require(_newAllowlisted != l1Router, "CANT_ALLOW_ROUTER"); - require(Address.isContract(_newAllowlisted), "MUST_BE_CONTRACT"); + require(AddressUpgradeable.isContract(_newAllowlisted), "MUST_BE_CONTRACT"); require(!callhookAllowlist[_newAllowlisted], "ALREADY_ALLOWLISTED"); callhookAllowlist[_newAllowlisted] = true; emit AddedToCallhookAllowlist(_newAllowlisted); diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index e534c06e7..39111ac0f 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -3,14 +3,16 @@ pragma solidity ^0.7.6; pragma abicoder v2; -import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; +import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; -import "../../arbitrum/L2ArbitrumMessenger.sol"; -import "../../arbitrum/AddressAliasHelper.sol"; -import "../../gateway/GraphTokenGateway.sol"; -import "../../gateway/ICallhookReceiver.sol"; -import "../token/L2GraphToken.sol"; +import { L2ArbitrumMessenger } from "../../arbitrum/L2ArbitrumMessenger.sol"; +import { AddressAliasHelper } from "../../arbitrum/AddressAliasHelper.sol"; +import { ITokenGateway } from "../../arbitrum/ITokenGateway.sol"; +import { Managed } from "../../governance/Managed.sol"; +import { GraphTokenGateway } from "../../gateway/GraphTokenGateway.sol"; +import { ICallhookReceiver } from "../../gateway/ICallhookReceiver.sol"; +import { L2GraphToken } from "../token/L2GraphToken.sol"; /** * @title L2 Graph Token Gateway Contract @@ -21,7 +23,7 @@ import "../token/L2GraphToken.sol"; * and https://github.com/livepeer/arbitrum-lpt-bridge) */ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, ReentrancyGuardUpgradeable { - using SafeMath for uint256; + using SafeMathUpgradeable for uint256; /// Address of the Graph Token contract on L1 address public l1GRT; diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol index 407348dd3..3f7882b5c 100644 --- a/contracts/l2/token/GraphTokenUpgradeable.sol +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -2,11 +2,11 @@ pragma solidity ^0.7.6; -import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol"; -import "@openzeppelin/contracts/cryptography/ECDSA.sol"; +import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol"; +import { ECDSAUpgradeable } from "@openzeppelin/contracts-upgradeable/cryptography/ECDSAUpgradeable.sol"; -import "../../upgrades/GraphUpgradeable.sol"; -import "../../governance/Governed.sol"; +import { GraphUpgradeable } from "../../upgrades/GraphUpgradeable.sol"; +import { Governed } from "../../governance/Governed.sol"; /** * @title GraphTokenUpgradeable contract @@ -100,7 +100,7 @@ abstract contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20Burn ) ); - address recoveredAddress = ECDSA.recover(digest, _v, _r, _s); + address recoveredAddress = ECDSAUpgradeable.recover(digest, _v, _r, _s); require(_owner == recoveredAddress, "GRT: invalid permit"); nonces[_owner] = nonces[_owner] + 1; diff --git a/contracts/l2/token/L2GraphToken.sol b/contracts/l2/token/L2GraphToken.sol index bdcfa973c..639444870 100644 --- a/contracts/l2/token/L2GraphToken.sol +++ b/contracts/l2/token/L2GraphToken.sol @@ -2,10 +2,8 @@ pragma solidity ^0.7.6; -import "@openzeppelin/contracts/math/SafeMath.sol"; - -import "./GraphTokenUpgradeable.sol"; -import "../../arbitrum/IArbToken.sol"; +import { GraphTokenUpgradeable } from "./GraphTokenUpgradeable.sol"; +import { IArbToken } from "../../arbitrum/IArbToken.sol"; /** * @title L2 Graph Token Contract @@ -13,8 +11,6 @@ import "../../arbitrum/IArbToken.sol"; * through the L2GraphTokenGateway. */ contract L2GraphToken is GraphTokenUpgradeable, IArbToken { - using SafeMath for uint256; - /// Address of the gateway (on L2) that is allowed to mint tokens address public gateway; /// Address of the corresponding Graph Token contract on L1 From eeb4bd03753c1fe2b9175ad0ec98a35babafb021 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 18:19:07 -0300 Subject: [PATCH 065/100] fix: use explicit imports (C4 QA) --- contracts/curation/Curation.sol | 25 ++++++++++++++----------- contracts/curation/CurationStorage.sol | 4 +++- contracts/gateway/BridgeEscrow.sol | 6 +++--- contracts/gateway/GraphTokenGateway.sol | 8 ++++---- contracts/governance/Controller.sol | 8 ++++---- contracts/governance/Managed.sol | 16 ++++++++-------- contracts/upgrades/GraphProxy.sol | 6 +++--- contracts/upgrades/GraphProxyAdmin.sol | 6 +++--- contracts/upgrades/GraphUpgradeable.sol | 2 +- 9 files changed, 43 insertions(+), 38 deletions(-) diff --git a/contracts/curation/Curation.sol b/contracts/curation/Curation.sol index fc59b33d1..565a51a89 100644 --- a/contracts/curation/Curation.sol +++ b/contracts/curation/Curation.sol @@ -2,17 +2,20 @@ pragma solidity ^0.7.6; -import "@openzeppelin/contracts/utils/Address.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@openzeppelin/contracts/proxy/Clones.sol"; - -import "../bancor/BancorFormula.sol"; -import "../upgrades/GraphUpgradeable.sol"; -import "../utils/TokenUtils.sol"; - -import "./CurationStorage.sol"; -import "./ICuration.sol"; -import "./GraphCurationToken.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; +import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; + +import { BancorFormula } from "../bancor/BancorFormula.sol"; +import { GraphUpgradeable } from "../upgrades/GraphUpgradeable.sol"; +import { TokenUtils } from "../utils/TokenUtils.sol"; +import { IRewardsManager } from "../rewards/IRewardsManager.sol"; +import { Managed } from "../governance/Managed.sol"; +import { IGraphToken } from "../token/IGraphToken.sol"; +import { CurationV1Storage } from "./CurationStorage.sol"; +import { ICuration } from "./ICuration.sol"; +import { IGraphCurationToken } from "./IGraphCurationToken.sol"; +import { GraphCurationToken } from "./GraphCurationToken.sol"; /** * @title Curation contract diff --git a/contracts/curation/CurationStorage.sol b/contracts/curation/CurationStorage.sol index dd2edd18b..a530d9199 100644 --- a/contracts/curation/CurationStorage.sol +++ b/contracts/curation/CurationStorage.sol @@ -2,7 +2,9 @@ pragma solidity ^0.7.6; -import "../governance/Managed.sol"; +import { ICuration } from "./ICuration.sol"; +import { IGraphCurationToken } from "./IGraphCurationToken.sol"; +import { Managed } from "../governance/Managed.sol"; abstract contract CurationV1Storage is Managed, ICuration { // -- Pool -- diff --git a/contracts/gateway/BridgeEscrow.sol b/contracts/gateway/BridgeEscrow.sol index c18b839f2..3c0fa5c1a 100644 --- a/contracts/gateway/BridgeEscrow.sol +++ b/contracts/gateway/BridgeEscrow.sol @@ -4,9 +4,9 @@ pragma solidity ^0.7.6; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; -import "../upgrades/GraphUpgradeable.sol"; -import "../governance/Managed.sol"; -import "../token/IGraphToken.sol"; +import { GraphUpgradeable } from "../upgrades/GraphUpgradeable.sol"; +import { Managed } from "../governance/Managed.sol"; +import { IGraphToken } from "../token/IGraphToken.sol"; /** * @title Bridge Escrow diff --git a/contracts/gateway/GraphTokenGateway.sol b/contracts/gateway/GraphTokenGateway.sol index 66c6e7110..5094f95c6 100644 --- a/contracts/gateway/GraphTokenGateway.sol +++ b/contracts/gateway/GraphTokenGateway.sol @@ -2,10 +2,10 @@ pragma solidity ^0.7.6; -import "../upgrades/GraphUpgradeable.sol"; -import "../arbitrum/ITokenGateway.sol"; -import "../governance/Pausable.sol"; -import "../governance/Managed.sol"; +import { GraphUpgradeable } from "../upgrades/GraphUpgradeable.sol"; +import { ITokenGateway } from "../arbitrum/ITokenGateway.sol"; +import { Pausable } from "../governance/Pausable.sol"; +import { Managed } from "../governance/Managed.sol"; /** * @title L1/L2 Graph Token Gateway diff --git a/contracts/governance/Controller.sol b/contracts/governance/Controller.sol index 504e1736a..bc287d2be 100644 --- a/contracts/governance/Controller.sol +++ b/contracts/governance/Controller.sol @@ -2,10 +2,10 @@ pragma solidity ^0.7.6; -import "./IController.sol"; -import "./IManaged.sol"; -import "./Governed.sol"; -import "./Pausable.sol"; +import { IController } from "./IController.sol"; +import { IManaged } from "./IManaged.sol"; +import { Governed } from "./Governed.sol"; +import { Pausable } from "./Pausable.sol"; /** * @title Graph Controller contract diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index 33a037d65..6b8fe624e 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -2,16 +2,16 @@ pragma solidity ^0.7.6; -import "./IController.sol"; +import { IController } from "./IController.sol"; -import "../curation/ICuration.sol"; -import "../epochs/IEpochManager.sol"; -import "../rewards/IRewardsManager.sol"; -import "../staking/IStaking.sol"; -import "../token/IGraphToken.sol"; -import "../arbitrum/ITokenGateway.sol"; +import { ICuration } from "../curation/ICuration.sol"; +import { IEpochManager } from "../epochs/IEpochManager.sol"; +import { IRewardsManager } from "../rewards/IRewardsManager.sol"; +import { IStaking } from "../staking/IStaking.sol"; +import { IGraphToken } from "../token/IGraphToken.sol"; +import { ITokenGateway } from "../arbitrum/ITokenGateway.sol"; -import "./IManaged.sol"; +import { IManaged } from "./IManaged.sol"; /** * @title Graph Managed contract diff --git a/contracts/upgrades/GraphProxy.sol b/contracts/upgrades/GraphProxy.sol index 11fb5ce3c..48d12d862 100644 --- a/contracts/upgrades/GraphProxy.sol +++ b/contracts/upgrades/GraphProxy.sol @@ -2,11 +2,11 @@ pragma solidity ^0.7.6; -import "@openzeppelin/contracts/utils/Address.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import "./GraphProxyStorage.sol"; +import { GraphProxyStorage } from "./GraphProxyStorage.sol"; -import "./IGraphProxy.sol"; +import { IGraphProxy } from "./IGraphProxy.sol"; /** * @title Graph Proxy diff --git a/contracts/upgrades/GraphProxyAdmin.sol b/contracts/upgrades/GraphProxyAdmin.sol index 6adf9674c..d96dbd449 100644 --- a/contracts/upgrades/GraphProxyAdmin.sol +++ b/contracts/upgrades/GraphProxyAdmin.sol @@ -2,10 +2,10 @@ pragma solidity ^0.7.6; -import "../governance/Governed.sol"; +import { Governed } from "../governance/Governed.sol"; -import "./IGraphProxy.sol"; -import "./GraphUpgradeable.sol"; +import { IGraphProxy } from "./IGraphProxy.sol"; +import { GraphUpgradeable } from "./GraphUpgradeable.sol"; /** * @title GraphProxyAdmin diff --git a/contracts/upgrades/GraphUpgradeable.sol b/contracts/upgrades/GraphUpgradeable.sol index bac95f1f5..92ce80ad7 100644 --- a/contracts/upgrades/GraphUpgradeable.sol +++ b/contracts/upgrades/GraphUpgradeable.sol @@ -2,7 +2,7 @@ pragma solidity ^0.7.6; -import "./IGraphProxy.sol"; +import { IGraphProxy } from "./IGraphProxy.sol"; /** * @title Graph Upgradeable From ae03579645da3aef78ac630da9739bc086a51730 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 24 Oct 2022 18:42:18 -0300 Subject: [PATCH 066/100] fix: standardize function order in some contracts (C4 QA) --- contracts/gateway/GraphTokenGateway.sol | 16 +-- contracts/gateway/L1GraphTokenGateway.sol | 88 ++++++------ contracts/l2/gateway/L2GraphTokenGateway.sol | 142 +++++++++---------- contracts/upgrades/GraphProxy.sol | 32 ++--- 4 files changed, 139 insertions(+), 139 deletions(-) diff --git a/contracts/gateway/GraphTokenGateway.sol b/contracts/gateway/GraphTokenGateway.sol index 5094f95c6..ca2ad4c95 100644 --- a/contracts/gateway/GraphTokenGateway.sol +++ b/contracts/gateway/GraphTokenGateway.sol @@ -35,14 +35,6 @@ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITok _setPauseGuardian(_newPauseGuardian); } - /** - * @dev Override the default pausing from Managed to allow pausing this - * particular contract instead of pausing from the Controller. - */ - function _notPaused() internal view override { - require(!_paused, "Paused (contract)"); - } - /** * @notice Change the paused state of the contract * @param _newPaused New value for the pause state (true means the transfers will be paused) @@ -62,6 +54,14 @@ abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITok return _paused; } + /** + * @dev Override the default pausing from Managed to allow pausing this + * particular contract instead of pausing from the Controller. + */ + function _notPaused() internal view override { + require(!_paused, "Paused (contract)"); + } + /** * @dev Runs state validation before unpausing, reverts if * something is not set properly diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index f88c766e3..06c4b7ea7 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -223,7 +223,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess bytes memory outboundCalldata; { bytes memory extraData; - (from, maxSubmissionCost, extraData) = parseOutboundData(_data); + (from, maxSubmissionCost, extraData) = _parseOutboundData(_data); require( extraData.length == 0 || callhookAllowlist[msg.sender] == true, "CALL_HOOK_DATA_NOT_ALLOWED" @@ -285,37 +285,17 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess } /** - * @notice Decodes calldata required for migration of tokens - * @dev Data must include maxSubmissionCost, extraData can be left empty. When the router - * sends an outbound message, data also contains the from address. - * @param _data encoded callhook data - * @return Sender of the tx - * @return Base ether value required to keep retryable ticket alive - * @return Additional data sent to L2 + * @notice Calculate the L2 address of a bridged token + * @dev In our case, this would only work for GRT. + * @param _l1ERC20 address of L1 GRT contract + * @return L2 address of the bridged GRT token */ - function parseOutboundData(bytes calldata _data) - private - view - returns ( - address, - uint256, - bytes memory - ) - { - address from; - uint256 maxSubmissionCost; - bytes memory extraData; - if (msg.sender == l1Router) { - // Data encoded by the Gateway Router includes the sender address - (from, extraData) = abi.decode(_data, (address, bytes)); - } else { - from = msg.sender; - extraData = _data; + function calculateL2TokenAddress(address _l1ERC20) external view override returns (address) { + IGraphToken token = graphToken(); + if (_l1ERC20 != address(token)) { + return address(0); } - // User-encoded data contains the max retryable ticket submission cost - // and additional L2 calldata - (maxSubmissionCost, extraData) = abi.decode(extraData, (uint256, bytes)); - return (from, maxSubmissionCost, extraData); + return l2GRT; } /** @@ -349,20 +329,6 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess ); } - /** - * @notice Calculate the L2 address of a bridged token - * @dev In our case, this would only work for GRT. - * @param _l1ERC20 address of L1 GRT contract - * @return L2 address of the bridged GRT token - */ - function calculateL2TokenAddress(address _l1ERC20) external view override returns (address) { - IGraphToken token = graphToken(); - if (_l1ERC20 != address(token)) { - return address(0); - } - return l2GRT; - } - /** * @dev Runs state validation before unpausing, reverts if * something is not set properly @@ -373,4 +339,38 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess require(l2Counterpart != address(0), "L2_COUNTERPART_NOT_SET"); require(escrow != address(0), "ESCROW_NOT_SET"); } + + /** + * @notice Decodes calldata required for migration of tokens + * @dev Data must include maxSubmissionCost, extraData can be left empty. When the router + * sends an outbound message, data also contains the from address. + * @param _data encoded callhook data + * @return Sender of the tx + * @return Base ether value required to keep retryable ticket alive + * @return Additional data sent to L2 + */ + function _parseOutboundData(bytes calldata _data) + private + view + returns ( + address, + uint256, + bytes memory + ) + { + address from; + uint256 maxSubmissionCost; + bytes memory extraData; + if (msg.sender == l1Router) { + // Data encoded by the Gateway Router includes the sender address + (from, extraData) = abi.decode(_data, (address, bytes)); + } else { + from = msg.sender; + extraData = _data; + } + // User-encoded data contains the max retryable ticket submission cost + // and additional L2 calldata + (maxSubmissionCost, extraData) = abi.decode(extraData, (uint256, bytes)); + return (from, maxSubmissionCost, extraData); + } } diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index 39111ac0f..480896791 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -122,6 +122,65 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran emit L1CounterpartAddressSet(_l1Counterpart); } + /** + * @notice Burns L2 tokens and initiates a transfer to L1. + * The tokens will be received on L1 only after the wait period (7 days) is over, + * and will require an Outbox.executeTransaction to finalize. + * @dev no additional callhook data is allowed + * @param _l1Token L1 Address of GRT (needed for compatibility with Arbitrum Gateway Router) + * @param _to Recipient address on L1 + * @param _amount Amount of tokens to burn + * @param _data Contains sender and additional data to send to L1 + * @return ID of the withdraw tx + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + bytes calldata _data + ) external returns (bytes memory) { + return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data); + } + + /** + * @notice Receives token amount from L1 and mints the equivalent tokens to the receiving address + * @dev Only accepts transactions from the L1 GRT Gateway. + * The function is payable for ITokenGateway compatibility, but msg.value must be zero. + * Note that allowlisted senders (some protocol contracts) can include additional calldata + * for a callhook to be executed on the L2 side when the tokens are received. In this case, the L2 transaction + * can revert if the callhook reverts, potentially locking the tokens on the bridge if the callhook + * never succeeds. This requires extra care when adding contracts to the allowlist, but is necessary to ensure that + * the tickets can be retried in the case of a temporary failure, and to ensure the atomicity of callhooks + * with token transfers. + * @param _l1Token L1 Address of GRT + * @param _from Address of the sender on L1 + * @param _to Recipient address on L2 + * @param _amount Amount of tokens transferred + * @param _data Extra callhook data, only used when the sender is allowlisted + */ + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable override nonReentrant notPaused onlyL1Counterpart { + require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); + require(msg.value == 0, "INVALID_NONZERO_VALUE"); + + L2GraphToken(calculateL2TokenAddress(l1GRT)).bridgeMint(_to, _amount); + + if (_data.length > 0) { + bytes memory callhookData; + { + (, callhookData) = abi.decode(_data, (bytes, bytes)); + } + ICallhookReceiver(_to).onTokenTransfer(_from, _amount, callhookData); + } + + emit DepositFinalized(_l1Token, _from, _to, _amount); + } + /** * @notice Burns L2 tokens and initiates a transfer to L1. * The tokens will be available on L1 only after the wait period (7 days) is over, @@ -151,7 +210,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran OutboundCalldata memory outboundCalldata; - (outboundCalldata.from, outboundCalldata.extraData) = parseOutboundData(_data); + (outboundCalldata.from, outboundCalldata.extraData) = _parseOutboundData(_data); require(outboundCalldata.extraData.length == 0, "CALL_HOOK_DATA_NOT_ALLOWED"); // from needs to approve this contract to burn the amount first @@ -176,26 +235,6 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran return abi.encode(id); } - /** - * @notice Burns L2 tokens and initiates a transfer to L1. - * The tokens will be received on L1 only after the wait period (7 days) is over, - * and will require an Outbox.executeTransaction to finalize. - * @dev no additional callhook data is allowed - * @param _l1Token L1 Address of GRT (needed for compatibility with Arbitrum Gateway Router) - * @param _to Recipient address on L1 - * @param _amount Amount of tokens to burn - * @param _data Contains sender and additional data to send to L1 - * @return ID of the withdraw tx - */ - function outboundTransfer( - address _l1Token, - address _to, - uint256 _amount, - bytes calldata _data - ) external returns (bytes memory) { - return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data); - } - /** * @notice Calculate the L2 address of a bridged token * @dev In our case, this would only work for GRT. @@ -209,45 +248,6 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran return address(graphToken()); } - /** - * @notice Receives token amount from L1 and mints the equivalent tokens to the receiving address - * @dev Only accepts transactions from the L1 GRT Gateway. - * The function is payable for ITokenGateway compatibility, but msg.value must be zero. - * Note that allowlisted senders (some protocol contracts) can include additional calldata - * for a callhook to be executed on the L2 side when the tokens are received. In this case, the L2 transaction - * can revert if the callhook reverts, potentially locking the tokens on the bridge if the callhook - * never succeeds. This requires extra care when adding contracts to the allowlist, but is necessary to ensure that - * the tickets can be retried in the case of a temporary failure, and to ensure the atomicity of callhooks - * with token transfers. - * @param _l1Token L1 Address of GRT - * @param _from Address of the sender on L1 - * @param _to Recipient address on L2 - * @param _amount Amount of tokens transferred - * @param _data Extra callhook data, only used when the sender is allowlisted - */ - function finalizeInboundTransfer( - address _l1Token, - address _from, - address _to, - uint256 _amount, - bytes calldata _data - ) external payable override nonReentrant notPaused onlyL1Counterpart { - require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); - require(msg.value == 0, "INVALID_NONZERO_VALUE"); - - L2GraphToken(calculateL2TokenAddress(l1GRT)).bridgeMint(_to, _amount); - - if (_data.length > 0) { - bytes memory callhookData; - { - (, callhookData) = abi.decode(_data, (bytes, bytes)); - } - ICallhookReceiver(_to).onTokenTransfer(_from, _amount, callhookData); - } - - emit DepositFinalized(_l1Token, _from, _to, _amount); - } - /** * @notice Creates calldata required to send tx to L1 * @dev encodes the target function with its params which @@ -277,6 +277,16 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran ); } + /** + * @dev Runs state validation before unpausing, reverts if + * something is not set properly + */ + function _checksBeforeUnpause() internal view override { + require(l2Router != address(0), "ROUTER_NOT_SET"); + require(l1Counterpart != address(0), "L1_COUNTERPART_NOT_SET"); + require(l1GRT != address(0), "L1GRT_NOT_SET"); + } + /** * @notice Decodes calldata required for migration of tokens * @dev extraData can be left empty @@ -284,7 +294,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran * @return Sender of the tx * @return Any other data sent to L1 */ - function parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) { + function _parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) { address from; bytes memory extraData; if (msg.sender == l2Router) { @@ -295,14 +305,4 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran } return (from, extraData); } - - /** - * @dev Runs state validation before unpausing, reverts if - * something is not set properly - */ - function _checksBeforeUnpause() internal view override { - require(l2Router != address(0), "ROUTER_NOT_SET"); - require(l1Counterpart != address(0), "L1_COUNTERPART_NOT_SET"); - require(l1GRT != address(0), "L1GRT_NOT_SET"); - } } diff --git a/contracts/upgrades/GraphProxy.sol b/contracts/upgrades/GraphProxy.sol index 48d12d862..d3f6eacec 100644 --- a/contracts/upgrades/GraphProxy.sol +++ b/contracts/upgrades/GraphProxy.sol @@ -59,6 +59,22 @@ contract GraphProxy is GraphProxyStorage, IGraphProxy { _setPendingImplementation(_impl); } + /** + * @notice Fallback function that delegates calls to implementation. Will run if call data + * is empty. + */ + receive() external payable { + _fallback(); + } + + /** + * @notice Fallback function that delegates calls to implementation. Will run if no other + * function in the contract matches the call data. + */ + fallback() external payable { + _fallback(); + } + /** * @notice Get the current admin * @@ -193,20 +209,4 @@ contract GraphProxy is GraphProxyStorage, IGraphProxy { } } } - - /** - * @notice Fallback function that delegates calls to implementation. Will run if no other - * function in the contract matches the call data. - */ - fallback() external payable { - _fallback(); - } - - /** - * @notice Fallback function that delegates calls to implementation. Will run if call data - * is empty. - */ - receive() external payable { - _fallback(); - } } From 19928cc3a5287e0fad772bf62cd5786b59fbb268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Tue, 25 Oct 2022 10:14:34 -0300 Subject: [PATCH 067/100] fix: whitelist to allowlist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- contracts/gateway/ICallhookReceiver.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/gateway/ICallhookReceiver.sol b/contracts/gateway/ICallhookReceiver.sol index 2f71d180c..ff0fbfab1 100644 --- a/contracts/gateway/ICallhookReceiver.sol +++ b/contracts/gateway/ICallhookReceiver.sol @@ -3,7 +3,7 @@ /** * @title Interface for contracts that can receive callhooks through the Arbitrum GRT bridge * @dev Any contract that can receive a callhook on L2, sent through the bridge from L1, must - * be whitelisted by the governor, but also implement this interface that contains + * be allowlisted by the governor, but also implement this interface that contains * the function that will actually be called by the L2GraphTokenGateway. */ pragma solidity ^0.7.6; From 4bea0941bb9a65eee38d5c803414ae593a7ec19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Carranza=20V=C3=A9lez?= Date: Tue, 25 Oct 2022 10:20:04 -0300 Subject: [PATCH 068/100] fix: adjust some revert strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tomás Migone --- contracts/l2/gateway/L2GraphTokenGateway.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index 480896791..ca9c62bca 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -282,9 +282,9 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran * something is not set properly */ function _checksBeforeUnpause() internal view override { - require(l2Router != address(0), "ROUTER_NOT_SET"); + require(l2Router != address(0), "L2_ROUTER_NOT_SET"); require(l1Counterpart != address(0), "L1_COUNTERPART_NOT_SET"); - require(l1GRT != address(0), "L1GRT_NOT_SET"); + require(l1GRT != address(0), "L1_GRT_NOT_SET"); } /** From ce0d0a3f2eebb6fb51e702d9d1e84fbc1546193a Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Tue, 25 Oct 2022 10:48:04 -0300 Subject: [PATCH 069/100] test: fix revert strings --- test/l2/l2GraphTokenGateway.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/l2/l2GraphTokenGateway.test.ts b/test/l2/l2GraphTokenGateway.test.ts index 7c222b73d..f85ea7dfb 100644 --- a/test/l2/l2GraphTokenGateway.test.ts +++ b/test/l2/l2GraphTokenGateway.test.ts @@ -175,7 +175,7 @@ describe('L2GraphTokenGateway', () => { }) it('cannot be paused if some state variables are not set', async function () { let tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) - await expect(tx).revertedWith('ROUTER_NOT_SET') + await expect(tx).revertedWith('L2_ROUTER_NOT_SET') await l2GraphTokenGateway.connect(governor.signer).setL2Router(mockRouter.address) tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) await expect(tx).revertedWith('L1_COUNTERPART_NOT_SET') @@ -183,7 +183,7 @@ describe('L2GraphTokenGateway', () => { .connect(governor.signer) .setL1CounterpartAddress(mockL1Gateway.address) tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) - await expect(tx).revertedWith('L1GRT_NOT_SET') + await expect(tx).revertedWith('L1_GRT_NOT_SET') }) it('can be paused and unpaused by the governor', async function () { await fixture.configureL2Bridge( From caf5ab5723353b9062878ade247a060a2b55514f Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 31 Oct 2022 20:15:34 -0300 Subject: [PATCH 070/100] fix: remove unneeded empty bytes from callhook data --- contracts/gateway/L1GraphTokenGateway.sol | 4 +--- contracts/l2/gateway/L2GraphTokenGateway.sol | 6 +----- test/gateway/l1GraphTokenGateway.test.ts | 8 +------- test/l2/l2GraphTokenGateway.test.ts | 12 +++--------- 4 files changed, 6 insertions(+), 24 deletions(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 06c4b7ea7..3c81080ae 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -316,8 +316,6 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess uint256 _amount, bytes memory _data ) public pure returns (bytes memory) { - bytes memory emptyBytes; - return abi.encodeWithSelector( ITokenGateway.finalizeInboundTransfer.selector, @@ -325,7 +323,7 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess _from, _to, _amount, - abi.encode(emptyBytes, _data) + _data ); } diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol index ca9c62bca..7af102fb4 100644 --- a/contracts/l2/gateway/L2GraphTokenGateway.sol +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -171,11 +171,7 @@ contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, Reentran L2GraphToken(calculateL2TokenAddress(l1GRT)).bridgeMint(_to, _amount); if (_data.length > 0) { - bytes memory callhookData; - { - (, callhookData) = abi.decode(_data, (bytes, bytes)); - } - ICallhookReceiver(_to).onTokenTransfer(_from, _amount, callhookData); + ICallhookReceiver(_to).onTokenTransfer(_from, _amount, _data); } emit DepositFinalized(_l1Token, _from, _to, _amount); diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts index 2542832ab..b619e7a42 100644 --- a/test/gateway/l1GraphTokenGateway.test.ts +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -339,13 +339,7 @@ describe('L1GraphTokenGateway', () => { const selector = l1GraphTokenGateway.interface.getSighash('finalizeInboundTransfer') const params = utils.defaultAbiCoder.encode( ['address', 'address', 'address', 'uint256', 'bytes'], - [ - grt.address, - tokenSender.address, - l2Receiver.address, - toGRT('10'), - utils.defaultAbiCoder.encode(['bytes', 'bytes'], [emptyCallHookData, callHookData]), - ], + [grt.address, tokenSender.address, l2Receiver.address, toGRT('10'), callHookData], ) const outboundData = utils.hexlify(utils.concat([selector, params])) diff --git a/test/l2/l2GraphTokenGateway.test.ts b/test/l2/l2GraphTokenGateway.test.ts index f85ea7dfb..236817afd 100644 --- a/test/l2/l2GraphTokenGateway.test.ts +++ b/test/l2/l2GraphTokenGateway.test.ts @@ -39,14 +39,10 @@ describe('L2GraphTokenGateway', () => { const senderTokens = toGRT('1000') const defaultData = '0x' - const notEmptyCallHookData = utils.defaultAbiCoder.encode( + const defaultDataWithNotEmptyCallHookData = utils.defaultAbiCoder.encode( ['uint256', 'uint256'], [toBN('1337'), toBN('42')], ) - const defaultDataWithNotEmptyCallHookData = utils.defaultAbiCoder.encode( - ['bytes', 'bytes'], - ['0x', notEmptyCallHookData], - ) before(async function () { ;[ @@ -419,7 +415,6 @@ describe('L2GraphTokenGateway', () => { ['uint256', 'uint256'], [toBN('0'), toBN('42')], ) - const data = utils.defaultAbiCoder.encode(['bytes', 'bytes'], ['0x', callHookData]) const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) await me.signer.sendTransaction({ to: await mockL1GatewayL2Alias.getAddress(), @@ -432,13 +427,12 @@ describe('L2GraphTokenGateway', () => { tokenSender.address, callhookReceiverMock.address, toGRT('10'), - data, + callHookData, ) await expect(tx).revertedWith('FOO_IS_ZERO') }) it('reverts if trying to call a callhook in a contract that does not implement onTokenTransfer', async function () { const callHookData = utils.defaultAbiCoder.encode(['uint256'], [toBN('0')]) - const data = utils.defaultAbiCoder.encode(['bytes', 'bytes'], ['0x', callHookData]) const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) await me.signer.sendTransaction({ to: await mockL1GatewayL2Alias.getAddress(), @@ -452,7 +446,7 @@ describe('L2GraphTokenGateway', () => { tokenSender.address, rewardsManager.address, toGRT('10'), - data, + callHookData, ) await expect(tx).revertedWith( "function selector was not recognized and there's no fallback function", From 598ecc3df395310afe1aeabd383b69eb10ea29a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 26 Sep 2022 14:31:43 -0400 Subject: [PATCH 071/100] chore: update arbitrum sdk to 3.0-rc1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- cli/commands/bridge/to-l1.ts | 15 ++- cli/cross-chain.ts | 19 ++-- package.json | 2 +- yarn.lock | 188 +---------------------------------- 4 files changed, 27 insertions(+), 197 deletions(-) diff --git a/cli/commands/bridge/to-l1.ts b/cli/commands/bridge/to-l1.ts index 42f319eb4..0913aa2e1 100644 --- a/cli/commands/bridge/to-l1.ts +++ b/cli/commands/bridge/to-l1.ts @@ -102,11 +102,16 @@ export const startSendToL1 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Prom const l2Receipt = new L2TransactionReceipt(receipt) const l2ToL1Message = (await l2Receipt.getL2ToL1Messages(cli.wallet))[0] - logger.info(`The transaction generated an L2 to L1 message in outbox with eth block number:`) - logger.info(l2ToL1Message.event.ethBlockNum.toString()) - logger.info( - `After the dispute period is finalized (in ~1 week), you can finalize this by calling`, - ) + const ethBlockNum = l2ToL1Message.getFirstExecutableBlock(l2Provider) + if (ethBlockNum === null) { + logger.info(`L2 to L1 message can or already has been executed. If not finalized call`) + } else { + logger.info(`The transaction generated an L2 to L1 message in outbox with eth block number:`) + logger.info(ethBlockNum.toString()) + logger.info( + `After the dispute period is finalized (in ~1 week), you can finalize this by calling`, + ) + } logger.info(`finish-send-to-l1 with the following txhash:`) logger.info(l2Receipt.transactionHash) } diff --git a/cli/cross-chain.ts b/cli/cross-chain.ts index ea3b081ac..58252ca6e 100644 --- a/cli/cross-chain.ts +++ b/cli/cross-chain.ts @@ -1,4 +1,5 @@ import { L1ToL2MessageGasEstimator } from '@arbitrum/sdk' +import { L1ToL2MessageNoGasParams } from '@arbitrum/sdk/dist/lib/message/L1ToL2MessageCreator' import { BigNumber, providers } from 'ethers' import { parseEther } from 'ethers/lib/utils' @@ -44,20 +45,24 @@ export const estimateRetryableTxGas = async ( logger.info('Estimating retryable ticket gas:') const baseFee = (await l1Provider.getBlock('latest')).baseFeePerGas const gasEstimator = new L1ToL2MessageGasEstimator(l2Provider) + const retryableEstimateData: L1ToL2MessageNoGasParams = { + from: gatewayAddress, + to: l2Dest, + data: depositCalldata, + l2CallValue: parseEther('0'), + excessFeeRefundAddress: gatewayAddress, + callValueRefundAddress: gatewayAddress, + } const gasParams = await gasEstimator.estimateAll( - gatewayAddress, - l2Dest, - depositCalldata, - parseEther('0'), + retryableEstimateData, baseFee as BigNumber, - gatewayAddress, - gatewayAddress, l1Provider, ) + // override fixed values return { maxGas: opts.maxGas ?? gasParams.gasLimit, gasPriceBid: opts.gasPriceBid ?? gasParams.maxFeePerGas, - maxSubmissionCost: opts.maxSubmissionCost ?? gasParams.maxSubmissionFee, + maxSubmissionCost: opts.maxSubmissionCost ?? gasParams.maxSubmissionCost, } } diff --git a/package.json b/package.json index 63fab4bd3..0b06cc250 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "ethers": "^5.6.0" }, "devDependencies": { - "@arbitrum/sdk": "^3.0.0-beta.6", + "@arbitrum/sdk": "3.0.0-rc.1", "@commitlint/cli": "^13.2.1", "@commitlint/config-conventional": "^13.2.0", "@defi-wonderland/smock": "^2.0.7", diff --git a/yarn.lock b/yarn.lock index 7b859117b..2cfb8a7a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,21 +2,15 @@ # yarn lockfile v1 -"@arbitrum/sdk@^3.0.0-beta.6": - version "3.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.0.0-beta.6.tgz#a36c3e39a7358396b5533f3288125107da6ae59e" - integrity sha512-kPCfgj72MeyVcIXQKoztLO29UTcpSbXFzc/S0oDgVNNcHcXp1hWUJqqkVRg0O43P2yKjZRT/I94K0Nj2nZNiiQ== +"@arbitrum/sdk@3.0.0-rc.1": + version "3.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.0.0-rc.1.tgz#5335bd833ded89bfe95328a44ccd2ba743f82239" + integrity sha512-8t4fgvQR/83whezeb1WKBeWtBnfmZ0vaDE1w96cKEt8bdrmTQ8yQF3Vpeq/Y21/SrOdDNj5CQWor1Y6wB+FbyQ== dependencies: "@ethersproject/address" "^5.0.8" "@ethersproject/bignumber" "^5.1.1" "@ethersproject/bytes" "^5.0.8" - "@typechain/ethers-v5" "9.0.0" - "@types/prompts" "^2.0.14" - "@types/yargs" "^17.0.9" - dotenv "^10.0.0" ethers "^5.1.0" - ts-node "^10.2.1" - typechain "7.0.0" "@babel/code-frame@7.12.11": version "7.12.11" @@ -191,18 +185,6 @@ dependencies: chalk "^4.0.0" -"@cspotcode/source-map-consumer@0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" - integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== - -"@cspotcode/source-map-support@0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" - integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== - dependencies: - "@cspotcode/source-map-consumer" "0.8.0" - "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1252,14 +1234,6 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== -"@typechain/ethers-v5@9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-9.0.0.tgz#6aa93bea7425c0463bd8a61eea3643540ef851bd" - integrity sha512-bAanuPl1L2itaUdMvor/QvwnIH+TM/CmG00q17Ilv3ZZMeJ2j8HcarhgJUZ9pBY1teBb85P8cC03dz3mSSx+tQ== - dependencies: - lodash "^4.17.15" - ts-essentials "^7.0.1" - "@typechain/ethers-v5@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-2.0.0.tgz#cd3ca1590240d587ca301f4c029b67bfccd08810" @@ -1470,13 +1444,6 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== -"@types/prompts@^2.0.14": - version "2.0.14" - resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-2.0.14.tgz#10cb8899844bb0771cabe57c1becaaaca9a3b521" - integrity sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA== - dependencies: - "@types/node" "*" - "@types/qs@^6.2.31": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" @@ -1560,13 +1527,6 @@ dependencies: "@types/yargs-parser" "*" -"@types/yargs@^17.0.9": - version "17.0.10" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a" - integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA== - dependencies: - "@types/yargs-parser" "*" - "@typescript-eslint/eslint-plugin@^4.0.0": version "4.33.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" @@ -1988,16 +1948,6 @@ array-back@^2.0.0: dependencies: typical "^2.6.1" -array-back@^3.0.1, array-back@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" - integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== - -array-back@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" - integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -3541,26 +3491,6 @@ command-line-args@^4.0.7: find-replace "^1.0.3" typical "^2.6.1" -command-line-args@^5.1.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" - integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== - dependencies: - array-back "^3.1.0" - find-replace "^3.0.0" - lodash.camelcase "^4.3.0" - typical "^4.0.0" - -command-line-usage@^6.1.0: - version "6.1.2" - resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.2.tgz#2b7ccd48a93fb19bd71ca8fe9900feab00e557b0" - integrity sha512-I+0XN613reAhpBQ6icsPOTwu9cvhc9NtLtUcY2fGYuwm9JZiWBzFDA8w0PHqQjru7Xth7fM/y9TJ13+VKdjh7Q== - dependencies: - array-back "^4.0.1" - chalk "^2.4.2" - table-layout "^1.0.1" - typical "^5.2.0" - commander@2.18.0: version "2.18.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" @@ -3959,11 +3889,6 @@ deep-equal@~1.1.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-extend@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -4166,11 +4091,6 @@ dotenv@*: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== -dotenv@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - dotenv@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-9.0.2.tgz#dacc20160935a37dea6364aa1bef819fb9b6ab05" @@ -5465,13 +5385,6 @@ find-replace@^1.0.3: array-back "^1.0.4" test-value "^2.1.0" -find-replace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" - integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== - dependencies: - array-back "^3.0.1" - find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -7892,11 +7805,6 @@ lodash.assign@^4.0.3, lodash.assign@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= - lodash.get@^4: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -10055,11 +9963,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -reduce-flatten@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" - integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== - regenerate@^1.2.1: version "1.4.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" @@ -11122,11 +11025,6 @@ string-argv@0.3.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== -string-format@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" - integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -11359,16 +11257,6 @@ sync-rpc@^1.2.1: dependencies: get-port "^3.1.0" -table-layout@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" - integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== - dependencies: - array-back "^4.0.1" - deep-extend "~0.6.0" - typical "^5.2.0" - wordwrapjs "^4.0.0" - table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" @@ -11622,16 +11510,6 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== -ts-command-line-args@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.2.1.tgz#fd6913e542099012c0ffb2496126a8f38305c7d6" - integrity sha512-mnK68QA86FYzQYTSA/rxIjT/8EpKsvQw9QkawPic8I8t0gjAOw3Oa509NIRoaY1FmH7hdrncMp7t7o+vYoceNQ== - dependencies: - chalk "^4.1.0" - command-line-args "^5.1.1" - command-line-usage "^6.1.0" - string-format "^2.0.0" - ts-essentials@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-1.0.4.tgz#ce3b5dade5f5d97cf69889c11bf7d2da8555b15a" @@ -11662,25 +11540,6 @@ ts-generator@^0.1.1: resolve "^1.8.1" ts-essentials "^1.0.0" -ts-node@^10.2.1: - version "10.7.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5" - integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== - dependencies: - "@cspotcode/source-map-support" "0.7.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.0" - yn "3.1.1" - ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -11823,22 +11682,6 @@ type@^2.5.0: resolved "https://registry.yarnpkg.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f" integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== -typechain@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/typechain/-/typechain-7.0.0.tgz#258ca136de1d451368bde01c318976a83062f110" - integrity sha512-ILfvBBFJ7j9aIk0crX03+N2GmzoDN1gtk32G1+XrasjuvXS0XAw2XxwQeQMMgKwlnxViJjIkG87sTMYXPkXA9g== - dependencies: - "@types/prettier" "^2.1.1" - debug "^4.1.1" - fs-extra "^7.0.0" - glob "^7.1.6" - js-sha3 "^0.8.0" - lodash "^4.17.15" - mkdirp "^1.0.4" - prettier "^2.1.2" - ts-command-line-args "^2.2.0" - ts-essentials "^7.0.1" - typechain@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/typechain/-/typechain-3.0.0.tgz#d5a47700831f238e43f7429b987b4bb54849b92e" @@ -11912,16 +11755,6 @@ typical@^2.6.0, typical@^2.6.1: resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" integrity sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg== -typical@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" - integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== - -typical@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" - integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== - uglify-js@^3.1.4: version "3.15.3" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.3.tgz#9aa82ca22419ba4c0137642ba0df800cb06e0471" @@ -12136,11 +11969,6 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache-lib@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" - integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -12864,14 +12692,6 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -wordwrapjs@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" - integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== - dependencies: - reduce-flatten "^2.0.0" - typical "^5.2.0" - workerpool@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" From a7ba5350e47a900a486d7757dbd950d049b0d7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 26 Sep 2022 15:43:08 -0400 Subject: [PATCH 072/100] fix(task): accept ownership only if pending owner matches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- tasks/deployment/ownership.ts | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/tasks/deployment/ownership.ts b/tasks/deployment/ownership.ts index 86591c974..1443aa599 100644 --- a/tasks/deployment/ownership.ts +++ b/tasks/deployment/ownership.ts @@ -1,4 +1,5 @@ -import { ContractTransaction } from 'ethers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { Contract, ContractTransaction, ethers } from 'ethers' import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' @@ -13,12 +14,36 @@ task('migrate:ownership', 'Accepts ownership of protocol contracts on behalf of console.log('> Accepting ownership of contracts') console.log(`- Governor: ${governor.address}`) + const governedContracts = [GraphToken, Controller, GraphProxyAdmin, SubgraphNFT] const txs: ContractTransaction[] = [] - txs.push(await GraphToken.connect(governor).acceptOwnership()) - txs.push(await Controller.connect(governor).acceptOwnership()) - txs.push(await GraphProxyAdmin.connect(governor).acceptOwnership()) - txs.push(await SubgraphNFT.connect(governor).acceptOwnership()) + for (const contract of governedContracts) { + const tx = await acceptOwnershipIfPending(contract, governor) + if (tx) { + txs.push() + } + } await Promise.all(txs.map((tx) => tx.wait())) console.log('Done!') }) + +async function acceptOwnershipIfPending( + contract: Contract, + signer: SignerWithAddress, +): Promise { + const pendingGovernor = await contract.connect(signer).pendingGovernor() + + if (pendingGovernor === ethers.constants.AddressZero) { + console.log(`No pending governor for ${contract.address}`) + return + } + + if (pendingGovernor === signer.address) { + console.log(`Accepting ownership of ${contract.address}`) + return contract.connect(signer).acceptOwnership() + } else { + console.log( + `Signer ${signer.address} is not the pending governor of ${contract.address}, it is ${pendingGovernor}`, + ) + } +} From 0aed5eb8bfbf13a34fdf74fc3e2ee19d070fc13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 26 Sep 2022 15:49:00 -0400 Subject: [PATCH 073/100] feat(task): split unpause task into protocol and bridge tasks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- tasks/deployment/unpause.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/deployment/unpause.ts b/tasks/deployment/unpause.ts index afb85e0ea..fb8c39d5d 100644 --- a/tasks/deployment/unpause.ts +++ b/tasks/deployment/unpause.ts @@ -2,7 +2,7 @@ import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' import GraphChain from '../../gre/helpers/network' -task('migrate:unpause', 'Unpause protocol (except bridge)') +task('migrate:unpause:protocol', 'Unpause protocol (except bridge)') .addOptionalParam('addressBook', cliOpts.addressBook.description) .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .setAction(async (taskArgs, hre) => { @@ -17,7 +17,7 @@ task('migrate:unpause', 'Unpause protocol (except bridge)') console.log('Done!') }) -task('migrate:unpause-bridge', 'Unpause bridge') +task('migrate:unpause:bridge', 'Unpause bridge') .addOptionalParam('addressBook', cliOpts.addressBook.description) .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .setAction(async (taskArgs, hre) => { @@ -29,8 +29,8 @@ task('migrate:unpause-bridge', 'Unpause bridge') const GraphTokenGateway = GraphChain.isL2(graph.chainId) ? L2GraphTokenGateway : L1GraphTokenGateway - const tx2 = await GraphTokenGateway.connect(governor).setPaused(false) - await tx2.wait() + const tx = await GraphTokenGateway.connect(governor).setPaused(false) + await tx.wait() console.log('Done!') }) From a3fa719a43024c6a5f5165e706e2fe43c4805d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 26 Sep 2022 15:50:31 -0400 Subject: [PATCH 074/100] feat(e2e): run L1 and L2 together in e2e tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- .gitignore | 4 + cli/commands/bridge/to-l2.ts | 4 +- config/graph.arbitrum-localhost.yml | 12 +- .../config/l1/l1GraphTokenGateway.test.ts | 2 +- e2e/scenarios/fixtures/bridge.ts | 29 +++ e2e/scenarios/lib/helpers.ts | 10 +- e2e/scenarios/lib/staking.ts | 4 +- e2e/scenarios/send-grt-to-l2.test.ts | 23 +++ e2e/scenarios/send-grt-to-l2.ts | 40 ++++ hardhat.config.ts | 7 +- scripts/e2e | 190 +++++++++++++++--- tasks/bridge/to-l2.ts | 56 ++++++ tasks/deployment/accounts.ts | 67 ------ tasks/deployment/nitro.ts | 119 +++++++++++ tasks/e2e/e2e.ts | 8 +- 15 files changed, 460 insertions(+), 115 deletions(-) create mode 100644 e2e/scenarios/fixtures/bridge.ts create mode 100644 e2e/scenarios/send-grt-to-l2.test.ts create mode 100644 e2e/scenarios/send-grt-to-l2.ts create mode 100644 tasks/bridge/to-l2.ts create mode 100644 tasks/deployment/nitro.ts diff --git a/.gitignore b/.gitignore index 1fcf808ed..2521a6cf5 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,7 @@ bin/ # Coverage and other reports /reports coverage.json + +# Local test files +addresses-local.json +arbitrum-addresses-local.json \ No newline at end of file diff --git a/cli/commands/bridge/to-l2.ts b/cli/commands/bridge/to-l2.ts index ffacee1c8..a7b6e7651 100644 --- a/cli/commands/bridge/to-l2.ts +++ b/cli/commands/bridge/to-l2.ts @@ -45,7 +45,8 @@ export const sendToL2 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise { + return { + deploymentFile: '../arbitrum-sdk/localNetwork.json', + funder: signers[0], + accountsToFund: [ + { + signer: signers[1], + amount: toGRT(10_000_000), + }, + ], + } +} diff --git a/e2e/scenarios/lib/helpers.ts b/e2e/scenarios/lib/helpers.ts index db9da32cb..902ed57fe 100644 --- a/e2e/scenarios/lib/helpers.ts +++ b/e2e/scenarios/lib/helpers.ts @@ -1,6 +1,8 @@ export function getGraphOptsFromArgv(): { - graphConfig: string | undefined addressBook: string | undefined + graphConfig: string | undefined + l1GraphConfig: string | undefined + l2GraphConfig: string | undefined } { const argv = process.argv.slice(2) @@ -8,7 +10,9 @@ export function getGraphOptsFromArgv(): { argv[index] && argv[index] !== 'undefined' ? argv[index] : undefined return { - graphConfig: getArgv(0), - addressBook: getArgv(1), + addressBook: getArgv(0), + graphConfig: getArgv(1), + l1GraphConfig: getArgv(2), + l2GraphConfig: getArgv(3), } } diff --git a/e2e/scenarios/lib/staking.ts b/e2e/scenarios/lib/staking.ts index d221afccd..53465cfb8 100644 --- a/e2e/scenarios/lib/staking.ts +++ b/e2e/scenarios/lib/staking.ts @@ -42,7 +42,7 @@ export const allocateFrom = async ( 'allocateFrom', [indexer.address, subgraphDeploymentID, amount, allocationId, metadata, proof], { - gasLimit: 2000000, + gasLimit: 4_000_000, }, ) } @@ -56,6 +56,6 @@ export const closeAllocation = async ( console.log(`\nClosing ${allocationId}...`) await sendTransaction(indexer, contracts.Staking, 'closeAllocation', [allocationId, poi], { - gasLimit: 2000000, + gasLimit: 4_000_000, }) } diff --git a/e2e/scenarios/send-grt-to-l2.test.ts b/e2e/scenarios/send-grt-to-l2.test.ts new file mode 100644 index 000000000..9b81371b6 --- /dev/null +++ b/e2e/scenarios/send-grt-to-l2.test.ts @@ -0,0 +1,23 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import { getBridgeFixture, BridgeFixture } from './fixtures/bridge' + +describe('Bridge GRT to L2', () => { + const graph = hre.graph() + let bridgeFixture: BridgeFixture + + before(async () => { + const l1Deployer = await graph.l1.getDeployer() + const l2Deployer = await graph.l2.getDeployer() + bridgeFixture = getBridgeFixture([l1Deployer, l2Deployer]) + }) + + describe('GRT balances', () => { + it(`L2 balances should match bridged amount`, async function () { + for (const account of bridgeFixture.accountsToFund) { + const l2GrtBalance = await graph.l2.contracts.GraphToken.balanceOf(account.signer.address) + expect(l2GrtBalance).eq(account.amount) + } + }) + }) +}) diff --git a/e2e/scenarios/send-grt-to-l2.ts b/e2e/scenarios/send-grt-to-l2.ts new file mode 100644 index 000000000..9884c9816 --- /dev/null +++ b/e2e/scenarios/send-grt-to-l2.ts @@ -0,0 +1,40 @@ +// ### Scenario description ### +// Bridge action > Bridge GRT tokens from L1 to L2 +// This scenario will bridge GRT tokens from L1 to L2. See fixtures for details. +// Run with: +// npx hardhat e2e:scenario send-grt-to-l2 --network --graph-config config/graph..yml + +import hre from 'hardhat' +import { TASK_BRIDGE_TO_L2 } from '../../tasks/bridge/to-l2' +import { getGraphOptsFromArgv } from './lib/helpers' +import { getBridgeFixture } from './fixtures/bridge' + +async function main() { + const graphOpts = getGraphOptsFromArgv() + const graph = hre.graph(graphOpts) + + const l1Deployer = await graph.l1.getDeployer() + const l2Deployer = await graph.l2.getDeployer() + + const bridgeFixture = getBridgeFixture([l1Deployer, l2Deployer]) + + // == Send GRT to L2 accounts + for (const account of bridgeFixture.accountsToFund) { + await hre.run(TASK_BRIDGE_TO_L2, { + ...graphOpts, + amount: account.amount.toString(), + sender: bridgeFixture.funder.address, + recipient: account.signer.address, + deploymentFile: bridgeFixture.deploymentFile, + }) + } +} + +// We recommend this pattern to be able to use async/await everywhere +// and properly handle errors. +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exitCode = 1 + }) diff --git a/hardhat.config.ts b/hardhat.config.ts index 1a1c2758b..3fef7643c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -29,7 +29,7 @@ const SKIP_LOAD = process.env.SKIP_LOAD === 'true' function loadTasks() { require('./gre/gre') - ;['contracts', 'misc', 'deployment', 'actions', 'verify', 'e2e'].forEach((folder) => { + ;['contracts', 'bridge', 'misc', 'deployment', 'actions', 'verify', 'e2e'].forEach((folder) => { const tasksPath = path.join(__dirname, 'tasks', folder) fs.readdirSync(tasksPath) .filter((pth) => pth.includes('.ts')) @@ -107,6 +107,9 @@ function setupNetworkProviders(hardhatConfig) { const DEFAULT_TEST_MNEMONIC = 'myth like bonus scare over problem client lizard pioneer submit female collect' +const DEFAULT_L2_TEST_MNEMONIC = + 'urge never interest human any economy gentle canvas anxiety pave unlock find' + const config: HardhatUserConfig = { paths: { sources: './contracts', @@ -169,7 +172,7 @@ const config: HardhatUserConfig = { localnitrol2: { chainId: 412346, url: 'http://localhost:8547', - accounts: { mnemonic: DEFAULT_TEST_MNEMONIC }, + accounts: { mnemonic: DEFAULT_L2_TEST_MNEMONIC }, graphConfig: 'config/graph.arbitrum-localhost.yml', }, }, diff --git a/scripts/e2e b/scripts/e2e index c5008f25e..cbd650e5f 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -3,17 +3,138 @@ set -eo pipefail source $(pwd)/scripts/evm +### > SCRIPT CONFIG < # Allow overriding config -GRAPH_CONFIG=${GRAPH_CONFIG:-"config/graph.localhost.yml"} -ADDRESS_BOOK=${ADDRESS_BOOK:-"addresses.json"} -NETWORK=${NETWORK:-"localhost"} +ADDRESS_BOOK=${ADDRESS_BOOK:-"addresses-local.json"} +ARBITRUM_ADDRESS_BOOK=${ARBITRUM_ADDRESS_BOOK:-"arbitrum-addresses-local.json"} +ARBITRUM_DEPLOYMENT_FILE=${ARBITRUM_DEPLOYMENT_FILE:-"$(pwd)/../arbitrum-sdk/localNetwork.json"} +L1_NETWORK=${L1_NETWORK:-"localhost"} +L2_NETWORK=${L2_NETWORK} # By default run only L1 tests on localhost network +L1_GRAPH_CONFIG=${L1_GRAPH_CONFIG:-"config/graph.localhost.yml"} +L2_GRAPH_CONFIG=${L2_GRAPH_CONFIG:-"config/graph.arbitrum-localhost.yml"} echo "Running e2e tests" -echo "- Using config: $GRAPH_CONFIG" echo "- Using address book: $ADDRESS_BOOK" -echo "- Using network: $NETWORK" +echo "- Using L1 network: $L1_NETWORK" +echo "- Using L1 config: $L1_GRAPH_CONFIG" -### Setup +if [[ -n "$L2_NETWORK" ]]; then + echo "- Using L2 network: $L2_NETWORK" + echo "- Using L2 config: $L2_GRAPH_CONFIG" + echo "- Using arbitrum address book: $ARBITRUM_ADDRESS_BOOK" + echo "- Using arbitrum deployment file: $ARBITRUM_DEPLOYMENT_FILE" +else + echo "- Skipping L2 tests" +fi + +### > SCRIPT AUX FUNCTIONS < +function pre_deploy() { + local NETWORK=$1 + local GRAPH_CONFIG=$2 + + # Create named accounts + npx hardhat migrate:accounts --network "$NETWORK" --graph-config "$GRAPH_CONFIG" + + # Fund accounts if using nitro test nodes + if [[ "$NETWORK" == *"localnitro"* ]]; then + npx hardhat nitro:fund-accounts --network "$NETWORK" --graph-config "$GRAPH_CONFIG" + fi +} + +function deploy() { + local NETWORK=$1 + local GRAPH_CONFIG=$2 + local ADDRESS_BOOK=$3 + + # Deploy protocol + npx hardhat migrate \ + --network "$NETWORK" \ + --skip-confirmation \ + --auto-mine \ + --force \ + --graph-config "$GRAPH_CONFIG" \ + --address-book "$ADDRESS_BOOK" +} + +function post_deploy () { + local NETWORK=$1 + local GRAPH_CONFIG=$2 + local ADDRESS_BOOK=$3 + + # Governor to accept contracts ownership + npx hardhat migrate:ownership --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + + # Unpause the protocol + npx hardhat migrate:unpause:protocol --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +} + +function configure_bridge () { + local L1_NETWORK=$1 + local L1_GRAPH_CONFIG=$2 + local L2_NETWORK=$3 + local L2_GRAPH_CONFIG=$4 + local ADDRESS_BOOK=$5 + local ARBITRUM_ADDRESS_BOOK=$6 + local ARBITRUM_DEPLOYMENT_FILE=$7 + + # These settings are only used for CLI bridge commands + # so we keep them here to avoid confusion with hardhat based tasks + local L1_CHAIN_ID=${L1_CHAIN_ID:-"1337"} + local L2_CHAIN_ID=${L2_CHAIN_ID:-"412346"} + + local L1_RPC=${L1_RPC:-"http://localhost:8545"} + local L2_RPC=${L2_RPC:-"http://localhost:8547"} + + local L1_MNEMONIC=${L1_MNEMONIC:-"myth like bonus scare over problem client lizard pioneer submit female collect"} + local L2_MNEMONIC=${L2_MNEMONIC:-"urge never interest human any economy gentle canvas anxiety pave unlock find"} + + # Copy required arbitrum contract addresses to the local arbitrum address book + npx hardhat nitro:address-book-setup --deployment-file "$ARBITRUM_DEPLOYMENT_FILE" --arbitrum-address-book "$ARBITRUM_ADDRESS_BOOK" + + # Configure the bridge + ./cli/cli.ts -a "$ADDRESS_BOOK" -p "$L2_RPC" -m "$L2_MNEMONIC" -n 2 -r "$ARBITRUM_ADDRESS_BOOK" protocol configure-l2-bridge "$L1_CHAIN_ID" + ./cli/cli.ts -a "$ADDRESS_BOOK" -p "$L1_RPC" -m "$L1_MNEMONIC" -n 2 -r "$ARBITRUM_ADDRESS_BOOK" protocol configure-l1-bridge "$L2_CHAIN_ID" + + # Unpause the bridge + npx hardhat migrate:unpause:bridge --network "$L2_NETWORK" --graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + npx hardhat migrate:unpause:bridge --network "$L1_NETWORK" --graph-config "$L1_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +} + +function test_e2e () { + local NETWORK=$1 + local GRAPH_CONFIG=$2 + local ADDRESS_BOOK=$3 + + npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +} + +function test_e2e_scenarios () { + local NETWORK=$1 + local GRAPH_CONFIG=$2 + local ADDRESS_BOOK=$3 + + npx hardhat e2e:scenario create-subgraphs --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + npx hardhat e2e:scenario open-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + + # skip close-allocations for arbitrum testnodes as we can't advance epoch + if [[ "$NETWORK" != *"localnitro"* ]]; then + npx hardhat e2e:scenario close-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + fi +} + +function test_e2e_scenarios_bridge () { + local L1_NETWORK=$1 + local L1_GRAPH_CONFIG=$2 + local L2_NETWORK=$3 + local L2_GRAPH_CONFIG=$4 + local ADDRESS_BOOK=$5 + + npx hardhat e2e:scenario send-grt-to-l2 --network "$L1_NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +} + + +### > SCRIPT START < ### +## SETUP # Compile contracts yarn build @@ -28,40 +149,47 @@ if [[ ! -f "$ADDRESS_BOOK" ]]; then echo '{}' > "$ADDRESS_BOOK" fi -# Pre-deploy actions -npx hardhat migrate:accounts --network "$NETWORK" --graph-config "$GRAPH_CONFIG" -if [[ "$NETWORK" == *"localnitro"* ]]; then - npx hardhat migrate:accounts:nitro --network "$NETWORK" --graph-config "$GRAPH_CONFIG" +# Create arbitrum address book if needed +if [[ ! -f "$ARBITRUM_ADDRESS_BOOK" ]]; then + echo '{}' > "$ARBITRUM_ADDRESS_BOOK" fi -# Deploy protocol -npx hardhat migrate \ - --network "$NETWORK" \ - --skip-confirmation \ - --auto-mine \ - --graph-config "$GRAPH_CONFIG" \ - --address-book "$ADDRESS_BOOK" +## DEPLOY +# Deploy L1 +echo "Deploying L1 protocol" +pre_deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" +deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" +post_deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" -# Post deploy actions -npx hardhat migrate:ownership --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" -npx hardhat migrate:unpause --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +# Deploy L2 +if [[ -n "$L2_NETWORK" ]]; then + echo "Deploying L2 protocol" + pre_deploy "$L2_NETWORK" "$L2_GRAPH_CONFIG" + deploy "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" + post_deploy "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" +fi -### Test -# Run tests -npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +# Configure bridge +if [[ -n "$L2_NETWORK" ]]; then + configure_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" "$ARBITRUM_ADDRESS_BOOK" +fi -# Skip GRT scenarios in L2 as we don't have bridged GRT yet -if [[ "$NETWORK" != "localnitrol2" ]]; then - npx hardhat e2e:scenario create-subgraphs --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" - npx hardhat e2e:scenario open-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + +## TEST +# Run e2e tests +test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" +if [[ -n "$L2_NETWORK" ]]; then + test_e2e "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" fi -# skip close-allocations for arbitrum testnodes as we can't advance epoch -if [[ "$NETWORK" != *"localnitro"* ]]; then - npx hardhat e2e:scenario close-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" +# Run scenario tests +test_e2e_scenarios "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" +if [[ -n "$L2_NETWORK" ]]; then + test_e2e_scenarios_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" + test_e2e_scenarios "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" fi -### Cleanup +## Cleanup # Exit error mode so the evm instance always gets killed if [[ "$NETWORK" == "localhost" ]]; then set +e diff --git a/tasks/bridge/to-l2.ts b/tasks/bridge/to-l2.ts new file mode 100644 index 000000000..a5ced288f --- /dev/null +++ b/tasks/bridge/to-l2.ts @@ -0,0 +1,56 @@ +import { task } from 'hardhat/config' +import { cliOpts } from '../../cli/defaults' +import { sendToL2 } from '../../cli/commands/bridge/to-l2' +import { loadEnv } from '../../cli/env' +import { TASK_NITRO_SETUP_SDK } from '../deployment/nitro' +import { BigNumber } from 'ethers' + +export const TASK_BRIDGE_TO_L2 = 'bridge:send-to-l2' + +task(TASK_BRIDGE_TO_L2, 'Bridge GRT tokens from L1 to L2') + .addParam('amount', 'Amount of tokens to bridge') + .addOptionalParam('sender', 'Address of the sender. L1 deployer if empty.') + .addOptionalParam('recipient', 'Receiving address in L2. Same to L1 address if empty.') + .addOptionalParam('addressBook', cliOpts.addressBook.description) + .addOptionalParam( + 'arbitrumAddressBook', + cliOpts.arbitrumAddressBook.description, + cliOpts.arbitrumAddressBook.default, + ) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam( + 'deploymentFile', + 'Nitro testnode deployment file. Must specify if using nitro test nodes.', + ) + .setAction(async (taskArgs, hre) => { + console.log('> Sending GRT to L2') + const graph = hre.graph(taskArgs) + + // Add nitro test node networks to sdk + if (taskArgs.deploymentFile) { + console.log('> Adding nitro test node network to sdk') + await hre.run(TASK_NITRO_SETUP_SDK, { deploymentFile: taskArgs.deploymentFile }) + } + + // Get the sender, use L1 deployer if not provided + const l1Deployer = await graph.l1.getDeployer() + const sender: string = taskArgs.sender ?? l1Deployer.address + + let wallet = await graph.l1.getWallet(sender) + + if (!wallet) { + throw new Error(`No wallet found for address ${sender}`) + } else { + console.log(`> Using wallet ${wallet.address}`) + wallet = wallet.connect(graph.l1.provider) + } + + // Patch sendToL2 opts + taskArgs.l2Provider = graph.l2.provider + taskArgs.amount = hre.ethers.utils.formatEther(taskArgs.amount) // sendToL2 expects amount in GRT + + await sendToL2(await loadEnv(taskArgs, wallet), taskArgs) + + console.log('Done!') + }) diff --git a/tasks/deployment/accounts.ts b/tasks/deployment/accounts.ts index c09ff2a2f..e5be935ea 100644 --- a/tasks/deployment/accounts.ts +++ b/tasks/deployment/accounts.ts @@ -2,7 +2,6 @@ import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' import { updateItemValue, writeConfig } from '../../cli/config' -import { BigNumber, ContractTransaction } from 'ethers' task('migrate:accounts', 'Creates protocol accounts and saves them in graph config') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) @@ -39,69 +38,3 @@ task('migrate:accounts', 'Creates protocol accounts and saves them in graph conf writeConfig(taskArgs.graphConfig, graphConfig.toString()) }) - -task('migrate:accounts:nitro', 'Funds protocol accounts on Arbitrum Nitro testnodes') - .addOptionalParam('graphConfig', cliOpts.graphConfig.description) - .addOptionalParam('privateKey', 'The private key for Arbitrum testnode genesis account') - .addOptionalParam('amount', 'The amount to fund each account with') - .setAction(async (taskArgs, hre) => { - // Arbitrum Nitro testnodes have a pre-funded genesis account whose private key is hardcoded here: - // - L1 > https://github.com/OffchainLabs/nitro/blob/01c558c06ad9cbaa083bebe3e51960e195c3fd6b/test-node.bash#L136 - // - L2 > https://github.com/OffchainLabs/nitro/blob/01c558c06ad9cbaa083bebe3e51960e195c3fd6b/testnode-scripts/config.ts#L22 - const genesisAccountPrivateKey = - taskArgs.privateKey ?? 'e887f7d17d07cc7b8004053fb8826f6657084e88904bb61590e498ca04704cf2' - const genesisAccount = new hre.ethers.Wallet(genesisAccountPrivateKey) - - // Get protocol accounts - const { getDeployer, getNamedAccounts, getTestAccounts, provider } = hre.graph(taskArgs) - const deployer = await getDeployer() - const testAccounts = await getTestAccounts() - const namedAccounts = await getNamedAccounts() - const accounts = [ - deployer, - ...testAccounts, - ...Object.keys(namedAccounts).map((k) => namedAccounts[k]), - ] - - // Amount to fund - // - If amount is specified, use that - // - Otherwise, use 95% of genesis account balance with a maximum of 100 Eth - let amount: BigNumber - const maxAmount = hre.ethers.utils.parseEther('100') - const genesisAccountBalance = await provider.getBalance(genesisAccount.address) - - if (taskArgs.amount) { - amount = hre.ethers.BigNumber.from(taskArgs.amount) - } else { - const splitGenesisBalance = genesisAccountBalance.mul(95).div(100).div(accounts.length) - if (splitGenesisBalance.gt(maxAmount)) { - amount = maxAmount - } else { - amount = splitGenesisBalance - } - } - - // Check genesis account balance - const requiredFunds = amount.mul(accounts.length) - if (genesisAccountBalance.lt(requiredFunds)) { - throw new Error('Insufficient funds in genesis account') - } - - // Fund accounts - console.log('> Funding protocol addresses') - console.log(`Genesis account: ${genesisAccount.address}`) - console.log(`Total accounts: ${accounts.length}`) - console.log(`Amount per account: ${hre.ethers.utils.formatEther(amount)}`) - console.log(`Required funds: ${hre.ethers.utils.formatEther(requiredFunds)}`) - - const txs: ContractTransaction[] = [] - for (const account of accounts) { - const tx = await genesisAccount.connect(provider).sendTransaction({ - value: amount, - to: account.address, - }) - txs.push(tx) - } - await Promise.all(txs.map((tx) => tx.wait())) - console.log('Done!') - }) diff --git a/tasks/deployment/nitro.ts b/tasks/deployment/nitro.ts new file mode 100644 index 000000000..9a69af5aa --- /dev/null +++ b/tasks/deployment/nitro.ts @@ -0,0 +1,119 @@ +import { BigNumber, ContractTransaction } from 'ethers' +import { subtask, task } from 'hardhat/config' +import { cliOpts } from '../../cli/defaults' +import { addCustomNetwork } from '@arbitrum/sdk/dist/lib/dataEntities/networks' +import fs from 'fs' + +export const TASK_NITRO_FUND_ACCOUNTS = 'nitro:fund-accounts' +export const TASK_NITRO_SETUP_SDK = 'nitro:sdk-setup' +export const TASK_NITRO_SETUP_ADDRESS_BOOK = 'nitro:address-book-setup' + +task(TASK_NITRO_FUND_ACCOUNTS, 'Funds protocol accounts on Arbitrum Nitro testnodes') + .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('privateKey', 'The private key for Arbitrum testnode genesis account') + .addOptionalParam('amount', 'The amount to fund each account with') + .setAction(async (taskArgs, hre) => { + // Arbitrum Nitro testnodes have a pre-funded genesis account whose private key is hardcoded here: + // - L1 > https://github.com/OffchainLabs/nitro/blob/01c558c06ad9cbaa083bebe3e51960e195c3fd6b/test-node.bash#L136 + // - L2 > https://github.com/OffchainLabs/nitro/blob/01c558c06ad9cbaa083bebe3e51960e195c3fd6b/testnode-scripts/config.ts#L22 + const genesisAccountPrivateKey = + taskArgs.privateKey ?? 'e887f7d17d07cc7b8004053fb8826f6657084e88904bb61590e498ca04704cf2' + const genesisAccount = new hre.ethers.Wallet(genesisAccountPrivateKey) + + // Get protocol accounts + const { getDeployer, getNamedAccounts, getTestAccounts, provider } = hre.graph(taskArgs) + const deployer = await getDeployer() + const testAccounts = await getTestAccounts() + const namedAccounts = await getNamedAccounts() + const accounts = [ + deployer, + ...testAccounts, + ...Object.keys(namedAccounts).map((k) => namedAccounts[k]), + ] + + // Amount to fund + // - If amount is specified, use that + // - Otherwise, use 95% of genesis account balance with a maximum of 100 Eth + let amount: BigNumber + const maxAmount = hre.ethers.utils.parseEther('100') + const genesisAccountBalance = await provider.getBalance(genesisAccount.address) + + if (taskArgs.amount) { + amount = hre.ethers.BigNumber.from(taskArgs.amount) + } else { + const splitGenesisBalance = genesisAccountBalance.mul(95).div(100).div(accounts.length) + if (splitGenesisBalance.gt(maxAmount)) { + amount = maxAmount + } else { + amount = splitGenesisBalance + } + } + + // Check genesis account balance + const requiredFunds = amount.mul(accounts.length) + if (genesisAccountBalance.lt(requiredFunds)) { + throw new Error('Insufficient funds in genesis account') + } + + // Fund accounts + console.log('> Funding protocol addresses') + console.log(`Genesis account: ${genesisAccount.address}`) + console.log(`Total accounts: ${accounts.length}`) + console.log(`Amount per account: ${hre.ethers.utils.formatEther(amount)}`) + console.log(`Required funds: ${hre.ethers.utils.formatEther(requiredFunds)}`) + + const txs: ContractTransaction[] = [] + for (const account of accounts) { + const tx = await genesisAccount.connect(provider).sendTransaction({ + value: amount, + to: account.address, + }) + txs.push(tx) + } + await Promise.all(txs.map((tx) => tx.wait())) + console.log('Done!') + }) + +// Arbitrum SDK does not support Nitro testnodes out of the box +// This adds the testnodes to the SDK configuration +subtask(TASK_NITRO_SETUP_SDK, 'Adds nitro testnodes to SDK config') + .addParam('deploymentFile', 'The testnode deployment file to use') + .setAction(async (taskArgs) => { + if (!fs.existsSync(taskArgs.deploymentFile)) { + throw new Error(`Deployment file not found: ${taskArgs.deploymentFile}`) + } + const deployment = JSON.parse(fs.readFileSync(taskArgs.deploymentFile, 'utf-8')) + addCustomNetwork({ + customL1Network: deployment.l1Network, + customL2Network: deployment.l2Network, + }) + }) + +// Read arbitrum contract addresses from deployment file and write them to the address book +task(TASK_NITRO_SETUP_ADDRESS_BOOK, 'Write arbitrum addresses to address book') + .addParam('deploymentFile', 'The testnode deployment file to use') + .addParam('arbitrumAddressBook', 'Arbitrum address book file') + .setAction(async (taskArgs) => { + if (!fs.existsSync(taskArgs.deploymentFile)) { + throw new Error(`Deployment file not found: ${taskArgs.deploymentFile}`) + } + const deployment = JSON.parse(fs.readFileSync(taskArgs.deploymentFile, 'utf-8')) + + const addressBook = { + '1337': { + L1GatewayRouter: { + address: deployment.l2Network.tokenBridge.l1GatewayRouter, + }, + IInbox: { + address: deployment.l2Network.ethBridge.inbox, + }, + }, + '412346': { + L2GatewayRouter: { + address: deployment.l2Network.tokenBridge.l2GatewayRouter, + }, + }, + } + + fs.writeFileSync(taskArgs.arbitrumAddressBook, JSON.stringify(addressBook)) + }) diff --git a/tasks/e2e/e2e.ts b/tasks/e2e/e2e.ts index d2ab8f96e..7b9a08b8b 100644 --- a/tasks/e2e/e2e.ts +++ b/tasks/e2e/e2e.ts @@ -66,8 +66,10 @@ task('e2e:init', 'Run deployment initialization e2e tests') task('e2e:scenario', 'Run scenario scripts and e2e tests') .addPositionalParam('scenario', 'Name of the scenario to run') - .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) + .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addFlag('skipScript', "Don't run scenario script") .setAction(async (args, hre: HardhatRuntimeEnvironment) => { setGraphConfig(args, hre) @@ -82,8 +84,10 @@ task('e2e:scenario', 'Run scenario scripts and e2e tests') if (!args.skipScript) { if (fs.existsSync(script)) { await runScriptWithHardhat(hre.hardhatArguments, script, [ - args.graphConfig, args.addressBook, + args.graphConfig, + args.l1GraphConfig, + args.l2GraphConfig, ]) } else { console.log(`No script found for scenario ${args.scenario}`) From ad11bdf6f8b9cceb6a43ede4948473ee61f0e27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Tue, 27 Sep 2022 17:13:12 -0400 Subject: [PATCH 075/100] feat(e2e): fetch deployment data from testnodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- .gitignore | 3 ++- e2e/scenarios/fixtures/bridge.ts | 2 +- scripts/e2e | 8 ++++---- tasks/bridge/to-l2.ts | 3 +-- tasks/deployment/nitro.ts | 28 +++++++++++++++++++++++++--- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 2521a6cf5..988791bb2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,5 @@ coverage.json # Local test files addresses-local.json -arbitrum-addresses-local.json \ No newline at end of file +arbitrum-addresses-local.json +localNetwork.json \ No newline at end of file diff --git a/e2e/scenarios/fixtures/bridge.ts b/e2e/scenarios/fixtures/bridge.ts index b1bac40ca..a1441ec89 100644 --- a/e2e/scenarios/fixtures/bridge.ts +++ b/e2e/scenarios/fixtures/bridge.ts @@ -17,7 +17,7 @@ export interface BridgeFixture { export const getBridgeFixture = (signers: SignerWithAddress[]): BridgeFixture => { return { - deploymentFile: '../arbitrum-sdk/localNetwork.json', + deploymentFile: 'localNetwork.json', funder: signers[0], accountsToFund: [ { diff --git a/scripts/e2e b/scripts/e2e index cbd650e5f..d452182d4 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -7,7 +7,7 @@ source $(pwd)/scripts/evm # Allow overriding config ADDRESS_BOOK=${ADDRESS_BOOK:-"addresses-local.json"} ARBITRUM_ADDRESS_BOOK=${ARBITRUM_ADDRESS_BOOK:-"arbitrum-addresses-local.json"} -ARBITRUM_DEPLOYMENT_FILE=${ARBITRUM_DEPLOYMENT_FILE:-"$(pwd)/../arbitrum-sdk/localNetwork.json"} +ARBITRUM_DEPLOYMENT_FILE=${ARBITRUM_DEPLOYMENT_FILE:-"localNetwork.json"} L1_NETWORK=${L1_NETWORK:-"localhost"} L2_NETWORK=${L2_NETWORK} # By default run only L1 tests on localhost network L1_GRAPH_CONFIG=${L1_GRAPH_CONFIG:-"config/graph.localhost.yml"} @@ -139,7 +139,7 @@ function test_e2e_scenarios_bridge () { yarn build # Start evm -if [[ "$NETWORK" == "localhost" ]]; then +if [[ "$L1_NETWORK" == "localhost" ]]; then evm_kill evm_start fi @@ -171,7 +171,7 @@ fi # Configure bridge if [[ -n "$L2_NETWORK" ]]; then - configure_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" "$ARBITRUM_ADDRESS_BOOK" + configure_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" "$ARBITRUM_ADDRESS_BOOK" "$ARBITRUM_DEPLOYMENT_FILE" fi @@ -191,7 +191,7 @@ fi ## Cleanup # Exit error mode so the evm instance always gets killed -if [[ "$NETWORK" == "localhost" ]]; then +if [[ "$L1_NETWORK" == "localhost" ]]; then set +e result=0 diff --git a/tasks/bridge/to-l2.ts b/tasks/bridge/to-l2.ts index a5ced288f..1fc96ace3 100644 --- a/tasks/bridge/to-l2.ts +++ b/tasks/bridge/to-l2.ts @@ -3,7 +3,6 @@ import { cliOpts } from '../../cli/defaults' import { sendToL2 } from '../../cli/commands/bridge/to-l2' import { loadEnv } from '../../cli/env' import { TASK_NITRO_SETUP_SDK } from '../deployment/nitro' -import { BigNumber } from 'ethers' export const TASK_BRIDGE_TO_L2 = 'bridge:send-to-l2' @@ -27,7 +26,7 @@ task(TASK_BRIDGE_TO_L2, 'Bridge GRT tokens from L1 to L2') console.log('> Sending GRT to L2') const graph = hre.graph(taskArgs) - // Add nitro test node networks to sdk + // If local, add nitro test node networks to sdk if (taskArgs.deploymentFile) { console.log('> Adding nitro test node network to sdk') await hre.run(TASK_NITRO_SETUP_SDK, { deploymentFile: taskArgs.deploymentFile }) diff --git a/tasks/deployment/nitro.ts b/tasks/deployment/nitro.ts index 9a69af5aa..7cecb9d48 100644 --- a/tasks/deployment/nitro.ts +++ b/tasks/deployment/nitro.ts @@ -3,10 +3,12 @@ import { subtask, task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' import { addCustomNetwork } from '@arbitrum/sdk/dist/lib/dataEntities/networks' import fs from 'fs' +import { execSync } from 'child_process' export const TASK_NITRO_FUND_ACCOUNTS = 'nitro:fund-accounts' export const TASK_NITRO_SETUP_SDK = 'nitro:sdk-setup' export const TASK_NITRO_SETUP_ADDRESS_BOOK = 'nitro:address-book-setup' +export const TASK_NITRO_FETCH_DEPLOYMENT_FILE = 'nitro:fetch-deployment-file' task(TASK_NITRO_FUND_ACCOUNTS, 'Funds protocol accounts on Arbitrum Nitro testnodes') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) @@ -77,7 +79,7 @@ task(TASK_NITRO_FUND_ACCOUNTS, 'Funds protocol accounts on Arbitrum Nitro testno // Arbitrum SDK does not support Nitro testnodes out of the box // This adds the testnodes to the SDK configuration subtask(TASK_NITRO_SETUP_SDK, 'Adds nitro testnodes to SDK config') - .addParam('deploymentFile', 'The testnode deployment file to use') + .addParam('deploymentFile', 'The testnode deployment file to use', 'localNetwork.json') .setAction(async (taskArgs) => { if (!fs.existsSync(taskArgs.deploymentFile)) { throw new Error(`Deployment file not found: ${taskArgs.deploymentFile}`) @@ -89,13 +91,33 @@ subtask(TASK_NITRO_SETUP_SDK, 'Adds nitro testnodes to SDK config') }) }) +subtask(TASK_NITRO_FETCH_DEPLOYMENT_FILE, 'Fetches nitro deployment file from a local testnode') + .addParam( + 'deploymentFile', + 'Path to the file where to deployment file will be saved', + 'localNetwork.json', + ) + .setAction(async (taskArgs) => { + console.log(`Attempting to fetch deployment file from testnode...`) + + const command = + 'docker exec $(docker ps -qf "name=sequencer") cat /deployment/localNetwork.json > localNetwork.json' + const stdOut = execSync(command) + console.log(stdOut.toString()) + + if (!fs.existsSync(taskArgs.deploymentFile)) { + throw new Error(`Unable to fetch deployment file: ${taskArgs.deploymentFile}`) + } + console.log(`Deployment file saved to ${taskArgs.deploymentFile}`) + }) + // Read arbitrum contract addresses from deployment file and write them to the address book task(TASK_NITRO_SETUP_ADDRESS_BOOK, 'Write arbitrum addresses to address book') .addParam('deploymentFile', 'The testnode deployment file to use') .addParam('arbitrumAddressBook', 'Arbitrum address book file') - .setAction(async (taskArgs) => { + .setAction(async (taskArgs, hre) => { if (!fs.existsSync(taskArgs.deploymentFile)) { - throw new Error(`Deployment file not found: ${taskArgs.deploymentFile}`) + await hre.run(TASK_NITRO_FETCH_DEPLOYMENT_FILE, taskArgs) } const deployment = JSON.parse(fs.readFileSync(taskArgs.deploymentFile, 'utf-8')) From e9cab3646422468b553fd7c8a0994be6b0a32847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Tue, 27 Sep 2022 17:42:00 -0400 Subject: [PATCH 076/100] chore: update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- TESTING.md | 59 ++++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/TESTING.md b/TESTING.md index 88b72a31c..b27ce868b 100644 --- a/TESTING.md +++ b/TESTING.md @@ -33,23 +33,38 @@ There are several types of e2e tests which can be run separately: - Read and write interactions with the blockchain. _Requires an account with sufficient balance!_ - Example: a test validating that a user can add signal to a subgraph. -### Hardhat local node +### Hardhat local node (L1) -To run all e2e tests against a hardhat local node run: +It can be useful to run E2E tests against a fresh protocol deployment on L1, this can be done with the following: ```bash yarn test:e2e ``` -The command will perform the following actions: +The command will: +- start a hardhat local node +- deploy the L1 protocol +- configure the new L1 deployment +- Run all L1 e2e tests -- Start a hardhat node (localhost) -- Run `migrate:accounts` hardhat task to create keys for all protocol roles (deployer, governor, arbiter, etc). This currently doesn't support multisig accounts. -- Run `migrate` hardhat task to deploy the protocol -- Run `migrate:ownership` hardhat task to transfer ownership of governed contracts to the governor -- Run `migrate:unpause` to unpause the protocol -- Run `e2e` hardhat task to run all deployment tests (config and init) -- Run `e2e:scenario` hardhat task to run a scenario +### Arbitrum Nitro testnodes (L1/L2) + +If you want to test the protocol on an L1/L2 setup, you can run: + +```bash +L1_NETWORK=localnitrol1 L2_NETWORK=localnitrol2 yarn test:e2e +``` + +In this case the command will: +- deploy the L1 protocol +- configure the new L1 deployment +- deploy the L2 protocol +- configure the new L2 deployment +- configure the L1/L2 bridge +- Run all L1 e2e tests +- Run all L2 e2e tests + +Note that you'll need to setup the testnodes before running the tests. See [Quick Setup](https://github.com/edgeandnode/nitro#quick-setup) for details on how to do this. ### Other networks @@ -82,26 +97,4 @@ Scenarios are defined by an optional script and a test file: - They run before the test file. - Test file - Should be named e2e/scenarios/{scenario-name}.test.ts. - - Standard chai/mocha/hardhat/ethers test file. - -## Setting up Arbitrum's testnodes - -Arbitrum provides a quick way of setting up L1 and L2 testnodes for local development and testing. The following steps will guide you through the process of setting them up. Note that a local installation of Docker and Docker Compose is required. - -```bash -git clone https://github.com/offchainlabs/nitro -cd nitro -git submodule update --init --recursive - -# Apply any changes you might want, see below for more info, and then start the testnodes -./test-node.bash --init -``` - -**Useful information** -- L1 RPC: [http://localhost:8545](http://localhost:8545/) -- L2 RPC: [http://localhost:8547](http://localhost:8547/) -- Blockscout explorer (L2 only): [http://localhost:4000/](http://localhost:4000/) -- Prefunded genesis key (L1 and L2): `e887f7d17d07cc7b8004053fb8826f6657084e88904bb61590e498ca04704cf2` - -**Enable automine on L1** -In `docker-compose.yml` file edit the `geth` service command by removing the `--dev.period 1` flag. \ No newline at end of file + - Standard chai/mocha/hardhat/ethers test file. \ No newline at end of file From 7eb7ba24647e00e73b25e537bce2a2ac0ca22685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Tue, 27 Sep 2022 17:42:15 -0400 Subject: [PATCH 077/100] fix(e2e): allow skipping bridge tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- scripts/e2e | 13 ++++++++++--- tasks/e2e/e2e.ts | 7 ++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/e2e b/scripts/e2e index d452182d4..a3e2357e4 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -104,8 +104,13 @@ function test_e2e () { local NETWORK=$1 local GRAPH_CONFIG=$2 local ADDRESS_BOOK=$3 + local SKIP_BRIDGE_TESTS=$4 - npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + if [[ -z "$SKIP_BRIDGE_TESTS" ]]; then + npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + else + npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --skip-bridge + fi } function test_e2e_scenarios () { @@ -177,8 +182,10 @@ fi ## TEST # Run e2e tests -test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" -if [[ -n "$L2_NETWORK" ]]; then +if [[ -z "$L2_NETWORK" ]]; then + test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" true +else + test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" test_e2e "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" fi diff --git a/tasks/e2e/e2e.ts b/tasks/e2e/e2e.ts index 7b9a08b8b..40313e4be 100644 --- a/tasks/e2e/e2e.ts +++ b/tasks/e2e/e2e.ts @@ -30,12 +30,17 @@ const setGraphConfig = async (args: TaskArguments, hre: HardhatRuntimeEnvironmen task('e2e', 'Run all e2e tests') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) + .addFlag('skipBridge', 'Skip bridge tests') .setAction(async (args, hre: HardhatRuntimeEnvironment) => { - const testFiles = [ + let testFiles = [ ...new glob.GlobSync(CONFIG_TESTS).found, ...new glob.GlobSync(INIT_TESTS).found, ] + if (args.skipBridge) { + testFiles = testFiles.filter((file) => !['l1', 'l2'].includes(file.split('/')[3])) + } + setGraphConfig(args, hre) await hre.run(TASK_TEST, { testFiles: testFiles, From 1436a0c859c038625b2e2532803b4d3caabc6221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Tue, 27 Sep 2022 17:44:12 -0400 Subject: [PATCH 078/100] fix(e2e): update github action to multi layer e2e tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- .github/workflows/e2e.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 7f7979a86..52940191e 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -25,9 +25,7 @@ jobs: run: | git clone https://github.com/edgeandnode/nitro pushd nitro - git checkout ci git submodule update --init --recursive ./test-node.bash --init --no-blockscout --detach popd - NETWORK=localnitrol1 ADDRESS_BOOK=addresses.json GRAPH_CONFIG=config/graph.localhost.yml yarn test:e2e - NETWORK=localnitrol2 ADDRESS_BOOK=addresses.json GRAPH_CONFIG=config/graph.arbitrum-localhost.yml yarn test:e2e + L1_NETWORK=localnitrol1 L2_NETWORK=localnitrol2 yarn test:e2e \ No newline at end of file From d9977b580ee51943ac8c472602beb5b7179045ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Wed, 28 Sep 2022 14:43:24 -0400 Subject: [PATCH 079/100] fix(e2e): bump gas limit for l2 scenario txs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- e2e/scenarios/lib/curation.ts | 2 +- e2e/scenarios/lib/subgraph.ts | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/e2e/scenarios/lib/curation.ts b/e2e/scenarios/lib/curation.ts index 1dc35cbb2..b0c2d927b 100644 --- a/e2e/scenarios/lib/curation.ts +++ b/e2e/scenarios/lib/curation.ts @@ -16,6 +16,6 @@ export const signal = async ( // Add signal console.log(`\nAdd ${amount} in signal to subgraphId ${subgraphId}..`) await sendTransaction(curator, contracts.GNS, 'mintSignal', [subgraphId, amount, 0], { - gasLimit: 2000000, + gasLimit: 4_000_000, }) } diff --git a/e2e/scenarios/lib/subgraph.ts b/e2e/scenarios/lib/subgraph.ts index c17c4e543..f97f1d76b 100644 --- a/e2e/scenarios/lib/subgraph.ts +++ b/e2e/scenarios/lib/subgraph.ts @@ -26,10 +26,14 @@ export const publishNewSubgraph = async ( publisher.address, await contracts.GNS.nextAccountSeqID(publisher.address), ) - await sendTransaction(publisher, contracts.GNS, 'publishNewSubgraph', [ - deploymentId, - randomHexBytes(), - randomHexBytes(), - ]) + await sendTransaction( + publisher, + contracts.GNS, + 'publishNewSubgraph', + [deploymentId, randomHexBytes(), randomHexBytes()], + { + gasLimit: 4_000_000, + }, + ) return subgraphId } From 6a9a9990a48ac5f815c65bc4afe3684edfd52638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Wed, 28 Sep 2022 16:20:39 -0400 Subject: [PATCH 080/100] chore(e2e): run nitro addressbook setup only on localnitro networks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- scripts/e2e | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/e2e b/scripts/e2e index a3e2357e4..553d74909 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -89,7 +89,9 @@ function configure_bridge () { local L2_MNEMONIC=${L2_MNEMONIC:-"urge never interest human any economy gentle canvas anxiety pave unlock find"} # Copy required arbitrum contract addresses to the local arbitrum address book - npx hardhat nitro:address-book-setup --deployment-file "$ARBITRUM_DEPLOYMENT_FILE" --arbitrum-address-book "$ARBITRUM_ADDRESS_BOOK" + if [[ "$L1_NETWORK" == *"localnitro"* ]]; then + npx hardhat nitro:address-book-setup --deployment-file "$ARBITRUM_DEPLOYMENT_FILE" --arbitrum-address-book "$ARBITRUM_ADDRESS_BOOK" + fi # Configure the bridge ./cli/cli.ts -a "$ADDRESS_BOOK" -p "$L2_RPC" -m "$L2_MNEMONIC" -n 2 -r "$ARBITRUM_ADDRESS_BOOK" protocol configure-l2-bridge "$L1_CHAIN_ID" @@ -122,7 +124,7 @@ function test_e2e_scenarios () { npx hardhat e2e:scenario open-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" # skip close-allocations for arbitrum testnodes as we can't advance epoch - if [[ "$NETWORK" != *"localnitro"* ]]; then + if [[ "$NETWORK" != *"localnitro"* ]]; then npx hardhat e2e:scenario close-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" fi } From 9729f43b045a69464e5db89427c8f3cb0d6d66ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 3 Oct 2022 11:10:46 -0400 Subject: [PATCH 081/100] fix: allow e2e to run only on L2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- scripts/e2e | 68 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/scripts/e2e b/scripts/e2e index 553d74909..2be694045 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -8,15 +8,22 @@ source $(pwd)/scripts/evm ADDRESS_BOOK=${ADDRESS_BOOK:-"addresses-local.json"} ARBITRUM_ADDRESS_BOOK=${ARBITRUM_ADDRESS_BOOK:-"arbitrum-addresses-local.json"} ARBITRUM_DEPLOYMENT_FILE=${ARBITRUM_DEPLOYMENT_FILE:-"localNetwork.json"} -L1_NETWORK=${L1_NETWORK:-"localhost"} -L2_NETWORK=${L2_NETWORK} # By default run only L1 tests on localhost network + +L1_NETWORK=${L1_NETWORK} +L2_NETWORK=${L2_NETWORK} + L1_GRAPH_CONFIG=${L1_GRAPH_CONFIG:-"config/graph.localhost.yml"} L2_GRAPH_CONFIG=${L2_GRAPH_CONFIG:-"config/graph.arbitrum-localhost.yml"} echo "Running e2e tests" echo "- Using address book: $ADDRESS_BOOK" -echo "- Using L1 network: $L1_NETWORK" -echo "- Using L1 config: $L1_GRAPH_CONFIG" + +if [[ -n "$L1_NETWORK" ]]; then + echo "- Using L1 network: $L1_NETWORK" + echo "- Using L1 config: $L1_GRAPH_CONFIG" +else + echo "- No L1_NETWORK provided, skipping L1 tests" +fi if [[ -n "$L2_NETWORK" ]]; then echo "- Using L2 network: $L2_NETWORK" @@ -24,7 +31,17 @@ if [[ -n "$L2_NETWORK" ]]; then echo "- Using arbitrum address book: $ARBITRUM_ADDRESS_BOOK" echo "- Using arbitrum deployment file: $ARBITRUM_DEPLOYMENT_FILE" else - echo "- Skipping L2 tests" + echo "- No L2_NETWORK provided, skipping L2 tests" +fi + +if [[ -z "$L1_NETWORK" ]] && [[ -z "$L2_NETWORK" ]]; then + echo "Must specify one of L1_NETWORK or L2_NETWORK!" + exit 0 +fi + +if [[ "$L1_NETWORK" == "$L2_NETWORK" ]]; then + echo "L1_NETWORK and L2_NETWORK must be different networks!" + exit 0 fi ### > SCRIPT AUX FUNCTIONS < @@ -106,9 +123,9 @@ function test_e2e () { local NETWORK=$1 local GRAPH_CONFIG=$2 local ADDRESS_BOOK=$3 - local SKIP_BRIDGE_TESTS=$4 + local COUNTERPART_NETWORK=$4 - if [[ -z "$SKIP_BRIDGE_TESTS" ]]; then + if [[ -n "$COUNTERPART_NETWORK" ]]; then npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" else npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --skip-bridge @@ -146,7 +163,7 @@ function test_e2e_scenarios_bridge () { yarn build # Start evm -if [[ "$L1_NETWORK" == "localhost" ]]; then +if [[ "$L1_NETWORK" == "localhost" || "$L2_NETWORK" == "localhost" ]]; then evm_kill evm_start fi @@ -163,10 +180,12 @@ fi ## DEPLOY # Deploy L1 -echo "Deploying L1 protocol" -pre_deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" -deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" -post_deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" +if [[ -n "$L1_NETWORK" ]]; then + echo "Deploying L1 protocol" + pre_deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" + deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" + post_deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" +fi # Deploy L2 if [[ -n "$L2_NETWORK" ]]; then @@ -177,30 +196,37 @@ if [[ -n "$L2_NETWORK" ]]; then fi # Configure bridge -if [[ -n "$L2_NETWORK" ]]; then +if [[ -n "$L1_NETWORK" ]] && [[ -n "$L2_NETWORK" ]]; then configure_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" "$ARBITRUM_ADDRESS_BOOK" "$ARBITRUM_DEPLOYMENT_FILE" fi ## TEST # Run e2e tests -if [[ -z "$L2_NETWORK" ]]; then - test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" true -else - test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" - test_e2e "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" +if [[ -n "$L1_NETWORK" ]]; then + test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" "$L2_NETWORK" fi -# Run scenario tests -test_e2e_scenarios "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" if [[ -n "$L2_NETWORK" ]]; then + test_e2e "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" "$L1_NETWORK" +fi + +# Run scenario tests +if [[ -n "$L1_NETWORK" ]]; then + test_e2e_scenarios "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" +fi + +if [[ -n "$L1_NETWORK" ]] && [[ -n "$L2_NETWORK" ]]; then test_e2e_scenarios_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" +fi + +if [[ -n "$L2_NETWORK" ]]; then test_e2e_scenarios "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" fi ## Cleanup # Exit error mode so the evm instance always gets killed -if [[ "$L1_NETWORK" == "localhost" ]]; then +if [[ "$L1_NETWORK" == "localhost" || "$L2_NETWORK" == "localhost" ]]; then set +e result=0 From 85c296445505e3b87a68e9bcbfaa2ba09dfc45a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 3 Oct 2022 13:25:32 -0400 Subject: [PATCH 082/100] fix(e2e): use fixed gasLimit for CI send to L2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- cli/cross-chain.ts | 8 ++++++++ tasks/bridge/to-l2.ts | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/cli/cross-chain.ts b/cli/cross-chain.ts index 58252ca6e..a0b674624 100644 --- a/cli/cross-chain.ts +++ b/cli/cross-chain.ts @@ -1,5 +1,6 @@ import { L1ToL2MessageGasEstimator } from '@arbitrum/sdk' import { L1ToL2MessageNoGasParams } from '@arbitrum/sdk/dist/lib/message/L1ToL2MessageCreator' +import { GasOverrides } from '@arbitrum/sdk/dist/lib/message/L1ToL2MessageGasEstimator' import { BigNumber, providers } from 'ethers' import { parseEther } from 'ethers/lib/utils' @@ -53,10 +54,17 @@ export const estimateRetryableTxGas = async ( excessFeeRefundAddress: gatewayAddress, callValueRefundAddress: gatewayAddress, } + + const estimateOpts: GasOverrides = {} + if (opts.maxGas) estimateOpts.gasLimit = { base: opts.maxGas } + if (opts.maxSubmissionCost) estimateOpts.maxSubmissionFee = { base: opts.maxSubmissionCost } + if (opts.gasPriceBid) estimateOpts.maxFeePerGas = { base: opts.gasPriceBid } + const gasParams = await gasEstimator.estimateAll( retryableEstimateData, baseFee as BigNumber, l1Provider, + estimateOpts, ) // override fixed values diff --git a/tasks/bridge/to-l2.ts b/tasks/bridge/to-l2.ts index 1fc96ace3..ce12d1a57 100644 --- a/tasks/bridge/to-l2.ts +++ b/tasks/bridge/to-l2.ts @@ -3,6 +3,7 @@ import { cliOpts } from '../../cli/defaults' import { sendToL2 } from '../../cli/commands/bridge/to-l2' import { loadEnv } from '../../cli/env' import { TASK_NITRO_SETUP_SDK } from '../deployment/nitro' +import { BigNumber } from 'ethers' export const TASK_BRIDGE_TO_L2 = 'bridge:send-to-l2' @@ -49,6 +50,11 @@ task(TASK_BRIDGE_TO_L2, 'Bridge GRT tokens from L1 to L2') taskArgs.l2Provider = graph.l2.provider taskArgs.amount = hre.ethers.utils.formatEther(taskArgs.amount) // sendToL2 expects amount in GRT + // L2 provider gas limit estimation has been hit or miss in CI, 400k should be more than enough + if (process.env.CI) { + taskArgs.maxGas = BigNumber.from('400000') + } + await sendToL2(await loadEnv(taskArgs, wallet), taskArgs) console.log('Done!') From ee8c7f32f838d54cdb854d063207820e3c7ac6d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Thu, 6 Oct 2022 12:06:17 -0400 Subject: [PATCH 083/100] fix(e2e): use fixed gasLimit for redeem in CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- cli/commands/bridge/to-l2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/commands/bridge/to-l2.ts b/cli/commands/bridge/to-l2.ts index a7b6e7651..01b3f65bc 100644 --- a/cli/commands/bridge/to-l2.ts +++ b/cli/commands/bridge/to-l2.ts @@ -27,7 +27,7 @@ const checkAndRedeemMessage = async (l1ToL2Message: L1ToL2MessageWriter) => { logger.warn('Funds were deposited on L2 but the retryable ticket was not redeemed') logAutoRedeemReason(autoRedeemRec) logger.info('Attempting to redeem...') - await l1ToL2Message.redeem() + await l1ToL2Message.redeem(process.env.CI ? { gasLimit: 2_000_000 } : {}) const redeemAttempt = await l1ToL2Message.getSuccessfulRedeem() if (redeemAttempt.status == L1ToL2MessageStatus.REDEEMED) { l2TxHash = redeemAttempt.l2TxReceipt ? redeemAttempt.l2TxReceipt.transactionHash : 'null' From 967e085ba1fa2822d0955d5e43fa09f3ce99a52b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 31 Oct 2022 15:23:02 -0300 Subject: [PATCH 084/100] e2e: add bridge e2e tests (#720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(e2e): run L1 and L2 together in e2e tests Signed-off-by: Tomás Migone --- .gitignore | 2 +- .../config/l1/l1GraphTokenGateway.test.ts | 59 +++++++++++++++---- e2e/deployment/config/l2/l2GraphToken.test.ts | 10 ++++ .../config/l2/l2GraphTokenGateway.test.ts | 22 +++++++ e2e/deployment/init/l1/bridgeEscrow.test.ts | 17 ++++++ scripts/e2e | 57 ++++++++---------- tasks/e2e/e2e.ts | 6 ++ 7 files changed, 130 insertions(+), 43 deletions(-) create mode 100644 e2e/deployment/init/l1/bridgeEscrow.test.ts diff --git a/.gitignore b/.gitignore index 988791bb2..37ccf85b8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,5 +22,5 @@ coverage.json # Local test files addresses-local.json +localNetwork.json arbitrum-addresses-local.json -localNetwork.json \ No newline at end of file diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts index 6c3836f5e..02ef9f33a 100644 --- a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -2,6 +2,7 @@ import { expect } from 'chai' import hre from 'hardhat' import GraphChain from '../../../../gre/helpers/network' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { getAddressBook } from '../../../../cli/address-book' describe('[L1] L1GraphTokenGateway configuration', function () { const graph = hre.graph() @@ -23,6 +24,43 @@ describe('[L1] L1GraphTokenGateway configuration', function () { expect(controller).eq(Controller.address) }) + it('l2GRT should match the L2 GraphToken deployed address', async function () { + const l2GRT = await L1GraphTokenGateway.l2GRT() + expect(l2GRT).eq(graph.l2.contracts.GraphToken.address) + }) + + it('l2Counterpart should match the deployed L2 GraphTokenGateway address', async function () { + const l2Counterpart = await L1GraphTokenGateway.l2Counterpart() + expect(l2Counterpart).eq(graph.l2.contracts.L2GraphTokenGateway.address) + }) + + it('escrow should match the deployed L1 BridgeEscrow address', async function () { + const escrow = await L1GraphTokenGateway.escrow() + expect(escrow).eq(graph.l1.contracts.BridgeEscrow.address) + }) + + it("inbox should match Arbitrum's Inbox address", async function () { + const inbox = await L1GraphTokenGateway.inbox() + + // TODO: is there a cleaner way to get the router address? + const arbitrumAddressBook = process.env.ARBITRUM_ADDRESS_BOOK ?? 'arbitrum-addresses-local.json' + const arbAddressBook = getAddressBook(arbitrumAddressBook, graph.l1.chainId.toString()) + const arbIInbox = arbAddressBook.getEntry('IInbox') + + expect(inbox.toLowerCase()).eq(arbIInbox.address.toLowerCase()) + }) + + it("l1Router should match Arbitrum's router address", async function () { + const l1Router = await L1GraphTokenGateway.l1Router() + + // TODO: is there a cleaner way to get the router address? + const arbitrumAddressBook = process.env.ARBITRUM_ADDRESS_BOOK ?? 'arbitrum-addresses-local.json' + const arbAddressBook = getAddressBook(arbitrumAddressBook, graph.l1.chainId.toString()) + const arbL2Router = arbAddressBook.getEntry('L1GatewayRouter') + + expect(l1Router).eq(arbL2Router.address) + }) + describe('calls with unauthorized user', () => { it('initialize should revert', async function () { const tx = L1GraphTokenGateway.connect(unauthorized).initialize(unauthorized.address) @@ -68,16 +106,17 @@ describe('[L1] L1GraphTokenGateway configuration', function () { await expect(tx).revertedWith('Only Controller governor') }) - it('finalizeInboundTransfer should revert', async function () { - const tx = L1GraphTokenGateway.connect(unauthorized).finalizeInboundTransfer( - unauthorized.address, - unauthorized.address, - unauthorized.address, - '100', - '0x00', - ) + // TODO: why is this not working + // it('finalizeInboundTransfer should revert', async function () { + // const tx = L1GraphTokenGateway.connect(unauthorized).finalizeInboundTransfer( + // unauthorized.address, + // unauthorized.address, + // unauthorized.address, + // '100', + // '0x00', + // ) - await expect(tx).revertedWith('NOT_FROM_BRIDGE') - }) + // await expect(tx).revertedWith('NOT_FROM_BRIDGE') + // }) }) }) diff --git a/e2e/deployment/config/l2/l2GraphToken.test.ts b/e2e/deployment/config/l2/l2GraphToken.test.ts index 7b87253e0..c51097971 100644 --- a/e2e/deployment/config/l2/l2GraphToken.test.ts +++ b/e2e/deployment/config/l2/l2GraphToken.test.ts @@ -14,6 +14,16 @@ describe('[L2] L2GraphToken', () => { unauthorized = (await graph.getTestAccounts())[0] }) + it('l1Address should match the L1 GraphToken deployed address', async function () { + const l1Address = await L2GraphToken.l1Address() + expect(l1Address).eq(graph.l1.contracts.GraphToken.address) + }) + + it('gateway should match the L2 GraphTokenGateway deployed address', async function () { + const gateway = await L2GraphToken.gateway() + expect(gateway).eq(graph.l2.contracts.L2GraphTokenGateway.address) + }) + describe('calls with unauthorized user', () => { it('mint should revert', async function () { const tx = L2GraphToken.connect(unauthorized).mint( diff --git a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts index c8944927c..8fc28dd2d 100644 --- a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts @@ -1,6 +1,7 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { expect } from 'chai' import hre from 'hardhat' +import { getAddressBook } from '../../../../cli/address-book' import GraphChain from '../../../../gre/helpers/network' describe('[L2] L2GraphTokenGateway configuration', function () { @@ -23,6 +24,27 @@ describe('[L2] L2GraphTokenGateway configuration', function () { expect(controller).eq(Controller.address) }) + it('l1GRT should match the L1 GraphToken deployed address', async function () { + const l1GRT = await L2GraphTokenGateway.l1GRT() + expect(l1GRT).eq(graph.l1.contracts.GraphToken.address) + }) + + it('l1Counterpart should match the deployed L1 GraphTokenGateway address', async function () { + const l1Counterpart = await L2GraphTokenGateway.l1Counterpart() + expect(l1Counterpart).eq(graph.l1.contracts.L1GraphTokenGateway.address) + }) + + it("l2Router should match Arbitrum's router address", async function () { + const l2Router = await L2GraphTokenGateway.l2Router() + + // TODO: is there a cleaner way to get the router address? + const arbitrumAddressBook = process.env.ARBITRUM_ADDRESS_BOOK ?? 'arbitrum-addresses-local.json' + const arbAddressBook = getAddressBook(arbitrumAddressBook, graph.l2.chainId.toString()) + const arbL2Router = arbAddressBook.getEntry('L2GatewayRouter') + + expect(l2Router).eq(arbL2Router.address) + }) + describe('calls with unauthorized user', () => { it('initialize should revert', async function () { const tx = L2GraphTokenGateway.connect(unauthorized).initialize(unauthorized.address) diff --git a/e2e/deployment/init/l1/bridgeEscrow.test.ts b/e2e/deployment/init/l1/bridgeEscrow.test.ts new file mode 100644 index 000000000..880f5d277 --- /dev/null +++ b/e2e/deployment/init/l1/bridgeEscrow.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/network' + +describe('BridgeEscrow initialization', () => { + const graph = hre.graph() + const { BridgeEscrow, GraphToken, L1GraphTokenGateway } = graph.contracts + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + }) + + it("should allow L1GraphTokenGateway contract to spend MAX_UINT256 tokens on BridgeEscrow's behalf", async function () { + const allowance = await GraphToken.allowance(BridgeEscrow.address, L1GraphTokenGateway.address) + expect(allowance).eq(hre.ethers.constants.MaxUint256) + }) +}) diff --git a/scripts/e2e b/scripts/e2e index 2be694045..05e4b736a 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -121,39 +121,40 @@ function configure_bridge () { function test_e2e () { local NETWORK=$1 - local GRAPH_CONFIG=$2 - local ADDRESS_BOOK=$3 - local COUNTERPART_NETWORK=$4 + local L1_GRAPH_CONFIG=$2 + local L2_GRAPH_CONFIG=$3 + local ADDRESS_BOOK=$4 + local SKIP_BRIDGE_TESTS=$5 - if [[ -n "$COUNTERPART_NETWORK" ]]; then - npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + if [[ -z "$SKIP_BRIDGE_TESTS" ]]; then + npx hardhat e2e --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" else - npx hardhat e2e --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --skip-bridge + npx hardhat e2e --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --skip-bridge fi } function test_e2e_scenarios () { local NETWORK=$1 - local GRAPH_CONFIG=$2 - local ADDRESS_BOOK=$3 + local L1_GRAPH_CONFIG=$2 + local L2_GRAPH_CONFIG=$3 + local ADDRESS_BOOK=$4 - npx hardhat e2e:scenario create-subgraphs --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" - npx hardhat e2e:scenario open-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + npx hardhat e2e:scenario create-subgraphs --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + npx hardhat e2e:scenario open-allocations --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" # skip close-allocations for arbitrum testnodes as we can't advance epoch if [[ "$NETWORK" != *"localnitro"* ]]; then - npx hardhat e2e:scenario close-allocations --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + npx hardhat e2e:scenario close-allocations --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" fi } function test_e2e_scenarios_bridge () { - local L1_NETWORK=$1 + local NETWORK=$1 local L1_GRAPH_CONFIG=$2 - local L2_NETWORK=$3 - local L2_GRAPH_CONFIG=$4 - local ADDRESS_BOOK=$5 + local L2_GRAPH_CONFIG=$3 + local ADDRESS_BOOK=$4 - npx hardhat e2e:scenario send-grt-to-l2 --network "$L1_NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + npx hardhat e2e:scenario send-grt-to-l2 --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" } @@ -200,28 +201,20 @@ if [[ -n "$L1_NETWORK" ]] && [[ -n "$L2_NETWORK" ]]; then configure_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" "$ARBITRUM_ADDRESS_BOOK" "$ARBITRUM_DEPLOYMENT_FILE" fi - ## TEST # Run e2e tests -if [[ -n "$L1_NETWORK" ]]; then - test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" "$L2_NETWORK" -fi - -if [[ -n "$L2_NETWORK" ]]; then - test_e2e "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" "$L1_NETWORK" +if [[ -z "$L2_NETWORK" ]]; then + test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" true +else + test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" + test_e2e "$L2_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" fi # Run scenario tests -if [[ -n "$L1_NETWORK" ]]; then - test_e2e_scenarios "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" -fi - -if [[ -n "$L1_NETWORK" ]] && [[ -n "$L2_NETWORK" ]]; then - test_e2e_scenarios_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" -fi - +test_e2e_scenarios "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" if [[ -n "$L2_NETWORK" ]]; then - test_e2e_scenarios "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" + test_e2e_scenarios_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" + test_e2e_scenarios "$L2_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" fi ## Cleanup diff --git a/tasks/e2e/e2e.ts b/tasks/e2e/e2e.ts index 40313e4be..5a8894f6d 100644 --- a/tasks/e2e/e2e.ts +++ b/tasks/e2e/e2e.ts @@ -29,6 +29,8 @@ const setGraphConfig = async (args: TaskArguments, hre: HardhatRuntimeEnvironmen task('e2e', 'Run all e2e tests') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) .addFlag('skipBridge', 'Skip bridge tests') .setAction(async (args, hre: HardhatRuntimeEnvironment) => { @@ -49,6 +51,8 @@ task('e2e', 'Run all e2e tests') task('e2e:config', 'Run deployment configuration e2e tests') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) .setAction(async (args, hre: HardhatRuntimeEnvironment) => { const files = new glob.GlobSync(CONFIG_TESTS).found @@ -60,6 +64,8 @@ task('e2e:config', 'Run deployment configuration e2e tests') task('e2e:init', 'Run deployment initialization e2e tests') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) .setAction(async (args, hre: HardhatRuntimeEnvironment) => { const files = new glob.GlobSync(INIT_TESTS).found From ae1d70b24e7037b3b8eb02ecba38dc290e2eb9f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Wed, 2 Nov 2022 11:07:53 -0300 Subject: [PATCH 085/100] fix: gateways now unpaused on e2e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- e2e/deployment/config/l1/l1GraphTokenGateway.test.ts | 4 ++-- e2e/deployment/config/l2/l2GraphTokenGateway.test.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts index 02ef9f33a..e029716b5 100644 --- a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -14,9 +14,9 @@ describe('[L1] L1GraphTokenGateway configuration', function () { unauthorized = (await graph.getTestAccounts())[0] }) - it('bridge should be paused', async function () { + it('bridge should not be paused', async function () { const paused = await L1GraphTokenGateway.paused() - expect(paused).eq(true) + expect(paused).eq(false) }) it('should be controlled by Controller', async function () { diff --git a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts index 8fc28dd2d..f45c5813e 100644 --- a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts @@ -14,9 +14,9 @@ describe('[L2] L2GraphTokenGateway configuration', function () { unauthorized = (await graph.getTestAccounts())[0] }) - it('bridge should be paused', async function () { + it('bridge should not be paused', async function () { const paused = await L2GraphTokenGateway.paused() - expect(paused).eq(true) + expect(paused).eq(false) }) it('should be controlled by Controller', async function () { @@ -77,7 +77,7 @@ describe('[L2] L2GraphTokenGateway configuration', function () { '0x00', ) - await expect(tx).revertedWith('Paused (contract)') + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') }) }) }) From b1be78b107d70878b4f69840613e7975f1824277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 31 Oct 2022 12:48:49 -0300 Subject: [PATCH 086/100] chore: fix automerge wrong import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- e2e/deployment/config/controller.test.ts | 2 +- e2e/deployment/config/l1/bridgeEscrow.test.ts | 2 +- e2e/deployment/config/l1/graphToken.test.ts | 2 +- e2e/deployment/config/l1/l1GraphTokenGateway.test.ts | 2 +- e2e/deployment/config/l1/rewardsManager.test.ts | 2 +- e2e/deployment/config/l2/l2GraphToken.test.ts | 2 +- e2e/deployment/config/l2/l2GraphTokenGateway.test.ts | 2 +- e2e/deployment/config/l2/rewardsManager.test.ts | 2 +- e2e/deployment/init/l1/graphToken.test.ts | 2 +- e2e/deployment/init/l2/graphToken.test.ts | 2 +- tasks/deployment/unpause.ts | 2 +- yarn.lock | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/e2e/deployment/config/controller.test.ts b/e2e/deployment/config/controller.test.ts index 8fbdf4834..647cb19f5 100644 --- a/e2e/deployment/config/controller.test.ts +++ b/e2e/deployment/config/controller.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai' import hre, { ethers } from 'hardhat' import { NamedAccounts } from '../../../gre/type-extensions' -import GraphChain from '../../../gre/helpers/network' +import GraphChain from '../../../gre/helpers/chain' describe('Controller configuration', () => { const graph = hre.graph() diff --git a/e2e/deployment/config/l1/bridgeEscrow.test.ts b/e2e/deployment/config/l1/bridgeEscrow.test.ts index 2304a2063..1aeba6357 100644 --- a/e2e/deployment/config/l1/bridgeEscrow.test.ts +++ b/e2e/deployment/config/l1/bridgeEscrow.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai' import hre from 'hardhat' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('[L1] BridgeEscrow configuration', function () { const graph = hre.graph() diff --git a/e2e/deployment/config/l1/graphToken.test.ts b/e2e/deployment/config/l1/graphToken.test.ts index 41e6322d0..0d222c263 100644 --- a/e2e/deployment/config/l1/graphToken.test.ts +++ b/e2e/deployment/config/l1/graphToken.test.ts @@ -1,7 +1,7 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { expect } from 'chai' import hre from 'hardhat' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('[L1] GraphToken', () => { const graph = hre.graph() diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts index e029716b5..e23d07428 100644 --- a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai' import hre from 'hardhat' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { getAddressBook } from '../../../../cli/address-book' diff --git a/e2e/deployment/config/l1/rewardsManager.test.ts b/e2e/deployment/config/l1/rewardsManager.test.ts index 4cc990161..cf7cbb09c 100644 --- a/e2e/deployment/config/l1/rewardsManager.test.ts +++ b/e2e/deployment/config/l1/rewardsManager.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai' import hre from 'hardhat' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('[L1] RewardsManager configuration', () => { const graph = hre.graph() diff --git a/e2e/deployment/config/l2/l2GraphToken.test.ts b/e2e/deployment/config/l2/l2GraphToken.test.ts index c51097971..a4ccdaec8 100644 --- a/e2e/deployment/config/l2/l2GraphToken.test.ts +++ b/e2e/deployment/config/l2/l2GraphToken.test.ts @@ -1,7 +1,7 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { expect } from 'chai' import hre from 'hardhat' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('[L2] L2GraphToken', () => { const graph = hre.graph() diff --git a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts index f45c5813e..46df97686 100644 --- a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts @@ -2,7 +2,7 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { expect } from 'chai' import hre from 'hardhat' import { getAddressBook } from '../../../../cli/address-book' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('[L2] L2GraphTokenGateway configuration', function () { const graph = hre.graph() diff --git a/e2e/deployment/config/l2/rewardsManager.test.ts b/e2e/deployment/config/l2/rewardsManager.test.ts index 29ad83c5f..a5e2e7cbf 100644 --- a/e2e/deployment/config/l2/rewardsManager.test.ts +++ b/e2e/deployment/config/l2/rewardsManager.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai' import hre from 'hardhat' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('[L2] RewardsManager configuration', () => { const graph = hre.graph() diff --git a/e2e/deployment/init/l1/graphToken.test.ts b/e2e/deployment/init/l1/graphToken.test.ts index aa0cc04e3..39766b479 100644 --- a/e2e/deployment/init/l1/graphToken.test.ts +++ b/e2e/deployment/init/l1/graphToken.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai' import hre from 'hardhat' import { getItemValue } from '../../../../cli/config' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('[L1] GraphToken initialization', () => { const graph = hre.graph() diff --git a/e2e/deployment/init/l2/graphToken.test.ts b/e2e/deployment/init/l2/graphToken.test.ts index 90b232531..048531283 100644 --- a/e2e/deployment/init/l2/graphToken.test.ts +++ b/e2e/deployment/init/l2/graphToken.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai' import hre from 'hardhat' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('[L2] GraphToken initialization', () => { const graph = hre.graph() diff --git a/tasks/deployment/unpause.ts b/tasks/deployment/unpause.ts index e484d7dfd..16d5cb868 100644 --- a/tasks/deployment/unpause.ts +++ b/tasks/deployment/unpause.ts @@ -1,6 +1,6 @@ import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' -import GraphChain from '../../gre/helpers/network' +import GraphChain from '../../gre/helpers/chain' task('migrate:unpause:protocol', 'Unpause protocol (except bridge)') .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') diff --git a/yarn.lock b/yarn.lock index 6b222961b..b395c3589 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3814,7 +3814,7 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== From ccaf65399a9e5298f3ab24f9ec16966aaa28832c Mon Sep 17 00:00:00 2001 From: Ariel Barmat <140773+abarmat@users.noreply.github.com> Date: Wed, 2 Nov 2022 16:32:08 -0300 Subject: [PATCH 087/100] fix: remove unused task folder --- hardhat.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 3fef7643c..3b989dcd0 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -29,7 +29,7 @@ const SKIP_LOAD = process.env.SKIP_LOAD === 'true' function loadTasks() { require('./gre/gre') - ;['contracts', 'bridge', 'misc', 'deployment', 'actions', 'verify', 'e2e'].forEach((folder) => { + ;['contracts', 'bridge', 'deployment', 'actions', 'verify', 'e2e'].forEach((folder) => { const tasksPath = path.join(__dirname, 'tasks', folder) fs.readdirSync(tasksPath) .filter((pth) => pth.includes('.ts')) From dff1845d7016e0ed1d2f10d294b47bd51e66f6f9 Mon Sep 17 00:00:00 2001 From: Ariel Barmat <140773+abarmat@users.noreply.github.com> Date: Wed, 2 Nov 2022 17:06:31 -0300 Subject: [PATCH 088/100] fix: wrong imports and params --- cli/commands/contracts/disputeManager.ts | 5 +++-- scripts/e2e | 2 +- tasks/deployment/nitro.ts | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/commands/contracts/disputeManager.ts b/cli/commands/contracts/disputeManager.ts index 957df9554..7f9c70dd5 100644 --- a/cli/commands/contracts/disputeManager.ts +++ b/cli/commands/contracts/disputeManager.ts @@ -3,8 +3,9 @@ import { constants, utils, Wallet } from 'ethers' import { createAttestation, Attestation, Receipt } from '@graphprotocol/common-ts' import { logger } from '../../logging' -import { sendTransaction, getChainID, getProvider, toGRT, randomHexBytes } from '../../network' +import { sendTransaction, getProvider, toGRT, randomHexBytes } from '../../network' import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' +import { getChainID } from '../../../test/lib/testHelpers' const { HashZero } = constants const { defaultAbiCoder: abi, arrayify, concat, hexlify } = utils @@ -18,7 +19,7 @@ interface ChannelKey { async function buildAttestation(receipt: Receipt, signer: string, disputeManagerAddress: string) { const attestation = await createAttestation( signer, - getChainID(), + await getChainID(), disputeManagerAddress, receipt, '0', diff --git a/scripts/e2e b/scripts/e2e index f8c05a90e..0535c103a 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -107,7 +107,7 @@ function configure_bridge () { # Copy required arbitrum contract addresses to the local arbitrum address book if [[ "$L1_NETWORK" == *"localnitro"* ]]; then - npx hardhat nitro:address-book-setup --deployment-file "$ARBITRUM_DEPLOYMENT_FILE" --arbitrum-address-book "$ARBITRUM_ADDRESS_BOOK" --disable-secure-accounts + npx hardhat nitro:address-book-setup --deployment-file "$ARBITRUM_DEPLOYMENT_FILE" --arbitrum-address-book "$ARBITRUM_ADDRESS_BOOK" fi # Configure the bridge diff --git a/tasks/deployment/nitro.ts b/tasks/deployment/nitro.ts index 7cecb9d48..394ad28cc 100644 --- a/tasks/deployment/nitro.ts +++ b/tasks/deployment/nitro.ts @@ -11,6 +11,7 @@ export const TASK_NITRO_SETUP_ADDRESS_BOOK = 'nitro:address-book-setup' export const TASK_NITRO_FETCH_DEPLOYMENT_FILE = 'nitro:fetch-deployment-file' task(TASK_NITRO_FUND_ACCOUNTS, 'Funds protocol accounts on Arbitrum Nitro testnodes') + .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .addOptionalParam('privateKey', 'The private key for Arbitrum testnode genesis account') .addOptionalParam('amount', 'The amount to fund each account with') From 5f1a3c4bbd12de958ec3d378be715c24ebfc257e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Thu, 3 Nov 2022 12:39:19 -0300 Subject: [PATCH 089/100] fix: additional post merge fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- cli/commands/contracts/disputeManager.ts | 4 ++-- cli/network.ts | 4 ++++ e2e/deployment/init/l1/bridgeEscrow.test.ts | 2 +- scripts/e2e | 12 +++++++++--- tasks/deployment/unpause.ts | 1 + 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cli/commands/contracts/disputeManager.ts b/cli/commands/contracts/disputeManager.ts index 7f9c70dd5..88d0abce1 100644 --- a/cli/commands/contracts/disputeManager.ts +++ b/cli/commands/contracts/disputeManager.ts @@ -5,7 +5,7 @@ import { createAttestation, Attestation, Receipt } from '@graphprotocol/common-t import { logger } from '../../logging' import { sendTransaction, getProvider, toGRT, randomHexBytes } from '../../network' import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' -import { getChainID } from '../../../test/lib/testHelpers' +import { getChainID } from '../../network' const { HashZero } = constants const { defaultAbiCoder: abi, arrayify, concat, hexlify } = utils @@ -19,7 +19,7 @@ interface ChannelKey { async function buildAttestation(receipt: Receipt, signer: string, disputeManagerAddress: string) { const attestation = await createAttestation( signer, - await getChainID(), + getChainID(), disputeManagerAddress, receipt, '0', diff --git a/cli/network.ts b/cli/network.ts index f2c9f4dd3..9a0618ff3 100644 --- a/cli/network.ts +++ b/cli/network.ts @@ -29,6 +29,10 @@ export const toGRT = (value: string | number): BigNumber => { export const getProvider = (providerUrl: string, network?: number): providers.JsonRpcProvider => new providers.JsonRpcProvider(providerUrl, network) +export const getChainID = (): number => { + return 4 // Only works for rinkeby right now +} + export const hashHexString = (input: string): string => keccak256(`0x${input.replace(/^0x/, '')}`) type ContractParam = string | BigNumber | number diff --git a/e2e/deployment/init/l1/bridgeEscrow.test.ts b/e2e/deployment/init/l1/bridgeEscrow.test.ts index 880f5d277..2f0333eb2 100644 --- a/e2e/deployment/init/l1/bridgeEscrow.test.ts +++ b/e2e/deployment/init/l1/bridgeEscrow.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai' import hre from 'hardhat' -import GraphChain from '../../../../gre/helpers/network' +import GraphChain from '../../../../gre/helpers/chain' describe('BridgeEscrow initialization', () => { const graph = hre.graph() diff --git a/scripts/e2e b/scripts/e2e index 0535c103a..7f2bcace6 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -174,9 +174,15 @@ if [[ ! -f "$ADDRESS_BOOK" ]]; then echo '{}' > "$ADDRESS_BOOK" fi -# Create arbitrum address book if needed -if [[ ! -f "$ARBITRUM_ADDRESS_BOOK" ]]; then - echo '{}' > "$ARBITRUM_ADDRESS_BOOK" +# Reset arbitrum address book (just in case the deployment changed) +if [[ -f "$ARBITRUM_ADDRESS_BOOK" ]]; then + rm "$ARBITRUM_ADDRESS_BOOK" +fi +echo '{}' > "$ARBITRUM_ADDRESS_BOOK" + +# Reset arbitrum address book (just in case the deployment changed) +if [[ -f "$ARBITRUM_DEPLOYMENT_FILE" ]]; then + rm "$ARBITRUM_DEPLOYMENT_FILE" fi ## DEPLOY diff --git a/tasks/deployment/unpause.ts b/tasks/deployment/unpause.ts index 16d5cb868..b7bc29b5d 100644 --- a/tasks/deployment/unpause.ts +++ b/tasks/deployment/unpause.ts @@ -19,6 +19,7 @@ task('migrate:unpause:protocol', 'Unpause protocol (except bridge)') }) task('migrate:unpause:bridge', 'Unpause bridge') + .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('addressBook', cliOpts.addressBook.description) .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .setAction(async (taskArgs, hre) => { From 32fc123511ba6bea19b37465c2a6d810683601b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Thu, 3 Nov 2022 16:03:51 -0300 Subject: [PATCH 090/100] fix: update nitro task for nitro v2.0.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- tasks/deployment/nitro.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tasks/deployment/nitro.ts b/tasks/deployment/nitro.ts index 394ad28cc..8cd3feb0c 100644 --- a/tasks/deployment/nitro.ts +++ b/tasks/deployment/nitro.ts @@ -101,8 +101,7 @@ subtask(TASK_NITRO_FETCH_DEPLOYMENT_FILE, 'Fetches nitro deployment file from a .setAction(async (taskArgs) => { console.log(`Attempting to fetch deployment file from testnode...`) - const command = - 'docker exec $(docker ps -qf "name=sequencer") cat /deployment/localNetwork.json > localNetwork.json' + const command = `docker exec $(docker ps -qf "name=sequencer") cat /workspace/localNetwork.json > ${taskArgs.deploymentFile}` const stdOut = execSync(command) console.log(stdOut.toString()) From 5daa3c1ba0ed844c9e88a73cf8066bc007f33f6a Mon Sep 17 00:00:00 2001 From: Ariel Barmat <140773+abarmat@users.noreply.github.com> Date: Fri, 4 Nov 2022 13:03:23 -0300 Subject: [PATCH 091/100] chore: add deployed addresses for L2 public testnet --- addresses.json | 238 +++++++++++++++++++++++++++++++ config/graph.arbitrum-goerli.yml | 14 +- 2 files changed, 245 insertions(+), 7 deletions(-) diff --git a/addresses.json b/addresses.json index a89a55f08..24307ea8d 100644 --- a/addresses.json +++ b/addresses.json @@ -496,6 +496,34 @@ }, "IEthereumDIDRegistry": { "address": "0xdCa7EF03e98e0DC2B855bE647C39ABe984fcF21B" + }, + "BridgeEscrow": { + "address": "0x8e4145358af77516B886D865e2EcacC0Fd832B75", + "initArgs": ["0x48eD7AfbaB432d1Fc6Ea84EEC70E745d9DAcaF3B"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x190ea3c8f731a77a8fd1cbce860f9561f233adeafe559b33201b7d21ccd298cf", + "proxy": true, + "implementation": { + "address": "0xDD569E05D54fBF5d02fE4a26aC03Ea00317A0A2e", + "creationCodeHash": "0x6a1fc897c0130a1c99221cde1938d247de13a0861111ac47ad81c691f323df1a", + "runtimeCodeHash": "0xc8e31a4ebea0c3e43ceece974071ba0b6db2bed6725190795e07a2d369d2a8ab", + "txHash": "0x369038dcc8d8e70d40782dd761a82cc453c7a4f1939284c724a5a72119e3e566" + } + }, + "L1GraphTokenGateway": { + "address": "0xc82fF7b51c3e593D709BA3dE1b3a0d233D1DEca1", + "initArgs": ["0x48eD7AfbaB432d1Fc6Ea84EEC70E745d9DAcaF3B"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x4a06731591df5c5f77c11bf8df7851234873eb6727fbbc93f5595a223f7cf3fc", + "proxy": true, + "implementation": { + "address": "0x06A7A68d0D0D496693508ad3f50A8EA962333B7D", + "creationCodeHash": "0x9dac8130793923c7f35f3943b755b7a88e2de9a25d0ae5c0b8cb020b6479151a", + "runtimeCodeHash": "0xcd798129b77d26c0b29369138d2d8dd413fcf6cb9b3838c5f95f50d9839a388a", + "txHash": "0xa4d75169094cd8601ec507234695d83042e888ec2ab49b0ce150d7aae908d895" + } } }, "1337": { @@ -692,5 +720,215 @@ "runtimeCodeHash": "0x07012b5692ec6cbeb7a6986e061fc5026a2f76545b07bfd9182985de002fa281", "txHash": "0xe3d870434e38ee37142a86e0fc54063df59c02c3b70135f070c3a1025c5e8246" } + }, + "421613": { + "GraphProxyAdmin": { + "address": "0x4037466bb242f51575d32E8B1be693b3E5Cd1386", + "creationCodeHash": "0x68b304ac6bce7380d5e0f6b14a122f628bffebcc75f8205cb60f0baf578b79c3", + "runtimeCodeHash": "0x8d9ba87a745cf82ab407ebabe6c1490197084d320efb6c246d94bcc80e804417", + "txHash": "0x9c4d5f8c0ab5a5bc36b0a063ab1ff04372ce7d917c0b200b94544b5da4f0230d" + }, + "BancorFormula": { + "address": "0x71319060b9fdeD6174b6368bE04F9A1b7c9aCe48", + "creationCodeHash": "0x7ae36017eddb326ddd79c7363781366121361d42fdb201cf57c57424eede46f4", + "runtimeCodeHash": "0xed6701e196ad93718e28c2a2a44d110d9e9906085bcfe4faf4f6604b08f0116c", + "txHash": "0x7fe8cabb7a4fe56311591aa8d68d6c82cb0d5c232fc5aaf28bed4d1ece0e42e5" + }, + "Controller": { + "address": "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "creationCodeHash": "0x798f913fbaa1b2547c917e3dc31679089ab27cba442c511c159803acdba28c15", + "runtimeCodeHash": "0x00ae0824f79c4e48d2d23a8d4e6d075f04f44f3ea30a4f4305c345bb98117c62", + "txHash": "0x6213da3e6367ef47cd6e1fe23e4d83296f16153a64236a5c91f865f2ec84c089" + }, + "EpochManager": { + "address": "0x8ECedc7631f4616D7f4074f9fC9D0368674794BE", + "initArgs": ["0x7f734E995010Aa8d28b912703093d532C37b6EAb", "554"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x62b0d6b8556be9443397ad1f6030fdc47b1a4a3ebcc63f34cdf4091420aec84b", + "proxy": true, + "implementation": { + "address": "0xAaB195Ed1B445A2A0E357494d9036bC746227AE2", + "creationCodeHash": "0x83bc0b08dbe1a9259666ec209f06223863f7bb9cfbf917a2d4b795c771a727fe", + "runtimeCodeHash": "0xed60261c6dc84ebc16830c36f3ee370a92802601d5a2fe1c3c19f5120dcbc2eb", + "txHash": "0xd4f8780490f63432580e3dd5b2b4d9b39e904e8b4ac5cfd23540658cbafe449d" + } + }, + "L2GraphToken": { + "address": "0x18C924BD5E8b83b47EFaDD632b7178E2Fd36073D", + "initArgs": ["0xEfc519BEd6a43a14f1BBBbA9e796C4931f7A5540"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x7ec14b524141af953959b537c1acbea9b49b12ee906563a6172123b09ab3d1f6", + "proxy": true, + "implementation": { + "address": "0x5dcAcF820D7b9F0640e8a23a5a857675A774C34a", + "creationCodeHash": "0x6c4146427aafa7375a569154be95c8c931bf83aab0315706dd78bdf79c889e4c", + "runtimeCodeHash": "0x004371d1d80011906953dcba17c648503fc94b94e1e0365c8d8c706ff91f93e9", + "txHash": "0xb748498a2ebc90e20dc8da981be832f4e00f08ea9ff289880738705e45d6aeca" + } + }, + "GraphCurationToken": { + "address": "0x2B757ad83e4ed51ecaE8D4dC9AdE8E3Fa29F7BdC", + "creationCodeHash": "0x1ee42ee271cefe20c33c0de904501e618ac4b56debca67c634d0564cecea9ff2", + "runtimeCodeHash": "0x340e8f378c0117b300f3ec255bc5c3a273f9ab5bd2940fa8eb3b5065b21f86dc", + "txHash": "0x1aa753cd01fa4505c71f6866dae35faee723d181141ed91b6e5cf3082ee90f9b" + }, + "ServiceRegistry": { + "address": "0x07ECDD4278D83Cd2425cA86256634f666b659e53", + "initArgs": ["0x7f734E995010Aa8d28b912703093d532C37b6EAb"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x8a13420fdc91139297ab1497fbf5b443c156bbc7b9d2a1ac97fb9f23abde2723", + "proxy": true, + "implementation": { + "address": "0xd18D4B4e84eA4713E04060c93bD079A974BE6C4a", + "creationCodeHash": "0x50808e8cce93cf78a23c9e6dd7984356bd2bd93be30b358982909140dd61f6ff", + "runtimeCodeHash": "0xaef79c87f7e80107c0dc568cf1f8950459b5174ee3aa565ec487556a655e71db", + "txHash": "0x2d6043d89a5f5c4f3d0df0f50264ab7efebc898be0b5d358a00715ba9f657a89" + } + }, + "Curation": { + "address": "0x7080AAcC4ADF4b1E72615D6eb24CDdE40a04f6Ca", + "initArgs": [ + "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "0x71319060b9fdeD6174b6368bE04F9A1b7c9aCe48", + "0x2B757ad83e4ed51ecaE8D4dC9AdE8E3Fa29F7BdC", + "1000000", + "10000", + "1000000000000000000" + ], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x2e5744fa4eca56cf6902e27fcc0509487f39bdb0d29b9eb0181db986235289a0", + "proxy": true, + "implementation": { + "address": "0xDA6c9d39b49c3d41CaC2030c6B75b40Efea09817", + "creationCodeHash": "0xa5fa77df71a72c5aadba812345978c291c5fa1a3a23129b6eba3a38ac85d8b5d", + "runtimeCodeHash": "0x1d265e9f658778b48a0247cfef79bfc9304d1faa1f1e085f2fea85629f68e2d5", + "txHash": "0x815eda87a2599d6f2c7458c7b164e7307d05018f0dd72073a50971d424313377" + } + }, + "SubgraphNFTDescriptor": { + "address": "0x30545f313bD2eb0F85E4f808Ae4D2C016efE78b2", + "creationCodeHash": "0xf16e8ff11d852eea165195ac9e0dfa00f98e48f6ce3c77c469c7df9bf195b651", + "runtimeCodeHash": "0x39583196f2bcb85789b6e64692d8c0aa56f001c46f0ca3d371abbba2c695860f", + "txHash": "0x060839a09e89cbd47adbb8c04cc76b21a00785600a4e8b44939dd928391777e1" + }, + "SubgraphNFT": { + "address": "0x5571D8FE183AD1367dF21eE9968690f0Eabdc593", + "constructorArgs": ["0xEfc519BEd6a43a14f1BBBbA9e796C4931f7A5540"], + "creationCodeHash": "0xc1e58864302084de282dffe54c160e20dd96c6cfff45e00e6ebfc15e04136982", + "runtimeCodeHash": "0x7216e736a8a8754e88688fbf5c0c7e9caf35c55ecc3a0c5a597b951c56cf7458", + "txHash": "0xc11917ffedda6867648fa2cb62cca1df3c0ed485a0a0885284e93a2c5d33455c" + }, + "GNS": { + "address": "0x6bf9104e054537301cC23A1023Ca30A6Df79eB21", + "initArgs": [ + "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "0x71319060b9fdeD6174b6368bE04F9A1b7c9aCe48", + "0x5571D8FE183AD1367dF21eE9968690f0Eabdc593" + ], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x3c2509730e06249d970818319bb507185d4fdea13d5600cef87928a718950c19", + "proxy": true, + "implementation": { + "address": "0x7eCb82A9Cf9B370d3fC2Ef66E38F38EDFAeaa125", + "creationCodeHash": "0xb0be24e926bb24420bb5a8d3f7bd0b70a545fdddbf8cb177a42478adf4435aae", + "runtimeCodeHash": "0x4cb62b9def5b691e43ed06808b18efe682fcefb7739909be0d6c87f1eda724cd", + "txHash": "0xf1d41fc99ed716a0c890ea62e13ee108ddcb4ecfc74efb715a4ef05605ce449b" + } + }, + "Staking": { + "address": "0xcd549d0C43d915aEB21d3a331dEaB9B7aF186D26", + "initArgs": [ + "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "100000000000000000000000", + "6646", + "10000", + "100000", + "2", + "4", + "12", + "16", + "77", + "100" + ], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0xc98ebdd0a80b97ef8f6305903ef6496a7781db76a5b1b3c3c3b2b10dbd9a7af5", + "proxy": true, + "implementation": { + "address": "0x8E56ee65Ed613f2AecA8898D19497D288601bdeb", + "creationCodeHash": "0x75b63ef816627315c635cae7f95917764e2cb797496280cdeaa9b3230bf7f7bc", + "runtimeCodeHash": "0x461ccf91c7c6188c94c6df430b6954dfd9c5cc2a79a5e4db21422e11b663d319", + "txHash": "0xb9ce53dafab3dcaad25b24d9f998888225103265bd2d84cb1545b4e06e96e3b6", + "libraries": { + "LibCobbDouglas": "0x86f0f6cd9a38A851E3AB8f110be06B77C199eC1F" + } + } + }, + "RewardsManager": { + "address": "0x5F06ABd1CfAcF7AE99530D7Fed60E085f0B15e8D", + "initArgs": ["0x7f734E995010Aa8d28b912703093d532C37b6EAb"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0xd4cfa95475e9e867fb24babd6a00a5b6b01d2267533e2412986aa1ff94d51c02", + "proxy": true, + "implementation": { + "address": "0x80b54Ba64d8a207785969d9ae0dA984EfE8D10dF", + "creationCodeHash": "0x98aaabec491a17401ca37209db0613c91285de061e859574526f841a4dd60c4a", + "runtimeCodeHash": "0x2795a83531898957014373bd4595f1f9a381ecfaf787bdfc64380563af06f06a", + "txHash": "0xb4bc7ae32ec98394c448f8773bdd3049ab83e236acb6823a7a322d88ecfbfd99" + } + }, + "DisputeManager": { + "address": "0x16DEF7E0108A5467A106dbD7537f8591f470342E", + "initArgs": [ + "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "0xF89688d5d44d73cc4dE880857A3940487076e5A4", + "10000000000000000000000", + "500000", + "25000", + "25000" + ], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x70188c9243c2226ac793ac8c0a9eecd76c9b44e53f7f6f97fa177a34808421a0", + "proxy": true, + "implementation": { + "address": "0x39aEdA1d6ea3B62b76C7c439beBfFCb5369a175C", + "creationCodeHash": "0x2e77ad7a1627b6e04bece0fe18b3ab543ef4a2d6914f2e5e640b2c8175aca3a8", + "runtimeCodeHash": "0x0186afe711eff4ceea28620d091e3c6034fd15be05894119c74a38b020e3a554", + "txHash": "0x4efbd28e55866c0292309964f47bd805922ad417e5980e14e055ad693024582d" + } + }, + "AllocationExchange": { + "address": "0x61809D6Cde07f27D2fcDCb67a42d0Af1988Be5e8", + "constructorArgs": [ + "0x18C924BD5E8b83b47EFaDD632b7178E2Fd36073D", + "0xcd549d0C43d915aEB21d3a331dEaB9B7aF186D26", + "0x05F359b1319f1Ca9b799CB6386F31421c2c49dBA", + "0xD06f366678AE139a94b2AaC2913608De568F1D03" + ], + "creationCodeHash": "0x96c5b59557c161d80f1617775a7b9537a89b0ecf2258598b3a37724be91ae80a", + "runtimeCodeHash": "0xed3d9cce65ddfa8a237d4d7d294ffdb13a082e0adcda3bbd313029cfae1365f3", + "txHash": "0x1df63329a21dca69d20e03c076dd89c350970d35319eeefab028cebbc78d29dc" + }, + "L2GraphTokenGateway": { + "address": "0xef2757855d2802bA53733901F90C91645973f743", + "initArgs": ["0x7f734E995010Aa8d28b912703093d532C37b6EAb"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x47bde4e3ad0bc077897a3de65058c4b7dd710aa447ec25942f716321cbdc590d", + "proxy": true, + "implementation": { + "address": "0xc68cd0d2ca533232Fd86D6e48b907338B2E0a74A", + "creationCodeHash": "0xbd52455bd8b14bfc27af623388fe2f9e06ddd4c4be3fc06c51558a912de91770", + "runtimeCodeHash": "0x29e47f693053f978d6b2ac0a327319591bf5b5e8a6e6c0744b8afcc0250bf667", + "txHash": "0xf68a5e1e516ee9a646f19bbe4d58336fdfcf5fc859f84cdac5e68b00bcd3a09a" + } + } } } diff --git a/config/graph.arbitrum-goerli.yml b/config/graph.arbitrum-goerli.yml index fa1a66ad3..5b41e4382 100644 --- a/config/graph.arbitrum-goerli.yml +++ b/config/graph.arbitrum-goerli.yml @@ -1,10 +1,10 @@ general: - arbitrator: &arbitrator "0x113DC95e796836b8F0Fa71eE7fB42f221740c3B0" # Arbitration Council (TODO: update) - governor: &governor "0x3e43EF77fAAd296F65eF172E8eF06F8231c9DeAd" # Graph Council (TODO: update) - authority: &authority "0x79fd74da4c906509862c8fe93e87a9602e370bc4" # Authority that signs payment vouchers (TODO: update) - availabilityOracle: &availabilityOracle "0x5d3B6F98F1cCdF873Df0173CDE7335874a396c4d" # Subgraph Availability Oracle (TODO: update) - pauseGuardian: &pauseGuardian "0x8290362Aba20D17c51995085369E001Bad99B21c" # Protocol pause guardian (TODO: update) - allocationExchangeOwner: &allocationExchangeOwner "0x74Db79268e63302d3FC69FB5a7627F7454a41732" # Allocation Exchange owner (TODO: update) + arbitrator: &arbitrator "0xF89688d5d44d73cc4dE880857A3940487076e5A4" # Arbitration Council (TODO: update) + governor: &governor "0x5CeeeE16F30357d49c50bcd7F520ca6527cf388a" # Graph Council (TODO: update) + authority: &authority "0xD06f366678AE139a94b2AaC2913608De568F1D03" # Authority that signs payment vouchers (TODO: update) + availabilityOracle: &availabilityOracle "0xA99A56fA38a6B9553853c84E11458AeCcdad509B" # Subgraph Availability Oracle (TODO: update) + pauseGuardian: &pauseGuardian "0x4B6C90B9fE29dfa521188B6547989C23d613b79B" # Protocol pause guardian (TODO: update) + allocationExchangeOwner: &allocationExchangeOwner "0x05F359b1319f1Ca9b799CB6386F31421c2c49dBA" # Allocation Exchange owner (TODO: update) contracts: Controller: @@ -66,7 +66,7 @@ contracts: controller: "${{Controller.address}}" bondingCurve: "${{BancorFormula.address}}" curationTokenMaster: "${{GraphCurationToken.address}}" - reserveRatio: 500000 # in parts per million + reserveRatio: 1000000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei calls: From fb40819e23759686e6f1a788d9266a0baae11788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 7 Nov 2022 11:36:09 -0300 Subject: [PATCH 092/100] fix: minor tweaks to e2e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- TESTING.md | 8 ++++---- tasks/e2e/e2e.ts | 14 +++++++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/TESTING.md b/TESTING.md index b27ce868b..08b89c1a5 100644 --- a/TESTING.md +++ b/TESTING.md @@ -38,7 +38,7 @@ There are several types of e2e tests which can be run separately: It can be useful to run E2E tests against a fresh protocol deployment on L1, this can be done with the following: ```bash -yarn test:e2e +L1_NETWORK=localhost yarn test:e2e ``` The command will: @@ -72,13 +72,13 @@ To run tests against a live testnet or even mainnet run: ```bash # All e2e tests -npx hardhat e2e --network --graph-config config/graph..yml +ARBITRUM_ADDRESS_BOOK= npx hardhat e2e --network --l1-graph-config config/graph..yml --l2-graph-config config/graph..yml # Only deployment config tests -npx hardhat e2e:config --network --graph-config config/graph..yml +ARBITRUM_ADDRESS_BOOK= npx hardhat e2e:config --network --l1-graph-config config/graph..yml --l2-graph-config config/graph..yml # Only deployment init tests -npx hardhat e2e:init --network --graph-config config/graph..yml +ARBITRUM_ADDRESS_BOOK= npx hardhat e2e:init --network --l1-graph-config config/graph..yml --l2-graph-config config/graph..yml # Only a specific scenario npx hardhat e2e:scenario --network --graph-config config/graph..yml diff --git a/tasks/e2e/e2e.ts b/tasks/e2e/e2e.ts index 9c7063b34..aa712380c 100644 --- a/tasks/e2e/e2e.ts +++ b/tasks/e2e/e2e.ts @@ -34,7 +34,6 @@ const setGraphConfig = async (args: TaskArguments, hre: HardhatRuntimeEnvironmen } task('e2e', 'Run all e2e tests') - .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) @@ -50,6 +49,9 @@ task('e2e', 'Run all e2e tests') testFiles = testFiles.filter((file) => !['l1', 'l2'].includes(file.split('/')[3])) } + // Disable secure accounts, we don't need them for this task + hre.config.graph.disableSecureAccounts = true + setGraphConfig(args, hre) await hre.run(TASK_TEST, { testFiles: testFiles, @@ -57,13 +59,16 @@ task('e2e', 'Run all e2e tests') }) task('e2e:config', 'Run deployment configuration e2e tests') - .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) .setAction(async (args, hre: HardhatRuntimeEnvironment) => { const files = new glob.GlobSync(CONFIG_TESTS).found + + // Disable secure accounts, we don't need them for this task + hre.config.graph.disableSecureAccounts = true + setGraphConfig(args, hre) await hre.run(TASK_TEST, { testFiles: files, @@ -71,13 +76,16 @@ task('e2e:config', 'Run deployment configuration e2e tests') }) task('e2e:init', 'Run deployment initialization e2e tests') - .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) .setAction(async (args, hre: HardhatRuntimeEnvironment) => { const files = new glob.GlobSync(INIT_TESTS).found + + // Disable secure accounts, we don't need them for this task + hre.config.graph.disableSecureAccounts = true + setGraphConfig(args, hre) await hre.run(TASK_TEST, { testFiles: files, From 1dd6f06449bccd07bbc111b29b0cda831b39e2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Migone?= Date: Mon, 7 Nov 2022 11:56:11 -0300 Subject: [PATCH 093/100] chore: bump npm package version to v2.0.0-testnet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomás Migone --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f28fd4e9a..053429c5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/contracts", - "version": "1.16.0", + "version": "2.0.0-testnet", "description": "Contracts for the Graph Protocol", "directories": { "test": "test" From d8fed24744f1c8a565aad3789dca6b1a8fa7a8db Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Mon, 14 Nov 2022 14:30:13 +0100 Subject: [PATCH 094/100] fix(L1GraphTokenGateway): add a counterpartGateway getter for Router compatibility --- contracts/gateway/L1GraphTokenGateway.sol | 10 ++++++++++ test/gateway/l1GraphTokenGateway.test.ts | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol index 3c81080ae..094d99611 100644 --- a/contracts/gateway/L1GraphTokenGateway.sol +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -298,6 +298,16 @@ contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMess return l2GRT; } + /** + * @notice Get the address of the L2GraphTokenGateway + * @dev This is added for compatibility with the Arbitrum Router's + * gateway registration process. + * @return Address of the L2 gateway connected to this gateway + */ + function counterpartGateway() external view returns (address) { + return l2Counterpart; + } + /** * @notice Creates calldata required to create a retryable ticket * @dev encodes the target function with its params which diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts index b619e7a42..76b00ee6b 100644 --- a/test/gateway/l1GraphTokenGateway.test.ts +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -178,7 +178,7 @@ describe('L1GraphTokenGateway', () => { .setL2CounterpartAddress(mockL2Gateway.address) await expect(tx).revertedWith('Only Controller governor') }) - it('sets L2Counterpart', async function () { + it('sets l2Counterpart which can be queried with counterpartGateway()', async function () { const tx = l1GraphTokenGateway .connect(governor.signer) .setL2CounterpartAddress(mockL2Gateway.address) @@ -186,6 +186,7 @@ describe('L1GraphTokenGateway', () => { .emit(l1GraphTokenGateway, 'L2CounterpartAddressSet') .withArgs(mockL2Gateway.address) expect(await l1GraphTokenGateway.l2Counterpart()).eq(mockL2Gateway.address) + expect(await l1GraphTokenGateway.counterpartGateway()).eq(mockL2Gateway.address) }) }) describe('setEscrowAddress', function () { From dcc2cec73a4640428b0aa04d2ee8fb2620bb10ea Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 23 Nov 2022 14:59:14 -0300 Subject: [PATCH 095/100] fix(e2e): no need to explicitly disable secure accounts --- scripts/e2e | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/e2e b/scripts/e2e index 7f2bcace6..70aae7e63 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -127,9 +127,9 @@ function test_e2e () { local SKIP_BRIDGE_TESTS=$5 if [[ -z "$SKIP_BRIDGE_TESTS" ]]; then - npx hardhat e2e --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts + npx hardhat e2e --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" else - npx hardhat e2e --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --skip-bridge --disable-secure-accounts + npx hardhat e2e --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --skip-bridge fi } From 12e54e5a14fd5c5ca5e4dfabfcd944aaa8480cd3 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 23 Nov 2022 15:04:47 -0300 Subject: [PATCH 096/100] chore: bump @arbitrum/sdk to 3.0.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 053429c5b..2290f2db5 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "ethers": "^5.6.0" }, "devDependencies": { - "@arbitrum/sdk": "3.0.0-rc.1", + "@arbitrum/sdk": "^3.0.0", "@commitlint/cli": "^13.2.1", "@commitlint/config-conventional": "^13.2.0", "@defi-wonderland/smock": "^2.0.7", diff --git a/yarn.lock b/yarn.lock index b395c3589..42084d9cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@arbitrum/sdk@3.0.0-rc.1": - version "3.0.0-rc.1" - resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.0.0-rc.1.tgz#5335bd833ded89bfe95328a44ccd2ba743f82239" - integrity sha512-8t4fgvQR/83whezeb1WKBeWtBnfmZ0vaDE1w96cKEt8bdrmTQ8yQF3Vpeq/Y21/SrOdDNj5CQWor1Y6wB+FbyQ== +"@arbitrum/sdk@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.0.0.tgz#a5dc48a00cb8c6e230a2c696c0e880a7f80c637d" + integrity sha512-Mws5WAxxirp3vk8JH3vyQ5H6q1NNUIAAGEd9oEnQYDMyTBHLKU293GA3s9w4w6ZfIq/RZq8YCexhy4D1R+mQng== dependencies: "@ethersproject/address" "^5.0.8" "@ethersproject/bignumber" "^5.1.1" From 43e719bcbeb825b53163ba980ddf1d0ab1de61c6 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 23 Nov 2022 15:05:12 -0300 Subject: [PATCH 097/100] chore: bump package version to 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2290f2db5..ef123981e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/contracts", - "version": "2.0.0-testnet", + "version": "2.0.0", "description": "Contracts for the Graph Protocol", "directories": { "test": "test" From 4e2872b4a285058956142d0309d4e95f894e0621 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 23 Nov 2022 15:50:38 -0300 Subject: [PATCH 098/100] fix: set flat bonding curve in all L2 configs --- config/graph.arbitrum-localhost.yml | 2 +- config/graph.arbitrum-one.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/graph.arbitrum-localhost.yml b/config/graph.arbitrum-localhost.yml index f424a0e44..a17755fbc 100644 --- a/config/graph.arbitrum-localhost.yml +++ b/config/graph.arbitrum-localhost.yml @@ -66,7 +66,7 @@ contracts: controller: "${{Controller.address}}" bondingCurve: "${{BancorFormula.address}}" curationTokenMaster: "${{GraphCurationToken.address}}" - reserveRatio: 500000 # in parts per million + reserveRatio: 1000000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei calls: diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml index e68d67521..0fff74390 100644 --- a/config/graph.arbitrum-one.yml +++ b/config/graph.arbitrum-one.yml @@ -66,7 +66,7 @@ contracts: controller: "${{Controller.address}}" bondingCurve: "${{BancorFormula.address}}" curationTokenMaster: "${{GraphCurationToken.address}}" - reserveRatio: 500000 # in parts per million + reserveRatio: 1000000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei calls: From 51c0441c75e357f466202a503b3ad98c1030e08a Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 23 Nov 2022 16:10:30 -0300 Subject: [PATCH 099/100] test(e2e): reenable finalizeInboundTransfer revert check --- .../config/l1/l1GraphTokenGateway.test.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts index e23d07428..a957f6882 100644 --- a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -106,17 +106,16 @@ describe('[L1] L1GraphTokenGateway configuration', function () { await expect(tx).revertedWith('Only Controller governor') }) - // TODO: why is this not working - // it('finalizeInboundTransfer should revert', async function () { - // const tx = L1GraphTokenGateway.connect(unauthorized).finalizeInboundTransfer( - // unauthorized.address, - // unauthorized.address, - // unauthorized.address, - // '100', - // '0x00', - // ) - - // await expect(tx).revertedWith('NOT_FROM_BRIDGE') - // }) + it('finalizeInboundTransfer should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).finalizeInboundTransfer( + unauthorized.address, + unauthorized.address, + unauthorized.address, + '100', + '0x00', + ) + + await expect(tx).revertedWith('NOT_FROM_BRIDGE') + }) }) }) From aa851044767fad114f71d737b077d7b964670619 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Wed, 23 Nov 2022 19:33:27 -0300 Subject: [PATCH 100/100] fix: use correct governance addresses on Arbitrum One --- config/graph.arbitrum-one.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml index 0fff74390..49c7b0b13 100644 --- a/config/graph.arbitrum-one.yml +++ b/config/graph.arbitrum-one.yml @@ -1,10 +1,10 @@ general: arbitrator: &arbitrator "0x113DC95e796836b8F0Fa71eE7fB42f221740c3B0" # Arbitration Council - governor: &governor "0x3e43EF77fAAd296F65eF172E8eF06F8231c9DeAd" # Graph Council - authority: &authority "0x79fd74da4c906509862c8fe93e87a9602e370bc4" # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle "0x5d3B6F98F1cCdF873Df0173CDE7335874a396c4d" # Subgraph Availability Oracle (TODO: update) - pauseGuardian: &pauseGuardian "0x8290362Aba20D17c51995085369E001Bad99B21c" # Protocol pause guardian (TODO: update) - allocationExchangeOwner: &allocationExchangeOwner "0x74Db79268e63302d3FC69FB5a7627F7454a41732" # Allocation Exchange owner (TODO: update) + governor: &governor "0x8C6de8F8D562f3382417340A6994601eE08D3809" # Graph Council + authority: &authority "0x79f2212de27912bCb25a452fC102C85c142E3eE3" # Authority that signs payment vouchers + availabilityOracle: &availabilityOracle "0xbCAEE36Ce38Ec534c7078db1f90118E72173645B" # Subgraph Availability Oracle + pauseGuardian: &pauseGuardian "0xB0aD33a21b98bCA1761729A105e2E34e27153aAE" # Protocol pause guardian + allocationExchangeOwner: &allocationExchangeOwner "0x270Ea4ea9e8A699f8fE54515E3Bb2c418952623b" # Allocation Exchange owner contracts: Controller: