# #\[ink::chain\_extension]

Na configuração padrão do `contracts-pallet`, um contrato inteligente só pode interagir com a runtime por meio de sua interface básica bem definida. Essa API já permite uma variedade de interações entre o `contracts-pallet` e o contrato inteligente em execução. Por exemplo, é possível chamar e instanciar outros contratos inteligentes na mesma cadeia, emitir eventos, consultar informações de contexto ou executar procedimentos de hash criptográfico incorporados.

Se esse conjunto básico de recursos não for suficiente para atender às necessidades de uma determinada blockchain construída com Substrate, é possível estender facilmente essa API usando o recurso chamado chain extension.

<figure><img src="https://use.ink/img/venn.png" alt=""><figcaption></figcaption></figure>

Com as chain extensions, você pode expor partes da lógica da sua runtime aos desenvolvedores de contratos inteligentes.

<mark style="color:purple;">NOTA: O repositório do ink! contém o</mark> [<mark style="color:purple;">exemplo</mark> <mark style="color:purple;"></mark><mark style="color:purple;">`rand-extension`</mark>](https://github.com/paritytech/ink-examples/tree/main/rand-extension)<mark style="color:purple;">. Este é um exemplo completo de uma chain extension implementada tanto em ink! quanto no Substrate.</mark>

### Estrutura[​](https://use.ink/macros-attributes/chain-extension#structure) <a href="#structure" id="structure"></a>

A interface consiste em um código de erro que indica erros leves, bem como a definição de alguns métodos de chain extension.  &#x20;

A estrutura geral segue a de uma simples definição de trait em Rust. O código de erro é definido como uma definição de tipo associado da definição do trait. Os métodos são definidos como métodos associados do trait sem implementação.&#x20;

Os métodos de chain extension não devem ter um receptor `self`, como `&self` ou `&mut self`, e devem ter entradas e saída que implementam o codec SCALE. O valor de retorno segue regras específicas que podem ser alteradas usando o atributo `handle_status` e a alternância entre tipos `Result` e `Non-Result`, que são descritos com mais detalhes abaixo.

### Uso[​](https://use.ink/macros-attributes/chain-extension#usage) <a href="#usage" id="usage"></a>

Normalmente, a definição de chain extension usando essa macro é fornecida pelo autor da chain extension em uma crate separada. Contratos inteligentes ink! que utilizam essa chain extension simplesmente dependem dessa crate e usam sua definição de ambiente associada para utilizar os métodos fornecidos pela chain extension.

### Atributos[​](https://use.ink/macros-attributes/chain-extension#attributes) <a href="#attributes" id="attributes"></a>

Existem dois atributos diferentes com os quais os métodos de chain extension podem ser marcados:

| Atributo                          | Obrigatório | Valor Padrão |                                                                             Description                                                                            |
| --------------------------------- | :---------: | ------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| `ink(extension = N: u32)`         |     Yes     | -            |                                                  Determines the unique function ID of the chain extension method.                                                  |
| `ink(handle_status = flag: bool)` |   Optional  | `true`       | Assumes that the returned status code of the chain extension method always indicates success and therefore always loads and decodes the output buffer of the call. |

Como acontece com todos os atributos do ink!, vários deles podem aparecer em uma lista contígua:

```rust
type Access = i32;

#[ink::chain_extension]
pub trait MyChainExtension {
    type ErrorCode = i32;

    #[ink(extension = 5, handle_status = false)]
    fn key_access_for_account(key: &[u8], account: &[u8]) -> Access;
}
```

...ou como vários atributos individuais do ink! aplicados ao mesmo item:

```rust
type Access = i32;

#[ink::chain_extension]
pub trait MyChainExtension {
  type ErrorCode = i32;

  #[ink(extension = 5)]
  #[ink(handle_status = false)]
  fn key_access_for_account(key: &[u8], account: &[u8]) -> Access;
}
```

### Detalhes: `handle_status`[​](https://use.ink/macros-attributes/chain-extension#details-handle_status) <a href="#details-handle_status" id="details-handle_status"></a>

Valor padrão: `true`

Por padrão, todos os métodos de chain extension devem retornar um `Result<T, E>` onde `E: From<Self::ErrorCode>`. O `Self::ErrorCode` representa o código de erro da chain extension. Isso significa que um contrato inteligente que chama um método de chain extension primeiro consulta o código de status retornado pelo método de chain extension e só carrega e decodifica a saída se o código de status indicar uma chamada bem-sucedida. Essa abordagem foi escolhida por ser mais eficiente quando nenhuma saída além do código de erro é necessária para uma chamada de chain extension. Ao projetar uma chain extension, tente utilizar o código de erro para retornar erros e use o buffer de saída apenas para informações que não se encaixam em um único valor `u32`.

Um método de chain extension que é marcado com `handle_status = false` assume que o código de erro retornado sempre indicará sucesso. Portanto, ele sempre carregará e decodificará o buffer de saída e perderá a restrição `E: From<Self::ErrorCode` para a chamada.

Observe que se um método de chain extension não retornar `Result<T, E>` onde `E: From<Self::ErrorCode>`, mas `handle_status = true`, ele ainda retornará um valor do tipo `Result<T, Self::ErrorCode>`.

### Uso: `handle_status` + Tipo de retorno`Result<T, E>` [​](https://use.ink/macros-attributes/chain-extension#usage-handle_status--resultt-e-return-type) <a href="#usage-handle_status--resultt-e-return-type" id="usage-handle_status--resultt-e-return-type"></a>

Utilize tanto `handle_status = false` quanto um tipo de retorno `non-Result` para o mesmo método de chain extension se uma chamada a ele nunca pode falhar e nunca retorna um tipo `Result`.

### Combinações[​](https://use.ink/macros-attributes/chain-extension#combinations) <a href="#combinations" id="combinations"></a>

Devido à possibilidade de marcar um método de chain extension com`handle_status` e a escolha entre (1) retorna apenas `Result<T, E>` ou (2) retornar apenas `T`, existem 4 casos diferentes com semânticas ligeiramente distintas:

| `handle_status` | Returns `Result<T, E>` | Effects                                                                                                                                                                                                                                                                                                                                                                                                       |
| :-------------: | :--------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|      `true`     |         `true`         | O método de extensão de cadeia é obrigado a retornar um valor do tipo`Result<T, E>` onde `E: From<Self::ErrorCode>`. Uma chamada sempre verificará se o código de status retornado indica sucesso e somente então carregará e decodificará o valor no buffer de saída.                                                                                                                                        |
|      `true`     |         `false`        | O método de extensão de cadeia pode retornar qualquer tipo non-`Result`. Uma chamada sempre verificará se o código de status retornado indica sucesso e somente então carregará e decodificará o valor no buffer de saída. O tipo de retorno real do método de extensão de cadeia ainda é `Result<T, Self::ErrorCode>` quando o método de extensão de cadeia foi definido para retornar um valor do tipo `T`. |
|     `false`     |         `true`         | O método de extensão de cadeia deve retornar um valor do tipo `Result<T, E>`. Uma chamada sempre assume que o código de status retornado indica sucesso e, portanto, sempre carrega e decodifica o buffer de saída diretamente.                                                                                                                                                                               |
|     `false`     |         `false`        | O método de extensão de cadeia pode retornar qualquer tipo non-`Result`. Uma chamada sempre assume que o código de status retornado indica sucesso e, portanto, sempre carrega e decodifica diretamente o buffer de saída.                                                                                                                                                                                    |

### Código de Erro[​](https://use.ink/macros-attributes/chain-extension#error-code) <a href="#error-code" id="error-code"></a>

Cada chain extension define exatamente um `ErrorCode` usando a seguinte sintaxe:

```rust
#[ink::chain_extension]
pub trait MyChainExtension {
    type ErrorCode = MyErrorCode;

    // more definitions ...
}
```

The defined `ErrorCode` must implement `FromStatusCode` which should be implemented as a more or less trivial conversion from the `u32` status code to a `Result<(), Self::ErrorCode>`. The `Ok(())` value indicates that the call to the chain extension method was successful.

By convention an error code of `0` represents success. However, chain extension authors may use whatever suits their needs.

### Example: Definition[​](https://use.ink/macros-attributes/chain-extension#example-definition) <a href="#example-definition" id="example-definition"></a>

In the below example a chain extension is defined that allows its users to read and write from and to the runtime storage using access privileges:

```rust
/// Custom chain extension to read to and write from the runtime.
#[ink::chain_extension]
pub trait RuntimeReadWrite {
    type ErrorCode = ReadWriteErrorCode;

    /// Reads from runtime storage.
    ///
    /// # Note
    ///
    /// Actually returns a value of type `Result<Vec<u8>, Self::ErrorCode>`.
    /// #[ink(extension = 1, returns_result = false)]
    /// fn read(key: &[u8]) -> Vec<u8>;
    ///
    /// Reads from runtime storage.
    ///
    /// Returns the number of bytes read and up to 32 bytes of the
    /// read value. Unused bytes in the output are set to 0.
    ///
    /// # Errors
    ///
    /// If the runtime storage cell stores a value that requires more than
    /// 32 bytes.
    ///
    /// # Note
    ///
    /// This requires `ReadWriteError` to implement `From<ReadWriteErrorCode>`
    /// and may potentially return any `Self::ErrorCode` through its return value.
    #[ink(extension = 2)]
    fn read_small(key: &[u8]) -> Result<(u32, [u8; 32]), ReadWriteError>;

    /// Writes into runtime storage.
    ///
    /// # Note
    ///
    /// Actually returns a value of type `Result<(), Self::ErrorCode>`.
    #[ink(extension = 3)]
    fn write(key: &[u8], value: &[u8]);

    /// Returns the access allowed for the key for the caller.
    ///
    /// # Note
    ///
    /// Assumes to never fail the call and therefore always returns `Option<Access>`.
    #[ink(extension = 4, handle_status = false)]
    fn access(key: &[u8]) -> Option<Access>;

    /// Unlocks previously acquired permission to access key.
    ///
    /// # Errors
    ///
    /// If the permission was not granted.
    ///
    /// # Note
    ///
    /// Assumes the call to never fail and therefore does _NOT_ require `UnlockAccessError`
    /// to implement `From<Self::ErrorCode>` as in the `read_small` method above.
    #[ink(extension = 5, handle_status = false)]
    fn unlock_access(key: &[u8], access: Access) -> Result<(), UnlockAccessError>;
}

#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
pub enum ReadWriteErrorCode {
  InvalidKey,
  CannotWriteToKey,
  CannotReadFromKey,
}

#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
pub enum ReadWriteError {
  ErrorCode(ReadWriteErrorCode),
  BufferTooSmall { required_bytes: u32 },
}

impl From<ReadWriteErrorCode> for ReadWriteError {
  fn from(error_code: ReadWriteErrorCode) -> Self {
    Self::ErrorCode(error_code)
  }
}

impl From<scale::Error> for ReadWriteError {
  fn from(_: scale::Error) -> Self {
    panic!("encountered unexpected invalid SCALE encoding")
  }
}

#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
pub struct UnlockAccessError {
  reason: String,
}

impl From<scale::Error> for UnlockAccessError {
  fn from(_: scale::Error) -> Self {
    panic!("encountered unexpected invalid SCALE encoding")
  }
}

#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
pub enum Access {
  ReadWrite,
  ReadOnly,
  WriteOnly,
}

impl ink::env::chain_extension::FromStatusCode for ReadWriteErrorCode {
  fn from_status_code(status_code: u32) -> Result<(), Self> {
    match status_code {
      0 => Ok(()),
      1 => Err(Self::InvalidKey),
      2 => Err(Self::CannotWriteToKey),
      3 => Err(Self::CannotReadFromKey),
      _ => panic!("encountered unknown status code"),
    }
  }
}
```

All the error types and other utility types used in the chain extension definition above are often required to implement various traits such as SCALE's `Encode` and `Decode` as well as `scale-info`'s `TypeInfo` trait.

A full example of the above chain extension definition can be seen [here](https://github.com/paritytech/ink/blob/017f71d60799b764425334f86b732cc7b7065fe6/crates/lang/macro/tests/ui/chain_extension/simple.rs).

### Example: Environment[​](https://use.ink/macros-attributes/chain-extension#example-environment) <a href="#example-environment" id="example-environment"></a>

In order to allow ink! smart contracts to use the above defined chain extension it needs to be integrated into an `Environment` definition as shown below:

```rust
type RuntimeReadWrite = i32;

use ink::env::{Environment, DefaultEnvironment};

pub enum CustomEnvironment {}

impl Environment for CustomEnvironment {
    const MAX_EVENT_TOPICS: usize =
        <DefaultEnvironment as Environment>::MAX_EVENT_TOPICS;

    type AccountId = <DefaultEnvironment as Environment>::AccountId;
    type Balance = <DefaultEnvironment as Environment>::Balance;
    type Hash = <DefaultEnvironment as Environment>::Hash;
    type BlockNumber = <DefaultEnvironment as Environment>::BlockNumber;
    type Timestamp = <DefaultEnvironment as Environment>::Timestamp;

    type ChainExtension = RuntimeReadWrite;
}
```

Above we defined the `CustomEnvironment` which defaults to ink!'s `DefaultEnvironment` for all constants and types but the `ChainExtension` type which is assigned to our newly defined chain extension.

### Example: Usage[​](https://use.ink/macros-attributes/chain-extension#example-usage) <a href="#example-usage" id="example-usage"></a>

An ink! smart contract can use the above defined chain extension through the `Environment` definition defined in the last example section using the `env` macro parameter as shown below.

Note that chain extension methods are accessible through `Self::extension()` or `self.extension()`. For example as in `Self::extension().read(..)` or `self.extension().read(..)`.

```rust
#[ink::contract(env = CustomEnvironment)]
mod read_writer {

    #[ink(storage)]
    pub struct ReadWriter {}

    impl ReadWriter {
        #[ink(constructor)]
        pub fn new() -> Self { Self {} }

        #[ink(message)]
        pub fn read(&self, key: Vec<u8>) -> Result<Vec<u8>, ReadWriteErrorCode> {
            self.env()
                .extension()
                .read(&key)
        }

        #[ink(message)]
        pub fn read_small(&self, key: Vec<u8>) -> Result<(u32, [u8; 32]), ReadWriteError> {
            self.env()
                .extension()
                .read_small(&key)
        }

        #[ink(message)]
        pub fn write(
            &self,
            key: Vec<u8>,
            value: Vec<u8>,
        ) -> Result<(), ReadWriteErrorCode> {
            self.env()
                .extension()
                .write(&key, &value)
        }

        #[ink(message)]
        pub fn access(&self, key: Vec<u8>) -> Option<Access> {
            self.env()
                .extension()
                .access(&key)
        }

        #[ink(message)]
        pub fn unlock_access(&self, key: Vec<u8>, access: Access) -> Result<(), UnlockAccessError> {
            self.env()
                .extension()
                .unlock_access(&key, access)
        }
    }

    /// Custom chain extension to read to and write from the runtime.
    #[ink::chain_extension]
    pub trait RuntimeReadWrite {
          type ErrorCode = ReadWriteErrorCode;
          #[ink(extension = 1)]
          fn read(key: &[u8]) -> Vec<u8>;
          #[ink(extension = 2)]
          fn read_small(key: &[u8]) -> Result<(u32, [u8; 32]), ReadWriteError>;
          #[ink(extension = 3)]
          fn write(key: &[u8], value: &[u8]);
          #[ink(extension = 4, handle_status = false)]
          fn access(key: &[u8]) -> Option<Access>;
          #[ink(extension = 5, handle_status = false)]
          fn unlock_access(key: &[u8], access: Access) -> Result<(), UnlockAccessError>;
    }

    #[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
    pub enum ReadWriteErrorCode {
          InvalidKey,
          CannotWriteToKey,
          CannotReadFromKey,
    }

    #[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
    pub enum ReadWriteError {
          ErrorCode(ReadWriteErrorCode),
          BufferTooSmall { required_bytes: u32 },
    }
    impl From<ReadWriteErrorCode> for ReadWriteError {
         fn from(error_code: ReadWriteErrorCode) -> Self {
             Self::ErrorCode(error_code)
         }
    }
    impl From<scale::Error> for ReadWriteError {
         fn from(_: scale::Error) -> Self {
             panic!("encountered unexpected invalid SCALE encoding")
         }
    }

    #[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
    pub struct UnlockAccessError {
         reason: String,
    }
    impl From<scale::Error> for UnlockAccessError {
         fn from(_: scale::Error) -> Self {
             panic!("encountered unexpected invalid SCALE encoding")
         }
    }
    #[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
    pub enum Access {
         ReadWrite,
         ReadOnly,
         WriteOnly,
    }
    impl ink::env::chain_extension::FromStatusCode for ReadWriteErrorCode {
         fn from_status_code(status_code: u32) -> Result<(), Self> {
             match status_code {
                 0 => Ok(()),
                 1 => Err(Self::InvalidKey),
                 2 => Err(Self::CannotWriteToKey),
                 3 => Err(Self::CannotReadFromKey),
                 _ => panic!("encountered unknown status code"),
             }
         }
    }
    pub enum CustomEnvironment {}
    impl ink::env::Environment for CustomEnvironment {
         const MAX_EVENT_TOPICS: usize =
             <ink::env::DefaultEnvironment as ink::env::Environment>::MAX_EVENT_TOPICS;

         type AccountId = <ink::env::DefaultEnvironment as ink::env::Environment>::AccountId;
         type Balance = <ink::env::DefaultEnvironment as ink::env::Environment>::Balance;
         type Hash = <ink::env::DefaultEnvironment as ink::env::Environment>::Hash;
         type BlockNumber = <ink::env::DefaultEnvironment as ink::env::Environment>::BlockNumber;
         type Timestamp = <ink::env::DefaultEnvironment as ink::env::Environment>::Timestamp;

         type ChainExtension = RuntimeReadWrite;
    }
}
```

### Technical Limitations[​](https://use.ink/macros-attributes/chain-extension#technical-limitations) <a href="#technical-limitations" id="technical-limitations"></a>

* Due to technical limitations it is not possible to refer to the `ErrorCode` associated type using `Self::ErrorCode` anywhere within the chain extension and its defined methods. Instead chain extension authors should directly use the error code type when required. This limitation might be lifted in future versions of ink!.
* It is not possible to declare other chain extension traits as super traits or super chain extensions of another.
