-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement create2/3 address vanity generators
- Loading branch information
Showing
5 changed files
with
235 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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++ | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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++ | ||
} | ||
} |