Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions crates/bootstrap/src/embedded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,22 @@ impl EmbeddedClass {
/// Future additions (`oz_account`, `controller_account`, `erc20`, `erc721`) require adding
/// the cairo source under `crates/contracts/contracts/`, registering it in
/// `crates/contracts/src/lib.rs`, and then appending an entry here.
pub const REGISTRY: &[EmbeddedClass] = &[EmbeddedClass {
name: "dev_account",
description: "Default katana dev account (Cairo 1)",
load: || contracts::Account::CLASS.clone(),
class_hash: contracts::Account::HASH,
casm_hash: contracts::Account::CASM_HASH,
}];
pub const REGISTRY: &[EmbeddedClass] = &[
EmbeddedClass {
name: "dev_account",
description: "Default katana dev account (Cairo 1)",
load: || contracts::Account::CLASS.clone(),
class_hash: contracts::Account::HASH,
casm_hash: contracts::Account::CASM_HASH,
},
EmbeddedClass {
name: "udc",
description: "OpenZeppelin Universal Deployer Contract (mainnet class)",
load: || contracts::OpenZeppelinUniversalDeployer::CLASS.clone(),
class_hash: contracts::OpenZeppelinUniversalDeployer::HASH,
casm_hash: contracts::OpenZeppelinUniversalDeployer::CASM_HASH,
},
];

/// Look up an embedded class by name.
pub fn get(name: &str) -> Option<&'static EmbeddedClass> {
Expand Down Expand Up @@ -75,6 +84,22 @@ mod tests {
assert_eq!(class.class_hash().unwrap(), entry.class_hash);
}

/// The `udc` entry must resolve to the OpenZeppelin Universal Deployer Sierra class
/// currently deployed on Starknet mainnet. Pinning the hash here guards against
/// accidental swaps of the load function or the underlying vendored artifact.
#[test]
fn udc_is_registered_with_mainnet_hash() {
use katana_primitives::Felt;
const MAINNET_UDC: Felt = Felt::from_hex_unchecked(
"0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8",
);
let entry = get("udc").expect("udc must be registered");
assert_eq!(entry.class_hash, MAINNET_UDC);
let class = entry.class();
assert!(class.as_sierra().is_some(), "embedded classes must be Sierra");
assert_eq!(class.class_hash().unwrap(), entry.class_hash);
}

#[test]
fn unknown_class_is_rejected() {
assert!(get("not_a_real_class").is_none());
Expand Down
41 changes: 41 additions & 0 deletions crates/contracts/contracts/openzeppelin_udc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# OpenZeppelin Universal Deployer Contract (mainnet binary)

This directory vendors the Sierra contract class for the new OpenZeppelin Universal Deployer
that is currently deployed on Starknet mainnet.

- **Sierra class hash:** `0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8`
- **Sierra version:** 1.7.0
- **Source contract:** `presets/src/universal_deployer.cairo` from
[OpenZeppelin/cairo-contracts](https://github.com/OpenZeppelin/cairo-contracts), conceptually
the v2.0.0 / v3.0.0-alpha.0 source (the source file is byte-identical between those tags).

## Why a vendored binary instead of a source build?

The Sierra hash above is the one OpenZeppelin documents at the v3.0.0-alpha.0 tag and is the
class actually registered on mainnet. We could not reproduce it locally with any scarb release
we tried (`2.11.4`, `2.12.2`, `2.13.1`, `2.15.0`) — every scarb version produces a different
hash from byte-identical source, because the bundled cairo-lang snapshot drifts release over
release. To guarantee that Katana's predeployed UDC class-hash-equals the mainnet contract, we
fetch the canonical artifact directly instead of relying on a recompile.

## How the file was produced

```bash
curl -s -X POST https://rpc.starknet.lava.build \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"starknet_getClass","params":["latest","0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8"]}' \
| jq '.result | .abi |= fromjson' \
> UniversalDeployer.contract_class.json
```

The only post-processing applied is parsing the `abi` field from a JSON-encoded string into a
JSON array, which matches the on-disk shape Scarb emits and is what
`katana_primitives::class::ContractClass::from_str` expects. This transformation does not
affect the computed class hash (the hash is over the raw ABI string), as verified by the
`openzeppelin_udc_hash_matches_mainnet` test in `crates/contracts/src/lib.rs`.

## Updating

If OpenZeppelin ships a new UDC and mainnet adopts it, refetch with the new class hash, drop
the JSON in this directory, and update the pinned hash in the test referenced above. There is
no scarb step.
Loading
Loading