From 3ef9f089ba3ec3110a891c8bab31e89e67d13dfb Mon Sep 17 00:00:00 2001 From: poma Date: Wed, 4 Nov 2020 05:36:01 +0300 Subject: [PATCH] deployer --- .env.example | 3 -- .github/workflows/build.yml | 28 +++++++------- contracts/Deployer.sol | 23 ++++++++++++ contracts/Echoer.sol | 11 ------ contracts/Mocks/SingletonFactory.sol | 23 ++++++++++++ migrations/1_deploy_echo.js | 10 ----- package.json | 5 +-- test/deployer.test.js | 55 ++++++++++++++++++++++++++++ test/echoer.test.js | 32 ---------------- 9 files changed, 116 insertions(+), 74 deletions(-) delete mode 100644 .env.example create mode 100644 contracts/Deployer.sol delete mode 100644 contracts/Echoer.sol create mode 100644 contracts/Mocks/SingletonFactory.sol delete mode 100644 migrations/1_deploy_echo.js create mode 100644 test/deployer.test.js delete mode 100644 test/echoer.test.js diff --git a/.env.example b/.env.example deleted file mode 100644 index 1a2b614..0000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -RPC_URL= -INFURA_TOKEN= -PRIVATE_KEY= diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9665c63..99d8073 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,20 +12,20 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 -# - uses: actions/setup-node@v1 -# with: -# node-version: 12 -# - run: yarn install -# - run: yarn test -# - run: yarn lint -# - name: Telegram Failure Notification -# uses: appleboy/telegram-action@0.0.7 -# if: failure() -# with: -# message: ❗ Build failed for [${{ github.repository }}](https://github.com/${{ github.repository }}/actions) because of ${{ github.actor }} -# format: markdown -# to: ${{ secrets.TELEGRAM_CHAT_ID }} -# token: ${{ secrets.TELEGRAM_BOT_TOKEN }} + - uses: actions/setup-node@v1 + with: + node-version: 12 + - run: yarn install + - run: yarn test + - run: yarn lint + - name: Telegram Failure Notification + uses: appleboy/telegram-action@0.0.7 + if: failure() + with: + message: ❗ Build failed for [${{ github.repository }}](https://github.com/${{ github.repository }}/actions) because of ${{ github.actor }} + format: markdown + to: ${{ secrets.TELEGRAM_CHAT_ID }} + token: ${{ secrets.TELEGRAM_BOT_TOKEN }} # # publish: # runs-on: ubuntu-latest diff --git a/contracts/Deployer.sol b/contracts/Deployer.sol new file mode 100644 index 0000000..f7c32ef --- /dev/null +++ b/contracts/Deployer.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +interface IDeployer { + function deploy(bytes memory _initCode, bytes32 _salt) external returns (address payable createdContract); +} + +contract Deployer { + IDeployer immutable deployer; + + constructor(IDeployer _deployer) public { + // Use EIP-2470 SingletonFactory address by default + deployer = address(_deployer) == address(0) ? IDeployer(0xce0042B868300000d44A59004Da54A005ffdcf9f) : _deployer; + } + + event Deployed(address indexed sender, address indexed addr); + + function deploy(bytes memory _initCode, bytes32 _salt) external { + address createdContract = deployer.deploy(_initCode, _salt); + require(createdContract != address(0), "Deploy failed"); + emit Deployed(msg.sender, createdContract); + } +} diff --git a/contracts/Echoer.sol b/contracts/Echoer.sol deleted file mode 100644 index 44d397b..0000000 --- a/contracts/Echoer.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.12; - -contract Echoer { - event Echo(address indexed who, bytes data); - - function echo(bytes calldata data) external { - emit Echo(msg.sender, data); - } -} diff --git a/contracts/Mocks/SingletonFactory.sol b/contracts/Mocks/SingletonFactory.sol new file mode 100644 index 0000000..701508f --- /dev/null +++ b/contracts/Mocks/SingletonFactory.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +/** + * @title Singleton Factory (EIP-2470) + * @notice Exposes CREATE2 (EIP-1014) to deploy bytecode on deterministic addresses based on initialization code and salt. + * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) + */ +contract SingletonFactory { + /** + * @notice Deploys `_initCode` using `_salt` for defining the deterministic address. + * @param _initCode Initialization code. + * @param _salt Arbitrary value to modify resulting address. + * @return createdContract Created contract address. + */ + function deploy(bytes memory _initCode, bytes32 _salt) public returns (address payable createdContract) { + assembly { + createdContract := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) + } + } +} +// IV is a value changed to generate the vanity address. +// IV: 6583047 diff --git a/migrations/1_deploy_echo.js b/migrations/1_deploy_echo.js deleted file mode 100644 index 498a5b3..0000000 --- a/migrations/1_deploy_echo.js +++ /dev/null @@ -1,10 +0,0 @@ -/* global artifacts */ -const Echoer = artifacts.require('Echoer') - -module.exports = function (deployer) { - return deployer.then(async () => { - const echoer = await deployer.deploy(Echoer) - - console.log('Echoer :', echoer.address) - }) -} diff --git a/package.json b/package.json index f8735fa..78594b7 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,7 @@ "eslint": "eslint --ext .js --ignore-path .gitignore .", "prettier:check": "prettier --check . --config .prettierrc", "prettier:fix": "prettier --write . --config .prettierrc", - "lint": "yarn eslint && yarn prettier:check", - "deploy:mainnet": "truffle migrate --network mainnet", - "deploy:kovan": "truffle migrate --network kovan", - "deploy:dev": "truffle migrate --skip-dry-run --network development" + "lint": "yarn eslint && yarn prettier:check" }, "repository": { "type": "git", diff --git a/test/deployer.test.js b/test/deployer.test.js new file mode 100644 index 0000000..d901b7c --- /dev/null +++ b/test/deployer.test.js @@ -0,0 +1,55 @@ +/* global artifacts, web3, contract */ +require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should() + +const { takeSnapshot, revertSnapshot } = require('../scripts/ganacheHelper') +const Deployer = artifacts.require('Deployer.sol') +const SingletonFactory = artifacts.require('SingletonFactory.sol') +const { keccak256, hexToBytes } = require('web3-utils') + +function getExpectedAddress(address, bytecode, salt) { + const arg = hexToBytes('0xff') + .concat(hexToBytes(address)) + .concat(hexToBytes(salt)) + .concat(hexToBytes(keccak256(bytecode))) + return '0x' + keccak256(arg).slice(26) +} + +contract('Deployer', (accounts) => { + let factory + let deployer + let snapshotId + + before(async () => { + factory = await SingletonFactory.new() + deployer = await Deployer.new(factory.address) + snapshotId = await takeSnapshot() + }) + + describe('#deployer', () => { + it('should work', async () => { + const bytecode = SingletonFactory.bytecode + const salt = '0x000000000000000000000000000000000000000000000000000000000000beef' + const expectedAddress = getExpectedAddress(factory.address, bytecode, salt) + const { logs } = await deployer.deploy(bytecode, salt) + + logs[0].event.should.be.equal('Deployed') + logs[0].args.sender.should.be.equal(accounts[0]) + logs[0].args.addr.toLowerCase().should.be.equal(expectedAddress) + }) + + it('should throw on repeated deploy', async () => { + const bytecode = SingletonFactory.bytecode + const salt = '0x000000000000000000000000000000000000000000000000000000000000beef' + await deployer.deploy(bytecode, salt) + await deployer.deploy(bytecode, salt).should.be.rejectedWith('Deploy failed') + }) + + it('should throw on failed deploy') + }) + + afterEach(async () => { + await revertSnapshot(snapshotId.result) + // eslint-disable-next-line require-atomic-updates + snapshotId = await takeSnapshot() + }) +}) diff --git a/test/echoer.test.js b/test/echoer.test.js deleted file mode 100644 index 4d61543..0000000 --- a/test/echoer.test.js +++ /dev/null @@ -1,32 +0,0 @@ -/* global artifacts, web3, contract */ -require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should() - -const { takeSnapshot, revertSnapshot } = require('../scripts/ganacheHelper') -const Echoer = artifacts.require('./Echoer.sol') - -contract('Echoer', (accounts) => { - let echoer - let snapshotId - - before(async () => { - echoer = await Echoer.deployed() - snapshotId = await takeSnapshot() - }) - - describe('#echo', () => { - it('should work', async () => { - const data = '0xbeef' - const { logs } = await echoer.echo(data) - - logs[0].event.should.be.equal('Echo') - logs[0].args.who.should.be.equal(accounts[0]) - logs[0].args.data.should.be.equal(data) - }) - }) - - afterEach(async () => { - await revertSnapshot(snapshotId.result) - // eslint-disable-next-line require-atomic-updates - snapshotId = await takeSnapshot() - }) -})