Flow & Cadence: лучшие практики, шаблоны и анти-шаблоны

Flow & Cadence: лучшие практики, шаблоны и анти-шаблоны

6 декабря 2022 г.

В этой серии статей мы познакомили вас с блокчейном Flow. , его язык смарт-контрактов Cadence и некоторые из наиболее основные инструменты, которые должны знать разработчики, сравнивая и противопоставляя Ethereum.

В этой статье мы поговорим о лучших практиках и шаблонах, которых следует придерживаться при использовании языка Cadence и разработке решений в сети Flow, а также о шаблонах, которых следует избегать.

Рекомендации

Использовать тестовую сеть Flow и эмулятор Flow

Один из самых важных шагов при разработке любого блокчейна — тестирование ваших проектов в смоделированной среде. Блокчейн Flow ничем не отличается. Для этого тестовая сеть Flow и Эмулятор Flow — два важнейших компонента, которые необходимо включить в процесс разработки. Они не только позволяют понять, как будет работать ваше децентрализованное приложение, но также помогут выявить любые критические ошибки перед развертыванием в основной сети.

Использовать стандартные контракты

Двумя наиболее распространенными вариантами использования любого блокчейна являются цифровые валюты и предоставление доказательств цифрового владения с помощью NFT. Вместо того чтобы пытаться переписывать необходимые контракты с нуля, разработчики Flow всегда должны импортировать официальные основные контракты, чтобы обеспечить безопасность пользователей и согласованность с остальной частью сети.

В настоящее время вам следует использовать три стандартных контракта:

* Взаимозаменяемый токен — Используется для создания новых токенов. * Не взаимозаменяемый токен  —  Используется для создания NFT. * Представления метаданных — Используется в качестве шаблона метаданных для NFT

Эти контракты, по сути, представляют собой интерфейсы, которые заставляют того, кто их реализует, добавлять свои объявления переменных и методов, что позволяет им взаимодействовать с другими смарт-контрактами в экосистеме Flow. Они уже существуют как в тестовой, так и в основной сети, поэтому обязательно импортируйте их с официальных адресов, указанных выше, при реализации их в своем контракте.

Вы можете найти другие основные контракты в документации Flow.

Создавайте тесты для смарт-контрактов, транзакций и скриптов

Flow JavaScript Testing Framework имеет решающее значение для тестирования сценариев развертывания ваших контрактов. Однако важно отметить, что для полной функциональности вам понадобится интерфейс командной строки Flow, работающий в фоновом режиме. С их помощью вы можете тестировать создание новых учетных записей, отправку транзакций, выполнение запросов, выполнение сценариев и многое другое. Кроме того, он интегрируется с эмулятором Flow, поэтому вы можете создавать, запускать и останавливать экземпляры эмулятора.

Добавьте свой контракт в каталог Flow NFT

Каталог Flow NFT существует как база данных для хранения контрактов NFT и метаданных. После загрузки вашего контракта ваши NFT становятся совместимыми с остальной частью экосистемы Flow, и другие разработчики могут легко поддерживать вашу коллекцию на своих торговых площадках или в других децентрализованных приложениях.

Вы можете добавить свой контракт в каталог, выполнив простой четырехэтапный процесс.

Определяйтесь с типами

Cadence — это сильный и статически типизированный язык, который позволяет разработчику указывать, какие типы содержат или возвращают переменные, интерфейсы и функции. Поэтому вы должны быть как можно более конкретными при подаче заявлений; используйте универсальные типы только в необходимых ситуациях. Невыполнение этого требования может привести к неуклюжим ошибкам.

Контроль доступа

Везде, где это возможно, вы должны тщательно контролировать доступ. К счастью, в блокчейне Flow вызывающий абонент не может получить доступ к объектам в хранилище учетной записи другого пользователя без ссылки на него. Это называется безопасностью на основе ссылок и означает, что по умолчанию ничто не является общедоступным.

Однако при написании кода Cadence необходимо соблюдать осторожность при объявлении переменных, структур, функций, ресурсов и т. д. Существует четыре уровня контроля доступа, которые разработчик может включить в свои объявления:

* pub/access(all) — «доступен/видим во всех областях». * доступ(аккаунт) — доступен только во всей учетной записи, где он определен (другие контракты в той же учетной записи могут получить доступ) * access(contract) — доступен только в рамках контракта, в котором он определен (не может быть доступен вне контракта) * priv/access(self) — доступно только в текущей и внутренней области видимости

По возможности избегайте использования pub/access(all). Ознакомьтесь с документацией Flow, чтобы получить дополнительную информацию об управлении доступом.

Создайте константы StoragePath и PublicPath внутри вашего контракта

Пути к ресурсам вашего контракта чрезвычайно важны и должны быть одинаковыми для всех транзакций и скриптов. Чтобы обеспечить единообразие, вы должны создать константы как для PublicPath, так и для StoragePath.

pub contract BestPractices {
    pub let CollectionStoragePath: StoragePath
    pub let CollectionPublicPath: PublicPath

    init(){
        self.CollectionStoragePath = /storage/bestPracticesCollection
        self.CollectionPublicPath = /public/bestPracticesCollection
    }
}

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

//This script checks if a user has the public Collection from BestPractices contract
import BestPractices from 0x...

pub fun main(addr: Address): Bool {
    return getAccount(addr).getLinkTarget(BestPractices.CollectionPublicPath) != nil
}

Ресурс администратора

Рекомендуется создать административный ресурс, содержащий определенные функции для выполнения действий по контракту, например функцию монетного двора. Это соглашение гарантирует, что только учетные записи с таким ресурсом или возможностью могут выполнять административные функции.

pub contract BestPractices {
    pub let CollectionStoragePath: StoragePath
    pub let CollectionPublicPath: PublicPath
    pub let AdminStoragePath: StoragePath

    pub resource AdminResource {
        pub fun mintNFT(){
            //your mint NFT code here! 
        }
    }

    init(){
        self.CollectionStoragePath = /storage/bestPracticesCollection
        self.CollectionPublicPath = /public/bestPracticesCollection
        self.AdminStoragePath = /storage/bestPracticesAdmin

        //Create the adminResource and store it inside Contract Deployer account
        let adminResource <- create AdminResource()
        self.account.save(<- adminResource, to: self.AdminStoragePath)
    }
}

Создание пользовательских событий

События — это значения, которые могут создаваться во время выполнения вашего кода Cadence. Например, при определении важных действий в ваших контрактах вы можете генерировать события, сигнализирующие об их завершении или приносящие определенное значение. В результате транзакции, которые взаимодействуют с вашими смарт-контрактами, могут получать дополнительную информацию через эти события.

pub contract BestPractices {
    pub let CollectionStoragePath: StoragePath
    pub let CollectionPublicPath: PublicPath
    pub let AdminStoragePath: StoragePath

//Create your own events
pub event AdminCreated()


    pub resource AdminResource {
        pub fun mintNFT(){
            //your mint NFT code here! 
        }
    }

    init(){
        self.CollectionStoragePath = /storage/bestPracticesCollection
        self.CollectionPublicPath = /public/bestPracticesCollection
        self.AdminStoragePath = /storage/bestPracticesAdmin

        //Create the adminResource and store it inside Contract Deployer account
        let adminResource <- create AdminResource()
        self.account.save(<- adminResource, to: self.AdminStoragePath)
    }
}

Паттерны блокчейна Cadence/Flow

Конкретизируйте типы в ограничениях типа

Одной из самых мощных функций языка Cadence, несомненно, являются возможности. Благодаря возможностям расширяется область доступа к ресурсам.

Важным моментом при создании возможности является указание, какие функции вашего ресурса должны быть доступны другим. Это можно сделать во время создания ссылки с помощью ограничений типа.

В этом примере мы используем контракт ExampleNFT, чтобы создать базовую функциональность, в которой любой учетная запись, которая хочет получить ExampleNFT, должна иметь коллекцию.

import NonFungibleToken from 0x...
import ExampleNFT from 0x...

transaction{
    prepare(acct: AuthAccount){
        let collection <- ExampleNFT.createEmptyCollection()
    // Put the new collection in storage
      acct.save(<-collection, to: ExampleNFT.CollectionStoragePath)
    // Create a public Capability for the collection
    acct.link<&ExampleNFT.Collection{ExampleNFT.CollectionPublic}>(ExampleNFT.CollectionPublicPath, target: ExampleNFT.CollectionStoragePath)
    }
}

& символ в &ExampleNFT указывает, что мы используем ссылку. После символа ссылки мы добавляем тип, к которому будет иметь доступ создаваемая нами возможность. На этом этапе нам нужно быть как можно более конкретными.

Этот шаблон усиливает безопасность и ограничивает функциональные возможности, которые может использовать пользователь, вызывающий функцию заимствования этой возможности.

Отсутствие типа {ExampleNFT.CollectionPublic} даст вам доступ ко всем функциям, которые существуют в ссылке ExampleNFT.Collection, включая функцию снятия, так что любой может получить доступ к коллекции пользователей и украсть их NFT.

Используйте функцию заимствования

Чтобы использовать функции ресурса, вы можете вызвать функцию Загрузить, чтобы удалить ресурс из учетной записи, использовать его функции и вызвать функцию Сохранить, чтобы сохранить его снова. . Однако этот подход является дорогостоящим и неэффективным.

Чтобы избежать этого, используйте вместо этого функцию заимствование. Это позволяет вам использовать ссылку на ресурс, который вы вызываете. Этот метод делает вашу транзакцию намного более эффективной и экономичной.

Использовать функции проверки и getLinkTarget

При создании приложений на блокчейне Flow вы обнаружите, что учетная запись пользователя играет жизненно важную роль. В отличие от других блокчейнов, таких как Ethereum, Flow хранит ресурсы, активы и многое другое непосредственно в учетной записи пользователя, а не в виде ссылки на адрес в общедоступной цифровой книге.

Для этого подхода требуется, чтобы у аккаунта было определенное место хранения, например коллекция или хранилище для взаимозаменяемых токенов. Однако это также добавляет сложности. Нужно быть уверенным, что у пользователя либо есть, либо нет коллекции в его аккаунте.

И функция check() (которая проверяет наличие возможности по заданному пути), и функция getLinkTarget() (которая возвращает путь данной возможности) должны используется при добавлении коллекций и возможностей в учетную запись пользователя. Эти функции гарантируют, что транзакция будет выполнена без проблем.

Использовать панику

Паника — это встроенная функция Cadence, позволяющая безоговорочно завершать программу. Это может произойти во время выполнения кода вашего смарт-контракта и вернуть сообщение об ошибке, что упрощает понимание, когда что-то идет не так, как ожидалось.

При объявлении переменных их можно определить как необязательные; это означает, что если они не относятся к указанному типу, они имеют значение nil.

Таким образом, в Cadence можно использовать два вопросительных знака, за которыми следует функция panic("лечебное сообщение") при запросе значения конкретной переменной или функции, которая возвращает необязательное значение.

let optionalAccount: AuthAccount? = //...
let account = optionalAccount ?? panic("missing account")

Эта команда ??panic("лечебное сообщение")пытается вернуть значение указанного типа. Если возвращаемое значение имеет неверный тип или равно нулю, выполнение прерывается, и на консоли отображается выбранное сообщение о лечении.

Анти-шаблоны

Хотя Cadence спроектирован таким образом, чтобы избежать многих потенциальных ошибок и эксплойтов, встречающихся в других блокчейн-экосистемах, разработчикам следует помнить о некоторых антишаблонах при создании. Ниже перечислены несколько важных, которые следует учитывать. Полный список анти-шаблонов ознакомьтесь с документацией по Flow.

Не указать тип при использовании функции заимствования

Разработчикам следует использовать упомянутую выше функцию заимствования, чтобы воспользоваться преимуществами функций, доступных в возможности. Однако должно быть понятно, что пользователи могут хранить в своей памяти что угодно. Поэтому очень важно убедиться, что заимствование относится к правильному типу.

Отказ от указания типа является анти-шаблоном, который может привести к ошибкам или даже поломке вашей транзакции и приложения.

//Bad Practice. Should be avoided
let collection = getAccount(address).getCapability(ExampleNFT.CollectionPublicPath)
                    .borrow<&ExampleNFT.Collection>()?? panic("Could not borrow a reference to the nft collection")

//Correct! 
let collection = getAccount(address).getCapability(ExampleNFT.CollectionPublicPath)
                    .borrow<&ExampleNFT.Collection{NonFungibleToken.CollectionPublic}>()
                    ?? panic("Could not borrow a reference to the nft collection")

Использование AuthAccount в качестве параметра функции

Для транзакции на этапе подготовки можно получить доступ к полю AuthAccount пользователя. Этот объект предоставляет доступ к хранилищу памяти и всем другим личным областям учетной записи для пользователя, который его предоставляет.

Передача этого поля в качестве аргумента не рекомендуется, и ее следует избегать, так как случаи, когда этот метод необходим, крайне редки.

//Bad Practice. Should be avoided

transaction() {
    prepare(acct: AuthAccount){
        //You take sensitive and important user data out of the scope of the transaction prepare phase
        ExampleNFT.function(acct: acct)
    }
}

Использование общего контроля доступа к словарям и массивам

Сохраняя словари и переменные массива в вашем контракте с областью действия pub/access(all), ваш смарт-контракт становится уязвимым, поскольку любой может манипулировать этими значениями и изменять их.

Создание возможностей и ссылок с использованием ключевого слова аутентификации

Создание возможностей и ссылок с помощью ключевого слова auth подвергает значение понижению приведения, что может предоставить доступ к функциям, которые изначально не предназначались.

//AVOID THIS!
signer.link<auth &ExampleNFT.CollectionPublic{NonFungibleToken.Receiver}>(
    /public/exampleNFT,
    target: /storage/exampleNFT
)

Заключение

При разработке на блокчейне Flow с использованием Cadence полезно знать шаблоны проектирования, анти-шаблоны и лучшие практики.

Следуя этим передовым методам, мы можем обеспечить постоянный уровень безопасности во всей экосистеме Flow, тем самым предоставляя пользователям наилучшие возможности. Для более глубокого понимания ознакомьтесь с документацией по Flow.

Хорошего дня!


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE