Porting Solidity Contracts to Optimism: A Guide Using Uniswap V2[KR]

Jason Hwang
Tokamak Network
Published in
12 min readMay 25, 2021

Intro

이번 포스팅에서는 기존 솔리디티 프로젝트를 Optimism으로 옮겨서 실행하는 과정에 대해 설명할 예정이며, Uniswap V2를 예제로 사용할 것 입니다. 포스팅은 기존 솔리디티 프로젝트를 Optimism으로 옮기기 위해 필요한 과정과 실제로 Optimism에 컨트랙트를 배포하는 과정으로 구성되어 있습니다.

Porting Process

기존 솔리디티 프로젝트들을 OVM에서 동작하도록 만들기 위해 필요한 수정사항은 크게 세 가지가 있습니다.

  1. Tooling updates: OVM은 현재 Waffle V3 를 통해 동작하고 있습니다. 구 버전의 Waffle을 사용하고 있다면 이를 V3로 업그레이드 해줘야하고, 다른 프레임워크를 사용하고 있다면 마이그레이션 작업이 필요합니다.
  2. Test suite updates: 도구에 대한 업데이트 외에도, 일부 테스트 코드는 EVM과 OVM의 차이를 감안하여 수정해야 하고, 로컬 OVM 노드에서 실행을 지원해야 합니다.
  3. Contract and compiler modification: EVM과 OVM의 차이로 인해 솔리디티 컨트랙트나 컴파일러 설정을 변경해줘야 하는 경우도 있습니다. Uniswap V2는 수정할 필요가 없지만, 이번 포스팅에서는 컨트랙트 코드 수정이 필요한 몇 가지 사례를 살펴보도록 하겠습니다.

Getting Started

먼저 Uniswap V2 Core 를 클론해야 합니다. 클론한 다음 yarn 을 이용해 패키지를 설치하고 yarn test 를 이용해 모든 테스트가 통과하는지 우선 확인 해야 합니다.

Package Upgrades

EVM과 OVM 양 쪽에서 테스트가 모두 될 수 있게 하려면 우선 package.json 을 아래와 같이수정해야 합니다.

그리고 Uniswap V2에서 사용하는 Waffle을 V2에서 V3로 업데이트 해줘야 합니다.

그 다음 yarn 을 통해 패키지를 업데이트 해줍니다. yarn test:evm 명령어를 이용해 테스트를 수행해보면 Waffle 버전 업그레이드로 인한 변경점 때문에 에러가 많이 발생할 것입니다.

EVM Test Suite Updates

Waffle 버전 업그레이드로 인한 변경 사항으로 인한 에러를 해결하기 위해 변경해야할 사항에 대한 안내를 하자고 합니다. 주요 수정사항으로는 ethers/utils 패키지가 @ethersproject 로 변경된 것 입니다.

먼저 test/UniswapV2ERC20.spec.ts 를 수정 해보겠습니다.

test/shared/utilities.ts 는 다음과 같이 수정해야 합니다.

test/UniswapV2Factory.spec.ts 은 다음과 같이 수정해야 합니다.

test/shared/fixtures.ts 는 다음과 같이 수정합니다.

마지막으로 test/UniswapV2Pair.spec.ts 를 다음과 같이 수정해야 합니다.

그 다음 yarn test:evm 명령어를 이용해 테스트를 수행하면 문제없이 동작할 것 입니다.

Optimism Setup

Optimism에서 동작하는 툴들을 설치하기 위해서는 Node v10이 필요합니다. 노드 버전 관리를 위해 Volta와 같은 도구를 사용하는 것을 권장합니다. Volta를 사용하고 있다면 volta pin node@10 && volta pin yarn 명령어를 실행하면 자동적으로 필요한 Node 버전에 맞게package.json 파일을 업데이트 합니다.

필요한 Optimism 패키지는 두개가 있습니다. 다음 명령어를 이용해 설치해 주도록 하겠습니다.

$ yarn add --dev @eth-optimism/solc@0.5.16-alpha.7 @eth-optimism/ovm-toolchain

Compiling for the OVM

그 다음은 컨트랙트를 OVM에 배포할 수 있는 형태로 컴파일 해야합니다. 그러기 위해서는 .waffe.json 파일을 수정해야 합니다. .waffle.json 파일은 EVM 전용으로 작성되어 있기 때문에 .waffle-ovm.json 을 아래와 같이 작성하면 됩니다.

{
"compilerVersion": "./node_modules/@eth-optimism/solc",
"outputType": "all",
"compilerOptions": {
"outputSelection": {
"*": {
"*": [
"evm.bytecode.object",
"evm.deployedBytecode.object",
"abi",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.sourceMap",
"metadata"
],
"": ["ast"]
}
},
"evmVersion": "istanbul",
"optimizer": {
"enabled": true,
"runs": 999999
}
}
}

마지막으로 package.json 의 스크립트를 수정해야 합니다. EVM과 OVM의 구분을 명확하게 하기위해 :evm:ovm 을 붙여 구분해 줍니다.

"scripts": {
"lint": "yarn prettier ./test/*.ts --check",
"lint:fix": "yarn prettier ./test/*.ts --write",
"clean": "rimraf ./build/",
"precompile:evm": "yarn clean",
"precompile:ovm": "yarn clean",
"compile:evm": "waffle .waffle.json",
"compile:ovm": "waffle .waffle-ovm.json",
"pretest:evm": "yarn compile:evm",
"pretest:ovm": "yarn compile:ovm",
"test:evm": "mocha",
"test:ovm": "export MODE=OVM && mocha",
"prepublishOnly": "yarn test:evm && yarn test:ovm"
},

Testing on the OVM

마지막으로, OVM에서 컨트랙트를 테스트하기 위해서는 provider, 체인 ID 그리고 가스 사용에 대한 내용을 수정해줘야 합니다. Waffle에서 테스트에 제공하는 new MockProvider() 는 EVM 전용이지만, OVM에서 테스트하기 위해서는 OVM 인스턴스가 필요합니다. 또한, OVM의 체인 ID는 420 이기 때문에 이를 위한 코드가 필요합니다. test/shared/config.ts 파일을 만들고 아래와 같이 코드를 작성하면 됩니다.

Provider change

import { MockProvider } from 'ethereum-waffle' 
import { waffleV3 } from '@eth-optimism/ovm-toolchain'

const isOVM = process.env.MODE === 'OVM'

const options: any = {
ganacheOptions: {
hardfork: 'istanbul',
mnemonic: 'horn horn horn horn horn horn horn horn horn horn horn horn',
gasLimit: 9999999
}
}
const provider = isOVM ? new waffleV3.MockProvider(options) : new MockProvider(options)

const chainId = isOVM ? 420 : 1

export { provider, chainId, isOVM }

그 다음 test/UniswapV2ERC20.spec.tstest/UniswapV2Pair.spec.ts 를 아래와 같이 수정해줍니다.

Chain ID change

그리고 test/shared/utilities.tstest/UniswapV2ERC20.spec.ts의 체인 ID를 수정합니다.

Gas usage change

OVM과 EVM은 일부 옵코드의 가스 소모량의 차이가 있기 때문에 이에 대한 수정을 하지 않을 경우 실패하는 테스트가 있을 것입니다. 이를 수정해 주도록 하겠습니다.

UniswapV2Factory.spec.ts 를 다음과 같이 수정해 줍니다.

그리고 UniswapV2Pair.spec.ts 를 다음과 같이 수정합니다.

그 다음 yarn test:ovmyarn test:evm 명령어를 이용해 테스트를 수행하면 아무런 문제없이 테스트가 통과될 것입니다.

지금까지 수행한 변경사항과 기존 Uniswap V2 코드의 차이는 여기에서 확인하실 수 있습니다.

Deploy Contract to Optimism

코드에 대한 수정이 완료되었다면 이제 실제로 옵티미스틱 롤업에 Uniswap V2를 배포해 보도록 하겠습니다. 배포 대상은 UniswapV2Factory와 UniswapV2Pair 입니다. 해당 과정은 아래 세 포스팅을 따라 옵티미스틱 롤업을 사용해 봤다는 것을 전제로 합니다.

옵티미스틱 롤업 사용하기
옵티미스틱 롤업에서 ERC20 사용하기
옵티미스틱 롤업에서 ERC721 사용하기

그리고 옵티미스틱 롤업이 실행이 되고 있지 않다면 실행해줘야 합니다.

$ cd optimism/ops
$ docker-compose up -d

Compile

위에서 했던 작업 내용을 바탕으로 컨트랙트를 OVM 에 배포할 수 있게 컴파일 하도록 하겠습니다.

 $ yarn compile:ovm

Deployment script

컨트랙트 배포를 위해 아래와 같이 스크립트를 작성해줍니다.

그 다음 해당 스크립트를 실행시킵니다.

$ node ./deploy_script.js

Result

배포가 정상적으로 되었다면 아래와 같은 결과를 확인할 수 있습니다.

Deploying L2 UniswapV2Factory...L2UniswapV2Factory Contract address: 0x70e0bA845a1A0F2DA3359C97E0285013525FFC49Deploying L2 UniswapV2Pair...L2UniswapV2Pair Contract address: 0x4826533B4897376654Bb4d4AD88B7faFD0C98528

Conclusion

이번 포스팅을 통해 기존 EVM에서 동작하던 컨트랙트를 OVM에서 동작하도록 변경하고, 테스트를 수행한 다음 실제로 옵티미스틱 롤업에 배포해 봤습니다. 테스트와 컨트랙트 배포가 정상적으로 이루어 진것으로 보아 실제로 옵티미스틱 롤업에서 UniswapV2가 문제없이 동작할 것으로 판단됩니다.

아직 옵티미스틱 롤업에서 Waffle V3 밖에 지원하지 않기 때문에, 트러플과 같은 다른 테스트 도구를 이용한다면 이를 Waffle V3로 마이그레이션 해줘야하는 불편함이 있습니다. 하지만, 이를 제외한다면 기존에 작성된 컨트랙트를 옵티미스틱 롤업으로 옮겨오는 것은 큰 불편함 없이 가능할것으로 보입니다.

Reference

https://github.com/ScopeLift/ovm-uniswap-v2-core/blob/master/README.md

https://medium.com/onther-tech/%EC%98%B5%ED%8B%B0%EB%AF%B8%EC%8A%A4%ED%8B%B1-%EB%A1%A4%EC%97%85%EC%97%90%EC%84%9C-erc20-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-bc774f94d8b3

https://medium.com/onther-tech/%EC%98%B5%ED%8B%B0%EB%AF%B8%EC%8A%A4%ED%8B%B1-%EB%A1%A4%EC%97%85-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-390cd737c02f

--

--