Skip to content

Commit

Permalink
fix: library reference substitution
Browse files Browse the repository at this point in the history
  • Loading branch information
juliopavila committed Sep 4, 2024
1 parent effb14d commit 8f02184
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 6 deletions.
94 changes: 90 additions & 4 deletions src/artifact/internal/getBuildArtifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,67 @@ import path from "path";
import { readdirSync, readFileSync, statSync } from "fs";

import { BuildArtifact } from "../../types";
import { getCreate2Address, keccak256 } from "ethers";

/**
* Replaces library references in the bytecode with actual deployed addresses.
*
* This function scans the bytecode and replaces placeholder references
* to libraries with their actual on-chain addresses. It ensures that
* the library addresses are valid and properly formatted.
*
* @param {string} bytecode - The bytecode that may contain library references.
* @param {Record<string, any>} linkReferences - References to libraries, as returned by the compiler.
* @param {Record<string, string>} libraryAddresses - A map of library names to their deployed addresses.
* @returns {string} - The updated bytecode with library references replaced by actual addresses.
*
* @throws {Error} - Throws if a library address is missing or incorrectly formatted.
*/
function replaceLibraryReferences(
bytecode: string,
linkReferences: Record<string, any>,
libraryAddresses: Record<string, string>
): string {
// Iterate through each source file in the linkReferences object
for (const sourceFile in linkReferences) {
// Iterate through each library name in the source file
for (const libName in linkReferences[sourceFile]) {
const libAddress = libraryAddresses[libName];

// Ensure that the library address is provided
if (!libAddress) {
throw new Error(`Library address for "${libName}" not found.`);
}

// Ensure the address is properly formatted (without '0x' and exactly 40 characters long)
const addressHex = libAddress.toLowerCase().replace("0x", "");
if (addressHex.length !== 40) {
throw new Error(
`Invalid library address for "${libName}": ${libAddress}`
);
}

// Iterate through each reference of the library within the bytecode
linkReferences[sourceFile][libName].forEach(
(ref: { start: number; length: number }) => {
// Extract the placeholder in the bytecode corresponding to the library
const placeholder = bytecode.slice(
ref.start * 2,
(ref.start + ref.length) * 2
);

// Replace the placeholder with the correctly padded address (if necessary)
const paddedAddress = addressHex.padEnd(ref.length * 2, "0");
bytecode = bytecode.replace(placeholder, paddedAddress);
}
);
}
}

// Remove any trailing underscores or erroneous placeholders

return bytecode.replace("__", "");
}

/**
* Retrieves the build artifact for a specified contract.
Expand All @@ -13,16 +74,41 @@ import { BuildArtifact } from "../../types";
*/
export default function getBuildArtifact(
_contractName: string,
buildDirPath: string
buildDirPath: string,
factory: string,
salt: string
): BuildArtifact {
const { artifactPath, buildInfoPath } = resolvePaths(
_contractName,
buildDirPath
);

const { sourceName, contractName, bytecode, abi } = JSON.parse(
readFileSync(artifactPath, "utf8")
);
const {
sourceName,
contractName,
bytecode: artifactBytecode,
abi,
linkReferences,
} = JSON.parse(readFileSync(artifactPath, "utf8"));
let bytecode = artifactBytecode;
if (linkReferences && Object.keys(linkReferences).length > 0) {
let libraryAddresses: Record<string, string> = {};
Object.keys(linkReferences).forEach((sourceFile) => {
Object.keys(linkReferences[sourceFile]).forEach((libName) => {
console.log(`Processing library: ${libName}`);
const { artifactPath } = resolvePaths(libName, buildDirPath);
const { bytecode } = JSON.parse(readFileSync(artifactPath, "utf8"));
const address = getCreate2Address(factory, salt, keccak256(bytecode));
libraryAddresses = { ...libraryAddresses, [libName]: address };
});
});

bytecode = replaceLibraryReferences(
artifactBytecode,
linkReferences,
libraryAddresses
);
}

const { solcLongVersion, input } = JSON.parse(
readFileSync(buildInfoPath, "utf8")
Expand Down
9 changes: 7 additions & 2 deletions src/artifact/writeMastercopyFromBuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ export default function writeMastercopyFromBuild({
buildDirPath?: string;
mastercopyArtifactsFile?: string;
}) {
const buildArtifact = getBuildArtifact(contractName, buildDirPath);
const buildArtifact = getBuildArtifact(
contractName,
buildDirPath,
factory,
salt
);

const mastercopies = existsSync(mastercopyArtifactsFile)
? JSON.parse(readFileSync(mastercopyArtifactsFile, "utf8"))
Expand All @@ -57,7 +62,7 @@ export default function writeMastercopyFromBuild({
if (mastercopies[contractVersion]) {
console.warn(`Warning: overriding artifact for ${contractVersion}`);
}

console.log("buildArtifact.bytecode", buildArtifact.bytecode);
const mastercopyArtifact: MastercopyArtifact = {
contractName,
sourceName: buildArtifact.sourceName,
Expand Down

0 comments on commit 8f02184

Please sign in to comment.