Повна інтерпретація вдосконаленої граматики Starknet

Оригінал: покращений синтаксис Starknet

Переклад і вичитка: "Китайська спільнота Starknet"

Повна інтерпретація покращеної граматики Starknet

огляд

Версія 2 компілятора Cairo вносить зміни до синтаксису Starknet, щоб зробити код більш чітким і безпечним. Загальнодоступний інтерфейс смарт-контракту визначається за допомогою ознак, а доступ до сховища здійснюється через ознаку ContractState. Приватні методи повинні бути визначені з іншою реалізацією, ніж публічний інтерфейс. Події тепер визначаються як переліки, де кожен варіант є однойменною структурою.

Відмова від відповідальності: терміни, що використовуються тут, стосуються різних версій компілятора Cairo, і синтаксис є тимчасовим, оскільки спільнота Starknet все ще обговорює, який термін найкраще використовувати. Після визначення цю статтю буде відповідно оновлено.

Компілятор v2

Тільки минулого тижня на Github була випущена нова основна версія 2.0.0-rc0 компілятора Cairo. Новий компілятор забезпечує значні покращення плагіна Starknet, роблячи наш код безпечнішим, чіткішим і зручнішим для повторного використання. Зверніть увагу, що ця нова версія компілятора ще не підтримується в Starknet testnet або mainnet, оскільки над нею все ще працюють в інтегрованому середовищі.

Мета цієї статті — показати вам, як переписати смарт-контракт Starknet, створений для компілятора Cairo версії 1.x, на смарт-контракт, сумісний із компілятором версії 2.x. Нашою відправною точкою є смарт-контракт Ownable, створений у попередній статті, який сумісний із компілятором Cario версії 1.x.

[contract] mod Ownable {use starknet::ContractAddress;use starknet::get_caller_address;

[event] fn OwnershipTransferred(previous_owner: ContractAddress, new_owner: ContractAddress) {}

struct Storage {owner: 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) {only_owner();let previous_owner = owner::read();owner::write(new_owner);OwnershipTransferred(previous_owner, new_owner) ;}

fn only_owner() {let caller = get_caller_address();assert(caller == owner::read(), 'Абонент не є власником');

Параметри проекту

Оскільки Protostar ще не підтримує компілятор версії 2, ця стаття спиратиметься на попередній випуск Scarb (версія 0.5.0-alpha.1), який підтримує його. Щоб установити цю конкретну версію Scarb, ви можете скористатися такою командою.

$ --proto '=https' --tlsv1.2 -sSf | bash -s -- -v 0.5.0-alpha.1

Після завершення інсталяції переконайтеся, що у вас правильна версія.

$ scarb --version>>>scarb 0.5.0-alpha.1 (546dad33d 2023-06-19)cairo:2.0.0-rc3()

Тепер можна створити проект Scarb.

$ scarb новий cairo1_v2$cdcairo1_v2

Ви повинні отримати структуру папок, як показано нижче.

$ дерево .>>>.├── Scarb.toml└── src└──lib.cairo

Щоб Scarb міг компілювати смарт-контракти Starknet, плагін Starknet потрібно ввімкнути як залежність.

// Scarb.toml... [dependencies] starknet="2.0.0-rc3"

Після завершення налаштування ми можемо перейти до src/lib.cairo і почати писати смарт-контракти.

Сховище та конструктор

У версії 2 компілятора Cairo смарт-контракти все ще визначаються модулями, анотованими атрибутом контракту, тільки цього разу атрибут названо на честь плагіна, який його визначає, у цьому випадку starknet.

#[starknet::contract]mod Ownable {}

Внутрішнє сховище все ще визначається як структура, яку потрібно називати сховищем, але цього разу воно має бути анотованим атрибутом сховища.

#[starknet::contract]mod Ownable {use super::ContractAddress; # [storage] struct Storage {власник: ContractAddress,

Щоб визначити конструктор, ми додаємо до функції атрибут constructor, як ми робили у v1, перевага полягає в тому, що тепер функція може мати будь-яке ім’я, її не потрібно називати «constructor», як у v1. Незважаючи на те, що це не обов’язково, я все одно називатиму функцію «конструктором» поза домовленістю, але ви можете називати її інакше.

Ще одна важлива зміна полягає в тому, що тепер конструктор автоматично передає посилання на ContractState, який діє як посередник для зберігання змінних, у даному випадку «власника».

#[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);

Зауважте, що синтаксис пам’яті для запису та читання змінився з v1. Раніше ми робили owner::write(), тепер ми використовуємо self.owner.write(). Те саме стосується читання зі сховища.

До речі, тип ContractState не потрібно вводити в область видимості вручну, він включений у прелюдію.

Публічні методи

Важлива відмінність від версії 1 компілятора Cairo полягає в тому, що тепер нам потрібно явно визначити загальнодоступний інтерфейс смарт-контракту за допомогою ознак, анотованих атрибутом starknet::interface.

використовуйте 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 { ...}

Якщо ви пам’ятаєте оригінальний код з версії 1, наш смарт-контракт має два «загальнодоступних» методи (get_owner і transfer_ownership) і один «приватний» метод (тільки_owner). Ця функція стосується лише загальнодоступних методів і не покладається на атрибути "зовнішній" або "перегляд", щоб вказати, які методи можуть змінювати стан контракту, а які ні. Замість цього тепер це явно виражено типом параметра self.

Якщо для методу потрібне посилання на ContractStorage (що, після впровадження, робить загальний T), цей метод може змінити внутрішній стан смарт-контракту. Це те, що ми звикли називати «зовнішнім» методом. З іншого боку, якщо для методу потрібен знімок ContractStorage, він може лише прочитати його, але не змінити. Це те, що ми звикли називати методом "перегляду".

Тепер ми можемо створити реалізацію для ознаки, яку ми щойно визначили, використовуючи ключове слово impl. Пам’ятайте, Cairo відрізняється від Rust тим, що реалізації мають імена.

використовуйте 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(новий_власник); }

fn get_owner(self: @ContractState) -> ContractAddress { self.owner.read() }

Ми створюємо реалізацію для нашої риси всередині модуля, що визначає смарт-контракт, передаючи тип ContractState як загальний тип T, щоб можна було отримати доступ до сховища як до конструктора.

Наша реалізація анотована атрибутом external(v0). Версія 0 в атрибуті означає, що селектор походить лише від назви методу, як це було в минулому. Недоліком є те, що якщо ви визначите іншу реалізацію іншого трейта для свого смарт-контракту, і два трейти використовують однакову назву для одного зі своїх методів, компілятор видасть помилку через повторюваний селектор.

Майбутня версія цієї властивості може додати новий спосіб оцінки селекторів для запобігання конфліктам, але це ще не працює. Наразі ми можемо використовувати лише версію 0 зовнішніх властивостей.

Приватні методи

Нам також потрібно визначити інший метод для смарт-контракту, only_owner. Цей метод перевіряє, чи особа, яка його викликає, має бути власником смарт-контракту.

Оскільки це приватний метод, який заборонено викликати ззовні, його не можна визначити як частину OwnableTrait (публічного інтерфейсу смарт-контракту). Замість цього ми використаємо атрибут generate_trait, щоб створити нову реалізацію автоматично згенерованого ознаки.

...#[starknet::contract]mod Ownable { ... #[generate_trait] impl PrivateMethods of PrivateMethodsTrait { fn only_owner(self: @ContractState) { let caller = get_caller_address(); assert(caller == self.owner.read(), 'Абонент не є власником'); }

Метод only_owner тепер можна використовувати, викликавши self.only_owner(), де потрібно.

#[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) { ... }

подія

У версії Cairo v1 подія була лише функцією без тіла та анотованою атрибутом event, тоді як у v2 подія є переліком, анотованим тим самим атрибутом, але тепер реалізованим за допомогою деяких додаткових функцій.

...#[starknet::contract]mod Ownable { ... # [event] #[derive(Drop, starknet::Event)] enum Event { OwnershipTransferred: OwnershipTransferred, }

#[derive(Drop, starknet::Event)] struct OwnershipTransferred { # [key] prev_owner: ContractAddress, # [key] новий_власник: ContractAddress,

Кожен варіант переліку події має бути структурою з таким самим іменем. У цій структурі ми використовуємо додатковий атрибут ключа, щоб визначити всі значення, які ми хочемо видавати, щоб повідомити системі, які значення ми хочемо індексувати Starknet, щоб індексатор міг шукати та отримувати швидше. У цьому випадку ми хочемо проіндексувати два значення (prev_owner і new_owner).

Характеристика ContractState визначає метод випромінювання, який можна використовувати для випромінювання подій.

...#[starknet::contract]mod Ownable { ... #[external(v0)] impl OwnableImpl of super::OwnableTrait { fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) { .. . self.emit(Event::OwnershipTransferred(OwnershipTransferred { prev_owner: prev_owner, new_owner: new_owner, })); } ... } ...}

З цією останньою функцією ми завершили перенесення смарт-контракту Ownable з v1 на v2. Повний код показано нижче.

використовуйте 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; використовувати starknet::get_caller_address;

[event] #[derive(Drop, starknet::Event)] enum Event { OwnershipTransferred: OwnershipTransferred, }

#[derive(Drop, starknet::Event)] struct OwnershipTransferred { # [key] prev_owner: ContractAddress, # [key] новий_власник: ContractAddress, }

[storage] struct Storage { власник: ContractAddress, }

[constructor] fn constructor(ref self: ContractState) { let deployer = get_caller_address(); self.owner.write(deployer); }

#[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(новий_власник); self.emit(Event::OwnershipTransferred(OwnershipTransferred { prev_owner: prev_owner, new_owner: new_owner, })); }

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(caller == self.owner.read(), 'Абонент не є власником'); }

Ви також можете знайти цей код на Github.

на закінчення

Версія 2 компілятора Cairo привносить новий синтаксис у Starknet, завдяки чому код смарт-контракту виглядає більш узгодженим із самим Cairo, а отже, більш схожим на Rust. Навіть за рахунок більш громіздкого коду переваги безпеки варті компромісу.

У цій статті ми торкнулися не всього нового синтаксису, особливо того, як він взаємодіє з іншими смарт-контрактами, але ви можете прочитати журнал змін компілятора, прочитати цю публікацію на форумі або переглянути відео на YouTube-каналі StarkWare, щоб дізнатися більше про це, щоб дізнатися більше.

Ця нова версія компілятора буде доступна в тестовій мережі Starknet через кілька тижнів і в основній мережі через кілька тижнів, тому поки не намагайтеся розгортати цей код, він поки що не працюватиме.

Каїр стає все кращим.

ресурс

  • Синтаксис контракту - Керівництво з міграції
  • Cairo 1: синтаксис контракту розвивається
Переглянути оригінал
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.
  • Нагородити
  • Прокоментувати
  • Поділіться
Прокоментувати
0/400
Немає коментарів
  • Закріпити