A versão 2 do compilador Cairo apresenta alterações na sintaxe Starknet para tornar o código mais explícito e seguro. A interface pública do contrato inteligente é definida usando traços e o acesso ao armazenamento é feito por meio do traço ContractState. Os métodos privados devem ser definidos com uma implementação diferente da interface pública. Os eventos agora são definidos como enums, onde cada variante é uma estrutura com o mesmo nome.
Isenção de responsabilidade: os termos usados aqui referem-se a diferentes versões do compilador Cairo, e a sintaxe é provisória, pois a comunidade Starknet ainda está debatendo qual é o melhor termo a ser usado. Uma vez determinado, este artigo será atualizado de acordo.
O Compilador v2
Na semana passada, uma nova versão principal 2.0.0-rc0 do compilador Cairo foi lançada no Github. O novo compilador fornece melhorias significativas para o plug-in Starknet, tornando nosso código mais seguro, mais explícito e mais reutilizável. Observe que esta nova versão do compilador ainda não é suportada no Starknet testnet ou mainnet, pois ainda está sendo trabalhada no ambiente integrado.
O objetivo deste artigo é mostrar como reescrever um contrato inteligente Starknet criado para o compilador Cairo versão 1.x em um contrato inteligente compatível com o compilador versão 2.x. Nosso ponto de partida é o contrato inteligente Ownable criado no artigo anterior, que é compatível com o compilador Cario versão 1.x.
[contract] mod Ownable {use starknet::ContractAddress;use starknet::get_caller_address;
fn apenas_owner() {deixe o chamador = get_caller_address();assert(chamador == proprietário::read(), 'O chamador não é o proprietário');
Configurações do Projeto
Como o Protostar ainda não oferece suporte ao compilador v2, este artigo contará com o pré-lançamento do Scarb (versão 0.5.0-alpha.1) que o suporta. Para instalar essa versão específica do Scarb, você pode usar o seguinte comando.
Após a conclusão da configuração, podemos ir para src/lib.cairo e começar a escrever contratos inteligentes.
Armazenamento e construtor
Na versão 2 do compilador Cairo, os contratos inteligentes ainda são definidos por módulos anotados com um atributo de contrato, só que desta vez o atributo recebe o nome do plugin que o define, neste caso starknet.
#[starknet::contract]mod próprio {}
O armazenamento interno ainda é definido como uma estrutura que deve ser chamada de Storage, só que desta vez deve ser anotada com um atributo de armazenamento.
Para definir o construtor, anotamos a função com o atributo constructor, como fizemos na v1, a vantagem é que agora a função pode ter qualquer nome, não precisa ser chamada de "constructor" como na v1. Embora não seja obrigatório, ainda me referirei à função como "construtor" por convenção, mas você pode chamá-la de maneira diferente.
Outra mudança importante é que agora o construtor passa automaticamente uma referência para ContractState, que age como um intermediário para armazenar variáveis, no caso "owner".
Observe que a sintaxe para gravar e ler o armazenamento foi alterada desde a v1. Antes de fazermos owner::write(), agora fazemos self.owner.write(). O mesmo se aplica à leitura do armazenamento.
A propósito, o tipo ContractState não precisa ser colocado no escopo manualmente, ele está incluído no prelúdio.
Métodos Públicos
Uma diferença importante da versão 1 do compilador Cairo é que agora precisamos definir explicitamente a interface pública de um contrato inteligente usando características anotadas com o atributo starknet::interface.
Se você se lembra do código original da v1, nosso contrato inteligente tem dois métodos "públicos" (get_owner e transfer_ownership) e um método "privado" (apenas_owner). Este recurso lida apenas com métodos públicos e não depende de atributos "external" ou "view" para indicar quais métodos podem modificar o estado do contrato e quais métodos não podem. Em vez disso, isso agora é explícito pelo tipo do parâmetro self.
Se um método exigir uma referência a ContractStorage (o que, uma vez implementado, o T genérico exige), esse método poderá modificar o estado interno do contrato inteligente. Isso é o que costumávamos chamar de método "externo". Por outro lado, se um método requer um instantâneo de ContractStorage, ele pode apenas lê-lo, não modificá-lo. Isso é o que costumávamos chamar de método "view".
Agora podemos criar uma implementação para a característica que acabamos de definir usando a palavra-chave impl. Lembre-se, Cairo difere de Rust porque as implementações têm nomes.
Criamos uma implementação para nosso trait dentro do módulo que define o contrato inteligente, passando o tipo ContractState como um tipo T genérico para que o armazenamento possa ser acessado como um construtor.
Nossa implementação é anotada com o atributo external(v0). A versão 0 no atributo significa que o seletor é derivado apenas do nome do método, como acontecia no passado. A desvantagem é que, se você definir outra implementação de uma característica diferente para seu contrato inteligente e duas características usarem o mesmo nome para um de seus métodos, o compilador lançará um erro devido ao seletor duplicado.
Uma versão futura dessa propriedade pode adicionar uma nova maneira de avaliar seletores para evitar conflitos, mas isso ainda não funciona. Atualmente, só podemos usar a versão 0 das propriedades externas.
Métodos Privados
Também precisamos definir outro método para o contrato inteligente, only_owner. Este método verifica se a pessoa que o chama deve ser o proprietário do contrato inteligente.
Como este é um método privado que não pode ser chamado de fora, ele não pode ser definido como parte do OwnableTrait (a interface pública do contrato inteligente). Em vez disso, usaremos o atributo generate_trait para criar uma nova implementação da característica gerada automaticamente.
...#[starknet::contract]mod Ownable { ... #[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(chamador == self.owner.read(), 'Chamador não é o proprietário'); }
O método only_owner agora pode ser usado chamando self.only_owner() quando necessário.
No Cairo v1, um evento era apenas uma função sem corpo e anotada com o atributo event, enquanto na v2 um evento é uma enumeração anotada com o mesmo atributo, mas agora implementada usando derivar alguns recursos adicionais.
Cada variante da enumeração do evento deve ser uma estrutura com o mesmo nome. Nesta estrutura, utilizamos o atributo chave opcional para definir todos os valores que queremos emitir, para informar ao sistema quais valores queremos que o Starknet indexe, para que o indexador possa buscar e recuperar mais rapidamente. Neste caso, queremos indexar dois valores (prev_owner e new_owner).
O atributo ContractState define um método emit que pode ser usado para emitir eventos.
#[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(chamador == self.owner.read(), 'Chamador não é o proprietário'); }
Você também pode encontrar este código no Github.
para concluir
A versão 2 do compilador Cairo traz uma nova sintaxe para Starknet, tornando o código de contrato inteligente mais consistente com o próprio Cairo e, por extensão, mais semelhante ao Rust. Mesmo à custa de um código mais complicado, os benefícios de segurança valem a pena.
Neste artigo, não abordamos tudo sobre a nova sintaxe, especialmente como ela interage com outros contratos inteligentes, mas você pode ler o changelog do compilador, ler esta postagem no fórum ou assistir a um vídeo no canal da StarkWare no YouTube para saber mais sobre isso. para saber mais informações.
Esta nova versão do compilador estará disponível na testnet da Starknet em algumas semanas e na mainnet em algumas semanas, então não tente implantar este código ainda, ele não funcionará ainda.
Cairo está cada vez melhor.
recurso
Sintaxe do Contrato - Guia de Migração
Cairo 1: a sintaxe do contrato está evoluindo
Ver original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
Interpretação completa da gramática aprimorada da Starknet
Original: Sintaxe Starknet Melhorada
Tradução e revisão: "Starknet Chinese Community"
visão geral
A versão 2 do compilador Cairo apresenta alterações na sintaxe Starknet para tornar o código mais explícito e seguro. A interface pública do contrato inteligente é definida usando traços e o acesso ao armazenamento é feito por meio do traço ContractState. Os métodos privados devem ser definidos com uma implementação diferente da interface pública. Os eventos agora são definidos como enums, onde cada variante é uma estrutura com o mesmo nome.
Isenção de responsabilidade: os termos usados aqui referem-se a diferentes versões do compilador Cairo, e a sintaxe é provisória, pois a comunidade Starknet ainda está debatendo qual é o melhor termo a ser usado. Uma vez determinado, este artigo será atualizado de acordo.
O Compilador v2
Na semana passada, uma nova versão principal 2.0.0-rc0 do compilador Cairo foi lançada no Github. O novo compilador fornece melhorias significativas para o plug-in Starknet, tornando nosso código mais seguro, mais explícito e mais reutilizável. Observe que esta nova versão do compilador ainda não é suportada no Starknet testnet ou mainnet, pois ainda está sendo trabalhada no ambiente integrado.
O objetivo deste artigo é mostrar como reescrever um contrato inteligente Starknet criado para o compilador Cairo versão 1.x em um contrato inteligente compatível com o compilador versão 2.x. Nosso ponto de partida é o contrato inteligente Ownable criado no artigo anterior, que é compatível com o compilador Cario versão 1.x.
[contract] mod Ownable {use starknet::ContractAddress;use starknet::get_caller_address;
[event] fn OwnershipTransferred(anterior_owner: ContractAddress, novo_owner: ContractAddress) {}
struct Storage {proprietário: ContractAddress,}
[constructor] fn constructor() {let deployer = get_caller_address();owner::write(deployer);}
[view] fn get_owner() -> ContractAddress {owner::read()}
[external] fn transfer_ownership(new_owner: ContractAddress) {apenas_owner();deixe anterior_owner = owner::read();owner::write(new_owner);OwnershipTransferred(anterior_owner, novo_owner) ;}
fn apenas_owner() {deixe o chamador = get_caller_address();assert(chamador == proprietário::read(), 'O chamador não é o proprietário');
Configurações do Projeto
Como o Protostar ainda não oferece suporte ao compilador v2, este artigo contará com o pré-lançamento do Scarb (versão 0.5.0-alpha.1) que o suporta. Para instalar essa versão específica do Scarb, você pode usar o seguinte comando.
$ --proto '=https' --tlsv1.2 -sSf | bash -s -- -v 0.5.0-alpha.1
Após a conclusão da instalação, verifique se você possui a versão correta.
$ scarb --version>>>scarb 0.5.0-alpha.1 (546dad33d 2023-06-19)cairo:2.0.0-rc3()
Um projeto Scarb agora pode ser criado.
$ scarb novo cairo1_v2$cdcairo1_v2
Você deve obter uma estrutura de pastas como abaixo.
$ tree .>>>.├── Scarb.toml└── src└──lib.cairo
Para que o Scarb compile os contratos inteligentes Starknet, o plug-in Starknet precisa ser ativado como uma dependência.
// Scarb.toml... [dependencies] starknet="2.0.0-rc3"
Após a conclusão da configuração, podemos ir para src/lib.cairo e começar a escrever contratos inteligentes.
Armazenamento e construtor
Na versão 2 do compilador Cairo, os contratos inteligentes ainda são definidos por módulos anotados com um atributo de contrato, só que desta vez o atributo recebe o nome do plugin que o define, neste caso starknet.
#[starknet::contract]mod próprio {}
O armazenamento interno ainda é definido como uma estrutura que deve ser chamada de Storage, só que desta vez deve ser anotada com um atributo de armazenamento.
#[starknet::contract]mod Ownable {use super::ContractAddress; # [storage] struct Storage {proprietário: ContractAddress,
Para definir o construtor, anotamos a função com o atributo constructor, como fizemos na v1, a vantagem é que agora a função pode ter qualquer nome, não precisa ser chamada de "constructor" como na v1. Embora não seja obrigatório, ainda me referirei à função como "construtor" por convenção, mas você pode chamá-la de maneira diferente.
Outra mudança importante é que agora o construtor passa automaticamente uma referência para ContractState, que age como um intermediário para armazenar variáveis, no caso "owner".
#[starknet::contract]mod Ownable {use super::ContractAddress; # [storage] struct Storage {owner: ContractAddress,} # [constructor] fn constructor(ref self: ContractState) {let deployer = get_caller_address();self.owner.write(deployer);
Observe que a sintaxe para gravar e ler o armazenamento foi alterada desde a v1. Antes de fazermos owner::write(), agora fazemos self.owner.write(). O mesmo se aplica à leitura do armazenamento.
A propósito, o tipo ContractState não precisa ser colocado no escopo manualmente, ele está incluído no prelúdio.
Métodos Públicos
Uma diferença importante da versão 1 do compilador Cairo é que agora precisamos definir explicitamente a interface pública de um contrato inteligente usando características anotadas com o atributo starknet::interface.
use starknet::ContractAddress;
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self: T, new_owner: ContractAddress); fn get_owner(self: @T) -> ContractAddress;}
#[starknet::contract]mod próprio { ...}
Se você se lembra do código original da v1, nosso contrato inteligente tem dois métodos "públicos" (get_owner e transfer_ownership) e um método "privado" (apenas_owner). Este recurso lida apenas com métodos públicos e não depende de atributos "external" ou "view" para indicar quais métodos podem modificar o estado do contrato e quais métodos não podem. Em vez disso, isso agora é explícito pelo tipo do parâmetro self.
Se um método exigir uma referência a ContractStorage (o que, uma vez implementado, o T genérico exige), esse método poderá modificar o estado interno do contrato inteligente. Isso é o que costumávamos chamar de método "externo". Por outro lado, se um método requer um instantâneo de ContractStorage, ele pode apenas lê-lo, não modificá-lo. Isso é o que costumávamos chamar de método "view".
Agora podemos criar uma implementação para a característica que acabamos de definir usando a palavra-chave impl. Lembre-se, Cairo difere de Rust porque as implementações têm nomes.
use starknet::ContractAddress;
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self: T, new_owner: ContractAddress); fn get_owner(self: @T) -> ContractAddress;}
#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { let prev_owner = self.owner.read(); self.owner.write(new_owner); }
fn get_owner(self: @ContractState) -> ContractAddress { self.owner.read() }
Criamos uma implementação para nosso trait dentro do módulo que define o contrato inteligente, passando o tipo ContractState como um tipo T genérico para que o armazenamento possa ser acessado como um construtor.
Nossa implementação é anotada com o atributo external(v0). A versão 0 no atributo significa que o seletor é derivado apenas do nome do método, como acontecia no passado. A desvantagem é que, se você definir outra implementação de uma característica diferente para seu contrato inteligente e duas características usarem o mesmo nome para um de seus métodos, o compilador lançará um erro devido ao seletor duplicado.
Uma versão futura dessa propriedade pode adicionar uma nova maneira de avaliar seletores para evitar conflitos, mas isso ainda não funciona. Atualmente, só podemos usar a versão 0 das propriedades externas.
Métodos Privados
Também precisamos definir outro método para o contrato inteligente, only_owner. Este método verifica se a pessoa que o chama deve ser o proprietário do contrato inteligente.
Como este é um método privado que não pode ser chamado de fora, ele não pode ser definido como parte do OwnableTrait (a interface pública do contrato inteligente). Em vez disso, usaremos o atributo generate_trait para criar uma nova implementação da característica gerada automaticamente.
...#[starknet::contract]mod Ownable { ... #[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(chamador == self.owner.read(), 'Chamador não é o proprietário'); }
O método only_owner agora pode ser usado chamando self.only_owner() quando necessário.
#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { self.only_owner (); ... } ... }
#[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { ... }
evento
No Cairo v1, um evento era apenas uma função sem corpo e anotada com o atributo event, enquanto na v2 um evento é uma enumeração anotada com o mesmo atributo, mas agora implementada usando derivar alguns recursos adicionais.
...#[starknet::contract]mod Ownable { ... # [event] #[derive(Drop, starknet::Event)] enum Event { OwnershipTransferred: OwnershipTransferred, }
#[derive(Drop, starknet::Event)] struct OwnershipTransferred { # [key] prev_owner: ContractAddress, # [key] new_owner: ContractAddress,
Cada variante da enumeração do evento deve ser uma estrutura com o mesmo nome. Nesta estrutura, utilizamos o atributo chave opcional para definir todos os valores que queremos emitir, para informar ao sistema quais valores queremos que o Starknet indexe, para que o indexador possa buscar e recuperar mais rapidamente. Neste caso, queremos indexar dois valores (prev_owner e new_owner).
O atributo ContractState define um método emit que pode ser usado para emitir eventos.
...#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { .. self.emit(Evento::OwnershipTransferred(OwnershipTransferred { anterior_proprietário: anterior_proprietário, novo_proprietário: novo_proprietário, })); } ... } ...}
Com este recurso final, concluímos a migração do contrato inteligente Ownable de v1 para v2. O código completo é mostrado abaixo.
use starknet::ContractAddress;
#[starknet::interface]trait OwnableTrait { fn transfer_ownership(ref self: T, new_owner: ContractAddress); fn get_owner(self: @T) -> ContractAddress;}
#[starknet::contract]mod Ownable { use super::ContractAddress; use starknet::get_caller_address;
[event] #[derive(Drop, starknet::Event)] enum Event { OwnershipTransferred: OwnershipTransferred, }
#[derive(Drop, starknet::Event)] struct OwnershipTransferred { # [key] prev_owner: ContractAddress, # [key] new_owner: ContractAddress, }
[storage] struct Armazenamento {proprietário: ContractAddress,}
[constructor] fn constructor(ref self: ContractState) { let deployer = get_caller_address(); self.owner.write(implantador); }
#[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { self.only_owner(); let prev_owner = self.owner.read(); self.owner.write(new_owner); self.emit(Evento::OwnershipTransferred(OwnershipTransferred { anterior_proprietário: anterior_proprietário, novo_proprietário: novo_proprietário, })); }
fn get_owner(self: @ContractState) -> ContractAddress { self.owner.read() } }
#[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(chamador == self.owner.read(), 'Chamador não é o proprietário'); }
Você também pode encontrar este código no Github.
para concluir
A versão 2 do compilador Cairo traz uma nova sintaxe para Starknet, tornando o código de contrato inteligente mais consistente com o próprio Cairo e, por extensão, mais semelhante ao Rust. Mesmo à custa de um código mais complicado, os benefícios de segurança valem a pena.
Neste artigo, não abordamos tudo sobre a nova sintaxe, especialmente como ela interage com outros contratos inteligentes, mas você pode ler o changelog do compilador, ler esta postagem no fórum ou assistir a um vídeo no canal da StarkWare no YouTube para saber mais sobre isso. para saber mais informações.
Esta nova versão do compilador estará disponível na testnet da Starknet em algumas semanas e na mainnet em algumas semanas, então não tente implantar este código ainda, ele não funcionará ainda.
Cairo está cada vez melhor.
recurso