Skip to content
This repository was archived by the owner on Apr 11, 2021. It is now read-only.
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
2 changes: 2 additions & 0 deletions common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { JsonRpcProvider, Provider } from '@ethersproject/providers'
chai.use(chaiAsPromised)
const should = chai.should()

export const expect = chai.expect

// Load up env variables if running locally
if (process.env.NODE_ENV === 'local') {
const envPath = path.join(__dirname, '/.env');
Expand Down
16 changes: 9 additions & 7 deletions contracts/ERC20.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: MIT

/*
Implements ERC20 token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
.*/


pragma solidity ^0.5.16;
pragma solidity >=0.7.0 <0.8.0;

import "./IERC20.sol";

Expand All @@ -21,7 +23,7 @@ contract ERC20 is IERC20 {
string public name; //fancy name: eg OVM Coin
uint8 public decimals; //How many decimals to show.
string public symbol; //An identifier: eg OVM
uint256 public totalSupply;
uint256 public override totalSupply;

constructor(
uint256 _initialAmount,
Expand All @@ -36,15 +38,15 @@ contract ERC20 is IERC20 {
symbol = _tokenSymbol; // Set the symbol for display purposes
}

function transfer(address _to, uint256 _value) external returns (bool success) {
function transfer(address _to, uint256 _value) override public returns (bool success) {
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}

function transferFrom(address _from, address _to, uint256 _value) external returns (bool success) {
function transferFrom(address _from, address _to, uint256 _value) override public returns (bool success) {
uint256 allowance = allowed[_from][msg.sender];
require(balances[_from] >= _value && allowance >= _value);
balances[_to] += _value;
Expand All @@ -56,17 +58,17 @@ contract ERC20 is IERC20 {
return true;
}

function balanceOf(address _owner) external view returns (uint256 balance) {
function balanceOf(address _owner) override public view returns (uint256 balance) {
return balances[_owner];
}

function approve(address _spender, uint256 _value) external returns (bool success) {
function approve(address _spender, uint256 _value) override public returns (bool success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}

function allowance(address _owner, address _spender) external view returns (uint256 remaining) {
function allowance(address _owner, address _spender) override public view returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}
18 changes: 10 additions & 8 deletions contracts/IERC20.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: MIT

// Abstract contract for the full ERC 20 Token standard
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
pragma solidity ^0.5.16;
pragma solidity >=0.7.0 <0.8.0;


contract IERC20 {
interface IERC20 {
Comment thread
smartcontracts marked this conversation as resolved.
/* This is a slight change to the ERC20 base standard.
function totalSupply() constant returns (uint256 supply);
is replaced with:
Expand All @@ -14,34 +16,34 @@ contract IERC20 {
function by the compiler.
*/
/// total amount of tokens
function totalSupply() external view returns (uint256);
function totalSupply() external view returns (uint256);

/// @param _owner The address from which the balance will be retrieved
/// @return The balance
/// @return balance The balance
function balanceOf(address _owner) external view returns (uint256 balance);

/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
/// @return success Whether the transfer was successful or not
function transfer(address _to, uint256 _value) external returns (bool success);

/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
/// @return success Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);

/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of tokens to be approved for transfer
/// @return Whether the approval was successful or not
/// @return success Whether the approval was successful or not
function approve(address _spender, uint256 _value) external returns (bool success);

/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
/// @return remaining Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) external view returns (uint256 remaining);

// solhint-disable-next-line no-simple-event-func-name
Expand Down
15 changes: 15 additions & 0 deletions contracts/OVMContextStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0;

contract OVMContextStorage {
mapping (uint256 => uint256) public blockNumbers;
mapping (uint256 => uint256) public timestamps;
uint256 public index = 0;

fallback() external {
blockNumbers[index] = block.number;
timestamps[index] = block.timestamp;
index++;
}
}
50 changes: 50 additions & 0 deletions contracts/OVMMulticall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT

/*

MIT License

Copyright (c) 2018 Maker Foundation

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

*/

pragma solidity ^0.7.0;

pragma experimental ABIEncoderV2;

/// @title OVMMulticall - Aggregate results from multiple read-only function calls
contract OVMMulticall {
struct Call {
address target;
bytes callData;
}

function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) {
blockNumber = block.number;
returnData = new bytes[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
(bool success, bytes memory ret) = calls[i].target.call(calls[i].callData);
require(success);
returnData[i] = ret;
}
}

// Helper functions
function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
timestamp = block.timestamp;
}

function getCurrentBlockNumber() public view returns (uint256 blockNumber) {
blockNumber = block.number;
}
}
2 changes: 1 addition & 1 deletion contracts/SimpleStorage.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.16;
pragma solidity >=0.7.0;

contract ICrossDomainMessenger {
address public xDomainMessageSender;
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@
},
"dependencies": {
"@eth-optimism/contracts": "^0.1.9",
"@eth-optimism/solc": "^0.5.16-alpha.6",
"@eth-optimism/solc": "^0.7.6-alpha.1",
"@eth-optimism/watcher": "0.0.1-alpha.6",
"@ethersproject/providers": "^5.0.7",
"bignumber.js": "^9.0.0",
"chai-bignumber": "^3.0.0",
"debug": "^4.2.0",
"dotenv": "^8.2.0",
"level": "^6.0.0",
"solc": "^0.5.16",
"solc": "^0.7.6",
"typescript": "^4.0.3"
}
}
2 changes: 0 additions & 2 deletions packages/tx-ingestion/test/sequencer-batch-append.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { deployContract } from 'ethereum-waffle'
import assert = require('assert')
import ERC20 = require('../../../contracts/build-ovm/ERC20.json')

// TODO(mark): Remove the skip of this test when
// the verifier is enabled in geth
describe('Queue Origin Sequencer Transactions', () => {
let optimismProvider
let provider: JsonRpcProvider
Expand Down
3 changes: 1 addition & 2 deletions packages/x-domain/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@
"@eth-optimism/contracts": "^0.1.9",
"@eth-optimism/core-db": "^0.0.1-alpha.30",
"@eth-optimism/core-utils": "^0.0.1-alpha.30",
"@eth-optimism/plugins": "^0.0.16",
"@eth-optimism/plugins": "^0.0.20",
"@eth-optimism/provider": "^0.0.1-alpha.14",
"@eth-optimism/rollup-core": "^0.0.1-alpha.28",
"@eth-optimism/watcher": "^0.0.1-alpha.6 ",
"@ethersproject/address": "^5.0.4",
"@ethersproject/bytes": "^5.0.4",
Expand Down
116 changes: 116 additions & 0 deletions packages/x-domain/test/ovmcontext.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { Config, sleep, expect } from '../../../common'
import { JsonRpcProvider } from '@ethersproject/providers'
import { getContractFactory } from '@eth-optimism/contracts'

import OVMContextStorageArtifact = require('../../../contracts/build-ovm/OVMContextStorage.json')
import OVMMulticallArtifact = require('../../../contracts/build-ovm/OVMMulticall.json')
import { Contract, ContractFactory, Wallet, BigNumber } from 'ethers'

/**
* These tests cover the OVM execution contexts. In the OVM execution
* of a L1 to L2 transaction, both `block.number` and `block.timestamp`
* must be equal to the blocknumber/timestamp of the L1 transaction.
*/
describe('OVM Context: Layer 2 EVM Context', () => {
let address: string
let CanonicalTransactionChain: Contract
let OVMMulticall: Contract
let OVMContextStorage: Contract

const l1Provider = new JsonRpcProvider(Config.L1NodeUrlWithPort())
const l2Provider = new JsonRpcProvider(Config.L2NodeUrlWithPort())

before(async () => {
// Create providers and signers
const l1Wallet = new Wallet(Config.DeployerPrivateKey()).connect(l1Provider)
const l2Wallet = Wallet.createRandom().connect(l2Provider)

// deploy the contract
const OVMContextStorageFactory = new ContractFactory(
OVMContextStorageArtifact.abi,
OVMContextStorageArtifact.bytecode,
l2Wallet
)

OVMContextStorage = await OVMContextStorageFactory.deploy()
const receipt = await OVMContextStorage.deployTransaction.wait()
address = OVMContextStorage.address

const addressResolverAddress = Config.AddressResolverAddress()
const AddressResolverFactory = getContractFactory('Lib_AddressManager')
const addressResolver = AddressResolverFactory
.attach(addressResolverAddress)
.connect(l1Provider)
const ctcAddress = await addressResolver.getAddress('OVM_CanonicalTransactionChain')
const CanonicalTransactionChainFactory = getContractFactory('OVM_CanonicalTransactionChain')

CanonicalTransactionChain = CanonicalTransactionChainFactory.connect(l1Wallet).attach(ctcAddress)

const OVMMulticallFactory = new ContractFactory(
OVMMulticallArtifact.abi,
OVMMulticallArtifact.bytecode,
l2Wallet,
)
OVMMulticall = await OVMMulticallFactory.deploy()
await OVMMulticall.deployTransaction.wait()
})

it('Enqueue: `block.number` and `block.timestamp` have L1 values', async () => {
for (let i = 0; i < 5; i++) {
const l2Tip = await l2Provider.getBlock('latest')
const tx = await CanonicalTransactionChain.enqueue(OVMContextStorage.address, 500_000, '0x')

// Wait for the enqueue to be ingested
while (true) {
const tip = await l2Provider.getBlock('latest')
if (tip.number === l2Tip.number + 1) {
break
}
await sleep(500)
}

// Get the receipt
const receipt = await tx.wait()
// The transaction did not revert
expect(receipt.status).to.equal(1)

// Get the L1 block that the enqueue transaction was in so that
// the timestamp can be compared against the layer two contract
const block = await l1Provider.getBlock(receipt.blockNumber)

// The contact is a fallback function that keeps `block.number`
// and `block.timestamp` in a mapping based on an index that
// increments each time that there is a transaction.
const blockNumber = await OVMContextStorage.blockNumbers(i)
expect(receipt.blockNumber).to.deep.equal(blockNumber.toNumber())
const timestamp = await OVMContextStorage.timestamps(i)
expect(block.timestamp).to.deep.equal(timestamp.toNumber())
}
})

/**
* `rollup_getInfo` is a new RPC endpoint that is used to return the OVM
* context. The data returned should match what is actually being used as the
* OVM context.
*/

it('should return same timestamp and blocknumbers between `eth_call` and `rollup_getInfo`', async () => {
// As atomically as possible, call `rollup_getInfo` and OVMMulticall for the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs better explanation of why the atomicity here is necessary.

// blocknumber and timestamp. If this is not atomic, then the sequencer can
// happend to update the timestamp between the `eth_call` and the `rollup_getInfo`
const [info, [, returnData]] = await Promise.all([
l2Provider.send('rollup_getInfo', []),
OVMMulticall.callStatic.aggregate([
[OVMMulticall.address, OVMMulticall.interface.encodeFunctionData('getCurrentBlockTimestamp')],
[OVMMulticall.address, OVMMulticall.interface.encodeFunctionData('getCurrentBlockNumber')]
])
])

const timestamp = BigNumber.from(returnData[0])
const blockNumber = BigNumber.from(returnData[1])

// TODO: this is a bug and needs to be fixed
//expect(info.ethContext.blockNumber).to.deep.equal(blockNumber.toNumber())
expect(info.ethContext.timestamp).to.deep.equal(timestamp.toNumber())
})
})
Loading