Создаем на Flow | Изучаем FCL — 9. Как передавать скриптам аргументы Options и аргументы Path
Published: 2022-07-01 18:15:23

## Краткий обзор
[В прошлый раз](https://dev.to/egormajj/sozdaiem-na-flow-izuchitie-fcl-2-pieriedavaitie-arghumienty-v-skripty-2ld2), когда мы рассказывали о том, как передавать аргументы в скриптах, мы намеренно опустили аргументы [Optionals](https://docs.onflow.org/cadence/language/values-and-types/#optionals) и [Paths](https://docs.onflow.org/cadence/language/accounts/#paths), чтобы снизить умственную нагрузку. Эти два аргумента по-прежнему важны и довольно часто встречаются, поэтому мы рассмотрим их в этом уроке.

После того, как вы выполните все примеры, описанные в этом посте, вы сможете:

- передавать любой аргумент Optional, независимо от его базового типа
- передавать Path аргументы 

Но сначала давайте посмотрим, что это такое!

## Теория
`Optionals` - это что-то вроде кота [Schroedinger](https://joshuahannan.medium.com/optionals-in-cadence-not-optional-fb39bb4b0081) в программировании: они могут представлять наличие или отсутствие значения, когда вы проверяете, что находится внутри. У опций есть два случая:

есть значение
есть `nil` (ничего).

Один из примеров использования, где вы можете найти это полезным (который приходит мне на ум) - это когда вы хотите передать Dictionary `{String: String?}` и хотите инициализировать некоторые строки пустыми значениями (по какой-либо причине 🤷♂️).

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

Рассмотрим некоторые распространенные Path `/public/flowTokenVault` - это path к [управлению доступом](https://docs.onflow.org/cadence/language/capability-based-access-control/#gatsby-focus-wrapper) ресурса FLOW token Vault. С помощью этой специфической способности вы можете получить ссылку на этот ресурс, чтобы посмотреть его баланс или пополнить его токенами. Однако не все возможности одинаковы! Некоторые могут дать вам больше функциональности, а другие - меньше.

Вернемся к нашей функции. Если вы посмотрите на нее, то увидите, что она состоит из двух частей:

- **/public** - это называется `domain`.
- **/flowTokenVault** - а это идентификатор.

Технически символ `/` является разделителем, но я думаю, что проще увидеть это так 😉.

Существует только три допустимых domain: `storage`, `private` и `public`. Во всех скриптах вы будете использовать `public` domain - поскольку он не позволит вам получить доступ к `private` областям и областям `storage` других учетных записей.

Давайте попробуем передать оба этих domain в некоторых скриптах! 💪

## Шаг 1 - Установка
Добавьте `"@onflow/fcl": "1.0.0"` в качестве зависимости

## Шаг 2 - Настройка
Как и в прошлый раз импортируем необходимые методы и настраиваем FCL:

```javascript
// Import methods from FCL
import { query, config } from "@onflow/fcl";

// Specify the API endpoint - this time we will use Mainnet
const api = "https://rest-mainnet.onflow.org";

// Configure FCL to use mainnet as the access node
config().put("accessNode.api", api);
```

## Шаг 3 - Передача  Path аргументов
Мы попытаемся передать 3 различных Path - `PublicPath`, `PrivatePath` и `StoragePath` - каждый для соответствующего domain:

```javascript
// Path Arguments
const passPathArgument = async () => {
  const cadence = `
    pub fun main(public: PublicPath, private: PrivatePath, storage: StoragePath): PrivatePath{
      // we can return any value, but let's return one of the Paths to see
      // how FCL decodes it into value
      return private
    }
  `;

  // Since we are not gonna use any of those for actual data access
  // we can construct any paths we like
  // this one will encode "/public/flowTokenVault"
  const publicPath = {
    domain: "public",
    identifier: "flowTokenVault"
  };

  // encode "/private/flowTokenVault"
  const privatePath = {
    domain: "private",
    identifier: "flowTokenVault"
  };

  // encode "/storage/flowTokenVault"
  const storagePath = {
    domain: "storage",
    identifier: "flowTokenVault"
  };

  // Notice that t.Path is not a function, but a constant!
  const args = (arg, t) => [
    arg(publicPath, t.Path),
    arg(privatePath, t.Path),
    arg(storagePath, t.Path)
  ];
  const result = await query({ cadence, args });
  console.log({ result });
};
```
Обратите внимание, что `t.Path` является постоянным, несмотря на то, что мы передаем объект 🤪.

## Шаг 4 - Передача Optional целому числу
Чтобы передать Optional, нам нужно обернуть тип в вызов `t.Optional()`. Вот и все 😊.

```javascript
// Optionals
const passOptionalIntegers = async () => {
  const cadence = `
    pub fun main(a: Int?): Int?{
      return a
    }
  `;

  // We will set value of our Int argument to null to check that script works correctly
  const a = null;
  const args = (arg, t) => [arg(a, t.Optional(t.Int))];

  const result = await query({ cadence, args });
  console.log({ result });
}; 
```

## Шаг 5 - Pass других Optional типов
По аналогии с нашей первоначальной статьей об аргументах, давайте передадим другие типы Optional все сразу:

```javascript
const passMultiOptional = async () => {
  const cadence = `
    pub fun main(a: String?, b: Bool?, c: UFix64?, d: Address?): Address?{
      return d
    }
  `;

    // it's perfectly fine to pass non-nil values alongside "empty"
  const a = "Hello";
  const b = null;
  const c = null;
  const d = "0x01";

  // Note the types are the same as we specify in Cadence code
  const args = (arg, t) => [
    arg(a, t.Optional(t.String)),
    arg(b, t.Optional(t.Bool)),
    arg(c, t.Optional(t.UFix64)),
    arg(d, t.Optional(t.Address))
  ];

  const result = await query({ cadence, args });
  console.log({ result });

  showResult("multiple", result);
};
```

## Шаг 6 - Pass Optional Array и Array of Optionals
Разница между ними заключается в цели для обертки t.Optional().

Вот  array of Optionals:

```javascript
const passArrayOfOptinalStrings = async () => {
  const cadence = `
    pub fun main(a: [String?]): String?{
      return a[0]
    }
  `;

  const a = ["Hello", null];
  // Type of the argument is composed of t.Array, t.Optional and t.String
  const args = (arg, t) => [arg(a, t.Array(t.Optional(t.String)))];
  const result = await query({ cadence, args });
  console.log({ result }); //
};
```
А этот для опционального массива Strings:

```javascript
const passOptionalArray = async () => {
  const cadence = `
    pub fun main(a: [String]?): String?{
      if let arr = a {
        return arr[0]
      }

      return nil
    }
  `;
  const a = null;
  // This time we will wrap our array in "t.Optional()" call
  const args = (arg, t) => [arg(a, t.Optional(t.Array(t.String)))];
  const optionalArray = await query({ cadence, args });
  console.log({ optionalArray }); //
};
```
## Шаг 7 - Передача Dictionary с Optional значениями

```javascript
const passDictionaryOfOptionals = async () => {
  // In this example we will pass a Cadence Dictionary as argument
  // keys will be of type "String" and values will be Optionals of type "Int?"
  const cadence = `
    pub fun main(a: {String: Int?}): Int??{
      return a["amount"]
    }
  `;

  // Dictionaries should be represented as array of key/value pairs of respective types
  // Note that we shall pass numeric value as string here
  const a = [{ key: "amount", value: "42" }];
  // Dictionary type is composed out of t.Dictionary, t.String and t.Int for our case
  const args = (arg, t) => [
    arg(a, t.Dictionary({ key: t.String, value: t.Optional(t.Int) }))
  ];

  const result = await query({ cadence, args });
  console.log({ result });
};
```

> 💡 Вы заметили, что скрипт в последнем примере возвращает Double Optional `Int(?)`. ? Это потому, что значение по ключу `amount` может не существовать, поэтому он вернет нам `Maybe(Maybe(Int))` - вам нужно будет развернуть его дважды, если вы хотите использовать его позже в коде 😉

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

```javascript
(async () => {
    console.clear();

    // Path arguments
  await passPathArgument();

    // Optional arguments
  await passOptionalIntegers();
  await passMultiOptional();
  await passArrayOfOptinalStrings();
  await passOptionalArray();
  await passDictionaryOfOptionals();
})();
```
Через несколько 🎇 мгновений после этого вы должны увидеть вывод в журнале консоли:

```javascript
{result: Object}
    result: {
        domain: "private",
        identifier: "flowTokenVault"
    }
{result: null}
{result: "0x0000000000000001"}
{result: "Hello"}
{optionalArray: null}
{result: "42"}
```
Вы сделали это - теперь эти типы не являются для вас испытанием! 💪

Надеюсь, информация в этом посте будет полезной, и вы вернетесь за новыми материалами 😉.

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

## Информационные ресурсы

- **Полный исходный код** - https://codesandbox.io/s/dev-to-fcl-09-optional-arguments-vt0d11
- **Cadence: Optionals** - https://docs.onflow.org/cadence/language/values-and-types/#optionals
- **Cadence: Paths **- https://docs.onflow.org/cadence/language/accounts/#paths
- **Cadence: Возможности** - https://docs.onflow.org/cadence/language/capability-based-access-control

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

- **(ENG) | Документация Flow** - https://docs.onflow.org/ - более детальная информации о блокчейне Flow и как взаимодействовать с ним
- **(ENG) | Flow Portal** - https://flow.com/ - your entry point to Flow
- **(ENG) | FCL JS** - https://github.com/onflow/fcl-js - Исходный код и возможность поучаствовать в разработке библиотеки FCL JS library
- **(ENG) | Cadence** - https://docs.onflow.org/cadence/ - Введение в язык программирования Cadence
- **Codesandbox** - https://codesandbox.io - Замечательная среда разработки и прототипирования прямо в вашем браузере