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.
Хорошего дня!
Оригинал