# substrate-node-template集成pallet-contract [官网文档](https://docs.substrate.io/tutorials/work-with-pallets/contracts-pallet/) & [官方文档 2](https://docs.substrate.io/reference/how-to-guides/pallet-design/add-contracts-pallet/) # 开始之前 - [ ] 按照 [构建本地区块链](https://github.com/hello-substrate/hello-substrate/blob/main/docs/substrate/get-start/构建本地区块链.md) 下载编译了 node-template # 添加 pallet 依赖 1. 打开 `runtime/Cargo.toml` 2. `[dependencies]`新增 ``` # 注意 v0.9.28 版本必须与其他 pallet 使用的一致 pallet-contracts = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28" } pallet-contracts-primitives = { version = "6.0.0", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28" } ``` 3. `[features]` -> `std = [...]` 中新增 ``` "pallet-contracts/std", "pallet-contracts-primitives/std", ``` # 实现 Contracts config trait 1. 打开 `runtime/src/lib.rs` 2. 导入 `pallet-contracts` 默认的 contract weight ``` use pallet_contracts::DefaultContractAccessWeight; ``` 3. 实现 `pallet_contracts::Config` ``` // pallet-contracts // Contracts price units. pub const MILLICENTS: Balance = 1_000_000_000; pub const CENTS: Balance = 1_000 * MILLICENTS; pub const DOLLARS: Balance = 100 * CENTS; const fn deposit(items: u32, bytes: u32) -> Balance { items as Balance * 15 * CENTS + (bytes as Balance) * 6 * CENTS } const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); parameter_types! { pub const DepositPerItem: Balance = deposit(1, 0); pub const DepositPerByte: Balance = deposit(0, 1); pub const DeletionQueueDepth: u32 = 128; pub DeletionWeightLimit: Weight = AVERAGE_ON_INITIALIZE_RATIO * BlockWeights::get().max_block; pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default(); } impl pallet_contracts::Config for Runtime { type Time = Timestamp; type Randomness = RandomnessCollectiveFlip; type Currency = Balances; type Event = Event; type Call = Call; type CallFilter = frame_support::traits::Nothing; type WeightPrice = pallet_transaction_payment::Pallet<Self>; type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>; type ChainExtension = (); type Schedule = Schedule; type CallStack = [pallet_contracts::Frame<Self>; 31]; type DeletionQueueDepth = DeletionQueueDepth; type DeletionWeightLimit = DeletionWeightLimit; type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; type AddressGenerator = pallet_contracts::DefaultAddressGenerator; type ContractAccessWeight = DefaultContractAccessWeight<BlockWeights>; type MaxCodeLen = ConstU32<{ 256 * 1024 }>; type RelaxedMaxCodeLen = ConstU32<{ 512 * 1024 }>; type MaxStorageKeyLen = ConstU32<{ 512 * 1024 }>; } ``` 4. 添加 pallet_contracts 到 construct_runtime!宏。 ``` Contracts: pallet_contracts, ``` # 暴露 Contracts RPC API 1. 打开 `runtime/Cargo.toml` 2. `[dependencies]` 新增 ``` pallet-contracts-rpc-runtime-api = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28" } ``` 3. `[features]` -> `std = [...]` 中新增 ``` "pallet-contracts-rpc-runtime-api/std", ``` 4. 打开 `runtime/src/lib.rs` 5. 添加以下常量启用合约调试 ``` const CONTRACTS_DEBUG_OUTPUT: bool = true; ``` 6. 在 `impl_runtime_apis!` 中实现 contracts runtime API ``` impl pallet_contracts_rpc_runtime_api::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash> for Runtime { fn call( origin: AccountId, dest: AccountId, value: Balance, gas_limit: u64, storage_deposit_limit: Option<Balance>, input_data: Vec<u8>, ) -> pallet_contracts_primitives::ContractExecResult<Balance> { Contracts::bare_call(origin, dest, value, gas_limit, storage_deposit_limit, input_data, CONTRACTS_DEBUG_OUTPUT) } fn instantiate( origin: AccountId, value: Balance, gas_limit: u64, storage_deposit_limit: Option<Balance>, code: pallet_contracts_primitives::Code<Hash>, data: Vec<u8>, salt: Vec<u8>, ) -> pallet_contracts_primitives::ContractInstantiateResult<AccountId, Balance> { Contracts::bare_instantiate(origin, value, gas_limit, storage_deposit_limit, code, data, salt, CONTRACTS_DEBUG_OUTPUT) } fn upload_code( origin: AccountId, code: Vec<u8>, storage_deposit_limit: Option<Balance>, ) -> pallet_contracts_primitives::CodeUploadResult<Hash, Balance> { Contracts::bare_upload_code(origin, code, storage_deposit_limit) } fn get_storage( address: AccountId, key: Vec<u8>, ) -> pallet_contracts_primitives::GetStorageResult { Contracts::get_storage(address, key) } } ``` # 更新外部节点 > 至此,您已完成将 pallet-contracts 添加到运行时。现在,您需要考虑外部节点是否需要任何相应的更新。Substrate 提供了一个 RPC 服务器来与节点交互。但是,默认的 RPC 服务器不提供对 pallet-contracts 的访问。要与 pallet-contracts 交互,您必须扩展现有的 RPC 服务器以包含 pallet-contracts 和 Contracts RPC API。要让 pallet-contracts 利用 RPC endpoint API,您需要将自定义 RPC endpoint 添加到外部节点配置中。 1. 打开 `node/Cargo.toml` 2. `[dependencies]` 新增 ``` pallet-contracts = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28" } pallet-contracts-rpc = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28" } ``` 3. 打开 `node/src/rpc.rs` 4. 添加 `BlockNumber` 和 `Hash` 到 `use node_template_runtime` 中 ``` use node_template_runtime::{opaque::Block, AccountId, Balance, Index, BlockNumber, Hash}; ``` 5. 在 `create_full` 方法中添加 `pallet_contracts_rpc` ``` use pallet_contracts_rpc::{Contracts, ContractsApiServer}; ``` 6. 在 `create_full` 方法 `where` 中添加 `pallet-contracts-rpc` ``` C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber, Hash>, ``` 7. 添加 Contracts RPC API 的扩展。 ``` //上面的 client 记得是 client.clone() module.merge(Contracts::new(client.clone()).into_rpc())?; ``` 8. 检查 runtime 是否正确编译 ``` cargo check -p node-template-runtime --release cargo build --release ```