Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,3 @@
path = l2geth/tests/testdata
url = https://github.com/ethereum/tests

[submodule "bedrock/erigon"]
path = bedrock/erigon
url = https://github.com/bobanetwork/erigon

[submodule "bedrock/reference-optimistic-geth"]
path = bedrock/reference-optimistic-geth
url = https://github.com/bobanetwork/reference-optimistic-geth
1 change: 0 additions & 1 deletion bedrock/erigon
Submodule erigon deleted from 587af3
1 change: 0 additions & 1 deletion bedrock/reference-optimistic-geth
Submodule reference-optimistic-geth deleted from b888ba
28 changes: 28 additions & 0 deletions integration-tests/contracts/TestFailingMintL1StandardERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.7.5;

import "@boba/contracts/contracts/standards/L1StandardERC721.sol";

/**
* A Failing mint L1ERC721 contract
*/
contract TestFailingMintL1StandardERC721 is L1StandardERC721 {
/**
* @param _l1Bridge Address of the L1 standard bridge.
* @param _l2Contract Address of the corresponding L2 NFT contract.
* @param _name ERC721 name.
* @param _symbol ERC721 symbol.
*/
constructor(
address _l1Bridge,
address _l2Contract,
string memory _name,
string memory _symbol,
string memory _baseTokenURI
)
L1StandardERC721(_l1Bridge, _l2Contract, _name, _symbol, _baseTokenURI) {}

function mint(address _to, uint256 _tokenId, bytes memory _data) public virtual override onlyL1Bridge {
revert("mint failing");
}
}
28 changes: 28 additions & 0 deletions integration-tests/contracts/TestFailingMintL2StandardERC721.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.7.5;

import "@boba/contracts/contracts/standards/L2StandardERC721.sol";

/**
* A Failing mint L2ERC721 contract
*/
contract TestFailingMintL2StandardERC721 is L2StandardERC721 {
/**
* @param _l2Bridge Address of the L2 standard bridge.
* @param _l1Contract Address of the corresponding L1 NFT contract.
* @param _name ERC721 name.
* @param _symbol ERC721 symbol.
*/
constructor(
address _l2Bridge,
address _l1Contract,
string memory _name,
string memory _symbol,
string memory _baseTokenURI
)
L2StandardERC721(_l2Bridge, _l1Contract, _name, _symbol, _baseTokenURI) {}

function mint(address _to, uint256 _tokenId, bytes memory _data) public virtual override onlyL2Bridge {
revert("mint failing");
}
}
169 changes: 168 additions & 1 deletion integration-tests/test/nft_bridge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import L2ERC721ExtraDataJson from '../artifacts/contracts/TestExtraDataL2Standar
import L2BillingContractJson from '@boba/contracts/artifacts/contracts/L2BillingContract.sol/L2BillingContract.json'
import L2GovernanceERC20Json from '@boba/contracts/artifacts/contracts/standards/L2GovernanceERC20.sol/L2GovernanceERC20.json'

import L1ERC721FailingMintJson from '../artifacts/contracts/TestFailingMintL1StandardERC721.sol/TestFailingMintL1StandardERC721.json'
import L2ERC721FailingMintJson from '../artifacts/contracts/TestFailingMintL2StandardERC721.sol/TestFailingMintL2StandardERC721.json'

import { OptimismEnv } from './shared/env'
import { ethers } from 'hardhat'

Expand Down Expand Up @@ -2076,7 +2079,7 @@ describe('NFT Bridge Test', async () => {
})

it('{tag:boba} should deposit NFT back without sending data for non-native token', async () => {
const approveTX = await L1ERC721.connect(env.l2Wallet).approve(
const approveTX = await L1ERC721.connect(env.l1Wallet).approve(
L1Bridge.address,
DUMMY_TOKEN_ID
)
Expand Down Expand Up @@ -2275,6 +2278,170 @@ describe('NFT Bridge Test', async () => {
})
})

describe('L1 native NFT - failing mint on L2', async () => {
before(async () => {
Factory__L1ERC721 = new ContractFactory(
ERC721Json.abi,
ERC721Json.bytecode,
env.l1Wallet
)

Factory__L2ERC721 = new ContractFactory(
L2ERC721FailingMintJson.abi,
L2ERC721FailingMintJson.bytecode,
env.l2Wallet
)

L1ERC721 = await Factory__L1ERC721.deploy('Test', 'TST')

await L1ERC721.deployTransaction.wait()

L2ERC721 = await Factory__L2ERC721.deploy(
L2Bridge.address,
L1ERC721.address,
'Test',
'TST',
'' // base-uri
)

await L2ERC721.deployTransaction.wait()

// register NFT
const registerL1BridgeTx = await L1Bridge.registerNFTPair(
L1ERC721.address,
L2ERC721.address,
'L1'
)
await registerL1BridgeTx.wait()

const registerL2BridgeTx = await L2Bridge.registerNFTPair(
L1ERC721.address,
L2ERC721.address,
'L1'
)
await registerL2BridgeTx.wait()
})

it('{tag:boba} should try deposit NFT to L2', async () => {
// mint nft
const mintTx = await L1ERC721.mint(env.l1Wallet.address, DUMMY_TOKEN_ID)
await mintTx.wait()

const approveTx = await L1ERC721.approve(L1Bridge.address, DUMMY_TOKEN_ID)
await approveTx.wait()

const depositTx = await env.waitForXDomainTransaction(
await L1Bridge.depositNFT(L1ERC721.address, DUMMY_TOKEN_ID, 9999999)
)

// submit a random l2 tx, so the relayer is unstuck for the tests
await env.l2Wallet_2.sendTransaction({
to: env.l2Wallet_2.address,
value: utils.parseEther('0.01'),
gasLimit: 1000000,
})

const backTx = await env.messenger.l2Provider.getTransaction(
depositTx.remoteReceipt.transactionHash
)
await env.waitForXDomainTransaction(backTx)

// check event DepositFailed is emittted
const returnedlogIndex = await getFilteredLogIndex(
depositTx.remoteReceipt,
L2NFTBridge.abi,
L2Bridge.address,
'DepositFailed'
)
const ifaceL2NFTBridge = new ethers.utils.Interface(L2NFTBridge.abi)
const log = ifaceL2NFTBridge.parseLog(
depositTx.remoteReceipt.logs[returnedlogIndex]
)
expect(log.args._tokenId).to.deep.eq(DUMMY_TOKEN_ID)

const ownerL1 = await L1ERC721.ownerOf(DUMMY_TOKEN_ID)
await expect(L2ERC721.ownerOf(DUMMY_TOKEN_ID)).to.be.revertedWith(
'ERC721: owner query for nonexistent token'
)

expect(ownerL1).to.deep.eq(env.l1Wallet.address)
}).timeout(100000)
})

describe('L2 native NFT - failing mint on L1', async () => {
before(async () => {
Factory__L2ERC721 = new ContractFactory(
ERC721Json.abi,
ERC721Json.bytecode,
env.l2Wallet
)

Factory__L1ERC721 = new ContractFactory(
L1ERC721FailingMintJson.abi,
L1ERC721FailingMintJson.bytecode,
env.l1Wallet
)

// deploy a L2 native NFT token each time if existing contracts are used for tests
L2ERC721 = await Factory__L2ERC721.deploy('Test', 'TST')

await L2ERC721.deployTransaction.wait()

L1ERC721 = await Factory__L1ERC721.deploy(
L1Bridge.address,
L2ERC721.address,
'Test',
'TST',
'' // base-uri
)

await L1ERC721.deployTransaction.wait()

// register NFT
const registerL1BridgeTx = await L1Bridge.registerNFTPair(
L1ERC721.address,
L2ERC721.address,
'L2'
)
await registerL1BridgeTx.wait()

const registerL2BridgeTx = await L2Bridge.registerNFTPair(
L1ERC721.address,
L2ERC721.address,
'L2'
)
await registerL2BridgeTx.wait()
})

it('{tag:boba} should try exit NFT from L2', async () => {
// mint nft
const mintTx = await L2ERC721.mint(env.l2Wallet.address, DUMMY_TOKEN_ID)
await mintTx.wait()

const approveTx = await L2ERC721.approve(L2Bridge.address, DUMMY_TOKEN_ID)
await approveTx.wait()

// Approve BOBA
const exitFee = await BOBABillingContract.exitFee()
const approveBOBATX = await L2BOBAToken.connect(env.l2Wallet).approve(
L2Bridge.address,
exitFee
)
await approveBOBATX.wait()

await env.waitForRevertXDomainTransactionL1(
L2Bridge.withdraw(L2ERC721.address, DUMMY_TOKEN_ID, 9999999)
)

await expect(L1ERC721.ownerOf(DUMMY_TOKEN_ID)).to.be.revertedWith(
'ERC721: owner query for nonexistent token'
)
const ownerL2 = await L2ERC721.ownerOf(DUMMY_TOKEN_ID)

expect(ownerL2).to.deep.eq(env.l2Wallet.address)
}).timeout(100000)
})

describe('Bridges pause tests', async () => {
before(async () => {
Factory__L1ERC721 = new ContractFactory(
Expand Down
24 changes: 15 additions & 9 deletions integration-tests/test/shared/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,15 +471,21 @@ export class OptimismEnv {
}
}

// async waitForRevertXDomainTransaction(
// tx: Promise<TransactionResponse> | TransactionResponse
// ) {
// const { remoteReceipt } = await this.waitForXDomainTransaction(tx)
// const [xDomainMsgHash] = await this.messenger.getMessageHashesFromL2Tx(
// remoteReceipt.transactionHash
// )
// await this.messenger.getL1TransactionReceipt(xDomainMsgHash)
// }
async waitForRevertXDomainTransactionL2(
tx: Promise<TransactionResponse> | TransactionResponse
) {
const { remoteReceipt } = await this.waitForXDomainTransaction(tx)
const backTx = await this.messenger.l2Provider.getTransaction(remoteReceipt.transactionHash)
await this.waitForXDomainTransaction(backTx)
}

async waitForRevertXDomainTransactionL1(
tx: Promise<TransactionResponse> | TransactionResponse
) {
const { remoteReceipt } = await this.waitForXDomainTransaction(tx)
const backTx = await this.messenger.l1Provider.getTransaction(remoteReceipt.transactionHash)
await this.waitForXDomainTransaction(backTx)
}

// async waitForRevertXDomainTransactionFast(
// tx: Promise<TransactionResponse> | TransactionResponse
Expand Down
98 changes: 98 additions & 0 deletions l2geth/rollup/rcfg/system_address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package rcfg

import (
"math/big"
"os"

"github.com/ethereum-optimism/optimism/l2geth/common"
)

// SystemAddress0 is the first deployable system address.
var SystemAddress0 = common.HexToAddress("0x4200000000000000000000000000000000000042")

// SystemAddress1 is the second deployable system address.
var SystemAddress1 = common.HexToAddress("0x4200000000000000000000000000000000000014")

// ZeroSystemAddress is the emprt system address.
var ZeroSystemAddress common.Address

// SystemAddressDeployer is a tuple containing the deployment
// addresses for SystemAddress0 and SystemAddress1.
type SystemAddressDeployer [2]common.Address

// SystemAddressFor returns the system address for a given deployment
// address. If no system address is configured for this deployer,
// ZeroSystemAddress is returned.
func (s SystemAddressDeployer) SystemAddressFor(addr common.Address) common.Address {
if s[0] == addr {
return SystemAddress0
}

if s[1] == addr {
return SystemAddress1
}

return ZeroSystemAddress
}

// SystemAddressFor is a convenience method that returns an environment-based
// system address if the passed-in chain ID is not hardcoded.
func SystemAddressFor(chainID *big.Int, addr common.Address) common.Address {
sysDeployer, hasHardcodedSysDeployer := SystemAddressDeployers[chainID.Uint64()]
if !hasHardcodedSysDeployer {
sysDeployer = envSystemAddressDeployer
}

return sysDeployer.SystemAddressFor(addr)
}

// SystemAddressDeployers maintains a hardcoded map of chain IDs to
// system addresses.
var SystemAddressDeployers = map[uint64]SystemAddressDeployer{
// Mainnet
10: {
common.HexToAddress("0xcDE47C1a5e2d60b9ff262b0a3b6d486048575Ad9"),
common.HexToAddress("0x53A6eecC2dD4795Fcc68940ddc6B4d53Bd88Bd9E"),
},

// Kovan
69: {
common.HexToAddress("0xd23eb5c2dd7035e6eb4a7e129249d9843123079f"),
common.HexToAddress("0xa81224490b9fa4930a2e920550cd1c9106bb6d9e"),
},

// Goerli
420: {
common.HexToAddress("0xc30276833798867c1dbc5c468bf51ca900b44e4c"),
common.HexToAddress("0x5c679a57e018f5f146838138d3e032ef4913d551"),
},

// Goerli nightly
421: {
common.HexToAddress("0xc30276833798867c1dbc5c468bf51ca900b44e4c"),
common.HexToAddress("0x5c679a57e018f5f146838138d3e032ef4913d551"),
},
}

var envSystemAddressDeployer SystemAddressDeployer

func initEnvSystemAddressDeployer() {
deployer0Env := os.Getenv("SYSTEM_ADDRESS_0_DEPLOYER")
deployer1Env := os.Getenv("SYSTEM_ADDRESS_1_DEPLOYER")

if deployer0Env == "" && deployer1Env == "" {
return
}
if !common.IsHexAddress(deployer0Env) {
panic("SYSTEM_ADDRESS_0_DEPLOYER specified but invalid")
}
if !common.IsHexAddress(deployer1Env) {
panic("SYSTEM_ADDRESS_1_DEPLOYER specified but invalid")
}
envSystemAddressDeployer[0] = common.HexToAddress(deployer0Env)
envSystemAddressDeployer[1] = common.HexToAddress(deployer1Env)
}

func init() {
initEnvSystemAddressDeployer()
}
Loading