Skip to content

Commit

Permalink
Merge pull request #5 from gnosis/feature/issue_3_singleton_factory
Browse files Browse the repository at this point in the history
Closes #3: Added singleton factory to migration
  • Loading branch information
rmeissner committed Oct 15, 2020
2 parents 3d69227 + ee78afb commit 1a181e5
Show file tree
Hide file tree
Showing 22 changed files with 396 additions and 74 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
@@ -0,0 +1,2 @@
/build
/node_modules
4 changes: 2 additions & 2 deletions .env.sample
@@ -1,2 +1,2 @@
MNEMONIC=""
INFURA_TOKEN=""
MNEMONIC=some mnemonic
INFURA_TOKEN=12365478965412
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -5,6 +5,7 @@ node_modules/
.openzeppelin/.session
env/
.env
.env.deploy
bin/
solc
coverage.json
Expand Down
6 changes: 6 additions & 0 deletions .solcover.js
@@ -0,0 +1,6 @@
module.exports = {
mocha: {
grep: "@skip-on-coverage", // Find everything with this tag
invert: true // Run the grep's inverse set.
}
};
20 changes: 11 additions & 9 deletions .travis.yml
@@ -1,17 +1,19 @@
language: node_js
node_js:
- '10'
language: minimal

services:
- docker

cache:
directories:
- node_modules

before_script:
-
- cp .env.sample .env
- docker build -t delegate-registry .

jobs:
include:
- script:
- yarn compile
- yarn test
- docker run delegate-registry yarn test
- script:
- yarn compile
- yarn coverage
- cat ./coverage/lcov.info | coveralls
- docker run delegate-registry yarn coverage
13 changes: 13 additions & 0 deletions Dockerfile
@@ -0,0 +1,13 @@
FROM node:12

ENV USER=root
WORKDIR "/"

RUN echo "{}" > networks.json
COPY package.json yarn.lock truffle-config.js ./

RUN yarn

COPY . .

RUN yarn compile
49 changes: 41 additions & 8 deletions README.md
Expand Up @@ -9,34 +9,67 @@ Install

```bash
yarn
// Setup env
cp .env.sample .env
```

### Build contracts

With docker:
```bash
docker-compose up
```

Without docker:
```bash
yarn compile
```

### Run all tests (requires Node version >=7 for `async/await`):

Running the tests with docker:

```bash
yarn truffle compile
docker build -t delegate-registry .
docker run delegate-registry yarn test
```

If you want to run it without docker:

```bash
yarn compile
yarn test
```

`yarn test` will start a ganache-cli with the correct configuration. If you want to run `yarn truffle test` you need to start a [ganache-cli](https://github.com/trufflesuite/ganache-cli) instance.
In this case it is expected that the deployment check test fails.

### Deploy

Docker is used to ensure that always the same bytecode is generated.

Preparation:
- Set `INFURA_TOKEN` in `.env`
- Set `NETWORK` in `.env`
- Run `yarn truffle compile`

Truffle:
- Set `MNEMONIC` in `.env`

Deploying with docker (should always result in the same registry address):

```bash
./deploy.sh <network>
```

If you want to run it without docker (might result in different registry address):

```bash
yarn truffle deploy
yarn compile
yarn deploy <network>
```

### Verify contract

Note: To completely replicate the bytecode that has been deployed it is required that the project path is `/delegate-registry` this can be archived using `sudo mkdir /delegate-registry && sudo mount -B <your_repo_path> /delegate-registry`. Make sure the run `yarn` again if the path has been changed after the initial `yarn install`. If you use a different path you will only get partial matches.
Note: To completely replicate the bytecode that has been deployed it is required that the project path is always the same. For this use the provided Dockerfile and map the the build folder into your local build folder. For this a docker-compose file is provided which can be used with:
```bash
docker-compose up
```

You can locally verify contract using the scripts `generate_meta.js` and `verify_deployment.js`.

Expand Down
31 changes: 0 additions & 31 deletions contracts/Migrations.sol

This file was deleted.

10 changes: 10 additions & 0 deletions deploy.sh
@@ -0,0 +1,10 @@
#!/bin/bash

cp .env .env.deploy
echo $'\nNETWORK='$1 >> .env.deploy

docker-compose -f docker-compose.yml -f docker-compose.deploy.yml --env-file .env.deploy up --build

node scripts/clean_build.js

rm .env.deploy
7 changes: 7 additions & 0 deletions docker-compose.deploy.yml
@@ -0,0 +1,7 @@
version: "3.5"

services:
build:
env_file:
- .env.deploy
command: "yarn deploy ${NETWORK}"
13 changes: 13 additions & 0 deletions docker-compose.yml
@@ -0,0 +1,13 @@
version: "3.5"

services:
build:
image: delegate-registry
build:
context: .
dockerfile: ./Dockerfile
env_file:
- .env
volumes:
- ./build:/build:rw
command: "yarn compile"
7 changes: 7 additions & 0 deletions migrations/1_deploy_registry.js
@@ -0,0 +1,7 @@
const { deployTruffleContract } = require('./utils/singleton_factory');

const DelegateRegistry = artifacts.require("./DelegateRegistry.sol");

module.exports = (d) => d.then(async () => {
await deployTruffleContract(web3, DelegateRegistry);
});
6 changes: 0 additions & 6 deletions migrations/1_initial_migration.js

This file was deleted.

5 changes: 0 additions & 5 deletions migrations/2_deploy_registry.js

This file was deleted.

14 changes: 14 additions & 0 deletions migrations/utils/address_utils.js
@@ -0,0 +1,14 @@
const web3 = require('web3');

const buildCreate2Address = (deployer, salt, bytecode) => {
return web3.utils.toChecksumAddress(`0x${web3.utils.soliditySha3(
{ t: 'bytes', v: '0xff' },
{ t: 'address', v: deployer },
{ t: 'bytes32', v: salt },
{ t: 'bytes32', v: web3.utils.keccak256(bytecode) }
).slice(-40)}`);
}

Object.assign(exports, {
buildCreate2Address,
})
9 changes: 9 additions & 0 deletions migrations/utils/promise_utils.js
@@ -0,0 +1,9 @@
const toConfirmationPromise = promiEvent => new Promise((resolve, reject) => {
promiEvent
.on('confirmation', (_n, receipt) => resolve(receipt))
.on('error', reject);
});

Object.assign(exports, {
toConfirmationPromise,
})
118 changes: 118 additions & 0 deletions migrations/utils/singleton_factory.js
@@ -0,0 +1,118 @@
const truffleContract = require("@truffle/contract")

const { toConfirmationPromise } = require('./promise_utils');
const { buildCreate2Address } = require('./address_utils');

const SINGLETON_FACTORY = '0xce0042B868300000d44A59004Da54A005ffdcf9f'
const SINGLETON_FACTORY_DEPLOYER = '0xBb6e024b9cFFACB947A71991E386681B1Cd1477D'
const SINGLETON_FACTORY_CODE = '0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470'
const SINGLETON_FACTORY_ABI = [
{
"constant": false,
"inputs": [
{
"internalType": "bytes",
"name": "_initCode",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "_salt",
"type": "bytes32"
}
],
"name": "deploy",
"outputs": [
{
"internalType": "address payable",
"name": "createdContract",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
]

const requireFactoryDeployment = async (web3) => {
return (await web3.eth.getCode(SINGLETON_FACTORY)) === '0x'
}

const deployFactory = async (web3) => {
await toConfirmationPromise(web3.eth.sendSignedTransaction(SINGLETON_FACTORY_CODE));
}

const ensureFactory = async (web3) => {
const [deployer] = await web3.eth.getAccounts();
if (await requireFactoryDeployment(web3)) {
// greetz @3esmit and @forshtat
await toConfirmationPromise(web3.eth.sendTransaction({
from: deployer,
to: SINGLETON_FACTORY_DEPLOYER,
value: 1e18
}));
await deployFactory(web3);
console.log(`Deployed EIP 2470 SingletonFactory at ${SINGLETON_FACTORY}`);
} else {
console.log(`EIP 2470 SingletonFactory already deployed at ${SINGLETON_FACTORY}`);
}
const factoryContract = truffleContract({ abi: SINGLETON_FACTORY_ABI })
factoryContract.setProvider(web3.currentProvider)
const singletonFactory = await factoryContract.at(SINGLETON_FACTORY)
return singletonFactory;
}

const deployContract = async (web3, bytecode, salt) => {
const [deployer] = await web3.eth.getAccounts();
const contractAddress = buildCreate2Address(SINGLETON_FACTORY, salt, bytecode);
const requireDeployment = (await web3.eth.getCode(contractAddress)) === '0x';
if (!requireDeployment) {
return { txHash: null, newContract: false, contractAddress };
}
const factory = await ensureFactory(web3);
const { tx } = await factory.deploy(bytecode, salt, { from: deployer });
return { txHash: tx, newContract: true, contractAddress };
}

const deployTruffleContract = async (web3, artifact, salt) => {
const artifactName = artifact.contractName || "Artifact"
const deploymentSalt = salt || '0x';
const { contractAddress, txHash, newContract } = await deployContract(web3, artifact.bytecode, deploymentSalt);
if (newContract) {
console.log(`Deployed ${artifactName} at ${contractAddress}`);

artifact.address = contractAddress;
artifact.transactionHash = txHash;
} else {
try {
const addressOnArtifact = artifact.address;
if (addressOnArtifact !== contractAddress) {
console.warn(`Expected to find ${contractAddress
} set as ${artifactName} address but instead found ${artifact.address
} so the address is being updated, but the transaction hash should be manually corrected`);
} else {
console.log(`Found ${artifactName} at ${contractAddress}`);
}
} catch (e) {
if (e.message.startsWith(`${artifactName} has no network configuration for its current network id`)) {
console.warn(`Expected to find ${contractAddress
} set as ${artifactName} address but instead couldn't find an address, so the address is being updated, but the transaction hash should be manually added`);
} else {
throw e;
}
}
artifact.address = contractAddress;
}
}

Object.assign(exports, {
SINGLETON_FACTORY,
SINGLETON_FACTORY_DEPLOYER,
SINGLETON_FACTORY_CODE,
requireFactoryDeployment,
deployFactory,
ensureFactory,
deployContract,
deployTruffleContract,
})

0 comments on commit 1a181e5

Please sign in to comment.