Построение на Flow | Изучение FCL — 16. Как отправить токены FLOW на другой аккаунт


Обзор

Токены Fungible часто используются в качестве средства оплаты — за продукты или услуги. В этой статье мы расскажем вам, как:

  • получить текущий баланс FLOW на определенном счете
  • составить транзакцию перевода FLOW
  • подписать и отправить ее с помощью кошелька Blocto или Lilico.

Теория

Токен FLOW — это контракт, который реализует интерфейс Fungible Token Standard. Если вы заглянете в код, то увидите, что на более высоком уровне totalSupply определен на реализации контракта, но он не знает, сколько токенов имеет каждый отдельный счет. Каждый счет, который хочет владеть токенами, должен иметь экземпляр ресурса Vault, хранящийся в хранилище счета. Ресурс Vault имеет методы, которые может вызывать владелец и другие пользователи. Эти методы ограничены интерфейсами ресурсов Provider, Receiver и Balance. Эти интерфейсы объявляют предусловия и постусловия, которые ограничивают выполнение функций в хранилище.

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

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

Используя ресурсы и интерфейсы, пользователи контрактов Fungible Token могут отправлять и получать токены одноранговым способом, без необходимости взаимодействия с централизованной книгой смарт-контрактов. Чтобы отправить токены другому пользователю, пользователь просто выводит токены из своего хранилища, а затем вызывает функцию депозита в хранилище другого пользователя для завершения перевода.

Шаг 1 — Установка

Добавьте "@onflow/fcl": "1.0.0" в качестве зависимости.

Шаг 2 — Конфигурация FCL

Давайте создадим файл testnet-config.js, чтобы мы могли повторно использовать его в последующих проектах. Он также сделает наш файл index.js чище и позволит легко перейти на версию mainnet. Содержимое файла будет практически таким же, как мы рассматривали в предыдущей статье:

// Configure FCL
import { config } from "@onflow/fcl";

config({
  // Use Testnet Access Node
  "accessNode.api": "https://rest-testnet.onflow.org",
  // We will also specify the network as some of the FCL parts need it to properly do it's work
  "flow.network": "testnet",
  // This will be the title of our DApp
  "app.detail.title": "Meow DApp",
  // This is just a kitten photo, we will use for the icon
  "app.detail.icon": "https://placekitten.com/g/200/200",
  // Next two will define where Wallet Discovery is located
  "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn",
  "discovery.authn.endpoint":
    "https://fcl-discovery.onflow.org/api/testnet/authn",

  // We will also set aliases for the contracts we will use in this example
  "0xFLOW": "0x7e60df042a9c0868",
  "0xFT": "0x9a0766d93b6608b7"
});
Вход в полноэкранный режим Выход из полноэкранного режима

Если вы посмотрите на конец конфигурации, то увидите, что мы определили два псевдонима для контрактов, которые мы будем импортировать в код Cadence:

Шаг 4 — Импорт

Вернемся к нашему файлу index.js, где мы добавим все необходимые импорты:

import { query, mutate, tx, reauthenticate } from "@onflow/fcl";
import "./testnet-config";
Вход в полноэкранный режим Выйти из полноэкранного режима

Шаг 3 — Реализация функции getFlowBalance

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

Она будет принимать единственный аргумент address, который будет передан скрипту Cadence, и возвращать значение UFix64, представленное в виде строки.

💡Если вы только что попали сюда и не совсем понимаете, что происходит — не бойтесь! Мы вас поняли! Вы можете ознакомиться с предыдущими статьями из этой серии:

  • Как выполнять скрипты
  • Как отправлять транзакции
const getFlowBalance = async (address) => {
  const cadence = `
    import FlowToken from 0xFLOW
    import FungibleToken from 0xFT

    pub fun main(address: Address): UFix64 {
      let account = getAccount(address)

      let vaultRef = account.getCapability(/public/flowTokenBalance)
        .borrow<&FlowToken.Vault{FungibleToken.Balance}>()
        ?? panic("Could not borrow Balance reference to the Vault")

      return vaultRef.balance
    }
  `;
  const args = (arg, t) => [arg(address, t.Address)];
  const balance = await query({ cadence, args });
  console.log({ balance });
};
Вход в полноэкранный режим Выход из полноэкранного режима

🧑🏫 Если вам интересно, где мы взяли код для этой транзакции — вы можете проверить репозиторий Fungible Token Standart на Github:

  • Get Balance Cadence script
  • Transfer Tokens Cadence Transaction

Проблема» заключается в том, что вы не сможете найти поле BalancePublicPath, установленное в FlowToken контракте, развернутом по адресу 0x9a0766d93b6608b7. Контракт был развернут до того, как эта полезная функция хранения общего пути стала широко использоваться. Поэтому мы жестко кодируем значение /public/flowTokenBalance, которое мы получили из метода init контракта. Мы также будем использовать определенный там /public/flowTokenReceiver для получения возможности хранилища получателя.

В то время как код Javacript не требует пояснений, давайте рассмотрим, что происходит в скрипте Cadence. Он принимает единственный аргумент address типа Address и возвращает значение UFix64:

pub fun main(address: Address): UFix64 {
Вход в полноэкранный режим Выход из полноэкранного режима

Сначала мы пытаемся «исследовать» счет, вызывая функцию getAccount, которая вернет нам структуру PublicAccount.

let account = getAccount(address)
Вход в полноэкранный режим Выйти из полноэкранного режима

Эта структура дает нам возможность вызвать метод getCapability для получения необходимых возможностей FLOW Vault. Мы знаем, что возможности должны быть открыты на публичном пути /public/flowTokenBalance. И мы также знаем, что это ограниченная возможность FungibleToken.Balance, которая позволит нам только прочитать баланс хранилища.

Мы можем проверить метод init контракта FlowToken (строка #187), чтобы получить нужный нам тип:

<&FlowToken.Vault{FungibleToken.Balance}>
Войти в полноэкранный режим Выйти из полноэкранного режима

Для того чтобы использовать эту возможность, нам нужно получить ссылку на нее с помощью метода .borrow. Мы можем задать тип, который мы ожидаем заимствовать, используя угловые скобки — и вставить тот же тип, который был определен в методе init.

let vaultRef = account.getCapability(/public/flowTokenBalance)
        .borrow<&FlowToken.Vault{FungibleToken.Balance}>()
        ?? panic("Could not borrow Balance reference to the Vault")
Вход в полноэкранный режим Выход из полноэкранного режима

Если ссылка не может быть создана (например, хранимая Capability имеет другой тип или не может быть приведена к данному типу) — транзакция остановит выполнение с сообщением panic.

Шаг 4 — Реализация функции sendFlow.

Следующая по списку — функция, которая будет принимать адрес получателя и количество токенов FLOW, которые мы хотим передать.

const sendFlow = async (recepient, amount) => {
  const cadence = `
    import FungibleToken from 0xFT
    import FlowToken from 0xFLOW

    transaction(recepient: Address, amount: UFix64){
      prepare(signer: AuthAccount){
        let sender = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)
          ?? panic("Could not borrow Provider reference to the Vault")

        let receiverAccount = getAccount(recepient)

        let receiver = receiverAccount.getCapability(/public/flowTokenReceiver)
          .borrow<&FlowToken.Vault{FungibleToken.Receiver}>()
          ?? panic("Could not borrow Receiver reference to the Vault")

                let tempVault <- sender.withdraw(amount: amount)
        receiver.deposit(from: <- tempVault)
      }
    }
  `;
  const args = (arg, t) => [arg(recepient, t.Address), arg(amount, t.UFix64)];
  const limit = 500;

  const txId = await mutate({ cadence, args, limit });

    console.log("Waiting for transaction to be sealed...");

    const txDetails = await tx(txId).onceSealed();
  console.log({ txDetails });
};
Вход в полноэкранный режим Выход из полноэкранного режима

Код Cadence очень похож на тот, который мы использовали выше для запроса баланса FLOW.

  • мы получаем полную ссылку на хранилище отправителя
  • мы получаем ограниченную Receiver ссылку на хранилище получателя
  • мы создаем временное хранилище с помощью метода withdraw и передаем сумму в качестве аргумента
  • мы пополняем хранилище Получателя с помощью метода deposit и tempVault.

Обратите внимание на синтаксис полной ссылки — мы не указываем ограничивающий интерфейс в фигурных скобках, а также указываем путь к хранилищу /storage/flowTokenVault . Каждый аккаунт инициализируется с FLOW Vault по этому пути, поэтому можно предположить, что он есть у каждого подписанта:

let sender = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)
Войти в полноэкранный режим Выход из полноэкранного режима

Наконец

Давайте добавим IIFE в конец файла и заполним его методами, которые мы только что определили:

(async () => {
  console.clear();
    // "reauthenticate" will ensure your session works properly
  // and present you a popup to sign in
  await reauthenticate();

  // This is an example account we've created to this exibition
  // You can replace it with one of your addresses
  const recepient = "0x3e68d80ca405bbac";

  // Check "initial" balance first
  await getFlowBalance(recepient);

  // Send some FLOW tokens to Recepient
  await sendFlow(recepient, "1.337");

  // Ensure that Recepient's balance has been changed
  await getFlowBalance(recepient);
})();
Войти в полноэкранный режим Выход из полноэкранного режима

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

{balance: "1002.67500000"}

Waiting for transaction to be sealed... 

txDetails: {
    blockId: "b555b7ca17fac5ad170e3bfc4a85afd3325eb5d4fb56e4be49550dce9413ffad"
    status: 4
    statusString: "SEALED"
    statusCode: 0
    errorMessage: ""
    events: Array(5)
}

{balance: "1004.01200000"}
Войти в полноэкранный режим Выйти из полноэкранного режима

Вы знаете, что делать дальше 😉.

До следующего раза! 👋

Ресурсы

  • Полный исходный код — https://codesandbox.io/s/dev-to-16-transfer-flow-tokens-pjq98u
  • Fungible Token Standard — https://github.com/onflow/flow-ft
  • FCL — mutate — https://docs.onflow.org/fcl/reference/api/#mutate
  • FCL — query — https://docs.onflow.org/fcl/reference/api/#query
  • Cadence — Возможности — https://docs.onflow.org/cadence/language/capability-based-access-control
  • Cadence — Ссылки — https://docs.onflow.org/cadence/language/references

Другие ресурсы, которые вы можете найти полезными:

  • Flow Docs Site — https://docs.onflow.org/ — более подробная информация о блокчейне Flow и о том, как с ним взаимодействовать.
  • Flow Portal — https://flow.com/ — ваша точка входа в Flow
  • FCL JS — https://github.com/onflow/fcl-js — исходный код и возможность внести свой вклад в библиотеку FCL JS
  • Cadence — https://docs.onflow.org/cadence/ — Введение в Cadence
  • Codesandbox — https://codesandbox.io — удивительная браузерная IDE, позволяющая быстро создавать прототипы.

Оцените статью
devanswers.ru
Добавить комментарий