Solidity smart contracts are generated from source into bytecode. However, much like the days of yore in C, linking in other libraries that may already be deployed is sometimes a necessity.
When a Solidity artifact is generated using Truffle or sol-compiler, there is a field called bytecode
or deployedBytecode
. If you have unlinked libraries and try to deploy this code, you will get an error along the lines of invalid contract bytecode:
{ Error: invalid contract bytecode (arg="bytecode", value="0x608060405260016004554360075543600__$e66798aa9224cd2742272d2e7f8089dbe6$__ffffffffffffffffffffffffffffffffffffffff1660601b81526014018281526020019350505050604051602081830303815290604052805190602001209050939250505056fea165627a7a72305820fdb2e9c02d3de9057e4d439bc377d8c8ae842b83649e495e3303688bfe6f7e930029", version=4.0.26)
If you examine deeper, you can see there is a very non-hexadecimal looking character $
which is the beginning of a link reference. The link reference itself is a string __$e66798aa9224cd2742272d2e7f8089dbe6$__
. This used to have a more human-readable name, such as ____________________$Library$_
, but it was changed to a more arcane algorithm. The libraryHashPlaceholder method creates this format, but what is the input we are giving it? It is of the form, libraryName:lib
.
For example, my MerkleTreeVerifier
library would have the name:
libraryHashPlaceholder('/Users/liamz/Documents/open-source/0dex/packages/contracts/contracts/MerkleTreeVerifier.sol:MerkleTreeVerifier')
// '$1eb98b648b444978ea3820de6fcdeb48d6$'
Integrating with sol-compiler
Now for the 0x compiler, you will need to do two things. Note that I'm also using abi-gen to generate TypeScript wrappers of our contracts.
1) Enable the metadata
option in compiler.json
. For example, here is mine:
{
"contractsDir": "contracts",
"artifactsDir": "build/artifacts",
"contracts": "*",
"compilerSettings": {
"optimizer": { "enabled": false },
"outputSelection": {
"*": {
"*": [
"metadata",
"abi",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
]
}
}
}
}
2) Add your libraries and link them.
export function addLibrary(name: string, address: string) {
let artifact = require(`@ohdex/contracts/lib/build/artifacts/${name}.json`);
let metadata = JSON.parse(artifact.compilerOutput.metadata)
let [k,v] = Object.entries(metadata.settings.compilationTarget)[0];
let key = `${k}:${v}`;
console.log(`Registered library ${name} to ${key}`)
libraries[key] = address;
}
// Deployment of libraries
// Note that if the library already exists, we just add the address instead
// ....
// Your sol-compiler generated wrapper
import { MerkleTreeVerifierContract } from '@ohdex/contracts/lib/build/wrappers/merkle_tree_verifier';
let merkleTreeVerifier = await MerkleTreeVerifierContract.deployAsync(
...getDeployArgs('MerkleTreeVerifier', pe, account)
)
addLibrary('MerkleTreeVerifier', merkleTreeVerifier.address)
// Linking libraries to your own contracts
let json = require(`@ohdex/contracts/lib/build/artifacts/${name}.json`);
let bytecode = json.compilerOutput.evm.bytecode.object;
bytecode = linker.linkBytecode(bytecode, libraries)
And tada! You've linked your libraries and deployed your contract!
Top comments (1)
Is it the same as what Hardhat doing it here?
hardhat.org/plugins/nomiclabs-hard...