Skip to content

Commit c1e34ac

Browse files
authored
doc(cheats): deployCode reverts w/ unlinked artifact libs (#12212)
* doc(cheats): explain + test that `deployCode` reverts when artifact contains unlinked libs * chore: run cargo cheats
1 parent bd66711 commit c1e34ac

File tree

3 files changed

+34
-9
lines changed

3 files changed

+34
-9
lines changed

crates/cheatcodes/assets/cheatcodes.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cheatcodes/spec/src/vm.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,51 +1925,59 @@ interface Vm {
19251925

19261926
/// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the
19271927
/// artifact in the form of <path>:<contract>:<version> where <contract> and <version> parts are optional.
1928+
/// Reverts if the target artifact contains unlinked library placeholders.
19281929
#[cheatcode(group = Filesystem)]
19291930
function deployCode(string calldata artifactPath) external returns (address deployedAddress);
19301931

19311932
/// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the
19321933
/// artifact in the form of <path>:<contract>:<version> where <contract> and <version> parts are optional.
1934+
/// Reverts if the target artifact contains unlinked library placeholders.
19331935
///
19341936
/// Additionally accepts abi-encoded constructor arguments.
19351937
#[cheatcode(group = Filesystem)]
19361938
function deployCode(string calldata artifactPath, bytes calldata constructorArgs) external returns (address deployedAddress);
19371939

19381940
/// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the
19391941
/// artifact in the form of <path>:<contract>:<version> where <contract> and <version> parts are optional.
1942+
/// Reverts if the target artifact contains unlinked library placeholders.
19401943
///
19411944
/// Additionally accepts `msg.value`.
19421945
#[cheatcode(group = Filesystem)]
19431946
function deployCode(string calldata artifactPath, uint256 value) external returns (address deployedAddress);
19441947

19451948
/// Deploys a contract from an artifact file. Takes in the relative path to the json file or the path to the
19461949
/// artifact in the form of <path>:<contract>:<version> where <contract> and <version> parts are optional.
1950+
/// Reverts if the target artifact contains unlinked library placeholders.
19471951
///
19481952
/// Additionally accepts abi-encoded constructor arguments and `msg.value`.
19491953
#[cheatcode(group = Filesystem)]
19501954
function deployCode(string calldata artifactPath, bytes calldata constructorArgs, uint256 value) external returns (address deployedAddress);
19511955

19521956
/// Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the
19531957
/// artifact in the form of <path>:<contract>:<version> where <contract> and <version> parts are optional.
1958+
/// Reverts if the target artifact contains unlinked library placeholders.
19541959
#[cheatcode(group = Filesystem)]
19551960
function deployCode(string calldata artifactPath, bytes32 salt) external returns (address deployedAddress);
19561961

19571962
/// Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the
19581963
/// artifact in the form of <path>:<contract>:<version> where <contract> and <version> parts are optional.
1964+
/// Reverts if the target artifact contains unlinked library placeholders.
19591965
///
19601966
/// Additionally accepts abi-encoded constructor arguments.
19611967
#[cheatcode(group = Filesystem)]
19621968
function deployCode(string calldata artifactPath, bytes calldata constructorArgs, bytes32 salt) external returns (address deployedAddress);
19631969

19641970
/// Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the
19651971
/// artifact in the form of <path>:<contract>:<version> where <contract> and <version> parts are optional.
1972+
/// Reverts if the target artifact contains unlinked library placeholders.
19661973
///
19671974
/// Additionally accepts `msg.value`.
19681975
#[cheatcode(group = Filesystem)]
19691976
function deployCode(string calldata artifactPath, uint256 value, bytes32 salt) external returns (address deployedAddress);
19701977

19711978
/// Deploys a contract from an artifact file, using the CREATE2 salt. Takes in the relative path to the json file or the path to the
19721979
/// artifact in the form of <path>:<contract>:<version> where <contract> and <version> parts are optional.
1980+
/// Reverts if the target artifact contains unlinked library placeholders.
19731981
///
19741982
/// Additionally accepts abi-encoded constructor arguments and `msg.value`.
19751983
#[cheatcode(group = Filesystem)]

crates/cheatcodes/src/fs.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ fn deploy_code(
401401
Ok(address.abi_encode())
402402
}
403403

404-
/// Returns the path to the json artifact depending on the input
404+
/// Returns the bytecode from a JSON artifact file.
405405
///
406406
/// Can parse following input formats:
407407
/// - `path/to/artifact.json`
@@ -411,6 +411,10 @@ fn deploy_code(
411411
/// - `path/to/contract.sol:0.8.23`
412412
/// - `ContractName`
413413
/// - `ContractName:0.8.23`
414+
///
415+
/// This function is safe to use with contracts that have library dependencies.
416+
/// `alloy_json_abi::ContractObject` validates bytecode during JSON parsing and will
417+
/// reject artifacts with unlinked library placeholders.
414418
fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result<Bytes> {
415419
let path = if path.ends_with(".json") {
416420
PathBuf::from(path)
@@ -942,4 +946,17 @@ mod tests {
942946
let artifact: ContractObject = serde_json::from_str(s).unwrap();
943947
assert!(artifact.deployed_bytecode.is_some());
944948
}
949+
950+
#[test]
951+
fn test_alloy_json_abi_rejects_unlinked_bytecode() {
952+
let artifact_json = r#"{
953+
"abi": [],
954+
"bytecode": "0x73__$987e73aeca5e61ce83e4cb0814d87beda9$__63baf2f868"
955+
}"#;
956+
957+
let result: Result<ContractObject, _> = serde_json::from_str(artifact_json);
958+
assert!(result.is_err(), "should reject unlinked bytecode with placeholders");
959+
let err = result.unwrap_err().to_string();
960+
assert!(err.contains("expected bytecode, found unlinked bytecode with placeholder"));
961+
}
945962
}

0 commit comments

Comments
 (0)