👾Modelo de Contrato

Nesta página, vamos explicar como criar um contrato básico e explicar seus elementos.

Criando um modelo

Navegue até o seu diretório de trabalho e execute:

cargo contract new foobar

Isso criará uma nova pasta de projeto chamada "foobar".

cd foobar/

No arquivo lib.rs, você encontrará o código inicial fornecido, que pode ser usado como ponto de partida.

Verifique rapidamente se ele compila e se os testes triviais passam com o seguinte comando:

cargo test

Também verifique se você pode compilar o arquivo Wasm executando:

cargo contract build

Se tudo estiver correto, então estamos prontos para começar a programar!

Conteúdo do Modelo

O modelo contém código estruturado que fornece um ponto de partida para escrever um contrato ink!. A seguir, daremos uma olhada no que os arquivos contêm. Os arquivos que você obtém localmente serão semelhantes, apenas adicionamos comentários explicativos aqui.

Cargo.toml

[package]
name = "foobar"
version = "0.1.0"
authors = ["[your_name] <[your_email]>"]
edition = "2021"

[dependencies]
# The `ink` crate contains the ink! eDSL and re-exports
# a number of other ink! specific crates. For example,
# `ink::env` is the `ink_env` crate that contains functions
# to interact with a contract's environment (querying information
# about a caller, the current block number, etc.).
ink = { version = "4.0.0-beta", default-features = false }

# Substrate blockchains use the SCALE codec for anything to
# do with data encoding/decoding. If an ink! contract is called
# the passed values have to be SCALE-encoded and return values
# have to be SCALE-decoded. All values that are put into a
# contract's storage are SCALE-encoded as well.
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }

# This crate is used to write information about a contract's
# types into the contract's metadata (i.e. its ABI). This is
# needed so that a client knows that a contract message requires
# e.g. an Array and that it has to SCALE-encode the value as an Array.
scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true }

[dev-dependencies]
# This developer dependency is for the End-to-End testing framework.
ink_e2e = { path = "../../crates/e2e" }

[lib]
name = "foobar"
path = "lib.rs"

# This setting typically specifies that you'd like the compiler to
# create a dynamic system library. For WebAssembly though it specifies
# that the compiler should create a `*.wasm` without a start function.
crate-type = [
    "cdylib",
]

[features]
default = ["std"]
std = [
    "ink/std",
    "scale/std",
    "scale-info/std",
]
ink-as-dependency = []

# This feature is just a convention, so that the end-to-end tests
# are only executed if `cargo test` is explicitly invoked with
# `--features e2e-tests`.
e2e-tests = []

lib.rs

Todo contrato ink! deve conter:

  • Exatamente uma estrutura#[ink(storage)] .

  • Pelo menos uma função #[ink(constructor)] .

  • Pelo menos uma função #[ink(message)].

O código estruturado será semelhante ao seguinte, embora tenhamos alterado os comentários para explicar o que está acontecendo em alto nível.

// If the `std` feature from the `Cargo.toml` is not enabled
// we switch on `no_std`, this has the effect of Rusts standard
// library not being included in our contract.
//
// The Rust standard library is OS-dependent and Wasm is
// architecture independent.
#![cfg_attr(not(feature = "std"), no_std)]

// This is the ink! macro, the starting point for your contract.
// Everything below it might look like Rust code, but it is actually
// run through a parser in ink!.
#[ink::contract]
pub mod flipper {
    /// This is the contract's storage.
    #[ink(storage)]
    pub struct Flipper {
        value: bool,
    }

    impl Flipper {
        /// A constructor that the contract can be initialized with.
        #[ink(constructor)]
        pub fn new(init_value: bool) -> Self {
            /* --snip-- */
        }

        /// An alternative constructor that the contract can be
        /// initialized with.
        #[ink(constructor)]
        pub fn new_default() -> Self {
            /* --snip-- */
        }

        /// A state-mutating function that the contract exposes to the
        /// outside world.
        ///
        /// By default functions are private, they have to be annotated
        /// with `#[ink(message)]` and `pub` to be available from the
        /// outside.
        #[ink(message)]
        pub fn flip(&mut self) {
            /* --snip-- */
        }

        /// A public contract function that has no side-effects.
        ///
        /// Note that while purely reading functions can be invoked
        /// by submitting a transaction on-chain, this is usually
        /// not done as they have no side-effects and the transaction
        /// costs would be wasted.
        /// Instead those functions are typically invoked via RPC to
        /// return a contract's state.
        #[ink(message)]
        pub fn get(&self) -> bool {
            /* --snip-- */
        }
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        /// This attribute denotes that the test is executed in
        /// a simulated, mocked blockchain environment. There are
        /// functions available to influence how the test environment
        /// is configured (e.g. setting an account to a specified balance).
        #[ink::test]
        fn default_works() {
            /* --snip-- */
        }

        /* --snip-- */
    }

    #[cfg(all(test, feature = "e2e-tests"))]
    mod e2e_tests {
        use super::*;
        use ink_e2e::build_message;

        type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

        /// With this attribute the contract will be compiled and deployed
        /// to a Substrate node that is required to be running in the
        /// background.
        ///
        /// We offer API functions that enable developers to then interact
        /// with the contract. ink! will take care of putting contract calls
        /// into transactions that will be submitted to the Substrate chain.
        ///
        /// Developers can define assertions on the outcome of their transactions,
        /// such as checking for state mutations, transaction failures or
        /// incurred gas costs.
        #[ink_e2e::test]
        async fn it_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> {
            /* --snip-- */
        }

        /* --snip-- */
    }
}

Last updated