@nomiclabs/buidler-truffle5
Version:
Truffle 5 Buidler compatibility plugin
135 lines • 6.74 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const plugins_1 = require("@nomiclabs/buidler/plugins");
const path_1 = __importDefault(require("path"));
class TruffleEnvironmentArtifacts {
constructor(artifactsPath, provisioner) {
this._provisioner = provisioner;
this._artifactsPath = artifactsPath;
}
require(contractPath) {
const name = this._getContractNameFromPath(contractPath);
return this._getTruffleContract(name);
}
contractNeedsLinking(Contract) {
return Contract.bytecode.includes("__");
}
contractWasLinked(Contract) {
try {
if (Contract.binary.includes("__")) {
return false;
}
}
catch (e) {
return false;
}
return true;
}
/**
* This functions links a contract with one or multiple libraries.
*
* We have this method here because our artifacts format is slightly different
* than Truffle's and doesn't include deployment information.
*
* This method also makes TruffleContract work with solc 0.5.x bytecode and
* link symbols.
*/
link(destination, ...libraries) {
if (libraries.length === 0) {
return;
}
for (const library of libraries) {
if (library.address === undefined ||
library.constructor.network_id === undefined) {
throw new plugins_1.BuidlerPluginError(`Error while linking library ${library._json.contractName} into contract ${destination.contractName}: library not deployed.`);
}
}
const destinationArtifact = plugins_1.readArtifactSync(this._artifactsPath, destination.contractName);
const libraryAddresses = {};
const linkReferences = destinationArtifact.linkReferences;
// Explanation of the following hacks:
//
// 1. When compiling a contract that uses libraries solc doesn't know the addresses
// of those. So when emitting the contract's bytecode it uses placeholders instead
// of them, and outputs a linkReferences object with info about them.
//
// 2. solc 0.4.x based those placeholders in the filename of the library and its name.
//
// 3. solc 0.5.x changed that and uses something like __$<hexa string>$__
//
// 4. TruffleContract linking process ignores the linkReferences object and generates
// the placeholders from the library name and replaces that in the bytecode. This
// process if broken, but we need to somewhat support it.
//
// 5. We use the version of Contract.link that takes a map of library names to
// addresses to support both versions of solc.
//
// 6. In order to do that, we fetch the first placeholder of each library. Note that
// depending on the version of solc used to compile the contrat it may be based on
// the library name, or based on a hexa string (a hash?).
//
// 7. We remove some underscores and escape some chars so that TruffleContract can
// match the original placeholder. Internally TruffleContract uses the library name
// to create a regex (without escaping anything) that matches the placeholder.
//
// 8. We used the resulting string as contract names to create a map from library name
// to their addresses, and finally call Contract.link with it.
//
// 9. TruffleContract doesn't validate the library names, so "\\$<hexa string>\\$" is
// accepted as name.
//
for (const file of Object.keys(linkReferences)) {
for (const contractName of Object.keys(linkReferences[file])) {
const library = libraries.find((c) => c.constructor.contractName === contractName);
const linksData = linkReferences[file][contractName];
if (library !== undefined) {
const firstLinkData = linksData[0];
// link data is exressed in bytes, but the bytecode is hex encoded, so we
// need to multiply everything by 2.
const linkPlaceholder = destinationArtifact.bytecode.substr(firstLinkData.start * 2 + 2, // The + 2 is because of the 0x prefix
firstLinkData.length * 2);
const libraryIdentifier = linkPlaceholder
.slice(2)
.replace(/_+$/, "")
.replace(/\$/g, "\\$");
libraryAddresses[libraryIdentifier] = library.address;
}
}
}
const arraysOfLibs = Object.values(linkReferences).map((v) => Object.keys(v));
// This is just a flatten
const libs = [].concat.apply([], arraysOfLibs);
for (const lib of libraries) {
const libName = lib.constructor.contractName;
if (libs.length === 0) {
throw new plugins_1.BuidlerPluginError(`Tried to link contract ${destination.contractName} with library ${libName}, but it uses no libraries.`);
}
if (!libs.includes(libName)) {
throw new plugins_1.BuidlerPluginError(`Tried to link contract ${destination.contractName} with library ${libName}, but it's not one of its libraries. ${destination.contractName}'s libraries are: ${libs.join(", ")}`);
}
}
// We never save the network_id's nor change them, so they are all the same.
// We assigin one here just because TruffleContract needs one.
destination.setNetwork(libraries[0].constructor.network_id);
destination.link(libraryAddresses);
}
_getContractNameFromPath(contractPath) {
const basename = path_1.default.basename(contractPath);
const lastDotIndex = basename.lastIndexOf(".");
if (lastDotIndex === -1) {
return basename;
}
return basename.slice(0, lastDotIndex);
}
_getTruffleContract(contractName) {
const artifact = plugins_1.readArtifactSync(this._artifactsPath, contractName);
const TruffleContractFactory = require("@nomiclabs/truffle-contract");
const Contract = TruffleContractFactory(artifact);
return this._provisioner.provision(Contract, this);
}
}
exports.TruffleEnvironmentArtifacts = TruffleEnvironmentArtifacts;
//# sourceMappingURL=artifacts.js.map
;