UDC Appchain 部署
虽然通用部署合约 (UDC) 部署在 Starknet 公共网络上,但应用链可能需要部署它们自己的 UDC 实例以供自己使用。本指南将引导您完成此过程,同时在所有网络上保持相同的最终地址。
前提条件
本指南假定您已具备以下条件:
注意:要在 Starknet 上声明合约,您可以使用 starknet-foundry 项目中的 sncast 工具。
关于 UDC 最终地址的说明
重要的是,Starknet 中的通用部署合约 (UDC) 在所有网络上保持 相同的地址,因为诸如 starkli 和 sncast 之类的基本开发者工具在部署合约时默认依赖于此地址。这些工具在 Starknet 生态系统中被广泛使用,以简化和标准化合约部署工作流程。
如果 UDC 地址是一致的,开发者可以编写部署脚本、CI/CD 管道和集成,这些脚本、管道和集成可以在测试网、主网和应用链上无缝地工作,而无需更新配置文件或处理每个环境的特殊情况。
在以下部分中,我们将引导您完成在应用链上部署 UDC 的过程,同时保持相同的地址,但有一个重要的假设:声明的 UDC 类哈希在所有网络上必须相同。不同的编译器版本可能会为相同的合约生成不同的类哈希,因此您需要确保使用相同的编译器版本来构建 UDC 类(以及发布配置文件)。
openzeppelin_presets
包中提供的最新版本的 UDC 是使用 Cairo v2.11.4(发布配置文件)编译的,生成的类哈希是 0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8
。
重要提示:如果您使用不同的编译器版本,则需要确保类哈希与上面的类哈希相同,以便在所有网络上保持相同的地址。
为了避免因使用不同版本的编译器而导致的问题,您可以直接导入部署在 Starknet 主网上的合约类,并在您的应用链上声明它。在撰写本文时,使用 sncast
工具无法轻松实现此目的,但您可以利用 starkli
来实现。
快速参考:
starkli class-by-hash --parse \
0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8 \
--network mainnet \
> udc.json
这将输出一个 udc.json
文件,您可以使用该文件在您的应用链上声明 UDC。
starkli declare udc.json --rpc <rpc-url>
Madara 应用链
Madara 是一种流行的 Starknet 节点实现,它具有友好而强大的界面,用于构建应用链。如果您将其用于此目的,您可能熟悉 Madara 引导程序,当您创建新的应用链时,它已经为您声明和部署了一些合约,包括帐户和 UDC。
但是,由于 UDC 已在 2025 年 6 月迁移到新版本,因此应用链可能是在此更改之前创建的,这意味着应用链上的 UDC 是旧版本。如果是这种情况,您可以按照以下步骤部署新的 UDC。
1. 声明并部署引导程序
在 Starknet 生态系统中,合约需要在部署之前声明,并且部署只能通过 deploy_syscall
或使用 deploy_account
交易来完成。后者需要向 UDC 添加帐户功能,这不是最佳选择,因此我们将使用 deploy_syscall
,这需要拥有一个启用了此功能的帐户。
注意:Madara 声明一个启用了此功能的帐户作为引导过程的一部分。您或许可以直接使用该实现来跳过此步骤。
引导程序合约
引导程序合约是一个简单的合约,它声明 UDC 并允许通过 deploy_syscall
部署它。您可以在下面找到一个参考实现:
注意:此参考实现面向 Cairo v2.11.4。如果您使用的是不同版本的 Cairo,您可能需要更新代码以匹配您的编译器版本。
#[starknet::contract(account)]
mod UniversalDeployerBootstrapper {
use core::num::traits::Zero;
use openzeppelin_account::AccountComponent;
use openzeppelin_introspection::src5::SRC5Component;
use openzeppelin_utils::deployments::calculate_contract_address_from_deploy_syscall;
use starknet::{ClassHash, ContractAddress, SyscallResultTrait};
component!(path: AccountComponent, storage: account, event: AccountEvent);
component!(path: SRC5Component, storage: src5, event: SRC5Event);
//
// Account features (deployable, declarer, and invoker)
//
#[abi(embed_v0)]
pub(crate) impl DeployableImpl =
AccountComponent::DeployableImpl<ContractState>;
#[abi(embed_v0)]
impl DeclarerImpl = AccountComponent::DeclarerImpl<ContractState>;
#[abi(embed_v0)]
impl SRC6Impl = AccountComponent::SRC6Impl<ContractState>;
impl AccountInternalImpl = AccountComponent::InternalImpl<ContractState>;
#[storage]
struct Storage {
#[substorage(v0)]
pub account: AccountComponent::Storage,
#[substorage(v0)]
pub src5: SRC5Component::Storage,
}
#[event]
#[derive(Drop, starknet::Event)]
pub(crate) enum Event {
#[flat]
AccountEvent: AccountComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event,
}
#[constructor]
pub fn constructor(ref self: ContractState, public_key: felt252) {
self.account.initializer(public_key);
}
#[abi(per_item)]
#[generate_trait]
impl ExternalImpl of ExternalTrait {
#[external(v0)]
fn deploy_udc(ref self: ContractState, udc_class_hash: ClassHash) {
self.account.assert_only_self();
starknet::syscalls::deploy_syscall(udc_class_hash, 0, array![].span(), true)
.unwrap_syscall();
}
#[external(v0)]
fn get_udc_address(ref self: ContractState, udc_class_hash: ClassHash) -> ContractAddress {
calculate_contract_address_from_deploy_syscall(
0, udc_class_hash, array![].span(), Zero::zero(),
)
}
}
}
部署引导程序
本指南假定您在要部署到的网络上有一个可用的功能帐户,并且熟悉通过 declare
交易声明合约的过程。回顾一下,我们部署此引导程序帐户合约的原因是为了能够通过 deploy_syscall
部署 UDC。
提示:以下示例中使用了 sncast v0.45.0。
作为一个快速示例,如果您的帐户已配置为 sncast,您可以使用以下命令声明引导程序合约:
sncast -p <profile-name> declare \
--contract-name UniversalDeployerBootstrapper
引导程序实现了 IDeployable
特性,这意味着它可以进行反事实部署。查阅反事实部署指南。继续使用 sncast 示例,您可以使用以下命令创建并部署引导程序:
2. 声明并部署 UDC
部署引导程序后,您可以通过它声明并部署 UDC。
声明 UDC
UDC 源代码可在 openzeppelin_presets
包中找到。您可以将其复制到您的项目并使用以下命令声明它:
sncast -p <profile-name> declare \
--contract-name UniversalDeployer
注意:如果您遵循了 关于 UDC 最终地址的说明 部分,您的声明类哈希应为 0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8
。
预览 UDC 地址
您可以使用以下命令预览 UDC 地址:
sncast call \
--network <network-name> \
--contract-address <bootstrapper-address> \
--function "get_udc_address" \
--arguments '<udc-class-hash>'
如果 UDC 类哈希与 关于 UDC 最终地址的说明 部分中的类哈希相同,则输出应为 0x2ceed65a4bd731034c01113685c831b01c15d7d432f71afb1cf1634b53a2125
。