FA1.2 Lorentz

lorentz-contract-param - is collection of Tezos smart contracts and tools to work with them.

Install lorentz-contract-param tools by following installation instructions in lorentz-contract-param source code repository

Originate

The FA1.2 contract must be initialized with several parameters:

Pair
{} -- Empty ledger big_map
(Pair
"$ALICE_ADDRESS" -- Administrator address
(Pair
False -- Is all activity currently paused? (no)
0 -- Total supply of tokens
)
)

Instead of providing contract parameters in raw Michelson, we can use lorentz-contract-param to generate them.

To originate the contract, run:

$ tezos-client --wait none originate contract ManagedLedger \
transferring 0 from $ALICE_ADDRESS running \
"$(lorentz-contract print --name ManagedLedger)" \
--init "$(lorentz-contract-storage ManagedLedgerBabylon \
--admin $ALICE_ADDRESS)" --burn-cap 5
Waiting for the node to be bootstrapped before injection...
Current head: BLXAzRot4GdK (timestamp: 2019-10-21T20:37:12-00:00, validation: 2019-10-21T20:38:30-00:00)
Node is bootstrapped, ready for injecting operations.
Estimated gas: 214007 units (will add 100 for safety)
Estimated storage: 7471 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash is 'ooSEtw5AkoCupsz97tZT9onb4Un8oHckaw5Bb8NBXxmiHA4TkQx'
NOT waiting for the operation to be included.
Use command
tezos-client wait for ooSEtw5AkoCupsz97tZT9onb4Un8oHckaw5Bb8NBXxmiHA4TkQx to be included --confirmations 30 --branch BLbT8WBT96TXvoNfD9VsDH6wzgMhkTTnqsPor8DRkxWhBRgtFVc
and/or an external block explorer to make sure that it has been included.
This sequence of operations was run:
Manager signed operations:
From: tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ
Fee to the baker:0.028853
Expected counter: 2033
Gas limit: 214107
Storage limit: 7491 bytes
Balance updates:
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ............ -0.028853
fees(tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU,23) ... +0.028853
Origination:
From: tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ
Credit:0
Script:
{...}
Initial storage:
(Pair {}
(Pair (Pair "tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ" False)
(Pair 12 (Left "tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ"))))
No delegate for this contract
This origination was successfully applied
Originated contracts:
KT1RUhPAABRhZBctcsWFtymyjpuBQdLTqaAQ
Storage size: 7214 bytes
Updated big_maps:
New map(45) of type (big_map address (pair nat (map address nat)))
Paid storage size diff: 7214 bytes
Consumed gas: 214007
Balance updates:
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ... -7.214
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ... -0.257
New contract KT1RUhPAABRhZBctcsWFtymyjpuBQdLTqaAQ originated.

Note we abbreviate large Script contents with {...}

Make a variable for the new contract address:

FA12_ADDRESS=KT1RUhPAABRhZBctcsWFtymyjpuBQdLTqaAQ

Note that it may take some time for the blockchain network to include our operations (typically no more than a few minutes). To check operation status use its hash to get a receipt:

$ tezos-client get receipt for oonKH1nip9m7MKwRPtZHkC2Fwehmuyqsn6bWhub6K3TbBJjvaC9
Operation found in block: BLKVEcSCehMfEWw4z33tGkP1kyivRMLV9PCDFgMgKEruaS5qr1Q (pass: 3, offset: 1)
Manager signed operations:
From: tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP
Fee to the baker:0.00126
Expected counter: 565660
Gas limit: 10000
Storage limit: 0 bytes
Balance updates:
tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP ............. -0.00126
fees(tz3WXYtyDUNL91qfiCJtVUX746QpNv5i5ve5,278) ... +0.00126
Revelation of manager public key:
Contract: tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP
Key: edpkuVNN5SQzWoyvTaR24oHq7QHadHJkc9vjMpgm913nocP2BJ3kfX
This revelation was successfully applied
Consumed gas: 10000
Manager signed operations:
From: tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP
Fee to the baker:0.033314
Expected counter: 565661
Gas limit: 242233
Storage limit: 9207 bytes
Balance updates:
tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP ............. -0.033314
fees(tz3WXYtyDUNL91qfiCJtVUX746QpNv5i5ve5,278) ... +0.033314
Origination:
From: tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP
For: tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP
Credit:0
Script:
{...}
Initial storage:
(Pair {}
(Pair (Pair "tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP" False)
(Pair 12 (Left "tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP"))))
No delegate for this contract
This origination was successfully applied
Originated contracts:
KT1LXEg3neJBfuugHSfKdtTsCCP2YEVMnHzR
Storage size: 8930 bytes
Paid storage size diff: 8930 bytes
Consumed gas: 242133
Balance updates:
tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP ... -8.93
tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP ... -0.257

Views

When a contract parameter has a non-unit return type, e.g. Get Total Supply, which returns the total supply of tokens, the contract needs to be able to store the result somewhere. In particular, another contract accepting the return value.

For the following examples, we need contracts accepting address and nat values:

$ tezos-client --wait none originate contract nat_storage transferring 0 \
from $ALICE_ADDRESS running "$(lorentz-contract print --name NatStorageContract)" \
--init 0 --burn-cap 0.295
Waiting for the node to be bootstrapped before injection...
Current head: BM8KmefAFDYs (timestamp: 2019-10-21T20:41:54-00:00, validation: 2019-10-21T20:42:37-00:00)
Node is bootstrapped, ready for injecting operations.
Estimated gas: 10909 units (will add 100 for safety)
Estimated storage: 295 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash is 'onrbx5Mi4zFGwZhMiev8PAnRcRZmJj41YyTVEotDq1TphseAq7K'
NOT waiting for the operation to be included.
Use command
tezos-client wait for onrbx5Mi4zFGwZhMiev8PAnRcRZmJj41YyTVEotDq1TphseAq7K to be included --confirmations 30 --branch BM8KmefAFDYsmzDGTNavPoxJDcx9r1GTU8Nsj75isKNSAg6W6rp
and/or an external block explorer to make sure that it has been included.
This sequence of operations was run:
Manager signed operations:
From: tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ
Fee to the baker:0.001367
Expected counter: 2034
Gas limit: 11009
Storage limit: 315 bytes
Balance updates:
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ............ -0.001367
fees(tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU,23) ... +0.001367
Origination:
From: tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ
Credit:0
Script:
{ parameter nat ; storage nat ; code { CAR ; NIL operation ; PAIR } }
Initial storage: 0
No delegate for this contract
This origination was successfully applied
Originated contracts:
KT1TEXAiui2k9wdZgLr8Bx7oDUmXWSTf5sG7
Storage size: 38 bytes
Paid storage size diff: 38 bytes
Consumed gas: 10909
Balance updates:
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ... -0.038
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ... -0.257
New contract KT1TEXAiui2k9wdZgLr8Bx7oDUmXWSTf5sG7 originated.
Contract memorized as nat_storage3.
$ tezos-client --wait none originate contract address_storage transferring 0 \
from $ALICE_ADDRESS running "$(lorentz-contract print --name AddressStorageContract)" \
--init "\"$FA12_ADDRESS\"" --burn-cap 0.334
Waiting for the node to be bootstrapped before injection...
Current head: BL7vzTSk714H (timestamp: 2019-10-21T20:43:40-00:00, validation: 2019-10-21T20:43:49-00:00)
Node is bootstrapped, ready for injecting operations.
Estimated gas: 11314 units (will add 100 for safety)
Estimated storage: 320 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash is 'ootVtQF6rs9NfmDaGh1bNGe8wk2qUXsSJR4mBoa5LXW4F77jNHT'
NOT waiting for the operation to be included.
Use command
tezos-client wait for ootVtQF6rs9NfmDaGh1bNGe8wk2qUXsSJR4mBoa5LXW4F77jNHT to be included --confirmations 30 --branch BL7vzTSk714HoNfSp7C8M3uAdAY5sN2VUhQgKmr1SxyDrGFKjzU
and/or an external block explorer to make sure that it has been included.
This sequence of operations was run:
Manager signed operations:
From: tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ
Fee to the baker:0.001447
Expected counter: 2035
Gas limit: 11414
Storage limit: 340 bytes
Balance updates:
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ............ -0.001447
fees(tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU,23) ... +0.001447
Origination:
From: tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ
Credit:0
Script:
{ parameter address ;
storage address ;
code { CAR ; NIL operation ; PAIR } }
Initial storage: "KT1XvNBJovT7XVedr29vw7QDWrqTU96NTQwR"
No delegate for this contract
This origination was successfully applied
Originated contracts:
KT1Kdd8aATDD2ckMTymZXYV5azyY76xfvarq
Storage size: 63 bytes
Paid storage size diff: 63 bytes
Consumed gas: 11314
Balance updates:
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ... -0.063
tz1VhNvW3aWCHRNawF4sHfBfJAu5WrpcEbSQ ... -0.257
New contract KT1Kdd8aATDD2ckMTymZXYV5azyY76xfvarq originated.

Note: you may have to set/increase the burn cap to allow the contract(s) to be originated, e.g.:

$ tezos-client originate contract address_storage transferring 0 \
from $ALICE_ADDRESS running "$(lorentz-contract print --name AddressStorageContract)" \
--init "\"$FA12_ADDRESS\"" --burn-cap 0.0001 --dry-run
Waiting for the node to be bootstrapped before injection...
Current head: BLY4388F8VTU (timestamp: 2019-08-30T17:03:46-00:00, validation: 2019-08-30T17:04:08-00:00)
Node is bootstrapped, ready for injecting operations.
Fatal error:
The operation will burn ꜩ0.334 which is higher than the configured burn cap (0.0001).
Use `--burn-cap 0.334` to emit this operation.

Then, using tezos-client get receipt for [Operation hash] for each operation hash, store the contract addresses in variables:

NAT_STORAGE_ADDRESS="KT1.."
ADDRESS_STORAGE_ADDRESS="KT1.."

Next, you may access the stored values with the following two commands for nat and address, respectively:

$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
0
$ tezos-client get contract storage for $ADDRESS_STORAGE_ADDRESS
"KT1FSGT6B7FLGiVph6jj85xuSq3wCnj9pMAp"

Interact using tezos-client

Commands to interact with our ManagedLedger contract instance using tezos-client and lorentz-contract-param all follow the same pattern with only a few parameters changing depending on which contract entry point we invoke. Using getTotalSupply as an example, invocation looks like this:

$ tezos-client --wait none transfer 0 from $ALICE_ADDRESS to $FA12_ADDRESS \
--arg "$(lorentz-contract-param \
ManagedLedgerBabylon-getTotalSupply \
--callback-contract $NAT_STORAGE_ADDRESS)"

Let's capture the pattern in a shell function:

$ fa12(){tezos-client --wait none transfer 0 from $ALICE_ADDRESS to $FA12_ADDRESS --burn-cap 0.5 --arg "$(lorentz-contract-param ManagedLedgerBabylon-$@;)"}

Note that we also set --burn-cap here. see burn-cap.

Get Total Supply

With fa12 function defined we can now invoke contract's getTotalSupply entrypoint like so:

$ fa12 getTotalSupply --callback-contract $NAT_STORAGE_ADDRESS

The output of this command looks like this:

Node is bootstrapped, ready for injecting operations.
Estimated gas: 125676 units (will add 100 for safety)
Estimated storage: no bytes added
Operation successfully injected in the node.
Operation hash is 'ooJXb7JsCKwu1jKoXzkm5Etm6JYtQRtSX2jfCSNE7ZNJNfVxAfj'
NOT waiting for the operation to be included.
Use command
tezos-client wait for ooJXb7JsCKwu1jKoXzkm5Etm6JYtQRtSX2jfCSNE7ZNJNfVxAfj to be included --confirmations 30 --branch BMXfSZ11UaGxf5NmoBKQNxe2c7LQCXkawYUKk9ow2vCBRDZjbDo
and/or an external block explorer to make sure that it has been included.
This sequence of operations was run:
Manager signed operations:
From: tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb
Fee to the baker:0.012885
Expected counter: 7
Gas limit: 125776
Storage limit: 0 bytes
Balance updates:
tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb ............. -0.012885
fees(tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU,121) ... +0.012885
Transaction:
Amount:0
From: tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb
To: KT1P33YQj5LQGBAYxvpXCsa6v2BXJ8N4i6PF
Parameter: (Left (Right (Right (Right (Pair Unit "KT1VMUd2mxJfkwYaKA5E8yQ9GT7314yeus8R")))))
This transaction was successfully applied
Updated storage:
(Pair 0 (Pair 0x00006b82198cb179e8306c1bedd08f12dc863f328886 (Pair False 5)))
Storage size: 4491 bytes
Consumed gas: 114348
Internal operations:
Transaction:
Amount:0
From: KT1P33YQj5LQGBAYxvpXCsa6v2BXJ8N4i6PF
To: KT1VMUd2mxJfkwYaKA5E8yQ9GT7314yeus8R
Parameter: 5
This transaction was successfully applied
Updated storage: 5
Storage size: 38 bytes
Consumed gas: 11328

All the commands produce similar output when successfull so we omit it in the following examples.

The value for total token supply will be recorded in simple storage of contract at NAT_STORAGE_ADDRESS which we can see using the following command (once tezos network includes our operations):

$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
0

Get Administrator

To get the current administrator's address, first put it into ADDRESS_STORAGE_ADDRESS contract:

$ fa12 getAdministrator --callback-contract $ADDRESS_STORAGE_ADDRESS

As with NAT_STORAGE_ADDRESS, we may access the result using get contract storage for and ensure that it matches Alice's address using echo $ALICE_ADDRESS:

$ tezos-client get contract storage for $ADDRESS_STORAGE_ADDRESS
"tz1L2UAwwU4k2nxjzB2mTnxW61wCcWaZeYkp"
$ echo $ALICE_ADDRESS
tz1L2UAwwU4k2nxjzB2mTnxW61wCcWaZeYkp

Get Balance

To get the balance of tokens for a particular address (stored in the NAT_STORAGE_ADDRESS contract):

$ fa12 getBalance --account $ALICE_ADDRESS --callback-contract $NAT_STORAGE_ADDRESS

We can then access the result using:

$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
0

Get Allowance

To get Bob's allowance to withdraw from Alice's account, where Bob's address is BOB_ADDRESS:

$ fa12 getAllowance --owner $ALICE_ADDRESS --spender $BOB_ADDRESS --callback-contract $NAT_STORAGE_ADDRESS

We can then access the result using:

$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
0

Transfer

From GetBalance above, we know that Alice doesn't have any tokens.

To transfer tokens from Alice to Bob, we first need to give Alice some tokens. We can do that by minting 5 tokens and allocating them to Alice (See the Mint subsection below):

$ fa12 mint --value 5 --to $ALICE_ADDRESS

To transfer 2 tokens from Alice to Bob:

$ fa12 transfer --value 2 --from $ALICE_ADDRESS --to $BOB_ADDRESS

Check Bob's balance:

$ fa12 getBalance --account $BOB_ADDRESS --callback-contract $NAT_STORAGE_ADDRESS
$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
0

Approve

For Alice to approve Bob to withdraw up to 3 tokens from her address:

$ fa12 approve --value 2 --spender $BOB_ADDRESS

We can then check the resulting allowance using:

$ fa12 getAllowance --owner $ALICE_ADDRESS --spender $BOB_ADDRESS --callback-contract $NAT_STORAGE_ADDRESS
$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
2

Mint

To mint 5 tokens and allocate them to Alice:

$ fa12 mint --value 5 --to $ALICE_ADDRESS
$ fa12 getBalance --account $ALICE_ADDRESS --callback-contract $NAT_STORAGE_ADDRESS
$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
5

Burn

If you've been following this guide step-by-step, Bob's account should have 3 tokens at this point. If it doesn't, add at least 1 using Transfer or Mint.

Before burning, we first add 2 tokens to Bob's address:

$ fa12 mint --value 2 --to $BOB_ADDRESS

We can ensure Bob has at least 3 tokens using:

$ fa12 getBalance --account $BOB_ADDRESS --callback-contract $NAT_STORAGE_ADDRESS
$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
5

Finally, we can burn 2 of Bob's tokens using:

$ fa12 burn --value 2 --from $BOB_ADDRESS

And then we may confirm that two tokens have been subtracted from Bob's account:

$ fa12 getBalance --account $BOB_ADDRESS --callback-contract $NAT_STORAGE_ADDRESS
$ tezos-client get contract storage for $NAT_STORAGE_ADDRESS
3

Pause Contract

To pause the contract:

$ fa12 setPause --paused True

We can check whether the contract is paused using:

$ tezos-client get contract storage for $FA12_ADDRESS
Pair 0 (Pair "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" (Pair True 7))

True value indicates that it's paused, a False value indicicates that it's not paused.

Transfers and approvals are are disabled while contract is paused. Try:

$ fa12 transfer --value 1 --from $ALICE_ADDRESS --to $BOB_ADDRESS

The command produces output like this (omitting included contract's Michelson code for brevity):

Node is bootstrapped, ready for injecting operations.
This simulation failed:
Manager signed operations:
From: tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb
Fee to the baker:0
Expected counter: 32
Gas limit: 800000
Storage limit: 60000 bytes
Transaction:
Amount:0
From: tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb
To: KT1P33YQj5LQGBAYxvpXCsa6v2BXJ8N4i6PF
Parameter: (Left (Left (Left (Pair "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb"
(Pair "tz1aSkwEot3L2kmUvcoxzjMomb9mvBNuzFK6" 1)))))
This operation FAILED.
Runtime error in contract KT1P33YQj5LQGBAYxvpXCsa6v2BXJ8N4i6PF:
001: { parameter
...
585: NIL operation ;
586: PAIR } } } } } }
At line 23 characters 87 to 95,
script reached FAILWITH instruction
with (Pair "TokenOperationsArePaused" Unit)
Fatal error:
transfer simulation failed

To unpause the contract:

$ fa12 setPause --paused False
$ tezos-client get contract storage for $FA12_ADDRESS
Pair 0 (Pair "tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb" (Pair False 7))

Set New Administrator

To make Bob the new administrator:

$ fa12 setAdministrator --new-administrator-address $BOB_ADDRESS

We can ensure that the administrator has been updated using:

$ fa12 getAdministrator --callback-contract $ADDRESS_STORAGE_ADDRESS
$ tezos-client get contract storage for $ADDRESS_STORAGE_ADDRESS
"tz1WWkcu7VFWgUTB9AAAomXGMS27tPKXrE9C"
$ echo $BOB_ADDRESS
tz1WWkcu7VFWgUTB9AAAomXGMS27tPKXrE9C

Our fa12 shell function is set up to invoke operations using $ALICE_ADDRESS, so now that $ALICE_ADDRESS is no longer the administrator attempts to invoke admin entrypoints from that address will fail:

$ fa12 mint --value 5 --to $ALICE_ADDRESS
...
586: PAIR } } } } } }
At line 447 characters 86 to 94,
script reached FAILWITH instruction
with (Pair "SenderIsNotAdmin" Unit)

Interact using PyTezos

Install PyTezos

If on Linux, install dependencies with apt:

sudo apt install libsodium-dev libsecp256k1-dev libgmp-dev

Otherwise, if on Mac OS, install them with brew:

brew tap cuber/homebrew-libsecp256k1
brew install libsodium libsecp256k1 gmp

Next, install PyTezos with pip3:

pip3 install pytezos

Originate the contract

Assuming that $ALICE_ADDRESS is defined, we can originate a copy of FA1.2 in much the same way as with lorentz-contracts.

Note: See the Client Setup Guide to make a wallet and define $ALICE_ADDRESS.

We begin by opening python3, setting the key and shell, and specifying fa12 as a contract:

Note: replace ~/Downloads/tz1R3vJ5TV8Y5pVj8dicBR23Zv8JArusDkYr.json with the path to your wallet .json file.

$ python3
Python 3.7.4 (default, Sep 7 2019, 18:27:02)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from pytezos import pytezos
>>> pytezos = pytezos.using(key='~/Downloads/tz1R3vJ5TV8Y5pVj8dicBR23Zv8JArusDkYr.json', shell='babylonnet')
>>> alice_address = 'tz1R3vJ5TV8Y5pVj8dicBR23Zv8JArusDkYr'
>>> bob_address = 'tz1bDCu64RmcpWahdn9bWrDMi6cu7mXZynHm'
>>> nat_storage_address = 'KT1J4mhVAaMYAPC4aPEkhC9i48PHBHxavkJ2'

Next, we define a Contract from a copy of ManagedLedger.tz with annotations:

>>> from pytezos import Contract
>>> import requests
>>> contract_url = 'https://gitlab.com/tzip/tzip/raw/16ca4fbb60f2ab52958136b76a1d12b342beba58/Proposals/TZIP-0007/ManagedLedger.tz'
>>> fa12src = Contract.from_michelson(requests.get(contract_url).text)

We need to create the initial storage to originate the contract.

To help with this, we can view the contract's storage schema:

>>> fa12src.storage
<pytezos.michelson.contract.ContractStorage object at 0x10d984590>
$storage:
{
"ledger": { $address : $ledger_item , ... } /* big_map */,
"admin": $address,
"paused": bool,
"totalSupply": $nat
}
$ledger_item:
{
"balance": $nat,
"approvals": { $address : $nat , ... }
}
$nat:
int /* Natural number */
$address:
string /* Base58 encoded `tz` or `KT` address */
Helpers
.big_map_decode()
.big_map_diff_decode()
.big_map_diff_encode()
.big_map_id()
.big_map_init()
.big_map_query()
.decode()
.default()
.encode()

Next, we choose initial storage values and encode it to prepare for contract origination:

>>> init_storage = fa12src.storage.encode({
...: "ledger": {},
...: "admin": pytezos.key.public_key_hash(),
...: "paused": False,
...: "totalSupply": 0
...: } )

Next, we originate the contract:

>>> origination_op = pytezos.origination(script=dict(code=fa12src.code, storage=init_storage)).autofill().sign().inject()
>>> origination_hash = origination_op['hash']
>>> origination_hash
'opXxLwE3j9ZY9Rv99Pomh6u7SonKAoywzpU6KkdWtFryUhBbXdP'

However, the origination does not return the resulting contract's address, so we need to find the completed operation in the network and derive the contract's address from it:

>>> opg = pytezos.shell.blocks[-5:].find_operation(origination_hash)
>>> contract_id = opg['contents'][0]['metadata']['operation_result']['originated_contracts'][0]
>>> contract_id
'KT1GV7wkUgZpEznQ48VR1UUSgXAU5Lk1SfpH'
>>> fa12 = pytezos.contract(contract_id)

Note: the find_operation command can fail if the operation has not yet been processed:

>>> opg = pytezos.shell.blocks[-5:].find_operation(origination_op)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.7/site-packages/pytezos/tools/docstring.py", line 67, in __call__
return method(self.class_instance, *args, **kwargs)
File "/usr/local/lib/python3.7/site-packages/pytezos/rpc/search.py", line 207, in find_operation
raise StopIteration(operation_group_hash)
StopIteration: oonhxpbH9Jc7fpbjfzvfbetppEBnjUTFmkbziVFV3B5KhjVdzpd

Inspect the Schema

We can view the parameter schema to find out which entry points the contract supports and what their arguments are:

>>> fa12.contract.parameter
<pytezos.michelson.contract.ContractParameter object at 0x10ce5d910>
$parameter:
{ "transfer": $transfer } ||
{ "approve": $approve } ||
{ "getAllowance": [ $address , $address , $contract (nat) ] } ||
{ "getBalance": [ $address , $contract (nat) ] } ||
{ "getTotalSupply": [ $unit , $contract (nat) ] } ||
{ "setPause": bool /* setPause */ } ||
{ "setAdministrator": $address /* setAdministrator */ } ||
{ "getAdministrator": [ $unit , $contract (address) ] } ||
{ "mint": $mint } ||
{ "burn": $burn }
$burn:
{
"from": $address,
"value": $nat
}
$mint:
{
"to": $address,
"value": $nat
}
$approve:
{
"spender": $address,
"value": $nat
}
$transfer:
{
"from": $address,
"to": $address,
"value": $nat
}
$contract:
string /* Base58 encoded `KT` address */
$nat:
int /* Natural number */
$address:
string /* Base58 encoded `tz` or `KT` address */
$unit:
None /* Void */
Helpers
.decode()
.encode()
.entries()

Interacting with FA1.2

Finally, using the parameter schema, we can perform a transfer using the following steps:

  • Mint 5 tokens to alice_address
  • Transfer 3 tokens from alice_address to bob_address
  • Store bob_address's balance in nat_storage_address
  • Get the storage value of nat_storage_address
>>> op = fa12.mint(to=alice_address, value=5).inject()
>>> op['hash']
'opDS6FUYmzXBRpX1DxpnucWBrcyEvCmAsSdjmHJMNpf7xQhGsRW'
>>> op = fa12.transfer(**{'from': alice_address, 'to': bob_address, 'value': 3}).inject()
>>> op['hash']
'ooQRsxqkV3vEtaDdarMzvzFuZLeKe6qfJkZEx9PntzqHGxVo8FU'
>>> op = fa12.getBalance(bob_address, nat_storage_address).inject()
>>> op['hash']
'opUVaphfSFgeUTACVmspr6bXrY82RLJ4mSf4DgT2kNZ6SiEcuh6'
>>> pytezos.contract(nat_storage_address).storage()
3

Note: Why .transfer(**{'from': ..}) instead of .transfer(from=..)? In Python, from is a keyword and may not be used to specify an argument so we use **{..} to convert a dictionary into keyword arguments.

Troubleshooting

Counter _ already used

This error occurs when a dependent operation has not yet been included and can take one of the following forms:

$ tezos-client --wait none transfer 0 from $ALICE_ADDRESS to $FA12_ADDRESS --arg ..
Waiting for the node to be bootstrapped before injection...
Current head: BMe8VmYBa1Ye (timestamp: 2019-08-12T15:34:56-00:00, validation: 2019-08-12T15:35:00-00:00)
Node is bootstrapped, ready for injecting operations.
Counter 565668 already used for contract tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP (expected 565669)
Fatal error:
transfer simulation failed
$ tezos-client --wait none transfer 0 from $ALICE_ADDRESS to $FA12_ADDRESS --arg ..
Waiting for the node to be bootstrapped before injection...
Current head: BL7QrQjGhkut (timestamp: 2019-08-12T15:39:26-00:00, validation: 2019-08-12T15:39:45-00:00)
Node is bootstrapped, ready for injecting operations.
Estimated gas: 212314 units (will add 100 for safety)
Estimated storage: no bytes added
Unregistred error:
{ "kind": "generic",
"error":
"Error while applying operation op1imCEMKFm7bTN9cwX4uoKPb6FjHJDv6NCkWKGVE7srh4XN6UP:\nbranch refused (Error:\n
Counter 565670 already used for contract tz1QLne6uZFxPRdRfJG8msx5RouENpJoRsfP (expected 565671)\n)" }
Fatal error:
transfer simulation failed

To fix, either:

  • Wait for the operation to be included
  • Run tezos-client wait for [Operation Hash] to be included with the dependent operation's Operation Hash before continuing
    • Alternatively, you may run: tz get receipt for [Operation Hash]

Higher than the configured burn cap

This error may occur when --burn-cap is not specified or it's set too low for the current operation:

$ tezos-client --wait none transfer 0 from $ALICE_ADDRESS to $FA12_ADDRESS --arg ..
Waiting for the node to be bootstrapped before injection...
Current head: BLBsvLowXAPf (timestamp: 2019-08-12T15:41:56-00:00, validation: 2019-08-12T15:42:45-00:00)
Node is bootstrapped, ready for injecting operations.
Fatal error:
The operation will burn ꜩ0.021 which is higher than the configured burn cap (0).
Use `--burn-cap 0.021` to emit this operation.

To fix, replace or add --burn-cap with the given recommendation (in this case, --burn-cap 0.021)