Closed Craigson closed 4 weeks ago
Should also clarify that you should be able to link to an existing library that's already on chain. It looked like that functionality is available in example. Also the ability to link multiple libraries. This becomes especially useful when upgrading a facet of an eip2535 diamond contact.
Maybe it in the vm.linkLib pass in an array of libraries and address.
dynamic linking is explicitly not supported rn. imo such script is hardly reusable unless the linked lib is modified thus it should be split into two different scripts
@joshieDo @mattsse what are your thoughts on supporting this?
hmm, I can see how this could be useful.
but this comes with additional complexity where we now need to link ad-hoc.
@snapple42 is this example public? would like to take a look.
Unless I'm missing something, this is possible for single chain deployment (no createFork
stuff).
At present it's not possible to deploy contracts and the linked libraries they make use of in the same script.
You can, they'll get deployed and be present in the broadcast log. You might have to move their addresses to foundry.toml
later on if you don't want to keep re-deploying them.
@joshieDo can you share an example / implementation of this? I cannot find a working implementation that shows how to achieve this.
In our test suite: https://github.com/foundry-rs/foundry/blob/96fa8a05ecab85649058337d1095fda2b08c3234/cli/tests/it/script.rs#L320-L330 https://github.com/foundry-rs/foundry/blob/96fa8a05ecab85649058337d1095fda2b08c3234/testdata/cheats/Broadcast.t.sol#L36
You shouldn't have to do anything specific for it to work. It should just work. Maybe I'm misunderstading it?
In the above example the script contract BroadcastTest
deploys contract Test
which depends on library F
. If you notice the test, the first account 0
has a nonce increment of 2
, since it deploys F
first, and then Test
.
Let me know if I understood correctly.
hmm, I can see how this could be useful.
but this comes with additional complexity where we now need to link ad-hoc.
@snapple42 is this example public? would like to take a look.
Not really public yet with the Foundry updates, but you can look at our existing diamond (which currently doesn't use foundry, but this is what I'm upgrading) at:
https://louper.dev/diamond/0x1A6f75D56aDC64d22bA4B483011ED84d2593d224?network=binance
Idea would be to upgrade a facet (so deploy a new contract) and link it to existing poolUtil library at:
0x930C0290CEa0499e44Db4cbC97d3313CA19a657c (BSC CHAIN)
That would be good to be able to define the address to link to basically so we can reuse existing code/contracts, and we don't get out of sync with versions.
It's not that the library is huge, but would be convenient to be able to reference it instead of re-deploying it.
You should be able to reference it for single-chain deployments (no vm.createFork
stuff) on foundry.toml
without any re-deployment necessary.
Reference: https://book.getfoundry.sh/reference/config/solidity-compiler#libraries
@joshieDo , my issue I'm experiencing is that if I'm deploying for example 3 contracts that all make use of a shared linked lib (that has never been deployed before, therefore not already linked in the foundry.toml
). If this is the first time the lib is being deployed, 3 versions of the lib are deployed (one for each contract) when doing it via a forge script.
The equivalent in Hardhat might look like this:
beforeEach(async () => {
signers = await ethers.getSigners();
const Lib = await ethers.getContractFactory("MyLibrary");
const lib = await Lib.deploy();
await lib.deployed();
const contractFactory = await ethers.getContractFactory("SampleNft", {
signer: signers[0],
libraries: {
MyLibrary: lib.address,
},
});
contract = await contractFactory.deploy();
});
If deployment is handled via docker, for example, it's not practical to first deploy lib/s, then copy/paste into the foundry.toml
before deploying the contracts.
@joshieDo @mattsse hoping to get some more feedback here, as this is an issue I'm currently facing with a large deployment. When linked libs reference each other, foundry is deploying duplicate versions of the external libs. In my main project, I have 8 external libraries, but a total of 12 are getting deployed, presumably of the cross-linking in the libs, which leads to wasted funds on deployment and issues with verification. Here's an example that reproduces the issue:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
library LibA {
function doubleIt(uint256[] storage values) external {
uint256 length = values.length;
for (uint i; i < length; ) {
values[i] *= 2;
unchecked { ++i; }
}
}
}
library LibB {
function tripleIt(uint256[] storage values) external {
uint256 length = values.length;
for (uint i; i < length; ) {
values[i] *= 3;
unchecked { ++i; }
}
}
}
library LibC {
function quadrupleIt(uint256[] storage values) external {
LibA.doubleIt(values);
LibA.doubleIt(values);
}
}
contract LibRegistry {
address public a;
address public b;
address public c;
constructor () {
a = address(LibA);
b = address(LibB);
c = address(LibC);
}
}
Where LibA
gets deployed on two separate occasions.
Waiting for receipts.
⠂ [00:00:00] [#################################################################] 5/5 receipts (0.0s)
##### anvil-hardhat
✅ Hash: 0xd56e4e4131e2eaf0e4dd78c71b70fb2c586fca55c22301665420d99d59cc1609
Contract Address: 0x8464135c8f25da09e49bc8782676a84730c318bc
Block: 1
Paid: 0.000444498687845124 ETH (117892 gas * 3.770388897 gwei)
##### anvil-hardhat
✅ Hash: 0x33210eb0677a6510e1aeb54147d1c93b3aaf58ef9aa234b74f68e4adcb0c211f
Contract Address: 0x71c95911e9a5d330f4d621842ec243ee1343292e
Block: 2
Paid: 0.000444498687845124 ETH (117892 gas * 3.770388897 gwei)
##### anvil-hardhat
✅ Hash: 0xb74a5bf8b8b4487e9fdd65f5f6092b99df5b06b120acb53cd1d013c1907b10dc
Contract Address: 0x948b3c65b89df0b4894abe91e6d02fe579834f8f
Block: 2
Paid: 0.000444498687845124 ETH (117892 gas * 3.770388897 gwei)
##### anvil-hardhat
✅ Hash: 0xe1bddcf803699eb478066b58fc72f85f05b9ad9f57a3bfc7851dbf4aff70717f
Contract Address: 0x712516e61c8b383df4a63cfe83d7701bce54b03e
Block: 2
Paid: 0.000504493115974188 ETH (133804 gas * 3.770388897 gwei)
##### anvil-hardhat
✅ Hash: 0x643d191ad78d1dda2796e71814a1849dc164fa40e1e0c190c1a11537d6c930c1
Contract Address: 0xbcf26943c0197d2ee0e5d05c716be60cc2761508
Block: 2
Paid: 0.000623071846784838 ETH (165254 gas * 3.770388897 gwei)
The run-latest.json
includes the 3 libraries, but doesn't take into account the duplicates. It's also been causing issues with verification, where I can't verify certain contracts that are referenced by other contracts.
"libraries": [
"src/LibRegistry.sol:LibA:0x948b3c65b89df0b4894abe91e6d02fe579834f8f",
"src/LibRegistry.sol:LibB:0x71c95911e9a5d330f4d621842ec243ee1343292e",
"src/LibRegistry.sol:LibC:0x712516e61c8b383df4a63cfe83d7701bce54b03e"
],
@joshieDo , my issue I'm experiencing is that if I'm deploying for example 3 contracts that all make use of a shared linked lib (that has never been deployed before, therefore not already linked in the
foundry.toml
). If this is the first time the lib is being deployed, 3 versions of the lib are deployed (one for each contract) when doing it via a forge script.The equivalent in Hardhat might look like this:
beforeEach(async () => { signers = await ethers.getSigners(); const Lib = await ethers.getContractFactory("MyLibrary"); const lib = await Lib.deploy(); await lib.deployed(); const contractFactory = await ethers.getContractFactory("SampleNft", { signer: signers[0], libraries: { MyLibrary: lib.address, }, }); contract = await contractFactory.deploy(); });
If deployment is handled via docker, for example, it's not practical to first deploy lib/s, then copy/paste into the
foundry.toml
before deploying the contracts.
This is the example, is possible replicate that in Foundry Forge?? because is a lot usefull for develop, in diamond pattern !!, because avoid redeploy in two steps and reduce a lot the time!!!
@joshieDo @mattsse hoping to get some more feedback here, as this is an issue I'm currently facing with a large deployment. When linked libs reference each other, foundry is deploying duplicate versions of the external libs. In my main project, I have 8 external libraries, but a total of 12 are getting deployed, presumably of the cross-linking in the libs, which leads to wasted funds on deployment and issues with verification. Here's an example that reproduces the issue:
// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; library LibA { function doubleIt(uint256[] storage values) external { uint256 length = values.length; for (uint i; i < length; ) { values[i] *= 2; unchecked { ++i; } } } } library LibB { function tripleIt(uint256[] storage values) external { uint256 length = values.length; for (uint i; i < length; ) { values[i] *= 3; unchecked { ++i; } } } } library LibC { function quadrupleIt(uint256[] storage values) external { LibA.doubleIt(values); LibA.doubleIt(values); } } contract LibRegistry { address public a; address public b; address public c; constructor () { a = address(LibA); b = address(LibB); c = address(LibC); } }
Where
LibA
gets deployed on two separate occasions.Waiting for receipts. ⠂ [00:00:00] [#################################################################] 5/5 receipts (0.0s) ##### anvil-hardhat ✅ Hash: 0xd56e4e4131e2eaf0e4dd78c71b70fb2c586fca55c22301665420d99d59cc1609 Contract Address: 0x8464135c8f25da09e49bc8782676a84730c318bc Block: 1 Paid: 0.000444498687845124 ETH (117892 gas * 3.770388897 gwei) ##### anvil-hardhat ✅ Hash: 0x33210eb0677a6510e1aeb54147d1c93b3aaf58ef9aa234b74f68e4adcb0c211f Contract Address: 0x71c95911e9a5d330f4d621842ec243ee1343292e Block: 2 Paid: 0.000444498687845124 ETH (117892 gas * 3.770388897 gwei) ##### anvil-hardhat ✅ Hash: 0xb74a5bf8b8b4487e9fdd65f5f6092b99df5b06b120acb53cd1d013c1907b10dc Contract Address: 0x948b3c65b89df0b4894abe91e6d02fe579834f8f Block: 2 Paid: 0.000444498687845124 ETH (117892 gas * 3.770388897 gwei) ##### anvil-hardhat ✅ Hash: 0xe1bddcf803699eb478066b58fc72f85f05b9ad9f57a3bfc7851dbf4aff70717f Contract Address: 0x712516e61c8b383df4a63cfe83d7701bce54b03e Block: 2 Paid: 0.000504493115974188 ETH (133804 gas * 3.770388897 gwei) ##### anvil-hardhat ✅ Hash: 0x643d191ad78d1dda2796e71814a1849dc164fa40e1e0c190c1a11537d6c930c1 Contract Address: 0xbcf26943c0197d2ee0e5d05c716be60cc2761508 Block: 2 Paid: 0.000623071846784838 ETH (165254 gas * 3.770388897 gwei)
The
run-latest.json
includes the 3 libraries, but doesn't take into account the duplicates. It's also been causing issues with verification, where I can't verify certain contracts that are referenced by other contracts."libraries": [ "src/LibRegistry.sol:LibA:0x948b3c65b89df0b4894abe91e6d02fe579834f8f", "src/LibRegistry.sol:LibB:0x71c95911e9a5d330f4d621842ec243ee1343292e", "src/LibRegistry.sol:LibC:0x712516e61c8b383df4a63cfe83d7701bce54b03e" ],
@Craigson I cannot reproduce with the latest version. It was fixed by #5164 and then by a full rewrite of the linker here #7027
Closing this for now. Feel free to reopen if you still encounter this.
Component
Forge
Describe the feature you would like
At present it's not possible to deploy contracts and the linked libraries they make use of in the same script.
The current steps to achieve this are as follows:
foundry.toml
file using the format outlined in the docs:<file>:<lib>:<address>
A possible solution could be the introduction of a cheatcode such as
linkLib
.Additional context
No response