diff --git a/package.json b/package.json index e7fc8da..1b80fc7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gnosis-guild/zodiac-core", - "version": "2.0.3", + "version": "2.0.4", "description": "Zodiac is a composable design philosophy and collection of standards for building DAO ecosystem tooling.", "author": "Auryn Macmillan ", "license": "LGPL-3.0+", diff --git a/src/artifact/internal/getBuildArtifact.ts b/src/artifact/internal/getBuildArtifact.ts index 2781c65..e24e6f7 100644 --- a/src/artifact/internal/getBuildArtifact.ts +++ b/src/artifact/internal/getBuildArtifact.ts @@ -40,68 +40,6 @@ export default function getBuildArtifact( }; } -/** - * 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} linkReferences - References to libraries, as returned by the compiler. - * @param {Record} 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. - */ -export function resolveLinksInBytecode( - contractVersion: string, - artifact: BuildArtifact, - mastercopies: Record> -): string { - let bytecode = artifact.bytecode; - - for (const libraryPath of Object.keys(artifact.linkReferences)) { - for (const libraryName of Object.keys( - artifact.linkReferences[libraryPath] - )) { - console.log(`libraryPath ${libraryPath} libraryName ${libraryName}`); - - if ( - !mastercopies[libraryName] || - !mastercopies[libraryName][contractVersion] - ) { - throw new Error( - `Could not link ${libraryName} for ${artifact.contractName}` - ); - } - - let { address: libraryAddress } = - mastercopies[libraryName][contractVersion]; - - assert(isAddress(libraryAddress)); - - for (const { length, start: offset } of artifact.linkReferences[ - libraryPath - ][libraryName]) { - assert(length == 20); - - // the offset is in bytes, and does not account for the trailing 0x - const left = 2 + offset * 2; - const right = left + length * 2; - - bytecode = `${bytecode.slice(0, left)}${libraryAddress.slice(2).toLowerCase()}${bytecode.slice(right)}`; - - console.log( - `Replaced library reference at ${offset} with address ${libraryAddress}` - ); - } - } - } - - return bytecode; -} - /** * Resolves the paths to the artifact and build info files for a specified contract. * diff --git a/src/artifact/internal/linkBuildArtifact.ts b/src/artifact/internal/linkBuildArtifact.ts new file mode 100644 index 0000000..0fe1767 --- /dev/null +++ b/src/artifact/internal/linkBuildArtifact.ts @@ -0,0 +1,128 @@ +import assert from "assert"; +import { isAddress } from "ethers"; + +import { BuildArtifact, MastercopyArtifact } from "../../types"; + +/** + * Resolves library links in a build artifact + * + */ +export default function linkBuildArtifact({ + artifact, + contractVersion, + minimalCompilerInput, + mastercopies, +}: { + artifact: BuildArtifact; + contractVersion: string; + minimalCompilerInput?: string; + mastercopies: Record>; +}): BuildArtifact { + const bytecode = linkBytecode(artifact, contractVersion, mastercopies); + const compilerInput = linkCompilerInput( + artifact, + contractVersion, + minimalCompilerInput || artifact.compilerInput, + mastercopies + ); + + return { + ...artifact, + bytecode, + compilerInput, + }; +} + +/** + * 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} linkReferences - References to libraries, as returned by the compiler. + * @param {Record} 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 linkBytecode( + artifact: BuildArtifact, + contractVersion: string, + mastercopies: Record> +): string { + let bytecode = artifact.bytecode; + + for (const libraryPath of Object.keys(artifact.linkReferences)) { + for (const libraryName of Object.keys( + artifact.linkReferences[libraryPath] + )) { + console.log(`libraryPath ${libraryPath} libraryName ${libraryName}`); + + if ( + !mastercopies[libraryName] || + !mastercopies[libraryName][contractVersion] + ) { + throw new Error( + `Could not link ${libraryName} for ${artifact.contractName}` + ); + } + + let { address: libraryAddress } = + mastercopies[libraryName][contractVersion]; + + assert(isAddress(libraryAddress)); + + for (const { length, start: offset } of artifact.linkReferences[ + libraryPath + ][libraryName]) { + assert(length == 20); + + // the offset is in bytes, and does not account for the trailing 0x + const left = 2 + offset * 2; + const right = left + length * 2; + + bytecode = `${bytecode.slice(0, left)}${libraryAddress.slice(2).toLowerCase()}${bytecode.slice(right)}`; + + console.log( + `Replaced library reference at ${offset} with address ${libraryAddress}` + ); + } + } + } + + return bytecode; +} + +function linkCompilerInput( + artifact: BuildArtifact, + contractVersion: string, + compilerInput: any, + mastercopies: Record> +): any { + const result = { ...compilerInput }; + for (const libraryPath of Object.keys(artifact.linkReferences)) { + for (const libraryName of Object.keys( + artifact.linkReferences[libraryPath] + )) { + const libraryAddress = + mastercopies[libraryName]?.[contractVersion]?.address; + if (!libraryAddress) { + continue; + } + + assert(isAddress(libraryAddress)); + + result.settings = { + ...result.settings, + libraries: { + ...result.settings.libraries, + [libraryPath]: { [libraryName]: libraryAddress }, + }, + }; + } + } + + return result; +} diff --git a/src/artifact/writeMastercopyFromBuild.ts b/src/artifact/writeMastercopyFromBuild.ts index 114c7c2..668da2c 100644 --- a/src/artifact/writeMastercopyFromBuild.ts +++ b/src/artifact/writeMastercopyFromBuild.ts @@ -8,9 +8,8 @@ import { defaultBuildDir, defaultMastercopyArtifactsFile, } from "./internal/paths"; -import getBuildArtifact, { - resolveLinksInBytecode, -} from "./internal/getBuildArtifact"; +import getBuildArtifact from "./internal/getBuildArtifact"; +import linkBuildArtifact from "./internal/linkBuildArtifact"; import { MastercopyArtifact } from "../types"; @@ -49,7 +48,7 @@ export default function writeMastercopyFromBuild({ compilerInput?: any; buildDirPath?: string; mastercopyArtifactsFile?: string; -}) { +}): MastercopyArtifact { const buildArtifact = getBuildArtifact(contractName, buildDirPath); const mastercopies = existsSync(mastercopyArtifactsFile) @@ -60,11 +59,12 @@ export default function writeMastercopyFromBuild({ console.warn(`Warning: overriding artifact for ${contractVersion}`); } - const bytecode = resolveLinksInBytecode( + const artifact = linkBuildArtifact({ + artifact: buildArtifact, contractVersion, - buildArtifact, - mastercopies - ); + minimalCompilerInput, + mastercopies, + }); const mastercopyArtifact: MastercopyArtifact = { contractName, @@ -74,15 +74,15 @@ export default function writeMastercopyFromBuild({ factory, address: predictSingletonAddress({ factory, - bytecode, + bytecode: artifact.bytecode, constructorArgs, salt, }), - bytecode, + bytecode: artifact.bytecode, constructorArgs, salt, abi: buildArtifact.abi, - compilerInput: minimalCompilerInput || buildArtifact.compilerInput, + compilerInput: artifact.compilerInput, }; const nextMastercopies = { @@ -111,4 +111,6 @@ export default function writeMastercopyFromBuild({ JSON.stringify(sortedMastercopies, null, 2), "utf8" ); + + return mastercopyArtifact; } diff --git a/src/artifact/writeMastercopyFromExplorer.ts b/src/artifact/writeMastercopyFromExplorer.ts index dad8bcc..51d17c8 100644 --- a/src/artifact/writeMastercopyFromExplorer.ts +++ b/src/artifact/writeMastercopyFromExplorer.ts @@ -106,4 +106,6 @@ export default async function writeMastercopyFromExplorer({ JSON.stringify(sortedMastercopies, null, 2), "utf8" ); + + return mastercopyArtifact; }