Skip to content

Commit

Permalink
feat: implement create2/3 address vanity generators
Browse files Browse the repository at this point in the history
  • Loading branch information
guidiaz committed Nov 9, 2023
1 parent efde9c2 commit a2918ef
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 4 deletions.
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
"coverage": "solidity-coverage",
"flatten": "node ./scripts/flatten.js 2>&1",
"flatten:all": "npm run clean && npm run flatten:core && npm run flatten:apps && npm run flatten:libs && npm run flatten:proxy",
"flatten:apps": "npm run flatten contracts/impls/apps/",
"flatten:core": "npm run flatten contracts/impls/core/",
"flatten:apps": "npm run flatten contracts/core/apps/",
"flatten:core": "npm run flatten contracts/core/core/",
"flatten:libs": "npm run flatten contracts/libs/WitnetErrorsLib.sol && npm run flatten contracts/libs/WitnetEncodingLib.sol && npm run flatten contracts/libs/WitnetPriceFeedsLib.sol",
"flatten:proxy": "npm run flatten contracts/impls/WitnetProxy.sol",
"flatten:proxy": "npm run flatten contracts/core/WitnetProxy.sol",
"fmt:js": "eslint \"**/*.js\"",
"fmt:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\" && solhint \"test/**/*.sol\" && solhint \"flattened/**/*.sol\"",
"fmt!:js": "eslint \"**/*.js\" --fix",
Expand Down Expand Up @@ -55,9 +55,11 @@
"eslint-plugin-import": "~2.26.0",
"eslint-plugin-n": "~15.6.0",
"eslint-plugin-promise": "~6.1.1",
"eth-create2": "~1.0.1",
"eth-helpers": "^1.3.0",
"eth-gas-reporter": "0.2.25",
"js-sha256": "0.9.0",
"nanoassert": "^2.0.0",
"sha3-wasm": "0.0.7",
"solhint": "3.3.7",
"solidity-coverage": "0.7.16",
"truffle": "~5.11.5",
Expand Down
37 changes: 37 additions & 0 deletions scripts/eth-create2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const assert = require('nanoassert')
const { utils } = require('eth-helpers')
const keccak = require('sha3-wasm').keccak256

const prefix = Buffer.from([0xff])

/**
* Generate Ethereum CREATE2 address
*
* @param {string|Buffer} address Ethereum address of creator contract as
* string or as a 20 byte `Buffer`
* @param {string|Buffer} salt 256 bit salt as either string or 32 byte `Buffer`
* @param {string|Buffer} initCode init_code as string or Buffer
* @returns {string} result address as hex encoded string. Not
* checksum'ed. This can be done with
* `eth-checksum` or similar modules
*/
module.exports = function create2 (address, salt, initCode) {
if (typeof address === 'string') address = utils.parse.address(address)
if (typeof salt === 'string') salt = utils.parse.uint256(salt)
if (typeof initCode === 'string') initCode = utils.parse.bytes(initCode)

assert(address.byteLength === 20, 'address must be 20 bytes')
assert(salt.byteLength === 32, 'salt must be 32 bytes')
assert(initCode.byteLength != null, 'initCode must be Buffer')

const codeHash = keccak().update(initCode).digest()

return utils.format.address(keccak()
.update(prefix)
.update(address)
.update(salt)
.update(codeHash)
.digest()
.slice(-20))
}

43 changes: 43 additions & 0 deletions scripts/eth-create3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const assert = require('nanoassert')
const { utils } = require('eth-helpers')
const keccak = require('sha3-wasm').keccak256

const factoryPrefix = Buffer.from([0xff])

/**
* Generate Ethereum CREATE3-library address
*
* @param {string|Buffer} address Ethereum address of creator contract as
* string or as a 20 byte `Buffer`
* @param {string|Buffer} salt 256 bit salt as either string or 32 byte `Buffer`
* @returns {string} result address as hex encoded string. Not
* checksum'ed. This can be done with
* `eth-checksum` or similar modules
*/
module.exports = function create3 (address, salt) {
if (typeof address === 'string') address = utils.parse.address(address)
if (typeof salt === 'string') salt = utils.parse.uint256(salt)

assert(address.byteLength === 20, 'address must be 20 bytes')
assert(salt.byteLength === 32, 'salt must be 32 bytes')

const factoryHash = utils.parse.uint256("0x21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f")
const factoryAddr = keccak()
.update(factoryPrefix)
.update(address)
.update(salt)
.update(factoryHash)
.digest()
.slice(-20)
;
assert(factoryAddr.byteLength === 20, 'address must be 20 bytes')

return utils.format.address(keccak()
.update(Buffer.from([0xd6, 0x94]))
.update(factoryAddr)
.update(Buffer.from([0x01]))
.digest()
.slice(-20)
)
}

83 changes: 83 additions & 0 deletions scripts/vanity2gen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const { assert } = require("chai")
const create2 = require("./eth-create2")
const fs = require("fs")
const utils = require("./utils")

const addresses = require("../migrations/witnet.addresses")

module.exports = async function () {
let artifact
let count = 0
let ecosystem = "default"
let from
let hits = 10
let offset = 0
let network = "default"
let prefix = "0x00"
let suffix = "00"
let hexArgs = ""
process.argv.map((argv, index, args) => {
if (argv === "--offset") {
offset = parseInt(args[index + 1])
} else if (argv === "--artifact") {
artifact = artifacts.require(args[index + 1])
} else if (argv === "--prefix") {
prefix = args[index + 1].toLowerCase()
assert(web3.utils.isHexStrict(prefix), "--prefix: invalid hex string")
} else if (argv === "--suffix") {
suffix = args[index + 1].toLowerCase()
assert(web3.utils.isHexStrict(suffix), "--suffix: invalid hex string")
} else if (argv === "--hits") {
hits = parseInt(args[index + 1])
} else if (argv === "--network") {
[ecosystem, network] = utils.getRealmNetworkFromString(args[index + 1].toLowerCase())
} else if (argv === "--hexArgs") {
hexArgs = args[index + 1].toLowerCase()
if (hexArgs.startsWith("0x")) hexArgs = hexArgs.slice(2)
assert(web3.utils.isHexStrict("0x" + hexArgs), "--hexArgs: invalid hex string")
} else if (argv === "--from") {
from = args[index + 1]
}
return argv
})
try {
from = from || addresses[ecosystem][network].WitnetDeployer
} catch {
console.error(`WitnetDeployer must have been previously deployed on network '${network}'.\n`)
console.info("Usage:\n")
console.info(" --artifact => Truffle artifact name (mandatory)")
console.info(" --hexArgs => Hexified constructor arguments")
console.info(" --hits => Number of vanity hits to look for (default: 10)")
console.info(" --network => Network name")
console.info(" --offset => Salt starting value minus 1 (default: 0)")
console.info(" --prefix => Prefix hex string to look for (default: 0x00)")
console.info(" --suffix => suffix hex string to look for (default: 0x00)")
process.exit(1)
}
if (!artifact) {
console.error("No --artifact was set!")
process.exit(1)
}
const initCode = artifact.toJSON().bytecode + hexArgs
console.log("Init code: ", initCode)
console.log("Artifact: ", artifact?.contractName)
console.log("From: ", from)
console.log("Hits: ", hits)
console.log("Offset: ", offset)
console.log("Prefix: ", prefix)
console.log("Suffix: ", suffix)
console.log("=".repeat(55))
suffix = suffix.slice(2)
while (count < hits) {
const salt = "0x" + utils.padLeft(offset.toString(16), "0", 32)
const addr = create2(from, salt, initCode).toLowerCase()
if (addr.startsWith(prefix) && addr.endsWith(suffix)) {
const found = `${offset} => ${web3.utils.toChecksumAddress(addr)}`
console.log(found)
fs.appendFileSync(`./migrations/salts/${artifact?.contractName}$${from.toLowerCase()}.tmp`, found + "\n")
count++
}
offset++
}
}

66 changes: 66 additions & 0 deletions scripts/vanity3gen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const { assert } = require("chai")
const create3 = require("./eth-create3")
const fs = require("fs")
const utils = require("./utils")

const addresses = require("../migrations/witnet.addresses")

module.exports = async function () {
let count = 0
let ecosystem = "default"
let from
let hits = 10
let offset = 0
let network = "default"
let prefix = "0x00"
let suffix = "0x00"
let hexArgs = ""
process.argv.map((argv, index, args) => {
if (argv === "--offset") {
offset = parseInt(args[index + 1])
} else if (argv === "--prefix") {
prefix = args[index + 1].toLowerCase()
assert(web3.utils.isHexStrict(prefix), "--prefix: invalid hex string")
} else if (argv === "--suffix") {
suffix = args[index + 1].toLowerCase()
assert(web3.utils.isHexStrict(suffix), "--suffix: invalid hex string")
} else if (argv === "--hits") {
hits = parseInt(args[index + 1])
} else if (argv === "--network") {
[ecosystem, network] = utils.getRealmNetworkFromString(args[index + 1].toLowerCase())
} else if (argv === "--from") {
from = args[index + 1]
}
return argv
})
try {
from = from || addresses[ecosystem][network].WitnetDeployer
} catch {
console.error(`WitnetDeployer must have been previously deployed on network '${network}'.\n`)
console.info("Usage:\n")
console.info(" --hits => Number of vanity hits to look for (default: 10)")
console.info(" --network => Network name")
console.info(" --offset => Salt starting value minus 1 (default: 0)")
console.info(" --prefix => Prefix hex string to look for (default: 0x00)")
console.info(" --suffix => suffix hex string to look for (default: 0x00)")
process.exit(1)
}
console.log("From: ", from)
console.log("Hits: ", hits)
console.log("Offset: ", offset)
console.log("Prefix: ", prefix)
console.log("Suffix: ", suffix)
console.log("=".repeat(55))
suffix = suffix.slice(2)
while (count < hits) {
const salt = "0x" + utils.padLeft(offset.toString(16), "0", 32)
const addr = create3(from, salt).toLowerCase()
if (addr.startsWith(prefix) && addr.endsWith(suffix)) {
const found = `${offset} => ${web3.utils.toChecksumAddress(addr)}`
console.log(found)
fs.appendFileSync(`./migrations/salts/create3$${from.toLowerCase()}.tmp`, found + "\n")
count++
}
offset++
}
}

0 comments on commit a2918ef

Please sign in to comment.