Commit aa61aba4 authored by Matthew Slipper's avatar Matthew Slipper Committed by Kelvin Fichter

integration-tests: Add contract tests

parent 222a3eef
...@@ -42,7 +42,7 @@ contract ERC20 { ...@@ -42,7 +42,7 @@ contract ERC20 {
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
uint256 allowance = allowed[_from][msg.sender]; uint256 allowance = allowed[_from][msg.sender];
require(balances[_from] >= _value && allowance >= _value); require(balances[_from] >= _value && allowance >= _value, "bad allowance");
balances[_to] += _value; balances[_to] += _value;
balances[_from] -= _value; balances[_from] -= _value;
if (allowance < MAX_UINT256) { if (allowance < MAX_UINT256) {
...@@ -65,4 +65,8 @@ contract ERC20 { ...@@ -65,4 +65,8 @@ contract ERC20 {
function allowance(address _owner, address _spender) public view returns (uint256 remaining) { function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowed[_owner][_spender]; return allowed[_owner][_spender];
} }
function destroy() public {
selfdestruct(payable(msg.sender));
}
} }
...@@ -18,18 +18,26 @@ const config: HardhatUserConfig = { ...@@ -18,18 +18,26 @@ const config: HardhatUserConfig = {
timeout: isLiveNetwork() ? 300_000 : 75_000, timeout: isLiveNetwork() ? 300_000 : 75_000,
}, },
solidity: { solidity: {
version: '0.8.9', compilers: [
settings: { {
optimizer: { enabled: true, runs: 200 }, version: '0.7.6',
metadata: { settings: {},
bytecodeHash: 'none',
}, },
outputSelection: { {
'*': { version: '0.8.9',
'*': ['storageLayout'], settings: {
optimizer: { enabled: true, runs: 200 },
metadata: {
bytecodeHash: 'none',
},
outputSelection: {
'*': {
'*': ['storageLayout'],
},
},
}, },
}, },
}, ],
}, },
gasReporter: { gasReporter: {
enabled: enableGasReport, enabled: enableGasReport,
......
...@@ -31,6 +31,9 @@ ...@@ -31,6 +31,9 @@
"@types/shelljs": "^0.8.8", "@types/shelljs": "^0.8.8",
"@typescript-eslint/eslint-plugin": "^4.26.0", "@typescript-eslint/eslint-plugin": "^4.26.0",
"@typescript-eslint/parser": "^4.26.0", "@typescript-eslint/parser": "^4.26.0",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.0.1",
"@uniswap/v3-sdk": "^3.6.2",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"chai": "^4.3.4", "chai": "^4.3.4",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
...@@ -50,10 +53,11 @@ ...@@ -50,10 +53,11 @@
"ethers": "^5.4.5", "ethers": "^5.4.5",
"hardhat": "^2.3.0", "hardhat": "^2.3.0",
"hardhat-gas-reporter": "^1.0.4", "hardhat-gas-reporter": "^1.0.4",
"lint-staged": "11.0.0",
"mocha": "^8.4.0", "mocha": "^8.4.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"shelljs": "^0.8.4", "shelljs": "^0.8.4",
"typescript": "^4.3.5", "typescript": "^4.3.5",
"lint-staged": "11.0.0" "uniswap-v3-deploy-plugin": "^0.1.0"
} }
} }
import chai, { expect } from 'chai' import { expect } from 'chai'
import { Wallet, BigNumber, providers } from 'ethers' import { Wallet, BigNumber, providers } from 'ethers'
import { injectL2Context } from '@eth-optimism/core-utils' import { injectL2Context } from '@eth-optimism/core-utils'
......
import chai, { expect } from 'chai' import { expect } from 'chai'
import { Wallet, Contract, ContractFactory, providers } from 'ethers' import { Wallet, Contract, ContractFactory, providers } from 'ethers'
import { ethers } from 'hardhat' import { ethers } from 'hardhat'
import { injectL2Context } from '@eth-optimism/core-utils' import { injectL2Context } from '@eth-optimism/core-utils'
......
...@@ -2,14 +2,13 @@ import { expect } from 'chai' ...@@ -2,14 +2,13 @@ import { expect } from 'chai'
/* Imports: External */ /* Imports: External */
import { Contract, ContractFactory } from 'ethers' import { Contract, ContractFactory } from 'ethers'
import { applyL1ToL2Alias } from '@eth-optimism/core-utils' import { applyL1ToL2Alias, awaitCondition } from '@eth-optimism/core-utils'
/* Imports: Internal */ /* Imports: Internal */
import simpleStorageJson from '../artifacts/contracts/SimpleStorage.sol/SimpleStorage.json' import simpleStorageJson from '../artifacts/contracts/SimpleStorage.sol/SimpleStorage.json'
import l2ReverterJson from '../artifacts/contracts/Reverter.sol/Reverter.json' import l2ReverterJson from '../artifacts/contracts/Reverter.sol/Reverter.json'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { awaitCondition } from '@eth-optimism/core-utils'
describe('Basic L1<>L2 Communication', async () => { describe('Basic L1<>L2 Communication', async () => {
let Factory__L1SimpleStorage: ContractFactory let Factory__L1SimpleStorage: ContractFactory
......
import { BigNumber, Contract, ContractFactory, utils, Wallet } from 'ethers'
import { ethers } from 'hardhat'
import { solidity } from 'ethereum-waffle'
import chai, { expect } from 'chai'
import { UniswapV3Deployer } from 'uniswap-v3-deploy-plugin/dist/deployer/UniswapV3Deployer'
import { OptimismEnv } from './shared/env'
import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk'
chai.use(solidity)
// Below methods taken from the Uniswap test suite, see
// https://github.com/Uniswap/v3-periphery/blob/main/test/shared/ticks.ts
const getMinTick = (tickSpacing: number) =>
Math.ceil(-887272 / tickSpacing) * tickSpacing
const getMaxTick = (tickSpacing: number) =>
Math.floor(887272 / tickSpacing) * tickSpacing
describe('Contract interactions', () => {
let env: OptimismEnv
let Factory__ERC20: ContractFactory
let otherWallet: Wallet
before(async () => {
env = await OptimismEnv.new()
Factory__ERC20 = await ethers.getContractFactory('ERC20', env.l2Wallet)
otherWallet = Wallet.createRandom().connect(env.l2Wallet.provider)
await env.l2Wallet.sendTransaction({
to: otherWallet.address,
value: utils.parseEther('0.1'),
})
})
describe('ERC20s', () => {
let contract: Contract
before(async () => {
Factory__ERC20 = await ethers.getContractFactory('ERC20', env.l2Wallet)
})
it('should successfully deploy the contract', async () => {
contract = await Factory__ERC20.deploy(100000000, 'OVM Test', 8, 'OVM')
await contract.deployed()
})
it('should support approvals', async () => {
const spender = '0x' + '22'.repeat(20)
const tx = await contract.approve(spender, 1000)
await tx.wait()
let allowance = await contract.allowance(env.l2Wallet.address, spender)
expect(allowance).to.deep.equal(BigNumber.from(1000))
allowance = await contract.allowance(otherWallet.address, spender)
expect(allowance).to.deep.equal(BigNumber.from(0))
const logs = await contract.queryFilter(
contract.filters.Approval(env.l2Wallet.address),
1,
'latest'
)
expect(logs[0].args._owner).to.equal(env.l2Wallet.address)
expect(logs[0].args._spender).to.equal(spender)
expect(logs[0].args._value).to.deep.equal(BigNumber.from(1000))
})
it('should support transferring balances', async () => {
const tx = await contract.transfer(otherWallet.address, 1000)
await tx.wait()
const balFrom = await contract.balanceOf(env.l2Wallet.address)
const balTo = await contract.balanceOf(otherWallet.address)
expect(balFrom).to.deep.equal(BigNumber.from(100000000).sub(1000))
expect(balTo).to.deep.equal(BigNumber.from(1000))
const logs = await contract.queryFilter(
contract.filters.Transfer(env.l2Wallet.address),
1,
'latest'
)
expect(logs[0].args._from).to.equal(env.l2Wallet.address)
expect(logs[0].args._to).to.equal(otherWallet.address)
expect(logs[0].args._value).to.deep.equal(BigNumber.from(1000))
})
it('should support being self destructed', async () => {
const tx = await contract.destroy()
await tx.wait()
const code = await env.l2Wallet.provider.getCode(contract.address)
expect(code).to.equal('0x')
})
})
describe('uniswap', () => {
let contracts: { [name: string]: Contract }
let tokens: Contract[]
before(async () => {
const tokenA = await Factory__ERC20.deploy(100000000, 'OVM1', 8, 'OVM1')
await tokenA.deployed()
const tokenB = await Factory__ERC20.deploy(100000000, 'OVM2', 8, 'OVM2')
await tokenB.deployed()
tokens = [tokenA, tokenB]
tokens.sort((a, b) => {
if (a.address > b.address) {
return 1
}
if (a.address < b.address) {
return -1
}
return 0
})
const tx = await tokens[0].transfer(otherWallet.address, 100000)
await tx.wait()
})
it('should deploy the Uniswap ecosystem', async () => {
contracts = await UniswapV3Deployer.deploy(env.l2Wallet)
})
it('should deploy and initialize a liquidity pool', async () => {
const tx =
await contracts.positionManager.createAndInitializePoolIfNecessary(
tokens[0].address,
tokens[1].address,
FeeAmount.MEDIUM,
// initial ratio of 1/1
BigNumber.from('79228162514264337593543950336')
)
await tx.wait()
})
it('should approve the contracts', async () => {
for (const wallet of [env.l2Wallet, otherWallet]) {
for (const token of tokens) {
let tx = await token
.connect(wallet)
.approve(contracts.positionManager.address, 100000000)
await tx.wait()
tx = await token
.connect(wallet)
.approve(contracts.router.address, 100000000)
await tx.wait()
}
}
})
it('should mint new positions', async () => {
const tx = await contracts.positionManager.mint(
{
token0: tokens[0].address,
token1: tokens[1].address,
tickLower: getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
tickUpper: getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]),
fee: FeeAmount.MEDIUM,
recipient: env.l2Wallet.address,
amount0Desired: 15,
amount1Desired: 15,
amount0Min: 0,
amount1Min: 0,
deadline: Date.now() * 2,
},
{
gasLimit: 10000000,
}
)
await tx.wait()
expect(
await contracts.positionManager.balanceOf(env.l2Wallet.address)
).to.eq(1)
expect(
await contracts.positionManager.tokenOfOwnerByIndex(
env.l2Wallet.address,
0
)
).to.eq(1)
})
it('should swap', async () => {
const tx = await contracts.router.connect(otherWallet).exactInputSingle(
{
tokenIn: tokens[0].address,
tokenOut: tokens[1].address,
fee: FeeAmount.MEDIUM,
recipient: otherWallet.address,
deadline: Date.now() * 2,
amountIn: 10,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0,
},
{
gasLimit: 10000000,
}
)
await tx.wait()
expect(await tokens[1].balanceOf(otherWallet.address)).to.deep.equal(
BigNumber.from('5')
)
})
})
})
...@@ -174,22 +174,6 @@ export const waitForL2Geth = async ( ...@@ -174,22 +174,6 @@ export const waitForL2Geth = async (
return injectL2Context(provider) return injectL2Context(provider)
} }
export const awaitCondition = async (
cond: () => Promise<boolean>,
rate = 1000,
attempts = 10
) => {
for (let i = 0; i < attempts; i++) {
const ok = await cond()
if (ok) {
return
}
await sleep(rate)
}
throw new Error('Timed out.')
}
export const gasPriceForL2 = async () => { export const gasPriceForL2 = async () => {
if (isLiveNetwork()) { if (isLiveNetwork()) {
return Promise.resolve(BigNumber.from(10000)) return Promise.resolve(BigNumber.from(10000))
......
...@@ -3192,6 +3192,19 @@ ...@@ -3192,6 +3192,19 @@
tiny-invariant "^1.1.0" tiny-invariant "^1.1.0"
tiny-warning "^1.0.3" tiny-warning "^1.0.3"
"@uniswap/v3-sdk@^3.6.2":
version "3.6.2"
resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-3.6.2.tgz#45fa659f7642e8807cb36939e4426355c7a5943c"
integrity sha512-RHJaFfO6+ugI0+v0xhGXuVadmJ9bTbAz/RnuS/xRpXrRIsLNRVC42bYZ3Ci0JPWhkaNygCcc3LJ36Bs6GganSg==
dependencies:
"@ethersproject/abi" "^5.0.12"
"@ethersproject/solidity" "^5.0.9"
"@uniswap/sdk-core" "^3.0.1"
"@uniswap/v3-periphery" "^1.1.1"
"@uniswap/v3-staker" "1.0.0"
tiny-invariant "^1.1.0"
tiny-warning "^1.0.3"
"@uniswap/v3-staker@1.0.0": "@uniswap/v3-staker@1.0.0":
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/@uniswap/v3-staker/-/v3-staker-1.0.0.tgz#9a6915ec980852479dfc903f50baf822ff8fa66e" resolved "https://registry.yarnpkg.com/@uniswap/v3-staker/-/v3-staker-1.0.0.tgz#9a6915ec980852479dfc903f50baf822ff8fa66e"
...@@ -5094,6 +5107,16 @@ cli-table3@^0.5.0: ...@@ -5094,6 +5107,16 @@ cli-table3@^0.5.0:
optionalDependencies: optionalDependencies:
colors "^1.1.2" colors "^1.1.2"
cli-table3@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee"
integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==
dependencies:
object-assign "^4.1.0"
string-width "^4.2.0"
optionalDependencies:
colors "^1.1.2"
cli-table@^0.3.1: cli-table@^0.3.1:
version "0.3.6" version "0.3.6"
resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.6.tgz#e9d6aa859c7fe636981fd3787378c2a20bce92fc" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.6.tgz#e9d6aa859c7fe636981fd3787378c2a20bce92fc"
...@@ -15258,6 +15281,15 @@ unique-slug@^2.0.0: ...@@ -15258,6 +15281,15 @@ unique-slug@^2.0.0:
dependencies: dependencies:
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
uniswap-v3-deploy-plugin@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/uniswap-v3-deploy-plugin/-/uniswap-v3-deploy-plugin-0.1.0.tgz#dd1798106373c16108aa2b5a0ec3b176c5c51d43"
integrity sha512-goowszquDWierss0oCB91MoQY2W1ue1fixA7XrxPQbVU0pt7ZGUv2KvxqYtcjpey9AcJrveGJoGYvpSLxmkzaw==
dependencies:
"@uniswap/v3-core" "1.0.0"
"@uniswap/v3-periphery" "^1.0.1"
cli-table3 "^0.6.0"
universal-user-agent@^6.0.0: universal-user-agent@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment