Skip to content

Commit

Permalink
Add linkReferences map as optional parameter to linkBytecode function
Browse files Browse the repository at this point in the history
Co-authored-by: arthcp <arth.c.patel@gmail.com>
  • Loading branch information
r0qs committed Nov 26, 2022
1 parent fc232fe commit 1bf0d30
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 7 deletions.
26 changes: 19 additions & 7 deletions linker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,24 @@ function libraryHashPlaceholder (fullyQualifiedLibraryName) {
*
* @param address Address to replace placeholders with. Must be the right length.
* It will **not** be padded with zeros if too short.
*
* @param linkReferences A optional mapping of libraries to lists of placeholder positions in the binary.
*/
function replacePlaceholder (bytecode, label, address) {
function replacePlaceholder (bytecode: string, label: string, address: string, linkReferences?: LinkReferences): string {
// Try to find link references if `linkReferences` is not provided
if (!linkReferences) {
linkReferences = findLinkReferences(bytecode);
}

// truncate to 36 characters
const truncatedName = label.slice(0, 36);
const libLabel = `__${truncatedName.padEnd(36, '_')}__`;

while (bytecode.indexOf(libLabel) >= 0) {
bytecode = bytecode.replace(libLabel, address);
if (linkReferences && linkReferences[truncatedName]) {
linkReferences[truncatedName].forEach(function (reference) {
const start = reference.start * 2;
const end = (reference.start + reference.length) * 2;
bytecode = bytecode.replace(bytecode.substring(start, end), address);
});
}

return bytecode;
Expand All @@ -55,10 +65,12 @@ function replacePlaceholder (bytecode, label, address) {
* @param libraries Mapping between fully qualified library names and the hex-encoded
* addresses they should be replaced with. Addresses shorter than 40 characters are automatically padded with zeros.
*
* @param linkReferences A optional mapping of libraries to lists of placeholder positions in the binary.
*
* @returns bytecode Hex-encoded bytecode string with placeholders replaced with addresses.
* Note that some placeholders may remain in the bytecode if `libraries` does not provide addresses for all of them.
*/
function linkBytecode (bytecode: string, libraries: LibraryAddresses): string {
function linkBytecode (bytecode: string, libraries: LibraryAddresses, linkReferences?: LinkReferences): string {
assert(typeof bytecode === 'string');
assert(typeof libraries === 'object');

Expand Down Expand Up @@ -103,8 +115,8 @@ function linkBytecode (bytecode: string, libraries: LibraryAddresses): string {
// remove 0x prefix
hexAddress = hexAddress.slice(2).padStart(40, '0');

bytecode = replacePlaceholder(bytecode, libraryName, hexAddress);
bytecode = replacePlaceholder(bytecode, libraryHashPlaceholder(libraryName), hexAddress);
bytecode = replacePlaceholder(bytecode, libraryName, hexAddress, linkReferences);
bytecode = replacePlaceholder(bytecode, libraryHashPlaceholder(libraryName), hexAddress, linkReferences);
}

return bytecode;
Expand Down
32 changes: 32 additions & 0 deletions test/linker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,36 @@ tape('Linking', function (t) {
st.ok(bytecode.indexOf('_') < 0);
st.end();
});

t.test('link properly by providing link reference', function (st) {
/*
'lib.sol': 'library L { function f() public returns (uint) { return 7; } }',
'cont.sol': 'import "lib.sol"; contract x { function g() public { L.f(); } }'
*/
let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029';
bytecode = linker.linkBytecode(bytecode, { 'lib.sol:L': '0x123456' }, { 'lib.sol:L': [{ start: 122, length: 20 }] });
st.ok(bytecode.indexOf('_') < 0);
st.end();
});

t.test('link properly by providing two references with same library name', function (st) {
let bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__lib2.sol:L____________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080fd5b5050505600a165627a7a72305820fdfb8eab411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029';
bytecode = linker.linkBytecode(bytecode, { 'lib2.sol:L': '0x123456' }, { 'lib2.sol:L': [{ start: 92, length: 20 }, { start: 180, length: 20 }] });
st.ok(bytecode.indexOf('_') < 0);
st.end();
});

t.test('link properly by providing multiple references for multiple libraries', function (st) {
let bytecode = '6060604052341561000f57600080fd5b61011a8061001e6000396000f30060606040526004361060255763ffffffff60e060020a60003504166326121ff08114602a575b600080fd5b3415603457600080fd5b603a603c565b005b73__lib2.sol:Lx___________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b1515608157600080fd5b6102c65a03f41515609157600080fd5b50505073__lib2.sol:Lx___________________________6326121ff06040518163ffffffff1660e060020a02815260040160006040518083038186803b151560d957600080fd5b6102c65a03f4151560e957600080__lib1.sol:L____________________________411d7bc86d7dfbb0c985c30bebf1cc105dc5b807291551b3d5aa29d90029';
bytecode = linker.linkBytecode(bytecode, { 'lib1.sol:L': '0x123456', 'lib2.sol:Lx': '0x789012' }, { 'lib2.sol:Lx': [{ start: 92, length: 20 }, { start: 180, length: 20 }], 'lib1.sol:L': [{ start: 262, length: 20 }] });
st.ok(bytecode.indexOf('_') < 0);
st.end();
});

t.test('linker fails if wrong link references is provided', function (st) {
let bytecode = '608060405234801561001057600080fd5b5061011f806100206000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b73__lib.sol:L_____________________________6326121ff06040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801560b757600080fd5b505af415801560ca573d6000803e3d6000fd5b505050506040513d602081101560df57600080fd5b8101908080519060200190929190505050505600a165627a7a72305820ea2f6353179c181d7162544d637b7fe2d9e8da9803a0e2d9eafc2188d1d59ee30029';
bytecode = linker.linkBytecode(bytecode, { 'lib1.sol:L': '0x123456' }, {});
st.ok(bytecode.indexOf('_') >= 0);
st.end();
});
});

0 comments on commit 1bf0d30

Please sign in to comment.