diff --git a/Cargo.lock b/Cargo.lock index cb478af64..2d70922a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5115,7 +5115,7 @@ dependencies = [ [[package]] name = "foundry-compilers" version = "0.11.6" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#7c69695e5c75451f158dd2456bf8c94a7492ea0b" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#4f9527f5104c4ed5d5e67cb53404fd054962d416" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5155,7 +5155,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" version = "0.11.6" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#7c69695e5c75451f158dd2456bf8c94a7492ea0b" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#4f9527f5104c4ed5d5e67cb53404fd054962d416" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -5165,7 +5165,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" version = "0.11.6" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#7c69695e5c75451f158dd2456bf8c94a7492ea0b" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#4f9527f5104c4ed5d5e67cb53404fd054962d416" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5188,7 +5188,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" version = "0.11.6" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#7c69695e5c75451f158dd2456bf8c94a7492ea0b" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#4f9527f5104c4ed5d5e67cb53404fd054962d416" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5202,7 +5202,7 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-zksolc" version = "0.11.6" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#7c69695e5c75451f158dd2456bf8c94a7492ea0b" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#4f9527f5104c4ed5d5e67cb53404fd054962d416" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -5223,7 +5223,7 @@ dependencies = [ [[package]] name = "foundry-compilers-core" version = "0.11.6" -source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#7c69695e5c75451f158dd2456bf8c94a7492ea0b" +source = "git+https://github.com/Moonsong-Labs/compilers?branch=zksync-v0.11.6#4f9527f5104c4ed5d5e67cb53404fd054962d416" dependencies = [ "alloy-primitives", "cfg-if 1.0.0", diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 5ffed0bf4..70575802f 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -394,7 +394,7 @@ impl ProjectCompiler { let is_target_file = self.files.is_empty() || self.files.iter().any(|f| artifact_id.path == *f); if is_target_file { - if let Some(mls) = &artifact.missing_libraries { + if let Some(mls) = artifact.missing_libraries() { missing_libs_unique.extend(mls.clone()); } } @@ -422,7 +422,7 @@ impl ProjectCompiler { ZkMissingLibrary { contract_path: contract_path.to_string(), contract_name: contract_name.to_string(), - missing_libraries: art.missing_libraries.clone().unwrap_or_default(), + missing_libraries: art.missing_libraries().cloned().unwrap_or_default(), } }) .collect(); diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 627681a93..20d7c1978 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -158,10 +158,28 @@ impl CreateArgs { let ZkContractArtifact { bytecode, hash, factory_dependencies, abi, .. } = artifact; let abi = abi.expect("Abi not found"); - let bin = bytecode.expect("Bytecode not found"); + + let bytecode = match bin.object() { + BytecodeObject::Bytecode(bytes) => bytes.to_vec(), + _ => { + let link_refs = bin + .missing_libraries + .iter() + .map(|library| { + let mut parts = library.split(':'); + let path = parts.next().unwrap(); + let name = parts.next().unwrap(); + format!("\t{name}: {path}") + }) + .collect::>() + .into_iter() + .collect::>() + .join("\n"); + eyre::bail!("Dynamic linking not supported in `create` command - deploy the following library contracts first, then provide the address to link at compile time\n{}", link_refs) + } + }; let bytecode_hash = H256::from_str(&hash.expect("Contract hash not found"))?; - let bytecode = bin.object.clone().into_bytes().unwrap().to_vec(); // Add arguments to constructor let config = self.eth.try_load_config_emit_warnings()?; @@ -221,12 +239,12 @@ impl CreateArgs { queue.push_back(dep.clone()) } + // TODO(zk): ensure factory deps are also linked let fdep_bytecode = fdep_art .bytecode .clone() .expect("Bytecode not found for factory dependency") - .object - .clone() + .object() .into_bytes() .unwrap() .to_vec(); @@ -243,7 +261,7 @@ impl CreateArgs { let sender = self.eth.wallet.from.expect("required"); self.deploy_zk( abi, - bin.object, + bin.object(), params, provider, chain_id, @@ -262,7 +280,7 @@ impl CreateArgs { let provider = ProviderBuilder::<_, _, AnyNetwork>::default().on_provider(provider); self.deploy_zk( abi, - bin.object, + bin.object(), params, provider, chain_id, diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 66fb2e3e1..2356d9b99 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -467,7 +467,8 @@ impl MultiContractRunnerBuilder { if let Some(abi) = contract.abi { let bytecode = contract.bytecode.as_ref(); - if let Some(bytecode_object) = bytecode.map(|b| b.object.clone()) { + // TODO(zk): retrieve link_references + if let Some(bytecode_object) = bytecode.map(|b| b.object()) { let compact_bytecode = CompactBytecode { object: bytecode_object.clone(), source_map: None, diff --git a/crates/forge/tests/fixtures/zk/Libraries.s.sol b/crates/forge/tests/fixtures/zk/Libraries.s.sol new file mode 100644 index 000000000..195d4dd7b --- /dev/null +++ b/crates/forge/tests/fixtures/zk/Libraries.s.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.8.7 <0.9.0; + +import {UsesFoo} from "../src/WithLibraries.sol"; +import "forge-std/Script.sol"; + +contract DeployUsesFoo is Script { + function run () external { + // should fail because `UsesFoo` is unlinked + bytes memory _code = vm.getCode("UsesFoo"); + } +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 2e3fd743a..6e2f39075 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -213,7 +213,7 @@ impl ForgeTestProfile { zk_config.zksync.startup = true; zk_config.zksync.fallback_oz = true; zk_config.zksync.optimizer_mode = '3'; - zk_config.zksync.zksolc = Some(foundry_config::SolcReq::Version(Version::new(1, 5, 4))); + zk_config.zksync.zksolc = Some(foundry_config::SolcReq::Version(Version::new(1, 5, 7))); zk_config.fuzz.no_zksync_reserved_addresses = true; zk_config diff --git a/crates/forge/tests/it/zk/linking.rs b/crates/forge/tests/it/zk/linking.rs new file mode 100644 index 000000000..679e34ca5 --- /dev/null +++ b/crates/forge/tests/it/zk/linking.rs @@ -0,0 +1,54 @@ +use foundry_test_utils::{forgetest_async, util, TestProject}; + +use crate::test_helpers::{deploy_zk_contract, run_zk_script_test}; + +// TODO(zk): add test that actually does the deployment +// of the unlinked contract via script, once recursive linking is supported +// and once we also support doing deploy-time linking + +forgetest_async!( + #[should_panic = "no bytecode for contract; is it abstract or unlinked?"] + script_using_unlinked_fails, + |prj, cmd| { + setup_libs_prj(&mut prj); + run_zk_script_test( + prj.root(), + &mut cmd, + "./script/Libraries.s.sol", + "DeployUsesFoo", + None, + 1, + Some(&["-vvvvv"]), + ); + } +); + +forgetest_async!( + #[should_panic = "Dynamic linking not supported"] + create_using_unlinked_fails, + |prj, cmd| { + setup_libs_prj(&mut prj); + + // we don't really connect to the rpc because + // we expect to fail before that point + let foo_address = deploy_zk_contract( + &mut cmd, + "127.0.0.1:1234", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "./src/WithLibraries.sol:UsesFoo", + ) + .expect("Failed to deploy UsesFoo contract"); + + assert!(!foo_address.is_empty(), "Deployed address should not be empty"); + } +); + +fn setup_libs_prj(prj: &mut TestProject) { + util::initialize(prj.root()); + prj.add_script("Libraries.s.sol", include_str!("../../fixtures/zk/Libraries.s.sol")).unwrap(); + prj.add_source( + "WithLibraries.sol", + include_str!("../../../../../testdata/zk/WithLibraries.sol"), + ) + .unwrap(); +} diff --git a/crates/forge/tests/it/zk/mod.rs b/crates/forge/tests/it/zk/mod.rs index bbe6cda52..6ee28853d 100644 --- a/crates/forge/tests/it/zk/mod.rs +++ b/crates/forge/tests/it/zk/mod.rs @@ -10,6 +10,7 @@ mod factory_deps; mod fork; mod fuzz; mod invariant; +mod linking; mod logs; mod nft; mod ownership; diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index c12a15b99..3bb0da6a7 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -154,7 +154,8 @@ impl LinkedBuildData { for (id, contract) in zk_contracts { if let Some(abi) = contract.abi { let bytecode = contract.bytecode.as_ref(); - if let Some(bytecode_object) = bytecode.map(|b| b.object.clone()) { + // TODO(zk): retrieve link_references + if let Some(bytecode_object) = bytecode.map(|b| b.object()) { let compact_bytecode = CompactBytecode { object: bytecode_object.clone(), source_map: None, diff --git a/crates/zksync/compiler/src/lib.rs b/crates/zksync/compiler/src/lib.rs index 194ed318c..69b086b04 100644 --- a/crates/zksync/compiler/src/lib.rs +++ b/crates/zksync/compiler/src/lib.rs @@ -76,7 +76,7 @@ pub fn config_create_project( { zksolc } else if !config.offline { - let default_version = semver::Version::new(1, 5, 4); + let default_version = semver::Version::new(1, 5, 7); let mut zksolc = ZkSolc::find_installed_version(&default_version)?; if zksolc.is_none() { ZkSolc::blocking_install(&default_version)?; diff --git a/crates/zksync/compiler/src/zksolc/mod.rs b/crates/zksync/compiler/src/zksolc/mod.rs index 974b881ec..f4e3b0699 100644 --- a/crates/zksync/compiler/src/zksolc/mod.rs +++ b/crates/zksync/compiler/src/zksolc/mod.rs @@ -136,7 +136,7 @@ impl DualCompiledContracts { // at this stage for zksolc, and BytecodeObject as ref will get the bytecode bytes. // We should be careful however and check/handle errors in // case an Unlinked BytecodeObject gets here somehow - let bytes = bytecode.object.clone().into_bytes().unwrap(); + let bytes = bytecode.object().into_bytes().unwrap(); zksolc_all_bytecodes.insert(hash.clone(), bytes.to_vec()); } } @@ -167,7 +167,7 @@ impl DualCompiledContracts { // bytes. However, we should check and // handle errors in case an Unlinked BytecodeObject gets // here somehow - let bytecode_vec = bytecode.object.clone().into_bytes().unwrap().to_vec(); + let bytecode_vec = bytecode.object().into_bytes().unwrap().to_vec(); let mut factory_deps_vec: Vec> = factory_deps_map .keys() .map(|factory_hash| { diff --git a/testdata/zk/WithLibraries.sol b/testdata/zk/WithLibraries.sol new file mode 100644 index 000000000..51ca18879 --- /dev/null +++ b/testdata/zk/WithLibraries.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.8.7 <0.9.0; + +library Foo { + function add(uint256 a, uint256 b) external pure returns (uint256 c) { + c = a + b; + } +} + +contract UsesFoo { + uint256 number; + + constructor() { + number = Foo.add(42, 0); + } +}