FA2-SmartPy

В этом руководстве показано, как взаимодействовать с «FA2-SmartPy» реализацией стандарта FA2 в некоторых распространенных сценариях использования. В первой части используются команды tezos-client для работы с основными передачами и запросами. Вторая часть идет дальше: она использует интерфейс командной строки fatoo для пакетной передачи и использует механизм «оператора» для делегирования прав передачи.

Базовое использование с tezos-client

Это предполагает, что у вас правильно настроен tezos-client для взаимодействия с Carthagenet или с «полной» песочницей.

Для этой части требуется 4 аккаунта, с несколькими импортироваными в tezos-client ꜩ . Аккаунты: administrator, originator, alice иbob.

Согласно нашему руководству по песочнице мы используем alice также как originator и administrator:

 $ tezos-client import secret key alice \
                unencrypted:edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq \
                --force
   tezos-client import secret key originator \
                unencrypted:edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq \
                --force
   tezos-client import secret key administrator \
                unencrypted:edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq \
                --force
   tezos-client import secret key bob \
                unencrypted:edsk3RFfvaFaxbHx8BMtEW1rKQcPtDML3LXjNqMNLCzC3wLC1bWbAt \
                --force

Получение кода Майкельсона

FA2-SmartPy использует средства метапрограммирования SmartPy для предоставления более одного контракта Майкельсона. Такие контракты также известны как «сборки». Некоторые сборки доступны по адресу https://gitlab.com/smondet/fa2-smartpy/-/tree/master/michelson, далее в руководстве будет описание различных сборок.

Скачиваем сборку «по умолчанию»:

 $ wget -O fa2_default.tz \
        'https://gitlab.com/smondet/fa2-smartpy/-/raw/a58e9f11/michelson/20200724-170337+0000_8cee712_contract.tz'

Создание

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

(Pair
   (Pair "<admin-pkh>" (Pair <nb-of-tokens> <ledger-big-map>))
   (Pair (Pair Unit <operators-big-set>)
         (Pair <paused> <tokens-big-map>)))

Ожидается, что <nb-of-tokens> является кардиналом карты <tokens-big-map>, а в <ledger-big-map> используются только «известные» токены. Чтобы правильно поддерживать все инварианты, рекомендуется инициализировать хранилище пустым и использовать точку входа %mint для заполнения контракта.

Создадим такой не приостановленный пустой контракт при установке адреса administrator:

 $ tezos-client originate contract myfa2 \
                transferring 0 from originator \
                running fa2_default.tz \
                --init '(Pair (Pair "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" (Pair 0 {})) (Pair (Pair Unit {}) (Pair False {})))' \
                --burn-cap 10 \
                --force --no-print-source
 Node is bootstrapped.
 Estimated gas: 130373 units (will add 100 for safety)
 Estimated storage: 4447 bytes added (will add 20 for safety)
 Operation successfully injected in the node.
 Operation hash is 'onosZKPHVN5dGy7LGKX2NV3Tyy3Dyg7qMNeGAn1Eqp7Mwx8sazk'
 Waiting for the operation to be included...
 Operation found in block: BM31KMRfkmiDDBMfaq9tCQe5TM6Xhxp3D8XseDWMynG7k81S9q8 (pass: 3, offset: 0)
 
 ...
 
           tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb ... -ꜩ0.257
 New contract KT1Wf2sNVoosmXZqXEppsPZkS4BNAUL7hjTL originated.
 The operation has only been included 0 blocks ago.
 We recommend to wait more.
 Use command
┃   tezos-client wait for onosZKPHVN5dGy7LGKX2NV3Tyy3Dyg7qMNeGAn1Eqp7Mwx8sazk to be included --confirmations 30 --branch BMHcbMLmK3avi1ntw9rtcMgyPFjzyUxXhf5a8zDdjKM8eqJHenx
 and/or an external block explorer.
 Contract memorized as myfa2.

Выпуск

Здесь мы хотим сделать перевод в качестве administrator, установленного в предыдущем разделе.

Точка входа для выпуска не имеет жесткой стандартизации в спецификации FA2, для fa2-smartpy она должна выглядеть так:

(Pair (Pair "<address>" <amount>) (Pair "<token-symbol>" <token-id>))

Сборка по умолчанию предполагает, что идентификаторы токенов являются последовательными натуральными числами (0, 1, 2, ...), если из-за какого-то конкретного ограничения пользователю требуются произвольные идентификаторы токенов, в FA2-SmartPy есть опция сборки для создания такого контракта ( см. документацию).

Например, давайте, как administrator, выпустим 100 TK0 токенов на счет alice:

 $ tezos-client transfer 0 from administrator to myfa2 \
                --entrypoint mint \
                --arg '(Pair (Pair "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" 100) (Pair "TK0" 0))' \
                --burn-cap 3
 Node is bootstrapped.
 Estimated gas: 113946 units (will add 100 for safety)
 Estimated storage: 163 bytes added (will add 20 for safety)
 Operation successfully injected in the node.
 Operation hash is 'onpcARPLVMjXqRfjFxyaZFGnpbCdsP2uSVKQwMiBHie4ujRf7fe'
 Waiting for the operation to be included...
 Operation found in block: BMCwBfn9B2E5zmrNfAu3z4rwoTo3h2DhDjTFgsn4BvTB21Xguff (pass: 3, offset: 0)
 
 ...
 
       Consumed gas: 113946
       Balance updates:
         tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb ... -ꜩ0.163
 The operation has only been included 0 blocks ago.
 We recommend to wait more.
 Use command
┃   tezos-client wait for onpcARPLVMjXqRfjFxyaZFGnpbCdsP2uSVKQwMiBHie4ujRf7fe to be included --confirmations 30 --branch BM31KMRfkmiDDBMfaq9tCQe5TM6Xhxp3D8XseDWMynG7k81S9q8
 and/or an external block explorer.

Перевод

Точка входа передачи в FA2 «группируется» на двух уровнях, т.е. один контрактный вызов содержит список элементов передачи, каждый элемент передачи является «адресом отправителя» и списком исходящих транзакций:

{
  Pair "<from-1>" {Pair "<to-1>" (Pair <token-id-1> <amount-1>)} ;
  Pair "<from-2>" {Pair "<to-2>" (Pair <token-id-2> <amount-2>) ; Pair "<to-3>" (Pair <token-id-3> <amount-3>)} ;
  ...
}

Здесь мы, как alice, переводим 5 из наших 100 TK0 на аккаунт bob:

 $ tezos-client transfer 0 from alice to myfa2 \
                --entrypoint transfer \
                --arg '{ Pair "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" {Pair "tz1aSkwEot3L2kmUvcoxzjMomb9mvBNuzFK6" (Pair 0 5)} }' \
                --burn-cap 3
 Node is bootstrapped.
 Estimated gas: 116015 units (will add 100 for safety)
 Estimated storage: 67 bytes added (will add 20 for safety)
 Operation successfully injected in the node.
 Operation hash is 'ontGCqsFh1uSDp5vmucU6EJxkivXw6bto2DN8LRekhXdo6WSTjK'
 Waiting for the operation to be included...
 Operation found in block: BLgT1CrszWaeEnxo4ncHWHwRZ1g39ig1NxT2zQB8AtCaFKhijnn (pass: 3, offset: 0)
 This sequence of operations was run:
 
 ...
 
       Consumed gas: 116015
       Balance updates:
         tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb ... -ꜩ0.067
 The operation has only been included 0 blocks ago.
 We recommend to wait more.
 Use command
┃   tezos-client wait for ontGCqsFh1uSDp5vmucU6EJxkivXw6bto2DN8LRekhXdo6WSTjK to be included --confirmations 30 --branch BMCwBfn9B2E5zmrNfAu3z4rwoTo3h2DhDjTFgsn4BvTB21Xguff
 and/or an external block explorer.

Получение баланса вне сети

В качестве примера взаимодействия с big-картами в хранилище контрактов с помощью Michelson и tezos-client мы получаем баланс Алисы в токенах TK0.

Нам нужен скрипт, который принимает тип хранилища контракта в качестве параметра (буквально копирует и вставляет) и использует Майкельсон для извлечения значения в карту %ledger; в этом случае мы просто отображаем его с инструкцией FAILWITH, но можно было бы сделать гораздо больше, включая помещение в память (оставлено как упражнение для читателя ☺). Сохраним его как get-balance.tz:

parameter
    (pair (pair (address %administrator)
                (pair (nat %all_tokens) (big_map %ledger (pair address nat) nat)))
          (pair (pair (unit %version_20200724_tzip_a57dfe86_contract)
                      (big_map %operators (pair (address %owner) (address %operator)) unit))
                (pair (bool %paused)
                      (big_map %tokens
                         nat
                         (pair (nat %token_id)
                               (pair (string %symbol)
                                     (pair (string %name) (pair (nat %decimals) (map %extras string string))))))))) ;
storage unit;
code
 {
    CAR ; # Get parameter
    CAR ; # Get the pair (admin , _)
    CDR ; # Get the pair (all_token, ledger)
    CDR ; # Get %ledger
    PUSH (pair address nat) (Pair "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" 0);
    GET ; # Get the value in the ledger at the above key
    FAILWITH
 };

В этом случае мы ожидаем сбоя команды tezos-client, поскольку мы хотим прочитать сообщение об ошибке:

 $ tezos-client run script get-balance.tz on storage Unit \
                and input \
                "$(tezos-client get contract storage for myfa2)"
 
 ...
 
   20:     GET ; # Get the value in the ledger at the above key
   21:     FAILWITH
   22:  };
 At line 21 characters 4 to 12,
 script reached FAILWITH instruction
 with (Some 95)
 Fatal error:
   error running script

Мы можем отчетливо видеть в значении ошибки (переданном в FAILWITH), что баланс aliceсоставляет 95 TK0 (100 выпущено минус 5 переведено в аккаунт bob).

Приложение fatoo

Получение и настройка клиента

В этом разделе мы используем интерфейс командной строки fatoo для некоторых сборок FA2-SmartPy. Вам нужно установить fatoo по вашему $PATH, или вы можете использовать Docker:

 $ fatoo --version
   # or:
   docker run -it --rm --entrypoint fatoo registry.gitlab.com/smondet/fa2-smartpy:a58e9f11-run --version

В приложении fatoo есть много команд, см. fatoo [subcommand] --help. В то же время он находится в стадии разработки, поэтому не стесняйтесь отправлять вопросы и запросы функций в основном репозитории.

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

  • fatoo_root_path: логи, вывод

  • fatoo_client: более важный - это URI, описывающий, как настроить tezos-client и взаимодействовать с узлом

Смотрите команду fatoo show-client-uri-documentation:

URI следует обычному шаблону: <scheme>://<host>:<port>/<path>?<options>:

  • <scheme> может быть http или http (--tls вариант);

  • <host>:<port> определяет подключение к узлу;

  • <path> это закрытый ключ (URI) для учетной записи «спонсора», используется для оплаты горючего и хранения

Доступные <options> это:

  • bake=true: используйте аккаунт funder запекать блоки после операций инъекции (полезно для «ручных» песочниц);

  • wait=<INT>: установите опцию --wait для tezos-client (сколько блоков нужно ждать после инъекции операции);

  • command=<STRING>: используйте альтернативную команду для tezos-client.

См., например, текущее значение по умолчанию: http://:2020/unencrypted:edsk3S7mCwuuMVS21jsYTczxBU4tgTbQp98J3YmTGcstuUxsrZxKYd?bake=true.

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

export fatoo_client='http://:20000/unencrypted:edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq?wait=0'

# Or, for docker, use:

alias fatoo='docker run -it -u "$UID" --network host -v "$PWD:/work" -w /work --rm -e fatoo_client="http://:20000/unencrypted:edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq?wait=0" --entrypoint fatoo registry.gitlab.com/smondet/fa2-smartpy:a58e9f11-run'

В приложении есть подкоманда client, которая просто правильно вызывает tezos-client, их настройку можно проверить с помощью:

 $ fatoo client bootstrapped
 Node is bootstrapped.

Настройка аккаунтов

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

 $ fatoo account-of-seed \
         "the-only-administrator-of-the-contract" \
         --output admin.csv
   fatoo account-of-seed \
         "the-0th-aka-first-owner" \
         --output owner0.csv
   fatoo account-of-seed \
         "ready-owner-one" \
         --output owner1.csv
   fatoo account-of-seed \
         "this-is-a-potential-token-owner-too" \
         --output owner2.csv

Полученные CSV имеют тот же формат, что и flextesa, они содержат: <phrase>,<pk>,<pkh>,<sk>. См., например:

 $ echo "Public key hash: $(cut -d, -f 3 admin.csv)"
   echo "Secret key: $(cut -d, -f 4 admin.csv)"
 Public key hash: tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe
 Secret key: unencrypted:edsk3ZAm4BwNkG2uUmCcA64BadPWuwNt16zZisnfcQEuvyStaBa6oG

Установим имена для всего этого:

 $ export admin_pkh="$(cut -d, -f 3 admin.csv)"
   export admin_sk="$(cut -d, -f 4 admin.csv)"
   export owner0_pkh="$(cut -d, -f 3 owner0.csv)"
   export owner0_sk="$(cut -d, -f 4 owner0.csv)"
   export owner1_pkh="$(cut -d, -f 3 owner1.csv)"
   export owner1_sk="$(cut -d, -f 4 owner1.csv)"
   export owner2_pkh="$(cut -d, -f 3 owner2.csv)"
   export owner2_sk="$(cut -d, -f 4 owner2.csv)"

Создание

Приложение содержит код для нескольких вариантов контракта:

 $ fatoo list-contract-variants \
         --details description --format markdown
 * `contract`: The default.
 * `dbg_contract`: The default in debug mode.
 * `baby_contract`: The default in Babylon mode.
 * `nolay_contract`: The default without right-combs.
 * `mutran_contract`: The default with mutez transfer entry-point.
 * `tokset_contract`: The default with non-consecutive token-IDs.
 * `perdesc_noops_contract`: The default without operators and with permissions-descriptor.
 * `perdesc_noops_dbg_contract`: The perdesc_noops_contract but in debug mode.
 * `single_contract`: The default for single-asset.
 * `single_mutran_contract`: The single-asset with mutez transfer entry-point.
 * `nft_mutran_contract`: The default in NFT mode with mutez transfer entry-point.
 * `lzep_contract`: The default with lazy-entry-points flag.
 * `lzepm_contract`: The default with lazy-entry-points-multiple flag.
 * `lzep_mutran_contract`: The default with mutez-transfer and lazy-entry-points flag.
 * `lzepm_mutran_contract`: The default with mutez-transfer and lazy-entry-points-multiple flag.

Можно сбросить код Майкельсона в файл (см. fatoo get-code --help), но в этом нет необходимости, поскольку можно напрямую создавать контракты из приложения. Давайте создадим mutran_contract, полномасштабную реализацию FA2 с дополнительной точкой входа, которая позволяет администратору переводить средства, которые потенциально могут оказаться на балансе контракта.

 $ fatoo originate mutran_contract \
         --administrator "${admin_pkh}" \
         --output-address kt1_mutran_contract.txt

 [FA2->Info]:
   Originations:
     * Success: mutran_contract (The default with mutez transfer entry-point)
       -> KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9

Команда сохранила адрес контракта в файле:

 $ cat kt1_mutran_contract.txt
 KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9

И, мы уже можем отображать состояние контракта (хранилища):

 $ fatoo show-storage "$(cat kt1_mutran_contract.txt)"
 [FA2->Info]:
   Contract: KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9
     Balance: 0 mutez
     Administrator: "tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe"
     Status: Ready
     Tokens-big-map: 42
     Ledger-big-map: 40
     Operators-big-map: 41
     All-Tokens: None
     Known-Owners-and-Operators: None

Выпуск и мультитрансфер

Чтобы выпускать токены, администратор должен иметь возможность вызывать контракт в цепочке, для этого нам нужно передать как минимум несколько μꜩ на этот адрес. Можно использовать tezos-client, но у fatoo есть команда быстрого доступа для перевода с настроенной учетной записи «спонсора» (суммы указаны в mutez):

 $ fatoo fund-address \
         "${admin_pkh}" \
         10_000_000
 [FA2->Info]: Balance for tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe is now
   1019633807 mutez.

Обратите внимание, что на данный момент owner0 не существует в цепочке, мы все еще выпускаем для них токены:

 $ fatoo call-mint --token-id 0 --token-symbol TQ0 \
         "${owner0_pkh}" 1_000_000 \
         --source "${admin_sk}" \
         --address "$(cat kt1_mutran_contract.txt)"
 (Pair (Pair "tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS" 1000000) (Pair "TQ0" 0))

Давайте добавим еще один токен TQ1, который все еще выпускается для owner0:

 $ fatoo call-mint --token-id 1 --token-symbol TQ1 \
         "${owner0_pkh}" 2_000 \
         --source "${admin_sk}" \
         --address "$(cat kt1_mutran_contract.txt)"
 (Pair (Pair "tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS" 2000) (Pair "TQ1" 1))

Посмотрим на хранилище; мы видим новые токены TQ0 и TQ1, и, поскольку мы указываем «известного владельца токена» в командной строке, мы можем видеть их балансы:

 $ fatoo show-storage "$(cat kt1_mutran_contract.txt)" \
         --known-address "$(cut -d, -f 3 owner0.csv)"
 [FA2->Info]:
   Contract: KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9
     Balance: 0 mutez
     Administrator: "tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe"
     Status: Ready
     Tokens-big-map: 42
     Ledger-big-map: 40
     Operators-big-map: 41
     All-Tokens: 0 = TQ0.
                 1 = TQ1.
     Known-Owners-and-Operators:
       * Owner: "tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS" [0 ops]
         - Balance: 1000000 TQ0(0)
         - Balance: 2000 TQ1(1)

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

 $ fatoo fund-address \
         "${owner0_pkh}" \
         1_000_000
 [FA2->Info]: Balance for tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS is now 1704820
   mutez.

Затем, поскольку владелец токена может выполнять самопередачу, мы используем секретный ключ для owner1 для передачи TQ0 и TQ1 owner1 и owner2:

 $ fatoo call-transfer \
         "from:${owner0_pkh} to:${owner1_pkh} amount: 10 token: 0" \
         "from:${owner0_pkh} to:${owner1_pkh} amount: 100 token: 1" \
         "from:${owner0_pkh} to:${owner2_pkh} amount: 10 token: 1" \
         --source "${owner0_sk}" \
         --address "$(cat kt1_mutran_contract.txt)"
┃ { Pair "tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS" { Pair "tz1YYrxf529d3EYzEv5TnsiTpRCzFFB87dAS" (Pair 0 10) ; Pair "tz1YYrxf529d3EYzEv5TnsiTpRCzFFB87dAS" (Pair 1 100) ; Pair "tz1TyFYCuKrQ7A3yB4AvpoPRLacb3J6iQB9V" (Pair 1 10)}}

Затем мы можем наблюдать получившееся состояние:

 $ fatoo show-storage "$(cat kt1_mutran_contract.txt)" \
         --known-address "$(cut -d, -f 3 owner0.csv)" \
         --known-address "$(cut -d, -f 3 owner1.csv)" \
         --known-address "$(cut -d, -f 3 owner2.csv)"
 [FA2->Info]:
   Contract: KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9
     Balance: 0 mutez
     Administrator: "tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe"
     Status: Ready
     Tokens-big-map: 42
     Ledger-big-map: 40
     Operators-big-map: 41
     All-Tokens: 0 = TQ0.
                 1 = TQ1.
     Known-Owners-and-Operators:
       * Owner: "tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS" [0 ops]
         - Balance: 999990 TQ0(0)
         - Balance: 1890 TQ1(1)
       * Owner: "tz1YYrxf529d3EYzEv5TnsiTpRCzFFB87dAS" [0 ops]
         - Balance: 10 TQ0(0)
         - Balance: 100 TQ1(1)
       * Owner: "tz1TyFYCuKrQ7A3yB4AvpoPRLacb3J6iQB9V" [0 ops]
         - Balance: 10 TQ1(1)

Использование операторов

Давайте создадим пару ключей для operator:

 $ fatoo account-of-seed \
         "youve-been-operated-ill-be-back" \
         --output operator.csv
   export operator_pkh="$(cut -d, -f 3 operator.csv)"
   export operator_sk="$(cut -d, -f 4 operator.csv)"

Теперь мы заставим всех владельцев делегировать «оператору», см. Также командуfatoo call-update-operators --help:

 $ fatoo call-update-operators \
         "add@ operator: ${operator_pkh} owner: ${owner0_pkh}" \
         --source "${owner0_sk}" \
         --address "$(cat kt1_mutran_contract.txt)"
   fatoo fund-address \
         "${owner1_pkh}" \
         1_000_000
   fatoo call-update-operators \
         "add@ operator: ${operator_pkh} owner: ${owner1_pkh}" \
         --source "${owner1_sk}" \
         --address "$(cat kt1_mutran_contract.txt)"
   fatoo fund-address \
         "${owner2_pkh}" \
         1_000_000
   fatoo call-update-operators \
         "add@ operator: ${operator_pkh} owner: ${owner2_pkh}" \
         --source "${owner2_sk}" \
         --address "$(cat kt1_mutran_contract.txt)"

Мы видим, что теперь один и тот же оператор присутствует в каждой учетной записи:

 $ fatoo show-storage "$(cat kt1_mutran_contract.txt)" \
         --known-address "$(cut -d, -f 3 owner0.csv)" \
         --known-address "$(cut -d, -f 3 owner1.csv)" \
         --known-address "$(cut -d, -f 3 owner2.csv)" \
         --known-address "$(cut -d, -f 3 operator.csv)"
 [FA2->Info]:
   Contract: KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9
     Balance: 0 mutez
     Administrator: "tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe"
     Status: Ready
     Tokens-big-map: 42
     Ledger-big-map: 40
     Operators-big-map: 41
     All-Tokens: 0 = TQ0.
                 1 = TQ1.
     Known-Owners-and-Operators:
       * Owner: "tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS"
         - Operator: "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85"
         - Balance: 999990 TQ0(0)
         - Balance: 1890 TQ1(1)
       * Owner: "tz1YYrxf529d3EYzEv5TnsiTpRCzFFB87dAS"
         - Operator: "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85"
         - Balance: 10 TQ0(0)
         - Balance: 100 TQ1(1)
       * Owner: "tz1TyFYCuKrQ7A3yB4AvpoPRLacb3J6iQB9V"
         - Operator: "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85"
         - Balance: 10 TQ1(1)
       * Owner: "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85" [0 ops] [0 toks]

Наконец, давайте заставим operator запустить трансфер пакетного ограбления всех токенов:

 $ fatoo fund-address \
         "${operator_pkh}" \
         2_000_000_000

 [FA2->Info]: Balance for tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85 is now
   2999839761 mutez.
 $ fatoo call-transfer \
         "from:${owner0_pkh} to:${operator_pkh} amount: 999990 token: 0" \
         "from:${owner0_pkh} to:${operator_pkh} amount: 1890 token: 1" \
         "from:${owner1_pkh} to:${operator_pkh} amount: 10 token: 0" \
         "from:${owner1_pkh} to:${operator_pkh} amount: 100 token: 1" \
         "from:${owner2_pkh} to:${operator_pkh} amount: 10 token: 1" \
         --source "${operator_sk}" \
         --address "$(cat kt1_mutran_contract.txt)"
┃ { Pair "tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS" { Pair "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85" (Pair 0 999990) ; Pair "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85" (Pair 1 1890)} ; Pair "tz1YYrxf529d3EYzEv5TnsiTpRCzFFB87dAS" { Pair "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85" (Pair 0 10) ; Pair "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85" (Pair 1 100)} ; Pair "tz1TyFYCuKrQ7A3yB4AvpoPRLacb3J6iQB9V" { Pair "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85" (Pair 1 10)}}

Затем мы можем наблюдать результирующее состояние, в котором все балансы равны 0, за исключением operator, которому принадлежат вообще все токены:

 $ fatoo show-storage "$(cat kt1_mutran_contract.txt)" \
         --known-address "$(cut -d, -f 3 owner0.csv)" \
         --known-address "$(cut -d, -f 3 owner1.csv)" \
         --known-address "$(cut -d, -f 3 owner2.csv)" \
         --known-address "$(cut -d, -f 3 operator.csv)"
 [FA2->Info]:
   Contract: KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9
     Balance: 0 mutez
     Administrator: "tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe"
     Status: Ready
     Tokens-big-map: 42
     Ledger-big-map: 40
     Operators-big-map: 41
     All-Tokens: 0 = TQ0.
                 1 = TQ1.
     Known-Owners-and-Operators:
       * Owner: "tz1MUP3sCWTUQRG2Hon7uhRfmuYZ4guEQntS"
         - Operator: "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85"
         - Balance: 0 TQ0(0)
         - Balance: 0 TQ1(1)
       * Owner: "tz1YYrxf529d3EYzEv5TnsiTpRCzFFB87dAS"
         - Operator: "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85"
         - Balance: 0 TQ0(0)
         - Balance: 0 TQ1(1)
       * Owner: "tz1TyFYCuKrQ7A3yB4AvpoPRLacb3J6iQB9V"
         - Operator: "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85"
         - Balance: 0 TQ1(1)
       * Owner: "tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85" [0 ops]
         - Balance: 1000000 TQ0(0)
         - Balance: 2000 TQ1(1)

Получение баланса контракта

Сборка контракта, которую мы создали выше, имеет дополнительную точку входа, чтобы иметь возможность перенести баланс контракта, например на случай, если кто-то случайно переведет μꜩ на контракт.

Итак, давайте представим, что после вышеупомянутого ограбления operator хочет публично дать чаевые/подкупить администратора(ов) контракта, пройдя через сам контракт (это может быть запутанным предлогом для включения XTZ в контракт…). Мы вызываем точку входа transfer с пустым списком элементов передачи, но с несколькими XTZ в качестве суммы:

 $ tezos-client import secret key operator \
                "${operator_sk}" --force
   tezos-client transfer 1_000 from operator \
                to "$(cat kt1_mutran_contract.txt)" \
                --entrypoint transfer \
                --arg '{}' --burn-cap 1
 
 ...
 
       Balance updates:
         tz1NkpWhHsBSZHPg2Ljz2hycRiZvcYdcyu85 ... -ꜩ1000
         KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9 ... +ꜩ1000
 The operation has only been included 0 blocks ago.
 We recommend to wait more.
 Use command
┃   tezos-client wait for op2Cibz8jgB2Djb8MEqUg8X7mfKLzywo9SuQxxR2nDMVJZMH14N to be included --confirmations 30 --branch BMWfkKTCCWBatJ3rRXjp2H7DW3cwU4dr8cmP2ddccvnFi1z4geT
 and/or an external block explorer.

Мы видим, что теперь fatoo показывает ненулевой баланс контракта:

 $ fatoo show-storage "$(cat kt1_mutran_contract.txt)"
 [FA2->Info]:
   Contract: KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9
     Balance: 1000000000 mutez
     Administrator: "tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe"
     Status: Ready
     Tokens-big-map: 42
     Ledger-big-map: 40
     Operators-big-map: 41
     All-Tokens: 0 = TQ0.
                 1 = TQ1.
     Known-Owners-and-Operators: None

Давайте заставим admin забрать эти деньги себе; точка входа называется mutez_transfer и принимает пару mutez × address:

 $ tezos-client import secret key admin \
                "${admin_sk}" --force
   tezos-client transfer 0 from admin \
                to "$(cat kt1_mutran_contract.txt)" \
                --entrypoint mutez_transfer \
                --arg "Pair 1000000000 \"${admin_pkh}\"" \
                --burn-cap 1
 
 ...
 
         Balance updates:
           KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9 ... -ꜩ1000
           tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe ... +ꜩ1000
 The operation has only been included 0 blocks ago.
 We recommend to wait more.
 Use command
┃   tezos-client wait for ooQZfBpcar3jevFxpbvUkdgZPJ67kSrtQ4DAaFDUpkoNAnrV7ji to be included --confirmations 30 --branch BLKDsTiAm4sdSGiKnTuiwmyaSo6NoaVgChPPab3voLoiuvwFfRV
 and/or an external block explorer.

Мы видим, что баланс КТ1 обнулен:

 $ fatoo show-storage "$(cat kt1_mutran_contract.txt)"
 [FA2->Info]:
   Contract: KT1MSQj5BUmuuDMqoz4jpwabAcuxmhnUjhd9
     Balance: 0 mutez
     Administrator: "tz1ZnxqPNMXyiZLTANYJLJ9ZTBpQ5Qu16BXe"
     Status: Ready
     Tokens-big-map: 42
     Ledger-big-map: 40
     Operators-big-map: 41
     All-Tokens: 0 = TQ0.
                 1 = TQ1.
     Known-Owners-and-Operators: None

... и увидим, что admin стал богаче:

 $ tezos-client get balance for \
                "${admin_pkh}"
 2019.268775 
 Warning:  the --addr --port --tls options are now deprecated; use --endpoint instead

Также читайте

Будем надеяться, в этом руководстве достаточно ясно представлена FA2-SmartPy реализация FA2 с точки зрения пользователя. Пожалуйста, поделитесь своим мнением, используя раздел проблемы репозитория. Дополнительные материалы для чтения:

Материалы разработаны TQ Tezos переведены на русский язык Tezos Ukraine

Last updated