;
}
construct_runtime!(
pub enum Runtime
{
...
...
Toggle: pallet_toggle,
}
);
```
### Compile Runtime {: #compile-runtime }
After completing the preceding steps, the module is declared a dependency in the project, configured, and added to the runtime.
Compile the template using the following command:
```bash
cargo build -p container-chain-simple-node --release
```
The terminal output will display an error, similar to the following, caused by different modules referencing different versions of the same dependency:
```bash
error: failed to select a version for `syn`.
```
### Patch Dependencies {: #patch-dependencies }
Finally, executing the `diener` [command](#solving-dependencies-conflicts-diener) will add a `patch` section to your workspace `Cargo.toml` file, overriding the dependencies and unifying origins and versions.
This is what the execution in the terminal looks like:
../diener patch --crates-to-patch ../polkadot-sdk \
--target https://github.com/paritytech/polkadot-sdk \
--point-to-git https://github.com/moondance-labs/polkadot-sdk \
--point-to-git-branch tanssi-polkadot-v1.3.0
[2024-01-10T23:26:27Z INFO diener] Running diener v0.4.7
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bridge-runtime-common.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bp-header-chain'
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bp-runtime'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural-tools'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural-tools-derive'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-core-hashing'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-api'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-api-proc-macro' .
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-core'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-debug-derive'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-externalities'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-std'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-storage'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-runtime-interface'.
As shown in the terminal output, `diener` adds a patch for the dependencies, creating a `patch` section in your `toml` overriding their origin:
```toml
[patch."https://github.com/paritytech/polkadot-sdk"]
bridge-runtime-common = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
bp-header-chain = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
bp-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
...
```
Finally, compiling will succeed, and the module will be built into your runtime.
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/builders/build/templates/custom-runtime/
--- BEGIN CONTENT ---
---
title: Custom Runtime
description: Any custom runtime can be adapted to be deployed through Tanssi, provided that specific modules are implemented and the required configurations are set.
icon: octicons-file-binary-24
categories: Custom-Runtime
---
# Custom Runtime
## Introduction {: #introduction }
For teams working on an existing Substrate framework project, it will be necessary to include some required modules and configurations into the runtime. This will ensure that the existing runtime can gracefully become a Tanssi network runtime, aligning with the [protocol rules](/builders/build/templates/overview/#base-setup-supporting-tanssi){target=\_blank}.
Failing to do so might lead to reduced interoperability and unnecessary exposure to vulnerabilities.
## Minimum Requirements
Already existing Substrate runtimes need to implement at least the [framework](#adding-cumulus-support) for communicating within the Tanssi ecosystem, along with [Tanssi-specific modules](#adding-tanssi-support).
Nevertheless, teams might have already implemented certain modules that can collide with some functionalities related to Tanssi, for example, block production, block authority assignment, and consensus.
The following modules are included by default in many popular templates and must be removed along with their configuration:
```rust
Authorship: pallet_authorship = 20,
CollatorSelection: pallet_collator_selection = 21,
Session: pallet_session = 22,
Aura: pallet_aura = 23,
AuraExt: cumulus_pallet_aura_ext = 24,
```
In any case, make sure to check your runtime and remove all the modules that might interfere with the block production as a service feature before starting the registration process.
## Integrating Your Stand-Alone Chain {: #adding-cumulus-support }
If your existing runtime is set up as a stand-alone chain, you'll need to add a consensus mechanism to integrate into the Tanssi ecosystem. Check any of the available templates in the [Tanssi repository](https://github.com/moondance-labs/tanssi){target=\_blank} for a reference setup or the [framework documentation](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/polkadot_sdk/cumulus/index.html){target=\_blank}.
## Adding Tanssi Protocol Support {: #adding-tanssi-support }
To support the Tanssi protocol, it will be necessary to add two modules through the following steps:
1. Include the dependencies in the `Cargo.toml` manifesto (usually located in the root folder). Open the `Cargo.toml` file and add the modules in the `dependencies` section
```toml
[dependencies]
...
pallet-cc-authorities-noting = {
git = "https://github.com/moondance-labs/tanssi",
branch = "master", default-features = false
}
pallet_authorities_noting = {
git = "https://github.com/moondance-labs/moonkit",
branch = "tanssi-polkadot-v0.9.43", default-features = false
}
...
```
2. Configure the modules. Open the file `lib.rs` located in the folder `*/runtime/src` and add the configuration for both modules:
```rust
impl pallet_author_inherent::Config for Runtime {
type AuthorId = NimbusId;
type AccountLookup = tp_consensus::NimbusLookUp;
type CanAuthor = pallet_cc_authorities_noting::CanAuthor;
type SlotBeacon = tp_consensus::AuraDigestSlotBeacon;
type WeightInfo =
pallet_author_inherent::weights::SubstrateWeight;
}
impl pallet_cc_authorities_noting::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SelfParaId = parachain_info::Pallet;
type RelayChainStateProvider =
cumulus_pallet_parachain_system::RelaychainDataProvider;
type AuthorityId = NimbusId;
type WeightInfo =
pallet_cc_authorities_noting::weights::SubstrateWeight;
}
```
Note that this configuration is agnostic from the use case
3. Declare the modules as part of the runtime. In the same `lib.rs` file, located in the folder `*/runtime/src`, add the modules to the construction of the runtime:
```rust
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
...
// Tanssi network
AuthoritiesNoting: pallet_cc_authorities_noting = 50,
AuthorInherent: pallet_author_inherent = 51,
...
}
);
```
4. Make sure your Header is configured as follows:
```rust
type Header = generic::Header;
/// An index to a block.
pub type BlockNumber = u32;
```
5. Add the block executor, to allow the operators in the Tanssi network to validate that the authors are the sequencers assigned by Tanssi (and not a malicious actor)
```rust
cumulus_pallet_parachain_system::register_validate_block! {
Runtime = Runtime,
BlockExecutor = pallet_author_inherent::BlockExecutor::
CheckInherents = CheckInherents,
}
```
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/pt/builders/build/customize/adding-built-in-module/
--- BEGIN CONTENT ---
---
title: Adicionando Módulos Substrate Embutidos
description: Aprenda a aproveitar os módulos pré-construídos e prontos para uso do Substrate para adicionar novas funcionalidades à sua rede de forma eficiente, sem precisar criar tudo do zero.
icon: octicons-package-24
categories: Custom-Runtime
---
# Adicionando um Módulo Embutido {: #adding-builtin-module }
## Introdução {: #introduction }
Substrate é uma estrutura de desenvolvimento de software poderosa e modular incluída nos SDKs Polkadot para construir blockchains. Ele fornece um conjunto abrangente de ferramentas e bibliotecas que abstraem funcionalidades complexas de blockchain, permitindo que os desenvolvedores se concentrem na construção de recursos e aplicações inovadoras, focando no runtime, que contém a lógica central e as regras da transição de estado para o caso de uso.
O que diferencia o Substrate é sua arquitetura modular, que permite a integração perfeita de [módulos embutidos](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame){target=\_blank} e a criação de módulos personalizados, facilitando o desenvolvimento de protocolos de blockchain.
Para casos que exigem apenas compatibilidade com EVM (Ethereum Virtual Machine), o template fornecido no [repositório Tanssi](https://github.com/moondance-labs/tanssi#container-chain-templates){target=\_blank} atende aos requisitos sem outras modificações. No entanto, as equipes que desejam construir uma rede Substrate devem adicionar e configurar módulos embutidos e personalizados dentro do runtime. Isso envolve compilar, gerar a especificação da cadeia e implantar por meio do protocolo Tanssi para transformá-lo em uma rede ao vivo powered by Tanssi.
Este artigo enfoca as etapas necessárias para adicionar um módulo embutido ao template EVM.
## Verificando Pré-requisitos {: #verifying-prerequisites }
Para seguir as etapas deste guia, você precisará ter o seguinte:
- Um ambiente de desenvolvimento saudável com o compilador Rust e o gerenciador de pacotes Cargo
- O [repositório Tanssi](https://github.com/moondance-labs/tanssi){target=\_blank}, clonado do GitHub
Você pode ler mais sobre como instalar os componentes necessários no [artigo de pré-requisitos](/pt/builders/build/customize/prerequisites/){target=\_blank}.
Como este artigo é baseado no template EVM, certifique-se de que ele compile corretamente antes de continuar, executando o seguinte comando:
```bash
cargo build -p container-chain-frontier-node --release
```
## Adicionando um Módulo Embutido ao Runtime {: #adding-builtin-module-to-runtime }
Como introduzido no artigo de [modularidade](/pt/learn/framework/modules/){target=\_blank}, o framework Substrate já inclui muitos módulos embutidos que abordam uma ampla gama de funcionalidades, prontos para serem usados em seu runtime.
Os módulos são projetados para fornecer a funcionalidade necessária em casos de uso muito diferentes, como DeFi, NFTs ou qualquer outro, e, portanto, são blocos de construção básicos que são inerentemente abstratos e podem ser configurados de acordo com as necessidades específicas da rede powered by Tanssi.
Para adicionar um módulo, as seguintes etapas são necessárias:
1. Tornar a dependência disponível dentro do projeto, declarando-a em [Cargo](https://doc.rust-lang.org/cargo){target=\_blank}, o gerenciador de pacotes da linguagem Rust
2. Tornar os recursos padrão (`std`) do módulo disponíveis para o compilador
3. Configurar o módulo
4. Adicionar o módulo ao runtime
5. Adicionar a configuração padrão à especificação da cadeia
No exemplo a seguir, o popular módulo Substrate `pallet-assets` é adicionado ao runtime do template EVM fornecido, encontrado no [repositório Tanssi](https://github.com/moondance-labs/tanssi){target=\_blank}, especificamente na pasta `container-chains/templates/frontier/`.
### Declarar a Dependência {: #declare-dependency }
Cada pacote contém um arquivo de manifesto chamado `Cargo.toml` que declara, entre outras coisas, todas as dependências em que o pacote se baseia, e o runtime da rede powered by Tanssi não é exceção.
Portanto, a primeira etapa é declarar a dependência e torná-la disponível para o runtime. Abra o arquivo `Cargo.toml` localizado na pasta `container-chains/templates/frontier/runtime` com um editor de texto e adicione o módulo, referenciando o código no Polkadot SDK:
```toml
[dependencies]
...
pallet-assets = {
git = "https://github.com/moondance-labs/polkadot-sdk",
branch = "{{ repository.tanssi.release_branch }}",
default-features = false
}
...
```
!!! note
Nossa equipe de engenharia contribui ativamente para o desenvolvimento do Substrate, corrigindo problemas e aprimorando funcionalidades. Como resultado, o repositório fork Tanssi frequentemente fica à frente do oficial. É por isso que este exemplo faz referência a um módulo embutido de um repositório Tanssi em vez do oficial.
### Tornar os Recursos Padrão Disponíveis para o Compilador {: #standard-features }
No Cargo, as flags de “recursos” fornecem um mecanismo para dizer ao compilador para incluir ou omitir determinadas partes do código, o que é um mecanismo útil para otimizar o tempo de compilação, minimizar os tamanhos dos arquivos binários ou desabilitar determinado comportamento (por exemplo, não incluir testes unitários ou funcionalidade de benchmarking no runtime pretendido para produção).
Para compilar os recursos padrão para o módulo Assets dentro do runtime, o mesmo arquivo `Cargo.toml` na pasta `runtime` deve ser editado, ativando a flag. Tudo o que está listado nesta seção garantirá que esteja disponível para o compilador ao construir o binário do runtime, que é, em última análise, o arquivo que contém todas as informações para executar sua rede powered by Tanssi inicialmente.
```toml
[features]
default = [
"std",
]
std = [
...,
"pallet-assets/std",
...
]
```
### Configurar o Módulo {:#configure-the-module }
Com a dependência declarada no projeto, o módulo agora pode ser configurado e adicionado ao runtime. Para fazer isso, você precisa editar o arquivo `lib.rs` que está localizado em:
```text
container-chains/templates/frontier/runtime/src/lib.rs
```
A configuração de novos módulos requer a implementação de um `trait` de configuração para o módulo (neste exemplo, para Assets) no runtime, expresso em Rust da seguinte forma:
```rust
// Implementa o trait pallet_assets::Config no runtime
impl pallet_assets::Config for Runtime { ... }
```
[Traits](https://doc.rust-lang.org/book/ch10-02-traits.html){target=\_blank} são uma forma de definir comportamento compartilhado em Rust e, neste caso, eles permitem que um novo runtime se beneficie da funcionalidade que o módulo Assets fornece apenas implementando seu trait de configuração e parâmetros.
Alguns dos parâmetros que o trait precisa definir podem ser valores constantes; nesse caso, eles precisam ser definidos e incluídos na macro `parameter_types!`, o que nos ajuda a reduzir o esforço de desenvolvimento, expandindo o código e convertendo cada uma das constantes no tipo de struct correto com funções que permitem que o runtime leia seu tipo e valores de forma padronizada.
O seguinte trecho de código mostra um exemplo das definições de constantes a serem usadas na configuração do módulo:
```rust
parameter_types! {
// A quantidade de fundos que devem ser reservados para um ativo
pub const AssetDeposit: Balance = 100;
// A quantidade de fundos que deve ser reservada ao criar
// uma nova aprovação de transferência
pub const ApprovalDeposit: Balance = 1;
// A quantidade básica de fundos que deve ser reservada ao adicionar metadados
// ao seu ativo
pub const MetadataDepositBase: Balance = 10;
// Os fundos adicionais que devem ser reservados para o número de bytes
// que você armazena em seus metadados
pub const MetadataDepositPerByte: Balance = 1;
// Comprimento máximo para o símbolo do ativo e nome amigável
pub const StringLimit: u32 = 50;
}
```
É importante notar que cada módulo embutido tem um propósito diferente e, portanto, cada um deles tem necessidades diferentes em termos dos parâmetros que devem ser configurados. O seguinte trecho de código implementa o trait e configura o módulo Assets, usando tipos e as constantes definidas anteriormente na macro `parameter_types!`:
```rust
// Implementando o trait de configuração de Ativos para o runtime
impl pallet_assets::Config for Runtime {
// Armazena os saldos em um inteiro sem sinal de 128bits
type Balance = u128;
// O ID de um ativo pode ser definido como um inteiro sem sinal de 64 bits
type AssetId = u64;
// Usa o módulo Balances como mecanismo para operações de moeda
type Currency = Balances;
// Configurar o módulo referenciando a anteriormente
// constantes definidas
type AssetDeposit = AssetDeposit;
type MetadataDepositBase = MetadataDepositBase;
type MetadataDepositPerByte = MetadataDepositPerByte;
type ApprovalDeposit = ApprovalDeposit;
type StringLimit = StringLimit;
// Mais configuração
...
}
```
??? code "Ver o script completo"
```rust
parameter_types! {
// The amount of funds that must be reserved for an asset
pub const AssetDeposit: Balance = 100;
// The amount of funds that must be reserved when creating
// a new transfer approval
pub const ApprovalDeposit: Balance = 1;
// The basic amount of funds that must be reserved when adding metadata
// to your asset
pub const MetadataDepositBase: Balance = 10;
// The additional funds that must be reserved for the number of bytes
// you store in your metadata
pub const MetadataDepositPerByte: Balance = 1;
// Maximum lenght for the asset symbol and friendly name
pub const StringLimit: u32 = 50;
}
// Implementing the Assets config trait for the runtime
impl pallet_assets::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// Stores the balances in an unsigned integer of 128bits
type Balance = u128;
// The id of an asset can be defined as an unsigned integer of 64 bits
type AssetId = u64;
// Uses module Balances as mechanism for currency operations
type Currency = Balances;
// Configure the module by referencing the previously
// defined constants
type AssetDeposit = AssetDeposit;
type MetadataDepositBase = MetadataDepositBase;
type MetadataDepositPerByte = MetadataDepositPerByte;
type ApprovalDeposit = ApprovalDeposit;
type StringLimit = StringLimit;
// More configuration
type AssetIdParameter = u64;
// Defines the allowed origins to create assets
type CreateOrigin =
frame_support::traits::AsEnsureOriginWithArg>;
// Root can create assets
type ForceOrigin = EnsureRoot;
type AssetAccountDeposit = frame_support::traits::ConstU128<1>;
type Freezer = ();
type Extra = ();
type WeightInfo = pallet_assets::weights::SubstrateWeight;
type RemoveItemsLimit = frame_support::traits::ConstU32<1000>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
type CallbackHandle = ();
}
```
A configuração completa do módulo contém mais parâmetros; para ver uma descrição detalhada de cada um deles, consulte o [trait de configuração oficial para a documentação do módulo Assets](https://paritytech.github.io/substrate/master/pallet_assets/pallet/trait.Config.html){target=\_blank}.
### Adicionar o Módulo ao Runtime {:#add-module-to-runtime}
In the same `lib.rs` file referenced in the previous section, there is a segment enclosed in the macro `construct_runtime!()`. This is where the pallet must be added to be included in the runtime. Since the example is based on the EVM template, the runtime is already configured to include many modules, including the modules for system support, the modules to add the Ethereum compatibility layer, the modules to support the Tanssi protocol, balances, and now also Assets:
```rust
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
// Coisas de suporte do sistema.
System: frame_system = 0,
ParachainSystem: cumulus_pallet_parachain_system = 1,
Timestamp: pallet_timestamp = 2,
ParachainInfo: parachain_info = 3,
Sudo: pallet_sudo = 4,
Utility: pallet_utility = 5,
...
Balances: pallet_balances = 10,
// Módulo Assets é adicionado aqui
Assets: pallet_assets = 11,
...
}
```
### Configurar o Módulo na Especificação da Cadeia {: #configure-chain-specs }
Finally, add the configuration in the chain specification for the genesis state in the file `chain_spec.rs` located at:
```text
container-chains/templates/frontier/node/src/`chain_spec.rs`
```
A função `testnet_genesis`, apresentada no seguinte trecho de código, define o estado inicial para os módulos incluídos no runtime (como contas financiadas inicialmente, por exemplo). Depois de adicionar o módulo Assets, é necessário inicializá-lo também e, no exemplo a seguir, seus valores padrão são definidos.
Mais detalhes sobre a especificação da cadeia e como configurá-la serão abordados no artigo [Personalizando Especificações de Cadeia](/pt/builders/build/customize/customizing-chain-specs/){target=_blank}.
```rust hl_lines="14"
fn testnet_genesis(
endowed_accounts: Vec,
id: ParaId,
root_key: AccountId,
) -> container_chain_template_frontier_runtime::GenesisConfig {
container_chain_template_frontier_runtime::GenesisConfig {
system: container_chain_template_frontier_runtime::SystemConfig {
code: container_chain_template_frontier_runtime::WASM_BINARY
.expect("O binário WASM não foi construído, por favor, construa-o!")
.to_vec(),
},
...
// Adicione o estado padrão para este módulo no estado de gênese
assets: Default::default()
...
}
```
Com o módulo incluído, esta nova versão do runtime desbloqueou um novo conjunto de funcionalidades prontas para serem compostas com ainda mais dos módulos embutidos do Substrate ou personalizados.
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/pt/builders/build/customize/adding-custom-made-module/
--- BEGIN CONTENT ---
---
title: Adicionar um Módulo Personalizado
description: Descubra como incorporar módulos feitos sob medida para adicionar recursos únicos ou especializados à sua rede que vão além das capacidades dos módulos embutidos.
icon: octicons-terminal-24
categories: Custom-Runtime
---
# Adicionar um Módulo Personalizado {: #adding-custom-made-module }
## Introdução {: #introduction }
Ao fornecer uma biblioteca abrangente de módulos pré-construídos que abordam muitos requisitos comuns, a estrutura simplifica enormemente o processo de construção de um blockchain e acelera a implantação e evolução em uma rede powered by Tanssi. No entanto, abordar um caso de uso inovador geralmente exige um esforço de desenvolvimento para atender totalmente aos requisitos e, no Substrate, adicionar lógica personalizada se traduz em escrever e integrar módulos de Runtime.
O exemplo apresentado no artigo [Modularidade](/pt/learn/framework/modules/#custom-module-example){target=\_blank} mostra um módulo de loteria simples que expõe duas transações:
- **Buy tickets** - esta função gerencia a entrada de um usuário na loteria. Essencialmente, ela verifica se o participante tem saldo suficiente, não está participando e cuida da transferência de fundos para registrar o usuário na loteria
- **Award prize** - esta função que lida com um usuário que entra na loteria. Em alto nível, ela busca um número pseudo-aleatório para obter um vencedor e lida com a distribuição do prêmio
A implementação dessas transações também usa armazenamento, emite eventos, define erros personalizados e depende de outros módulos para lidar com a moeda (para cobrar pelos bilhetes e transferir o valor total para o vencedor) e aleatorizar a seleção do vencedor.
Neste artigo, as seguintes etapas, necessárias para construir e adicionar o módulo de exemplo ao Runtime, serão abordadas:
1. Criar os arquivos do módulo de loteria (pacote).
2. Configurar as dependências do módulo.
3. Adicionar lógica personalizada.
4. Configurar o Runtime com o novo módulo.
É importante ressaltar que nenhum dos códigos apresentados neste artigo se destina ao uso em produção.
## Verificando Pré-requisitos {: #checking-prerequisites }
Para seguir as etapas deste guia, você precisará ter o seguinte:
- Clonar o [repositório Tanssi](https://github.com/moondance-labs/tanssi){target=\_blank} do Github
- Compilador Rust e gerenciador de pacotes Cargo
Você pode ler mais sobre como instalar o Rust e o Cargo no artigo de [pré-requisitos](/pt/builders/build/customize/prerequisites/#installing-rust){target=\_blank}.
## Criando os Arquivos do Módulo de Loteria {: #creating-lottery-module-files }
Antes de iniciar seu processo de codificação, é essencial criar os arquivos que contêm sua lógica. Os módulos Substrate são abstratos e destinados ao reaproveitamento em diferentes tempos de execução com várias personalizações. Para conseguir isso, você usará o Cargo, o gerenciador de pacotes do Rust, para criar o módulo como um novo pacote.
Como mencionado na seção de pré-requisitos, a primeira etapa é clonar o [repositório Tanssi](https://github.com/moondance-labs/tanssi){target=\_blank} e, na pasta raiz, navegar até `pallets`, onde o módulo será criado.
```bash
cd container-chains/pallets
```
Em seguida, crie o pacote do módulo com o Cargo:
```bash
cargo new lottery-example
```
Por padrão, o Cargo cria o novo pacote em uma pasta com o nome fornecido (`lottery-example`, neste caso), contendo um arquivo de manifesto, `Cargo.toml` e uma pasta `src` com um arquivo `main.rs`. Para respeitar a convenção de nomenclatura usada no Substrate, o arquivo `main.rs` é renomeado para `lib.rs`:
```bash
mv lottery-example/src/main.rs lottery-example/src/lib.rs
```
Depois de executar todos os comandos, o módulo é criado e está pronto para conter a lógica personalizada que você adicionará nas seções a seguir.
## Configurar as Dependências do Módulo {: #configure-module-dependencies}
Como o módulo funciona como um pacote independente, ele tem seu próprio arquivo Cargo.toml, onde você deve especificar os atributos e dependências do módulo.
Por exemplo, você pode usar atributos para especificar detalhes como o nome do módulo, versão, autores e outras informações relevantes. Por exemplo, no módulo `lottery-example`, o arquivo `Cargo.toml` pode ser configurado da seguinte forma:
```toml
#[pallet::storage]
#[pallet::getter(fn get_participants)]
pub(super) type Participants = StorageValue<
_,
BoundedVec,
OptionQuery
>;
```
Este arquivo também define as dependências do módulo, como a funcionalidade principal que permite a integração perfeita com o Runtime e outros módulos, acesso ao armazenamento, emissão de eventos e muito mais.
O exemplo completo do arquivo `Cargo.toml` define, além dos atributos, as dependências exigidas pelo Substrate:
??? code "Ver o arquivo Cargo.toml completo"
```rust
[package]
name = "module-lottery-example"
version = "4.0.0-dev"
description = "Simple module example"
authors = [""]
homepage = ""
edition = "2021"
publish = false
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [
"derive",
] }
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
frame-benchmarking = {
version = "4.0.0-dev",
default-features = false,
optional = true,
git = "https://github.com/paritytech/substrate.git",
branch = "polkadot-v1.0.0"
}
frame-support = {
version = "4.0.0-dev",
default-features = false,
git = "https://github.com/paritytech/substrate.git",
branch = "polkadot-v1.0.0"
}
frame-system = {
version = "4.0.0-dev",
default-features = false,
git = "https://github.com/paritytech/substrate.git",
branch = "polkadot-v1.0.0"
}
[dev-dependencies]
sp-core = { version = "21.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
sp-io = { version = "23.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
sp-runtime = { version = "24.0.0", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" }
[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"scale-info/std",
]
runtime-benchmarks = ["frame-benchmarking/runtime-benchmarks"]
try-runtime = ["frame-support/try-runtime"]
```
## Adicionando Lógica Personalizada {: #adding-custom-logic}
Conforme apresentado na seção [módulo personalizado](/pt/learn/framework/modules/#custom-modules){target=\_blank} do artigo sobre modularidade, a criação de um módulo envolve a implementação das seguintes macros de atributo, das quais as três primeiras são obrigatórias:
- **`#[frame_support::pallet]`** - atributo de entrada que marca o módulo como utilizável no runtime
- **`#[pallet::pallet]`** - aplicado a uma estrutura usada para recuperar informações do módulo com facilidade
- **`#[pallet::config]`** - atributo obrigatório para definir a configuração dos tipos de dados do módulo
- **`#[pallet::call]`** - macro usada para definir funções expostas como transações, permitindo que sejam despachadas para o runtime; aqui os desenvolvedores adicionam transações e lógica personalizadas
- **`#[pallet::error]`** - como transações podem falhar (por exemplo, fundos insuficientes) e, por segurança, um módulo não deve gerar exceções, todos os possíveis erros devem ser identificados e listados em um enum para serem retornados em uma execução malsucedida
- **`#[pallet::event]`** - eventos podem ser definidos e usados para fornecer mais informações ao usuário
- **`#[pallet::storage]`** - macro usada para definir elementos que serão persistidos em storage; como recursos são escassos em uma blockchain, deve ser usada com parcimônia para armazenar apenas informações essenciais
### Implementando a Estrutura Básica do Módulo {: #implementing-basic-structure }
As duas primeiras macros obrigatórias, `#[frame_support::pallet]` e `#[pallet::pallet]`, fornecem a estrutura básica do módulo e são necessárias para habilitar o módulo a ser usado em um Runtime Substrate.
A seguir, é apresentada a estrutura geral de um módulo Substrate personalizado.
```rust
#[frame_support::pallet(dev_mode)]
pub mod pallet {
...
#[pallet::pallet]
pub struct Pallet(_);
// Toda a lógica vai aqui
}
```
A próxima etapa seria adicionar a terceira macro obrigatória (`#[pallet::config]`) e toda a lógica personalizada, conforme mostrado nas seções a seguir.
### Implementando a Configuração do Módulo {: #implementing-module-configuration }
Para tornar os módulos altamente adaptáveis, sua configuração é abstrata o suficiente para permitir que sejam adaptados aos requisitos específicos do caso de uso que o Runtime implementa.
A implementação da macro `#[pallet::config]` é obrigatória e define a dependência do módulo em outros módulos e os tipos e valores especificados pelas configurações específicas do Runtime.
No módulo `lottery-example` personalizado que você está construindo, o módulo depende de outros módulos para gerenciar a moeda e a função aleatória para selecionar o vencedor. O módulo também lê e usa o preço do bilhete e o número máximo de participantes diretamente das configurações do Runtime. Consequentemente, a configuração precisa incluir essas dependências:
- **Events** - o módulo depende da definição de um evento do Runtime para poder emiti-los
- **Currency** - o módulo `lottery-example` precisa poder transferir fundos, portanto, precisa da definição do sistema monetário do Runtime
- **Randomness** - este módulo é usado para selecionar de forma justa o vencedor do prêmio da lista de participantes. Ele gera os números aleatórios usando os hashes de bloco anteriores e o número do bloco atual como semente
- **Ticket coste** - o preço a ser cobrado dos compradores que participam da loteria
- **Maximum number of participants** - o limite máximo de participantes permitido em cada rodada da loteria
- **Module Id** - o identificador exclusivo do módulo é necessário para acessar a conta do módulo para manter os fundos dos participantes até serem transferidos para o vencedor
A implementação da configuração descrita para este exemplo é mostrada no seguinte trecho de código:
```rust
#[pallet::config]
pub trait Config: frame_system::Config {
// Definição do evento
type RuntimeEvent: From>
+ IsType<::RuntimeEvent>;
// Moeda
type Currency: Currency;
// Aleatoriedade
type MyRandomness: Randomness>;
// Custo do bilhete
#[pallet::constant]
type TicketCost: Get>;
// Número máximo de participantes
#[pallet::constant]
type MaxParticipants: Get;
// ID do módulo
#[pallet::constant]
type PalletId: Get;
}
```
Esta definição abstrata de dependências é crucial para evitar o acoplamento a um caso de uso específico e para permitir que os módulos sirvam como blocos de construção básicos para as redes Substrate.
### Implementando Transações {: #implementing-transactions }
Chamadas representam o comportamento que um Runtime expõe na forma de transações que podem ser despachadas para processamento, expondo a lógica personalizada adicionada ao módulo.
Cada chamada está incluída na macro `#[pallet::call]` e apresenta os seguintes elementos:
- **Call Index** - é um identificador exclusivo obrigatório para cada chamada despachável
- **Weight** - é uma medida do esforço computacional que uma extrínseca leva ao ser processada. Mais sobre pesos está na [documentação do Polkadot](https://docs.polkadot.com/polkadot-protocol/parachain-basics/blocks-transactions-fees/fees/#how-fees-are-calculated){target=\_blank}
- **Origin** - identifica a conta de assinatura que está fazendo a chamada
- **Result** - o valor de retorno da chamada, que pode ser um `Erro` se alguma coisa der errado
A seguinte trecho apresenta a estrutura geral da implementação da macro mencionada e os elementos de chamada:
```rust
#[pallet::call]
impl Pallet {
#[pallet::call_index(0)]
#[pallet::weight(0)]
pub fn one_call(origin: OriginFor) -> DispatchResult { }
#[pallet::call_index(1)]
#[pallet::weight(0)]
pub fn another_call(origin: OriginFor) -> DispatchResult { }
// Outras chamadas
}
```
Neste módulo `lottery-example`, definimos duas chamadas com a seguinte lógica:
```rust
#[pallet::call]
impl Pallet {
#[pallet::call_index(0)]
#[pallet::weight(0)]
pub fn buy_ticket(origin: OriginFor) -> DispatchResult {
// 1. Valida a assinatura de origem
// 2. Verifica se o usuário tem saldo suficiente para pagar o preço do bilhete
// 3. Verifica se o usuário já não está participando
// 4. Adiciona o usuário como um novo participante do prêmio
// 5. Transfere o custo do bilhete para a conta do módulo, para ser mantido até ser transferido para o vencedor
// 6. Notifica o evento
}
#[pallet::call_index(1)]
#[pallet::weight(0)]
pub fn award_prize(origin: OriginFor) -> DispatchResult {
// 1. Valida a assinatura de origem
// 2. Obtém um número aleatório do módulo de aleatoriedade
// 3. Seleciona o vencedor da lista de participantes
// 4. Transfere o prêmio total para a conta do vencedor
// 5. Redefine a lista de participantes e prepara-se para outra rodada da loteria
}
}
```
Essas chamadas também emitem eventos para manter o usuário informado e podem retornar erros caso alguma das validações dê errado.
Aqui está a implementação completa das chamadas com a lógica da loteria personalizada:
??? code "Ver o código de chamadas completo"
```rust
#[pallet::call]
impl Pallet {
#[pallet::call_index(0)]
#[pallet::weight(0)]
pub fn buy_ticket(origin: OriginFor) -> DispatchResult {
// 1. Validates the origin signature
let buyer = ensure_signed(origin)?;
// 2. Checks that the user has enough balance to afford the ticket price
ensure!(
T::Currency::free_balance(&buyer) >= T::TicketCost::get(),
Error::::NotEnoughCurrency
);
// 3. Checks that the user is not already participating
if let Some(participants) = Self::get_participants() {
ensure!(
!participants.contains(&buyer),
Error::::AccountAlreadyParticipating
);
}
// 4. Adds the user as a new participant for the prize
match Self::get_participants() {
Some(mut participants) => {
ensure!(
participants.try_push(buyer.clone()).is_ok(),
Error::::CanNotAddParticipant
);
Participants::::set(Some(participants));
},
None => {
let mut participants = BoundedVec::new();
ensure!(
participants.try_push(buyer.clone()).is_ok(),
Error::::CanNotAddParticipant
);
Participants::::set(Some(participants));
}
};
// 5. Transfers the ticket cost to the module's account
// to be hold until transferred to the winner
T::Currency::transfer(
&buyer,
&Self::get_pallet_account(),
T::TicketCost::get(),
ExistenceRequirement::KeepAlive)?;
// 6. Notify the event
Self::deposit_event(Event::TicketBought { who: buyer });
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(0)]
pub fn award_prize(origin: OriginFor) -> DispatchResult {
// 1. Validates the origin signature
let _who = ensure_root(origin)?;
match Self::get_participants() {
Some(participants) => {
// 2. Gets a random number from the randomness module
let nonce = Self::get_and_increment_nonce();
let (random_seed, _) = T::MyRandomness::random(&nonce);
let random_number = ::decode(&mut random_seed.as_ref())
.expect("secure hashes should always be bigger than u32; qed");
// 3. Selects the winner from the participants lit
let winner_index = random_number as usize % participants.len();
let winner = participants.as_slice().get(winner_index).unwrap();
// 4. Transfers the total prize to the winner's account
let prize = T::Currency::free_balance(&Self::get_pallet_account());
T::Currency::transfer(
&Self::get_pallet_account(),
&winner,
prize,
ExistenceRequirement::AllowDeath)?;
// 5. Resets the participants list, and gets ready for another lottery round
Participants::::kill();
// 6. Notify the event
Self::deposit_event(Event::PrizeAwarded { winner: winner.clone() } );
},
None => {
// Notify the event (No participants)
Self::deposit_event(Event::ThereAreNoParticipants);
}
};
Ok(())
}
}
```
### Implementando Erros Personalizados {: #implementing-custom-errors}
A macro `#[pallet::error]` é usada para anotar uma enumeração de erros potenciais que poderiam ocorrer durante a execução. É crucial para a segurança garantir que todas as situações de erro sejam tratadas com elegância, sem causar a falha do Runtime.
O exemplo a seguir desta implementação de macro mostra os erros que podem ocorrer no módulo da loteria:
```rust
// Erros informam aos usuários que algo deu errado.
#[pallet::error]
pub enum `Error` {
NotEnoughCurrency,
AccountAlreadyParticipating,
CanNotAddParticipant,
}
```
### Implementando Eventos {: #implementing-events }
A macro `#[pallet::event]` é aplicada a uma enumeração de eventos para informar o usuário sobre quaisquer alterações no estado ou ações importantes que ocorreram durante a execução no Runtime.
Como exemplo, para o módulo `lottery-example`, esta macro pode ser configurada com os seguintes eventos:
```rust
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event {
// Evento emitido quando um bilhete é comprado
TicketBought { who: T::AccountId },
// Evento emitido quando o prêmio é concedido
PrizeAwarded { winner: T::AccountId },
// Evento emitido quando não há participantes
ThereAreNoParticipants,
}
```
### Implementando o Armazenamento para Persistência de Estado {: #implementing-storage }
A macro `#[pallet::storage]` inicializa uma estrutura de armazenamento de Runtime. No ambiente altamente restrito de blockchains, decidir o que armazenar e qual estrutura usar pode ser fundamental em termos de desempenho. Mais sobre esse tópico é abordado na [documentação Substrate](https:/docs.polkadot.com/develop/parachains/customize-parachain/make-custom-pallet/#pallet-storage){target=\_blank}.
Neste exemplo, o módulo `lottery-example` precisa de uma estrutura de armazenamento de valor básica para persistir a lista de participantes em um vetor de capacidade limitada ([BoundedVec](https:/crates.parity.io/frame_support/storage/bounded_vec/struct.BoundedVec.html){target=\_blank}). Isso pode ser inicializado da seguinte forma:
```rust
#[pallet::storage]
#[pallet::getter(fn get_participants)]
pub(super) type Participants = StorageValue<
_,
BoundedVec,
OptionQuery
>;
```
### O Módulo Completo {: #complete-module }
Para juntar todas as peças, após implementar todas as macros necessárias e adicionar a lógica personalizada, o módulo agora está completo e pronto para ser usado no Runtime.
??? code "Ver o arquivo do módulo completo"
```rust
#![cfg_attr(not(feature = "std"), no_std)]
/// Learn more about FRAME and the core library of Substrate FRAME pallets:
///
pub use pallet::*;
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::{*, ValueQuery, OptionQuery};
use frame_system::pallet_prelude::*;
use scale_info::prelude::vec::Vec;
use frame_support::
{
sp_runtime::traits::AccountIdConversion,
traits:: {
Currency, ExistenceRequirement, Randomness
},
PalletId,
};
type BalanceOf =
<::Currency as Currency<::AccountId>>::Balance;
#[pallet::pallet]
pub struct Pallet(_);
/// Configure the module by specifying the parameters and types on which it depends.
#[pallet::config]
pub trait Config: frame_system::Config {
// Event definition
type RuntimeEvent: From>
+ IsType<::RuntimeEvent>;
// Currency
type Currency: Currency;
// Randomness
type MyRandomness: Randomness>;
// Ticket cost
#[pallet::constant]
type TicketCost: Get>;
// Maximum number of participants
#[pallet::constant]
type MaxParticipants: Get;
// Module Id
#[pallet::constant]
type PalletId: Get;
}
// The pallet's runtime storage items.
#[pallet::storage]
#[pallet::getter(fn get_participants)]
pub(super) type Participants = StorageValue<
_,
BoundedVec,
OptionQuery
>;
#[pallet::storage]
#[pallet::getter(fn get_nonce)]
pub(super) type Nonce = StorageValue<
_,
u64,
ValueQuery
>;
// Pallets use events to inform users when important changes are made.
// https://docs.substrate.io/main-docs/build/events-errors/
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event {
/// Event emitted when a ticket is bought
TicketBought { who: T::AccountId },
/// Event emitted when the prize is awarded
PrizeAwarded { winner: T::AccountId },
/// Event emitted when the prize is to be awarded, but there are no participants
ThereAreNoParticipants,
}
// Errors inform users that something went wrong
#[pallet::error]
pub enum Error {
NotEnoughCurrency,
AccountAlreadyParticipating,
CanNotAddParticipant,
}
#[pallet::call]
impl Pallet {
#[pallet::call_index(0)]
#[pallet::weight(0)]
pub fn buy_ticket(origin: OriginFor) -> DispatchResult {
// 1. Validates the origin signature
let buyer = ensure_signed(origin)?;
// 2. Checks that the user has enough balance to afford the ticket price
ensure!(
T::Currency::free_balance(&buyer) >= T::TicketCost::get(),
Error::::NotEnoughCurrency
);
// 3. Checks that the user is not already participating
if let Some(participants) = Self::get_participants() {
ensure!(
!participants.contains(&buyer),
Error::::AccountAlreadyParticipating
);
}
// 4. Adds the user as a new participant for the prize
match Self::get_participants() {
Some(mut participants) => {
ensure!(
participants.try_push(buyer.clone()).is_ok(),
Error::::CanNotAddParticipant
);
Participants::::set(Some(participants));
},
None => {
let mut participants = BoundedVec::new();
ensure!(
participants.try_push(buyer.clone()).is_ok(),
Error::::CanNotAddParticipant
);
Participants::::set(Some(participants));
}
};
// 5. Transfers the ticket cost to the module's account
// to be hold until transferred to the winner
T::Currency::transfer(
&buyer,
&Self::get_pallet_account(),
T::TicketCost::get(),
ExistenceRequirement::KeepAlive)?;
// 6. Notify the event
Self::deposit_event(Event::TicketBought { who: buyer });
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(0)]
pub fn award_prize(origin: OriginFor) -> DispatchResult {
// 1. Validates the origin signature
let _who = ensure_root(origin)?;
match Self::get_participants() {
Some(participants) => {
// 2. Gets a random number from the randomness module
let nonce = Self::get_and_increment_nonce();
let (random_seed, _) = T::MyRandomness::random(&nonce);
let random_number = ::decode(&mut random_seed.as_ref())
.expect("secure hashes should always be bigger than u32; qed");
// 3. Selects the winner from the participants lit
let winner_index = random_number as usize % participants.len();
let winner = participants.as_slice().get(winner_index).unwrap();
// 4. Transfers the total prize to the winner's account
let prize = T::Currency::free_balance(&Self::get_pallet_account());
T::Currency::transfer(
&Self::get_pallet_account(),
&winner,
prize,
ExistenceRequirement::AllowDeath)?;
// 5. Resets the participants list, and gets ready for another lottery round
Participants::::kill();
// 6. Notify the event
Self::deposit_event(Event::PrizeAwarded { winner: winner.clone() } );
},
None => {
// Notify the event (No participants)
Self::deposit_event(Event::ThereAreNoParticipants);
}
};
Ok(())
}
}
impl Pallet {
fn get_pallet_account() -> T::AccountId {
T::PalletId::get().into_account_truncating()
}
fn get_and_increment_nonce() -> Vec {
let nonce = Nonce::::get();
Nonce::::put(nonce.wrapping_add(1));
nonce.encode()
}
}
}
```
## Configurar o Runtime {: #configure-runtime }
Finalmente, com o módulo finalizado, ele pode ser incluído no Runtime. Ao fazer isso, as transações `buy_tickets` e `award_prize` serão chamáveis pelos usuários. Isso também significa que a [API Polkadot.js](/pt/builders/toolkit/substrate-api/libraries/polkadot-js-api/){target=\_blank} será decorada com este módulo e todas as chamadas disponíveis que ele contém.\n\nPara configurar o Runtime, abra o arquivo `lib.rs`, que contém a definição para o Runtime do Template incluído e está localizado (no caso de usar o compatível com EVM) na pasta:
```text
*/container-chains/templates/frontier/runtime/src/
```
Para adicionar o módulo da loteria, configure os módulos da seguinte forma:
```rust
// Adicione a configuração para o módulo de aleatoriedade. Nenhum parâmetro necessário.
impl pallet_insecure_randomness_collective_flip::Config for Runtime {
}
// // ID de módulo personalizado
parameter_types! {
pub const PalletId: PalletId = PalletId(*b"loex5678");
}
// Adicione a configuração para o módulo da loteria
impl pallet_lottery_example::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type TicketCost = ConstU128<1000000000000000>;
type PalletId = PalletId;
type MaxParticipants = ConstU32<500>;
type MyRandomness = RandomCollectiveFlip;
}
```
Com os módulos configurados, adicione a macro `construct_runtime!` (que define os módulos que serão incluídos ao construir o Runtime) e os módulos de aleatoriedade e loteria.
```rust
construct_runtime!(
pub struct Runtime {
...
// Inclua a lógica personalizada do pallet-template no Runtime.
RandomCollectiveFlip: pallet_insecure_randomness_collective_flip,
Lottery: pallet_lottery_example,
...
}
)
```
Com tudo definido, a rede agora tem suporte para uma implementação básica de uma loteria.
{{ trans("disclaimer.third_party") }}
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/pt/builders/build/customize/adding-external-module/
--- BEGIN CONTENT ---
---
title: Adicionar Módulos Externos
description: Aprenda a resolver problemas de referência duplicada de dependências ao personalizar seu template de rede Powered by Tanssi com a ferramenta em Rust chamada Diener.
icon: octicons-plug-24
categories: Custom-Runtime
---
# Adicionar um Módulo Externo {: #adding-external-module }
## Introdução {: #introduction }
Desenvolvedores construindo sobre os [Templates oferecidos pela Tanssi](/pt/builders/build/templates/){target=\_blank} podem querer adicionar alguns módulos/dependências externos em seu Runtime para expandir certas funcionalidades.
O repositório Tanssi e os Templates pegam todas as dependências de [um fork](https://github.com/moondance-labs/polkadot-sdk){target=\_blank} do repositório oficial do Polkadot SDK. Este fork é mantido pela equipe de engenharia da Tanssi, que geralmente contribui ativamente para o desenvolvimento da Substrate, corrigindo problemas e aprimorando funcionalidades, e, como resultado, o repositório do fork frequentemente fica temporariamente à frente do oficial.
Um problema de dupla referência pode surgir ao adicionar uma dependência externa, como uma pallet de terceiros. Isso acontece se um módulo Tanssi faz referência a uma dependência do repositório fork do Polkadot SDK, e a terceiros faz referência à mesma dependência do repositório oficial do Polkadot SDK. Para resolver esse problema, as referências às dependências devem ser unificadas.
## Resolvendo Conflitos de Dependências com Diener {: #solving-dependencies-conflicts-diener }
Para lidar de forma eficiente com as dependências e suas origens, você pode conferir a ferramenta [diener](https://github.com/paritytech/diener){target=\_blank}.
Se o arquivo executável `diener`, o [repositório do Polkadot SDK](https://github.com/paritytech/polkadot-sdk){target=\_blank} clonado e seu fork Tanssi estiverem localizados na mesma pasta, entre na pasta do fork Tanssi e execute o seguinte comando:
```bash
../diener patch --crates-to-patch ../polkadot-sdk \
--target https://github.com/paritytech/polkadot-sdk \
--point-to-git https://github.com/moondance-labs/polkadot-sdk \
--point-to-git-branch {{ repository.tanssi.release_branch }}
```
Este comando aplica as alterações ao arquivo `Cargo.toml`, corrigindo as dependências e resolvendo os problemas de dupla referência.
Você pode visitar a [documentação do diener](https://docs.rs/crate/diener/latest){target=\_blank} para saber mais sobre a ferramenta e outras funções extras que ela oferece.
## Exemplo do Problema de Dupla Referência {: #double-reference-issue }
Para ilustrar a situação, as seguintes etapas adicionam um [módulo externo](https://github.com/papermoonio/pallet-toggle){target=\_blank} de demonstração a um Runtime personalizado com base no [Template de rede powered by Tanssi de linha de base](/pt/builders/build/templates/substrate/){target=\_blank}. Uma maneira de seguir este tutorial é clonar o [repositório Tanssi Github](https://github.com/moondance-labs/tanssi){target=\_blank}, que atuará como o repositório raiz do projeto.
Este tutorial gerará um erro de tempo de compilação de referência múltipla. Finalmente, as etapas mostrarão como corrigir o erro de compilação corrigindo as dependências com a ferramenta `diener`, o Runtime será compilado com sucesso e funcionará conforme o esperado.
### Adicionar uma Dependência de Terceiros {: #add-third-party-dependency }
Semelhante ao que é descrito no artigo [módulo embutido](/pt/builders/build/customize/adding-built-in-module/#adding-a-built-in-module-to-runtime){target=\_blank}, a adição de um módulo de terceiros requer as seguintes etapas:
1. Declare a dependência no arquivo `Cargo.toml` raiz
2. Torne as características padrão disponíveis para o compilador
3. Configure e adicione o módulo ao Runtime
Se o módulo de terceiros fizer referência a alguma dependência já referenciada de uma fonte ou versão distinta, a compilação falhará.
O diagrama a seguir mostra como duas referências diferentes para a mesma dependência estão sendo incluídas no Runtime, fazendo com que a compilação falhe:

Para resolver este problema, será necessário aplicar um patch para que as referências para a dependência sejam unificadas:

### Declarando a Dependência {: #declaring-dependency }
A primeira etapa para reproduzir o problema de dupla referência é declarar a dependência no arquivo `Cargo.toml` localizado na pasta raiz do repositório, na seção `[dependencies]`. Para este exemplo, um simples [módulo de alternância](https://github.com/papermoonio/pallet-toggle){target=\_blank} é usado.
Este módulo `toggle`, construído para fins de teste e educacionais, adiciona lógica básica ao Runtime, permitindo que os usuários alternem um estado entre verdadeiro e falso.
```toml
[dependencies]
...
pallet-toggle = {
git = "https://github.com/papermoonio/pallet-toggle",
default-features = false
}
...
```
### Tornando os Recursos Padrão Disponíveis para o Compilador {: #add-standard-features }
Tendo declarado o módulo no arquivo `Cargo.toml` do espaço de trabalho, a dependência pode agora ser adicionada ao arquivo `Cargo.toml` do Template específico, que, para este exemplo que usa o repositório Tanssi GitHub, está localizado na pasta `container-chains/templates/simple/runtime`.
```toml
[dependencies]
...
pallet-toggle = { workspace = true }
...
```
No mesmo arquivo `Cargo.toml`, adicione os seguintes recursos.
```toml
[features]
default = [
"std",
]
std = [
...,
"pallet-toggle/std",
...
]
...
runtime-benchmarks = [
...,
"pallet-toggle/runtime-benchmarks",
]
try-runtime = [
...,
"pallet-toggle/try-runtime",
]
```
### Configurar e Adicionar o Módulo ao Runtime {: #configure-module-in-the-runtime }
Em seguida, adicione o seguinte snippet ao arquivo `lib.rs` dentro da pasta de Runtime. Isso configura o módulo e adiciona o módulo dentro da macro `construct_runtime!`.
```rust
...
impl pallet_toggle::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_toggle::weights::SubstrateWeight;
}
construct_runtime!(
pub enum Runtime
{
...
...
Toggle: pallet_toggle,
}
);
```
### Compilar o Runtime {: #compile-runtime }
Após concluir as etapas anteriores, o módulo é declarado uma dependência no projeto, configurado e adicionado ao Runtime.
Compile o Template usando o seguinte comando:
```bash
cargo build -p container-chain-simple-node --release
```
A saída do terminal exibirá um erro, semelhante ao seguinte, causado por diferentes módulos referenciando diferentes versões da mesma dependência:
```bash
error: failed to select a version for `syn`.
```
### Dependências de Patch {: #patch-dependencies }
Finalmente, a execução do [comando](#solving-dependencies-conflicts-diener) `diener` adicionará uma seção `patch` ao seu arquivo `Cargo.toml` do espaço de trabalho, substituindo as dependências e unificando as origens e versões.
É assim que a execução no terminal se parece:
../diener patch --crates-to-patch ../polkadot-sdk \
--target https://github.com/paritytech/polkadot-sdk \
--point-to-git https://github.com/moondance-labs/polkadot-sdk \
--point-to-git-branch tanssi-polkadot-v1.3.0
[2024-01-10T23:26:27Z INFO diener] Running diener v0.4.7
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bridge-runtime-common.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bp-header-chain'
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bp-runtime'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural-tools'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural-tools-derive'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-core-hashing'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-api'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-api-proc-macro' .
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-core'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-debug-derive'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-externalities'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-std'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-storage'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-runtime-interface'.
Como mostrado na saída do terminal, `diener` adiciona um patch para as dependências, criando uma seção `patch` em seu `toml` substituindo sua origem:
```toml
[patch."https://github.com/paritytech/polkadot-sdk"]
bridge-runtime-common = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
bp-header-chain = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
bp-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
...
```
Finalmente, a compilação terá sucesso e o módulo será integrado ao seu Runtime.
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/pt/builders/build/templates/custom-runtime/
--- BEGIN CONTENT ---
---
title: Runtime Personalizado
description: Qualquer runtime personalizado pode ser adaptado para ser implantado pela Tanssi, desde que módulos específicos sejam implementados e as configurações necessárias sejam definidas.
icon: octicons-file-binary-24
categories: Custom-Runtime
---
# Runtime Personalizado
## Introdução {: #introduction }
Para as equipes que trabalham em um projeto de framework Substrate existente, será necessário incluir alguns módulos e configurações obrigatórias no runtime. Isso garantirá que o runtime existente possa se tornar um runtime de rede Tanssi sem problemas, alinhando-se com as [regras do protocolo](/pt/builders/build/templates/overview/#base-setup-supporting-tanssi){target=\_blank}.
A falha em fazê-lo pode levar à interoperabilidade reduzida e exposição desnecessária a vulnerabilidades.
## Requisitos Mínimos
Runtimes Substrate já existentes precisam implementar pelo menos o [framework](#adding-cumulus-support) para se comunicar dentro do ecossistema Tanssi, juntamente com os [módulos específicos da Tanssi](#adding-tanssi-support).
No entanto, as equipes podem já ter implementado certos módulos que podem colidir com algumas funcionalidades relacionadas à Tanssi, por exemplo, produção de blocos, atribuição de autoridade de bloco e consenso.
Os seguintes módulos são incluídos por padrão em muitos Templates populares e devem ser removidos junto com sua configuração:
```rust
Authorship: pallet_authorship = 20,
CollatorSelection: pallet_collator_selection = 21,
Session: pallet_session = 22,
Aura: pallet_aura = 23,
AuraExt: cumulus_pallet_aura_ext = 24,
```
Em qualquer caso, certifique-se de verificar seu runtime e remover todos os módulos que podem interferir na produção de blocos como um recurso de serviço antes de iniciar o processo de registro.
## Integrando Sua Cadeia Stand-Alone {: #adding-cumulus-support }
Se seu runtime existente estiver configurado como uma cadeia stand-alone, você precisará adicionar um mecanismo de consenso para integrar no ecossistema Tanssi. Verifique qualquer um dos Templates disponíveis no [repositório Tanssi](https://github.com/moondance-labs/tanssi){target=\_blank} para uma configuração de referência ou a [documentação do framework](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/polkadot_sdk/cumulus/index.html){target=\_blank}.
## Adicionando Suporte ao Protocolo Tanssi {: #adding-tanssi-support }
Para suportar o protocolo Tanssi, será necessário adicionar dois módulos através das seguintes etapas:
1. Inclua as dependências no manifesto `Cargo.toml` (geralmente localizado na pasta raiz). Abra o arquivo `Cargo.toml` e adicione os módulos na seção `dependencies`
```toml
[dependencies]
...
pallet-cc-authorities-noting = {
git = "https://github.com/moondance-labs/tanssi",
branch = "master", default-features = false
}
pallet_authorities_noting = {
git = "https://github.com/moondance-labs/moonkit",
branch = "tanssi-polkadot-v0.9.43", default-features = false
}
...
```
2. Configure os módulos. Abra o arquivo `lib.rs` localizado na pasta `*/runtime/src` e adicione a configuração para ambos os módulos:
```rust
impl pallet_author_inherent::Config for Runtime {
type AuthorId = NimbusId;
type AccountLookup = tp_consensus::NimbusLookUp;
type CanAuthor = pallet_cc_authorities_noting::CanAuthor;
type SlotBeacon = tp_consensus::AuraDigestSlotBeacon;
type WeightInfo =
pallet_author_inherent::weights::SubstrateWeight;
}
impl pallet_cc_authorities_noting::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SelfParaId = parachain_info::Pallet;
type RelayChainStateProvider =
cumulus_pallet_parachain_system::RelaychainDataProvider;
type AuthorityId = NimbusId;
type WeightInfo =
pallet_cc_authorities_noting::weights::SubstrateWeight;
}
```
Observe que essa configuração é agnóstica do caso de uso
3. Declare os módulos como parte do runtime. No mesmo arquivo `lib.rs`, localizado na pasta `*/runtime/src`, adicione os módulos à construção do runtime:
```rust
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
...
// Tanssi network
AuthoritiesNoting: pallet_cc_authorities_noting = 50,
AuthorInherent: pallet_author_inherent = 51,
...
}
);
```
4. Certifique-se de que seu cabeçalho está configurado da seguinte forma:
```rust
type Header = generic::Header;
/// An index to a block.
pub type BlockNumber = u32;
```
5. Adicione o executor do bloco, para permitir que os operators na rede Tanssi validem que os autores são os Sequencers atribuídos pela Tanssi (e não um ator mal-intencionado)
```rust
cumulus_pallet_parachain_system::register_validate_block! {
Runtime = Runtime,
BlockExecutor = pallet_author_inherent::BlockExecutor::
CheckInherents = CheckInherents,
}
```
--- END CONTENT ---
## Basics Concepts [shared: true]
The following section contains foundational documentation shared across all Tanssi products.
It describes the architecture and infrastructure that serve as the backbone for all integrations built with Tanssi.
This includes the network development framework, Substrate and EVM development tools, templates, and guidance for node operators.
This context is provided to help understand how the system works under the hood, but responses should stay focused on the specific product unless the user explicitly asks about the general architecture.
---
## List of shared concept pages:
## Full content for shared concepts:
Doc-Content: https://docs.tanssi.network/learn/decentralized-networks/included-templates/
--- BEGIN CONTENT ---
---
title: Network Templates Included in Tanssi
description: Tanssi protocol provides useful templates to start building your decentralized network, including a ready-to-use EVM template for Ethereum compatibility.
icon: octicons-copy-24
categories: Basics
---
# Network Templates Included in Tanssi {: #network-templates-included-in-tanssi }
## Introduction {: #introduction }
Building a new network from scratch can be a daunting prospect. Fortunately, thanks to the [network development framework](/learn/framework/overview/){target=\_blank} used by Tanssi and its modular-oriented architecture, developers can leverage some pre-bundled network templates that help them jumpstart the process and benefit in some aspects, such as:
- **Head Start** - Tanssi network templates provide a starting point for your project, saving significant time and effort by providing a basic structure and a set of tested and ready-to-use functionalities. It allows developers to accelerate the construction of prototypes or minimum viable products (MVPs) and reduce the time to market
- **Consistency** - included Tanssi network templates follow established design patterns, coding standards, and best practices widely accepted among the developer community. They also provide a default set of architecture definitions to streamline blockchain development
- **UX** - Tanssi network templates cover the most demanded use cases, such as the EVM support for an Ethereum-compatible network
- **Customizability** - Tanssi network templates are a great starting point and are completely customizable. The functionalities and default configurations they include can be modified, replaced, or extended to meet the specific requirements of the use case
- **Upgrades and Compatibility** - Tanssi is built on top of an evolving framework, with new features, enhancements, and bug fixes being regularly introduced. The provided Tanssi network templates are kept up-to-date with these upgrades
## Start Building a Network {: #start-building }
To start building a decentralized network to deploy in Tanssi, some useful Tanssi network templates to kick-start the development process are provided in the [official repository](https://github.com/moondance-labs/tanssi){target=\_blank}.
The process is as simple as:
1. Select one of the templates
2. Add the specific logic to adapt the runtime to the requirements of the use case
3. Deploy in Tanssi

The two included templates are the *baseline network template* and the *baseline EVM template*, which are presented in the following sections.
### Baseline Network Template {: #baseline-network-template }
As presented in the [Overview](/learn/tanssi/overview/){target=\_blank} article, networks deployed through Tanssi are fully sovereign and customizable blockchains.
As part of the Tanssi ecosystem, networks must include the essential components to implement the consensus mechanism and be able to interact and synchronize with the security provider of their choice (for example, [Symbiotic](https://symbiotic.fi/){target=\_blank} on Ethereum). The baseline Tanssi network template includes all the necessary functionality for the sequencers logic, p2p, database, and synchronization layers between the network and the security provider, allowing developers to focus solely on customizing their product.
This template also includes Tanssi's [Author Noting](https://github.com/moondance-labs/tanssi/blob/master/pallets/author-noting/src/lib.rs){target=\_blank} module, which implements the logic for retrieving and validating the set of sequencers assigned to provide block production services to the network. It also includes logic that allows a sequencer to sign the block when the consensus mechanism determines that it is the sequencer's turn to produce the block (and thus be rewarded accordingly).
The source code for this template is public and accessible on the [Tanssi GitHub repository](https://github.com/moondance-labs/tanssi/blob/master/chains/container-chains/runtime-templates/simple/src/lib.rs){target=\_blank}.
### Baseline EVM (Ethereum Virtual Machine) Template {: #baseline-evm-template }
Extending the [baseline Tanssi network template](#baseline-network-template), this template provides not only Tanssi protocol support but also an EVM and full Ethereum compatibility.
Leveraging a set [EVM-specific modules](https://github.com/polkadot-evm/frontier){target=\_blank}, this template includes an Ethereum compatibility layer for networks to allow running unmodified Ethereum dApps.
Using this template, networks support the deployment and running of any existing smart contract written in Solidity or Vyper with no changes. By emulating Ethereum block production and exposing the expected RPC interface, developers can also continue using the same tools like [Metamask](https://metamask.io){target=\_blank}, [Hardhat](https://hardhat.org){target=\_blank}, [Remix](https://remix.ethereum.org){target=\_blank}, [Foundry](https://github.com/foundry-rs/foundry){target=\_blank}, and many more out of the box, with no extra adapters.
With this EVM template, developers can deploy a [Moonbeam](https://moonbeam.network){target=\_blank}-like network in no time and add their custom logic and features specific to their use case.
The source code for this template is public and accessible on the [Tanssi GitHub repository](https://github.com/moondance-labs/tanssi/blob/master/chains/container-chains/runtime-templates/frontier/src/lib.rs){target=\_blank}.
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/learn/decentralized-networks/overview/
--- BEGIN CONTENT ---
---
title: Network Overview
description: Learn the high-level definitions of how a Tanssi network works, its architecture, and its block production as a service mechanism with deterministic finality.
icon: octicons-home-24
categories: Basics
---
# Tanssi Networks Overview {: #networks-tanssi-overview }
## Introduction {: #introduction }
Networks deployed through Tanssi receive many [benefits](/learn/tanssi/overview/#what-tanssi-provides){target=\_blank}, like block production as a service, data retrievability as a service, and security through an [external security providers](/learn/tanssi/external-security-providers/){target=\_blank} such as [Symbiotic](https://symbiotic.fi/){target=\_blank} on Ethereum.
Also, because Tanssi-powered networks are based on a [modular tech stack](/learn/framework/){target=\_blank}, they profit from unique advantages when customizations are required at a runtime level. This [modularity](/learn/framework/modules/){target=\_blank} allows developers to add functionality directly into the runtime or extend the capabilities of the EVM itself via precompiled contracts.
For example, Tanssi provides a ready-to-use [template](/learn/decentralized-networks/included-templates#baseline-evm-template){target=\_blank} that includes [Frontier](https://github.com/polkadot-evm/frontier){target=\_blank} modules, enabling the effortless deployment of an EVM-compatible networks, similar to [Moonbeam](https://moonbeam.network){target=\_blank}.
This section covers the fundamentals of a Tanssi network, its architecture, its core modules and functionalities, and the transaction fee mechanism.
## General Architecture {: #general-architecture}
As previously discussed, networks deployed through Tanssi are customizable blockchains that, among other features, receive block production as a service and inherit security with deterministic block finality within seconds from an external security provider.
Tanssi-powered networks are fully decentralized networks. The decentralized nature of the networks considerably increases their resilience and fault tolerance since they don't rely on a single authority or entity to ensure their liveness, security, and performance but on trustless, decentralized protocols. For example, they receive block production services from a decentralized and incentivized set of sequencers managed by Tanssi.
The Tanssi protocol runs with an [external security provider](/learn/tanssi/external-security-providers/){target=\_blank}, which has a set of operators (also called validators) with assets at stake, validating the transactions from the Tanssi network itself and all of the networks deployed through Tanssi. This way, all Tanssi-powered networks inherit the economic security derived from the Tanssi protocol and, indirectly, from the operators, which verify every transaction from every network. Tanssi networks don't need to run their own operator set nor bootstrap liquidity to secure their protocol.
Tanssi networks also benefit from a set of Data-Preservers, with full archive nodes, ensuring the data availability layer availability. These data-preservers are incentivized through Tanssi's data retrieval services and also provide the RPC infrastructure for apps and users interacting with Tanssi networks.
```mermaid
flowchart TB
networks["Tanssi Networks
(Decentralized Networks)"]
subgraph tanssi["Tanssi Protocol"]
direction TB
sequencers["Decentralized Sequencers Set"]
node["Full Archive Nodes with
RPC Services"]
end
security["External Security Provider
Operators"]
networks<--Block Production-->tanssi
networks<--Shared Security Model-->tanssi
networks<--Data Availability
RPC endpoints-->tanssi
tanssi<--Transactions
Validation and Finality-->security
```
## Network Transaction Flow {: #network-transaction-flow }
A transaction submitted to a Tanssi-powered network follows a complex yet seamless path from submission to block inclusion and finalization. The network infrastructure, Tanssi, and the chosen [security provider](/learn/tanssi/external-security-providers/){target=\_blank} work together at different levels to ensure the process happens as quickly as possible, usually taking around 30 seconds. Remember that a transaction in a Tanssi network reaches deterministic finality. Consequently, once the transaction is final, it becomes irreversible and unchangeable, and the state transition resulting from executing that transaction is final.
For example, a user initiates a transaction when interacting via an application deployed to a Tanssi-powered network. The RPC provider will share the transaction, which sits in the chain's transaction pool, with all network participants. A sequencer assigned by Tanssi to that network will eventually pick up the transaction and include it in the next network block.
Then, the sequencer will share with the security provider's operators:
- The block itself with the state transitions
- The storage components in the Tanssi network database that the block is modifying
- The necessary hashes of the unaffected points in the Merkle tree of the storage
These components constitute the proof of validity (PoV).
Next, the PoV is verified by the security provider's operators. Note that the operators do not check that the Tanssi network storage is valid but that the state transitions that affect it are. A summary of that verification is then gossiped to other operators so they can verify it and include it in the next Tanssi block. Lastly, that Tanssi block with all the networks' verifications is finalized.
The transaction flow process is summarized in the following diagram:

--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/learn/decentralized-networks/runtime-features/
--- BEGIN CONTENT ---
---
title: Core Runtime Features
description: Learn about the core features of a Tanssi network, the transactions types, how they are executed and included in a block, and the forkless runtime upgrades.
icon: octicons-package-24
categories: Basics
---
# Core Runtime Features {: #core-runtime-features }
## Introduction {: #introduction}
Networks deployed through Tanssi have [many benefits](/learn/tanssi/overview/#what-tanssi-provides){target=\_blank} due to its unique [architecture](/learn/tanssi/overview/#tanssi-architecture){target=\_blank}.
Nevertheless, Tanssi-powered networks are also unique due to the [framework](/learn/framework/){target=\_blank} (Substrate) they are built on top of, which provides some unique characteristics that developers can leverage to fine-tune specific behaviors in their runtime.
This section covers some of these Tanssi network core runtime-specific features, including the different origins a transaction might have, the different types of transactions and how they are executed and included in a block, the special account known as _SUDO_, and the quite unique feature of Tanssi networks: the forkless runtime upgrades.
## Origins {: #origins}
Generally speaking, all calls in a Tanssi network have an origin. But what is an origin? Developers from the EVM realm might be familiar with the concept of _msg.sender_ in EVM transactions. Origins are to Tanssi networks what _msg.sender_ is to an EVM transaction, but supercharged with many extra functionalities.
An origin defines where the call is coming from. In contrast to Ethereum-compatible chains, there can be many origins in Tanssi networks. For example, the _msg.sender_ of an EVM transaction is known as a _signed origin_, which means that the call is a transaction that was signed by some on-chain account's private key. This allows the runtime to authenticate the source of the call and, for example, charge transaction fees to the associated account.
However, origins can do much more than represent a private key/public key pair. Origins also have different privilege levels. For example, a _signed origin_ can send a transaction that is dispatched by the private key/public key pair but should not be able to authorize a runtime upgrade.
Some of the most common types of origins are:
- **Root** - a system-level origin with the highest privilege level. It can be thought of as a superuser of the chain, which can execute any call
- **Signed** - as mentioned before, the origin of a transaction signed by an on-chain account's private key, which includes the account identifier (address) as the signer
- **None** - a lack of origin. Used in specific actions that must be agreed upon at a runtime level. For example, you can program your runtime so that a transaction with _none_ origin can enact a pre-authorized runtime upgrade, which means that the transaction has no fee associated with it
- **Custom** - developers can also create custom origins for specific use cases. For example, [Moonbeam's on-chain governance](https://docs.moonbeam.network/learn/features/governance){target=\_blank} has specific origins for each type of governance vote, called _tracks_. Consequently, each track can be configured to only execute calls with specific privilege levels. One track is _Root_, whose origin is the _Root_ origin mentioned before, and has a very restrictive configuration for votes to go through. But other tracks have much lower privilege levels to do some less critical network operations
## Transaction Types {: #transaction-types}
Tanssi networks have three main types of transactions:
- **Signed Transactions** - include a signed payload requesting to execute some runtime call. Generally, the signature is associated with a private key/public key pair. Depending on the runtime logic, the account associated with the signature pays a transaction fee
- **Unsigned Transactions** - include an unsigned payload requesting to execute some runtime call. Because these transactions are unsigned, there is no account associated with them. Consequently, runtimes need to define specific conditions that prevent network spam or replay attacks because there is no fee mechanism to prevent such malicious behaviors. One example of an unsigned transaction is executing pre-approved actions, like a runtime upgrade
- **Inherent Transactions** - an unsigned transaction that a sequencer inserts into a block when initializing its construction. These transactions are part of the block and are not stored in the transaction pool or shared among network participants. In addition, the data inserted through inherent transactions can skip runtime validation, and it might be up to operators to accept it. One example is the block timestamp. This is injected into the block by an inherent transaction, and operators can accept or reject the block based on whether the timestamp is within some acceptable range
## Transaction Execution {: #transaction-execution}
When a user or application submits a signed transaction to a Tanssi network, the transaction is validated at a full-node level using rules defined in the runtime, and then it is queued in a transaction pool. This ensures that only transactions that comply with certain chain-specific conditions are considered to be included in a block.
!!! note
The most common type of transaction is a signed transaction. Nevertheless, unsigned transactions are also validated before they are queued in the transaction pool.
The valid transaction queue comprises two pools: ready and future. The ready queue contains all transactions that can be included in a new pending block. The future queue is for transactions that don't meet all the criteria to be included now but might become valid. For example, transactions with a future nonce. Invalid transactions are directly rejected.
During the block-building process, a sequencer uses a [priority system](https://github.com/paritytech/substrate/blob/fb24fda76d613305ebb2e5728c75362c94b64aa1/frame/transaction-payment/src/lib.rs#L614-L681){target=\_blank} through a transaction orchestration module to order transactions for the next block, until the block reaches its maximum capacity. The block building and execution order has the following operations:
- **Initializing a Block** - known as `on_initialize`, enables you to define runtime logic executed before any other transaction is accounted for. For example, inherent transactions, like the timestamp in the previous example, are commonly executed when initializing a block. Once the initialization logic is completed, the transaction orchestration module verifies the parent hash in the block header and the trie root to ensure the information is correct
- **Transaction Execution** - with the block already initialized, the transaction orchestration module executes each valid transaction according to its priority. The initial state is not cached before the execution, meaning that if one of the transactions fails mid-execution, any state changes committed up to that moment cannot be reverted, and the subsequent block will be invalid. Consequently, runtime logic should perform all necessary checks to ensure all valid transactions will succeed
- **Finalizing a Block** - after all queued valid transactions are executed or the block limit is reached, the orchestration module calls into each runtime module the `on_idle` and `on_finalize` functions. These two functions allow the definition of extra business logic that is automatically executed in the block finalization process. After the last `on_finalize` function is called, the orchestration module ensures that the block digest and storage root match what was calculated when the block was initialized
## Forkless Upgrades {: #forkless-upgrades}
Networks deployed through Tanssi have a thrilling feature: [forkless upgrades](https://docs.polkadot.com/develop/parachains/maintenance/runtime-upgrades/){target=\_blank}. Forkless upgrades allow developers to change the state transition function that governs the chain without creating a network fork, as seen on Ethereum multiple times. Furthermore, if the Tanssi network is set up with an on-chain governance system, upgrades to the network can happen in a truly decentralized and trustless way.
Forkless upgrades are made possible by storing the state transition function as a WebAssembly (Wasm) blob in both the Tanssi network itself and the Tanssi-powered network. When a new runtime is scheduled through a function call in the Tanssi-powered network, the Tanssi network validates this block, so it is notified and readies itself to validate incoming blocks using the most recent state transition function. Following a specified runtime upgrade delay period, a Tanssi sequencer on the Tanssi-powered network constructs a block that references a Tanssi network block, signaling to the Tanssi network that it can now apply the new runtime. Consequently, this new state transition function is utilized for that specific block. As all infrastructure participants at the network level employ the on-chain Wasm blob, every Tanssi network node operator can validate new blocks using the latest state transition function.
A high-level summary of the runtime upgrade process is shown in the following diagram:

## SUDO Account {: #sudo-account}
Tanssi networks may use a specific module called [SUDO](https://paritytech.github.io/polkadot-sdk/master/pallet_sudo/pallet/struct.Pallet.html){target=\_blank}. This module introduces a new type of account, also named _SUDO_, that can execute transactions with the [_Root_ origin](#origins).
Consequently, the SUDO account can perform **any** action that the runtime allows the _Root_ origin to execute. This can include:
- Mint new native Tanssi network tokens
- Perform [forkless runtime upgrades](#forkless-upgrades)
- Send transactions impersonating other [origin types](#origins). Therefore, SUDO can send transactions on behalf of other users without accessing their private key
_SUDO_ is recommended for TestNets as it allows them to swiftly make changes without the need to go through a lengthy on-chain governance process. It is good practice to have _SUDO_ keys stored safely and grant access to _SUDO_ calls via proxy accounts. Nevertheless, having _SUDO_ enabled in a production environment can lead to undesired consequences.
**Understanding the centralization risks of having _SUDO_ in a production environment is key.**
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/learn/decentralized-networks/tx-fees/
--- BEGIN CONTENT ---
---
title: Transaction Fees
description: Learn about the transaction fee mechanism in Tanssi networks, how it works from a Substrate perspective, and in the Ethereum EVM emulation layer with EIP-1559.
icon: material-piggy-bank-outline
categories: Basics
---
# Transaction Fees {: #transaction-fees }
## Introduction {: #introduction}
Tanssi-powered networks are built with a [modular framework](/learn/framework/){target=\_blank} called [Substrate](https://docs.polkadot.com/develop/parachains/intro-polkadot-sdk/){target=\_blank}. With this framework, you can build unique ways to handle transaction fees. For example, most transactions use a specific module called [Transaction Payment](https://docs.rs/pallet-transaction-payment/latest/pallet_transaction_payment){target=\_blank}. However, transaction fees on Tanssi-powered EVM-compatible networks can be charged at an EVM execution level, bypassing other fee-related modules.
Under the hood, for execution time, instead of working with a gas-based mechanism, all Tanssi networks work with a [weight-based mechanism](https://docs.polkadot.com/polkadot-protocol/parachain-basics/blocks-transactions-fees/fees/){target=\_blank}. Weight refers to the time (in picoseconds) it takes to validate a block. Generally speaking, for both EVM and non-EVM Tanssi networks, all function calls have a weight associated with them, which sets limits on storage input/output and computation. For Tanssi EVM networks, there is a gas-to-weight mapping that fully complies with the expected gas requirements for Ethereum API-based tools.
A transaction fee scheme is applied on top of the weight-based mechanism to ensure economic incentives are in line to limit the execution time, computation, and number of calls (database read/writes) to perform operations. Transaction fees are fundamental to preventing network spam, as they represent the cost of using the Tanssi network service. Consequently, a user interacting with the network through a specific function call will pay a transaction fee determined by a baseline fee algorithm.
This page covers the fundamentals of transaction fees for Tanssi networks. It first covers the underlying transaction fee architecture and how it is adapted to a fully EIP-1559-compliant model for Tanssi EVM networks.
## Baseline Fees Calculation {: #baseline-fees }
Every action that alters the state of a Tanssi network incurs a transaction fee. This fee is essential for the network's operation, covering the computational resources required to process transactions, similar to the gas and gas price parameters in EVM-compatible chains like Ethereum.
Tanssi networks [modular framework](/learn/framework/){target=\_blank} use a weight-based fee calculation mechanism to determine transaction fees. This approach considers various factors, including computational resources and storage operations (inputs/outputs), to reflect the true cost of transactions accurately. By accounting for these elements, the network ensures fair and efficient resource allocation.
Furthermore, Tanssi networks modularity ensures that EVM-compatible networks support legacy and [EIP-1559 compatible](https://eips.ethereum.org/EIPS/eip-1559){target=\_blank} transaction pricing mechanisms, ensuring full compatibility with development environments commonly used in Ethereum.
This section outlines all the different concepts associated with transaction fees for Tanssi networks.
### Weight {: #baseline-weight}
Broadly speaking, weight refers to the execution time it takes to validate a block, measured in picoseconds. Weight is divided into two separate variables:
- **`refTime`** - corresponds to the weight associated with computation time and database reads/writes
- **`proofSize`** - corresponds to the weight associated with the size of the Proof-Of-Validity (or PoV for short). The PoV is associated with the relevant state of a transaction, and it is what the Tanssi network sequencer shares with the security provider operators to get a block validated and finalized as part of the [network transaction flow](/learn/decentralized-networks/overview/#network-transaction-flow){target=\_blank}
To find the weights for all function calls, they are benchmarked in a system with reference hardware, and the approximate values of `refTime` and `proofSize` are set. This process is repeated for all function calls that consume blockspace and affect the PoV.
For transactions in which the fees are handled by the [transaction payment](https://docs.rs/pallet-transaction-payment/latest/pallet_transaction_payment){target=\_blank} module, all weight-based parameters are then passed through a _weight to fee_ algorithm that converts all to a final value, deducted from the sender's account when executing the function call. The algorithm can be customized, but Tanssi networks have a constant value set.
For EVM transactions, gas is converted to weight through a gas-to-weight algorithm so that all EVM calls can be mapped to block execution time. Nevertheless, fees are handled at an EVM execution level.
### Baseline Transaction Fees {: #baseline-transaction-fees}
With all function calls benchmarked, the transaction fee for each specific call can be obtained. Transaction fees are typically comprised of the following elements:
- **`BaseFee`** - baseline cost for a transaction to be included. It accounts for the transaction inclusion overhead, like signature verification. The fee is defined by two separate parameters:
- **`ExtrinsicBaseWeight`** - a constant value that represents the weight of the transaction inclusion overhead
- **`WeightToFee`** - a polynomial function that converts weight to fee
- **`WeightFee`** - fee defined by two separate parameters:
- **`BenchmarkedWeight`** - weight that accounts for the complexity (execution time) of a specific call
- **`CongestionMultiplier`** - a function that converts weight to fee and can be adjusted to account for the congestion of the network (weight consumed in the previous block). The default strategy for Tanssi networks is [`SlowAdjustingFeeUpdate`](https://research.web3.foundation/Polkadot/overview/token-economics#2-slow-adjusting-mechanism){target=\_blank}, which adjusts this multiplier slowly over time following the network load
- **`LengthFee`** - a fee correlated to the length in bytes of the function call. The fee is defined by two separate parameters:
- **`ByteLengthFunctionCall`** - length in bytes of the call being executed
- **`LengthToFee`** - a function that defines the per-byte fee algorithm. For Tanssi networks, this is a constant value
- **`Tip`** - an optional value that increases the overall fee, increasing the priority of the transaction by incentivizing sequencers to include it in the next block
Therefore, in general terms, the transaction fee can be calculated with the following equation:
```text
BaseFee = ExtrinsicBaseWeight * WeightToFee
WeightFee = BenchmarkedWeight * CongestionMultiplier
LengthFee = ByteLengthFunctionCall * LengthToFee
InclusionFee = BaseFee + WeightFee + LengthFee
FinalFee = InclusionFee + Tip
```
All non-EVM function calls available to developers use these baseline calculations for transaction fees. Tanssi EVM networks have an extra layer to translate this fee scheme into an Ethereum-like scheme from an Ethereum JSON-RPC and EVM perspective.
### EVM Transaction Fees {: #evm-transaction-fees }
Tanssi offers [templates for full Tanssi EVM-compatible networks](/builders/build/templates/evm/){target=\_blank}. Such networks provide an Ethereum-like environment for developers, where they can use Eth-specific libraries like [Ethers.js](/builders/toolkit/ethereum-api/libraries/ethersjs/){target=\_blank}, [Hardhat](/builders/toolkit/ethereum-api/dev-env/hardhat/){target=_blank}, and [Foundry](/builders/toolkit/ethereum-api/dev-env/foundry/){target=\_blank}.
In addition, all Tanssi EVM-compatible networks have an [EIP-1559 compatible](https://eips.ethereum.org/EIPS/eip-1559){target=\_blank} transaction pricing mechanism for EVM transactions. But they support both commonly used EVM transaction types:
- **Type 0 (Legacy)** - the transaction fee is calculated through a single gas price value that is included in the signed transaction blob. Because Tanssi EVM-compatible networks have a dynamic pricing mechanism, gas price must be greater than the current block's `baseFee` for a transaction to be considered valid
- **Type 2 (EIP-1559)** - the transaction fee is calculated with a combination of the `maxFeePerGas` and `maxPriorityFeePerGas` from the signed transaction blob, and the network's `baseFee` dynamically changes based on block congestion
Independently of the transaction type, the outcome of all EVM transactions is that there is an associated cost in native tokens that the network must charge.
By default, Tanssi EVM-compatible networks are configured with the following parameters:
- **Minimum BaseFee** - the minimum gas price of the network in case there are no transactions for long periods. The default value is set to 1 GWei
- **Block Fulness Target (Elasticity)** - the target gas used in a block so that the `baseFee` remains the same. [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559){target=\_blank} defines this value as a constant set to 2, meaning that the target usage is 50% of the block gas limit. All Tanssi EVM-compatible networks are set with the same target
- **Maximum BaseFee Increase** - the maximum amount the `baseFee` can increase or decrease, in percent points, based on the previous block target usage. [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559){target=\_blank} defines this value as a constant set to 12.5%. Consequently, if the block is full/empty, the `baseFee` will increase/decrease by 12.5%, and any intermediate values are linearly adjusted. Developers can configure this value for Tanssi EVM-compatible networks, but the default value is 12.5%
!!! note
One key difference in Tanssi EVM-compatible networks EIP-1559 implementation is that the transaction fees are calculated using the previous block `baseFee`.
The EVM transaction fee cost associated with all Tanssi EVM-compatible networks is captured at an EVM execution level. Nevertheless, EVM transactions do take block execution time. Therefore a gas-to-weight algorithm is required to account for the weight consumed by a specific call relative to the gas it is consuming.
Ultimately, the transaction fee and weight associated to an EVM call in a Tanssi EVM-compatible network can be calculated with the following formula:
=== "EIP-1559"
```text
Gas Price = baseFee + maxPriorityFeePerGas < maxFeePerGas ?
baseFee + maxPriorityFeePerGas :
maxFeePerGas;
Transaction Fee = Gas Price * Gas Used
Transaction Weight = Gas Used * GasToWeight
```
=== "Legacy"
```text
Transaction Fee = GasPrice * GasUsed
Transaction Weight = GasUsed * GasToWeight
```
`GasToWeight` is a constant value set to `{{ templates.evm.gas_to_weight }}`.
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/learn/framework/architecture/
--- BEGIN CONTENT ---
---
title: Framework Architecture
description: In a Substrate node, two main components are the runtime, which controls the blockchain's state transition, and the client, which manages everything else.
icon: octicons-stack-24
categories: Basics
---
# Framework Architecture {: #framework-architecture }
## Introduction {: #introduction }
Substrate is a software development kit (SDK) for building blockchains. This framework is the foundation and engine powering many projects across the Web3 ecosystem, including the Tanssi network itself and the networks deployed through Tanssi.
Written in the Rust language and designed with a modular architecture, Substrate is extremely performant, flexible, and highly customizable, making it the best choice for developing blockchains.
In this article, the architecture of a Substrate node is covered.
## Architecture {: #architecture }
The Substrate framework is designed for maximum customizability, providing a fully functional implementation for every important internal aspect of a blockchain. It allows developers to focus on the specifics of the use case and the runtime characteristics, and it provides the ability to change any of the default features (should the need arise).
The architecture of a Substrate node contains two main components:
- **Core Client** - handles the communication with the outer world (other nodes, dApps, end users, among others), and many other internal responsibilities, such as storage and communication
- **Runtime** - implements the custom logic of the Tanssi network, executes transactions, and manages the state transitions
From the end-user perspective, all the interaction with the Tanssi network is usually made through dApps or directly via the node RPC endpoints, for example, using a wallet. When a user triggers a request to fetch data or sends transactions to a node, the core client is responsible for responding or queuing the transactions until execution in the runtime. Still, all these internal aspects of the node design are kept transparent to the user.

## The Core Client {: #core-client }
The core client comprises components responsible for everything in the operation of a node in the network except for what happens in the runtime.
Some of the main components are:
- **Networking** - this component handles the communication with the peers in the network (synchronizing blocks, propagating transactions, and so on) and exposes the endpoints that allow dApps to integrate and interact with the Tanssi network
- **Storage** - this component manages the state storage of the Tanssi network in a highly efficient key-value database
- **Consensus** - this component ensures that all the participants in the network agree on the state of the blockchain, validating transactions, state transitions, and the resulting blocks
The default configuration of a Substrate node and the built-in implementations of the components are usually the best choice for most use cases. Still, teams are welcome to innovate and change or replace any piece of the node or even write a completely different implementation of the core client, such as [Kagome](https://github.com/soramitsu/kagome#intro){target=\_blank} (C++ implementation) and [Gossamer](https://github.com/ChainSafe/gossamer#a-go-implementation-of-the-polkadot-host){target=\_blank} (Golang implementation).
## The Runtime {: #runtime }
The runtime plays a crucial role in the operation of the Tanssi network. It contains the core logic and rules to meet the requirements of the use case the developers are building, and, therefore, it is responsible for validating the transactions and executing the state transitions.
Being the core element in a Tanssi network, designing the Substrate architecture an important decision has been made regarding the format for the runtime: it is compiled to [WebAssembly (Wasm)](https://webassembly.org){target=\_blank} byte code.
The Wasm format offers many advantages to a deployed Tanssi network, including:
- **Portability** - the Wasm format is platform-independent, meaning that the same binary can be distributed and run on different nodes using different hardware architectures and operating systems
- **Deterministic Execution** - the Wasm format ensures deterministic execution of code, which means that the same input will always produce the same output. Determinacy is a critical aspect in blockchains to obtain the same state transitions across every node in the network and reach a consensus
- **Forkless Upgradeability** - Substrate stores the runtime Wasm blob on-chain, meaning that the runtime itself becomes part of the state. This design allows upgrading the runtime logic in a forkless way using a transaction
Besides the format, internally, a Substrate runtime is built by composing different modules, either provided and ready-to-use by Substrate or custom-made. Each one of these modules define, among other things, the transactions they expose, the logic behind them, what needs to be stored in the chain state, the best format to do it, and how they cooperate with other modules composing functionality. More details about building a runtime will be covered in the [modules](/learn/framework/modules/){target=\_blank} section.
## Client-Runtime Communication {: #client-runtime-communication }
As previously described, the two main components of a Substrate node (the core client and the runtime) have a clear separation of concerns. Beyond the functional responsibilities, at a lower level, their binary representation and execution environments are different. While the node is compiled to be installed and run on a specific platform (be it Linux x64 or any other), the Tanssi network runtime is compiled to a Wasm format that is platform-agnostic and runs in an isolated execution environment.
Bearing in mind the separated execution environments, all the communication between the node client and the runtime occurs through a limited and well-defined interface, allowing the necessary operations such as:
- **Executing Transactions** - when a user submits a transaction to the client node, the node passes this transaction to the runtime through the defined API for its execution
- **State Queries** - the client node can query the current state of the blockchain to retrieve information such as account balances and any other domain-specific data
- **Consensus and Finality** - the client node coordinates consensus and finalization of the blocks, but it is the runtime's responsibility to determine the validity of new blocks, validate transactions, and ensure that the consensus rules are followed
- **Event Notifications** - the runtime emits events while executing transactions that the client node can use to keep external users updated about specific actions or changes in the state
--- END CONTENT ---
Doc-Content: https://docs.tanssi.network/learn/framework/modules/
--- BEGIN CONTENT ---
---
title: Network Modules for your Runtime
description: Substrate is a modular blockchain development framework with an extensive set of ready-to-use components to bundle with custom logic into the network Runtime.
icon: material-puzzle-outline
categories: Custom-Runtime, Basics
---
# Network Framework Modules {: #network-framework-modules }
## Introduction {: #introduction }
The Substrate framework provides complete and ready-to-use implementations of the main functions a Tanssi network needs to work properly, including cryptography, consensus, governance, and so on. These implementations are fully customizable and could be replaced with custom logic if needed.
When building the Runtime, which defines the state transition rules between two blocks applied to a set of transactions, the intended behavior and features of the blockchain need to be set by determining the rules of the state transition.
To build the Runtime, Substrate provides many built-in modules (also known as pallets) that can be freely used as building blocks to compose and interact with any other custom-made modules, allowing teams to create unique behaviors according to the specific requirements of their Tanssi network.

## Built-in Modules {: #built-in-modules }
When designing and writing the rules of a Tanssi network, the available set of functional modules brings a solution to many of the coding requirements that would otherwise need to be built from scratch.
Here is a list of some of the most popular modules:
- **[Balances](https://paritytech.github.io/substrate/master/pallet_balances/index.html){target=\_blank}** - it provides functions for handling accounts and balances for the Tanssi network native currency
- **[Assets](https://paritytech.github.io/substrate/master/pallet_assets/index.html){target=\_blank}** - it provides functions for handling any type of fungible tokens
- **[NFTs](https://paritytech.github.io/substrate/master/pallet_nfts/index.html){target=\_blank}** - it provides functions for dealing with non-fungible tokens
- **[Democracy](https://paritytech.github.io/substrate/master/pallet_democracy/index.html){target=\_blank}** - it provides functions to manage and administer general stakeholder voting
- **[Multisig](https://paritytech.github.io/substrate/master/pallet_multisig/index.html){target=\_blank}** - it provides functions for multi-signature dispatch
- **[Recovery](https://paritytech.github.io/substrate/master/pallet_recovery/index.html){target=\_blank}** - it provides functions to allow users to regain access to their accounts when the private key is lost. This works by granting other accounts the right to sign transactions on behalf of the lost account (note that it is necessary to have previously chosen the authorized accounts)
- **[Staking](https://paritytech.github.io/substrate/master/pallet_staking/index.html){target=\_blank}** - it provides functions to administer staked tokens, support rewarding, slashing, depositing, withdrawing, and so on
In addition to those previously listed, other modules like [identity](https://paritytech.github.io/substrate/master/pallet_identity/index.html){target=\_blank}, [smart contracts](https://paritytech.github.io/substrate/master/pallet_contracts/index.html){target=\_blank}, [vesting](https://paritytech.github.io/substrate/master/pallet_vesting/index.html){target=\_blank}, and many others that are freely available can speed up the development of the Tanssi network and, consequently, the time to market.
!!! note
The framework also includes other modules that provide core protocol functionality, such as consensus and low-level data encoding.
## Custom-Made Modules {: #custom-modules }
Developers creating new modules enjoy complete freedom to express any desired behavior in the core logic of the blockchain, like exposing new transactions, storing sensible information, and validating and enforcing business logic.
As explained in the [Architecture](/learn/framework/architecture/#client-runtime-communication){target=\_blank} article, a module needs to be able to communicate with the core client by exposing and integrating with a very specific API that allows the runtime to expose transactions, access storage, and code and decode information stored on-chain. It also needs to include many other required wiring codes that make the module work in the node.
To improve developer experience when writing modules, Substrate relies heavily on [Rust macros](https://doc.rust-lang.org/book/ch19-06-macros.html){target=\_blank}. Macros are special instructions that automatically expand to Rust code just before compile-time, allowing modules to keep up to seven times the amount of code out of sight of the developers. This allows developers to focus on the specific functional requirements when writing modules instead of dealing with technicalities and the necessary scaffolding code.
All modules in Substrate, including custom-made ones, implement these attribute macros, of which the first three are mandatory:
- **`#[frame_support::pallet]`** - this attribute is the entry point that marks the module as usable in the runtime
- **`#[pallet::pallet]`** - applied to a structure that is used to retrieve module information easily
- **`#[pallet::config]`** - is a required attribute to define the configuration for the data types of the module
- **`#[pallet::call]`** - this macro is used to define functions that will be exposed as transactions, allowing them to be dispatched to the runtime. It is here that the developers add their custom transactions and logic
- **`#[pallet::error]`** - as transactions may not be successful (insufficient funds, as an error example), and for security reasons, a custom module can never end up throwing an exception, all the possible errors are to be identified and listed in an enum to be returned upon an unsuccessful execution
- **`#[pallet::event]`** - events can be defined and used as a means to provide more information to the user
- **`#[pallet::storage]`** - this macro is used to define elements that will be persisted in storage. As resources are scarce in a blockchain, it should be used wisely to store only sensible information
All these macros act as attributes that must be applied to the code just above Rust modules, functions, structures, enums, types, etc., allowing the module to be built and added to the runtime, which, in time, will expose the custom logic to the outer world, as exposed in the following section.
### Custom Module Example { #custom-module-example }
As an example of a custom module, the following code (not intended for production use) showcases the use of the previously mentioned macros by presenting a simple lottery with minimal functionality, exposing two transactions:
- **buy_ticket** - this transaction verifies that the user signing the request has not already bought a ticket and has enough funds to pay for it. If everything is fine, the module transfers the ticket price to a special account and registers the user as a participant for the prize
- **award_prize** - this transaction generates a random number to pick the winner from the list of participants. The winner gets the total amount of the funds transferred to the module's special account
```rust
#![cfg_attr(not(feature = "std"), no_std)]
/// Learn more about FRAME and the core library of Substrate FRAME pallets:
///
pub use pallet::*;
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::{*, ValueQuery, OptionQuery};
use frame_system::pallet_prelude::*;
use scale_info::prelude::vec::Vec;
use frame_support::
{
sp_runtime::traits::AccountIdConversion,
traits:: {
Currency, ExistenceRequirement, Randomness
},
PalletId,
};
type BalanceOf =
<::Currency as Currency<::AccountId>>::Balance;
#[pallet::pallet]
pub struct Pallet(_);
/// Configure the module by specifying the parameters and types on which it depends.
#[pallet::config]
pub trait Config: frame_system::Config {
// Event definition
type RuntimeEvent: From>
+ IsType<::RuntimeEvent>;
// Currency
type Currency: Currency;
// Randomness
type MyRandomness: Randomness>;
// Ticket cost
#[pallet::constant]
type TicketCost: Get>;
// Maximum number of participants
#[pallet::constant]
type MaxParticipants: Get;
// Module Id
#[pallet::constant]
type PalletId: Get;
}
// The pallet's runtime storage items.
#[pallet::storage]
#[pallet::getter(fn get_participants)]
pub(super) type Participants = StorageValue<
_,
BoundedVec,
OptionQuery
>;
#[pallet::storage]
#[pallet::getter(fn get_nonce)]
pub(super) type Nonce = StorageValue<
_,
u64,
ValueQuery
>;
// Pallets use events to inform users when important changes are made.
// https://docs.substrate.io/main-docs/build/events-errors/
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event {
/// Event emitted when a ticket is bought
TicketBought { who: T::AccountId },
/// Event emitted when the prize is awarded
PrizeAwarded { winner: T::AccountId },
/// Event emitted when the prize is to be awarded, but there are no participants
ThereAreNoParticipants,
}
// Errors inform users that something went wrong
#[pallet::error]
pub enum Error {
NotEnoughCurrency,
AccountAlreadyParticipating,
CanNotAddParticipant,
}
#[pallet::call]
impl Pallet {
#[pallet::call_index(0)]
#[pallet::weight(0)]
pub fn buy_ticket(origin: OriginFor) -> DispatchResult {
// 1. Validates the origin signature
let buyer = ensure_signed(origin)?;
// 2. Checks that the user has enough balance to afford the ticket price
ensure!(
T::Currency::free_balance(&buyer) >= T::TicketCost::get(),
Error::::NotEnoughCurrency
);
// 3. Checks that the user is not already participating
if let Some(participants) = Self::get_participants() {
ensure!(
!participants.contains(&buyer),
Error::::AccountAlreadyParticipating
);
}
// 4. Adds the user as a new participant for the prize
match Self::get_participants() {
Some(mut participants) => {
ensure!(
participants.try_push(buyer.clone()).is_ok(),
Error::::CanNotAddParticipant
);
Participants::::set(Some(participants));
},
None => {
let mut participants = BoundedVec::new();
ensure!(
participants.try_push(buyer.clone()).is_ok(),
Error::::CanNotAddParticipant
);
Participants::::set(Some(participants));
}
};
// 5. Transfers the ticket cost to the module's account
// to be hold until transferred to the winner
T::Currency::transfer(
&buyer,
&Self::get_pallet_account(),
T::TicketCost::get(),
ExistenceRequirement::KeepAlive)?;
// 6. Notify the event
Self::deposit_event(Event::TicketBought { who: buyer });
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(0)]
pub fn award_prize(origin: OriginFor) -> DispatchResult {
// 1. Validates the origin signature
let _who = ensure_root(origin)?;
match Self::get_participants() {
Some(participants) => {
// 2. Gets a random number from the randomness module
let nonce = Self::get_and_increment_nonce();
let (random_seed, _) = T::MyRandomness::random(&nonce);
let random_number = ::decode(&mut random_seed.as_ref())
.expect("secure hashes should always be bigger than u32; qed");
// 3. Selects the winner from the participants lit
let winner_index = random_number as usize % participants.len();
let winner = participants.as_slice().get(winner_index).unwrap();
// 4. Transfers the total prize to the winner's account
let prize = T::Currency::free_balance(&Self::get_pallet_account());
T::Currency::transfer(
&Self::get_pallet_account(),
&winner,
prize,
ExistenceRequirement::AllowDeath)?;
// 5. Resets the participants list, and gets ready for another lottery round
Participants::::kill();
// 6. Notify the event
Self::deposit_event(Event::PrizeAwarded { winner: winner.clone() } );
},
None => {
// Notify the event (No participants)
Self::deposit_event(Event::ThereAreNoParticipants);
}
};
Ok(())
}
}
impl Pallet {
fn get_pallet_account() -> T::AccountId {
T::PalletId::get().into_account_truncating()
}
fn get_and_increment_nonce() -> Vec