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 \
"[email protected] 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 \
"[email protected] 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 \
"[email protected] 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