A better Ticket transfer UX

By Gauthier Sebille

Marigold, Nomadic Labs and TriliTech are pleased to introduce the possibility to transfer tickets from user account to Smart Contracts and the new Michelson Ticket constructor!

This new transfer feature and this new Michelson Ticket constructor will be available in protocol Q.

A bit of history

A few months ago, we have posted a proposal to improve the Ticket transfer UX.

We propose to use the transaction operation instead of the transfer_ticket one and also to introduce a new Michelson Ticket constructor.

  1. Switching from transfer_ticket to transaction allowing users to transfer tickets along with any required data from the implicit account to the smart contract directly.

  2. Introducing new Michelson Ticket constructor solves the inability to differentiate tickets from nested pairs just by looking at the transaction payload

Previously, tickets were represented as a nested Pair, like:

Pair %ticketer (Pair %contents %amount)

In our proposal, Tickets will now be represented as:

Ticket %ticketer %contents_type %contents %amount 

This is way more explicit for users that they are dealing with Tickets! Moreover, we have added a new field contents_type, to ensure that the provided contents match the ticket type.

A detailed example

What is better than a detailed example to explain the newly introduced Michelson Ticket constructor?

Using the octez-client

Thanks to the normalize data command of the octez-client we can see how the introduction of the new Ticket constructor works:

First, let’s use the --unparsing-mode Optimized_legacy which uses the “legacy” nested Pair representation for Tickets:

$ octez-client normalize data 'Ticket "KT1FHqsvc7vRS3u54L66DdMX4gb6QKqxJ1JW" string "Marigold" 1' of type 'ticket string' --unparsing-mode Optimized_legacy

Warning:

                 This is NOT the Tezos Mainnet.

           Do NOT use your fundraiser keys on this network.

Pair 0x01499568d6d97798fac71c8e5b810e1cc3062a5d9300 (Pair "Marigold" 1)

As we can see, the ticket is normalized as Pair %ticketer (Pair %contents %amount)

Let’s run the exact same command but with the --unparsing-mode Optimized which uses the new Michelson Ticket constructor!

$ octez-client normalize data 'Ticket "KT1FHqsvc7vRS3u54L66DdMX4gb6QKqxJ1JW" string "Marigold" 1' of type 'ticket string' --unparsing-mode Optimized

Warning:

                 This is NOT the Tezos Mainnet.

           Do NOT use your fundraiser keys on this network.

Ticket 0x01499568d6d97798fac71c8e5b810e1cc3062a5d9300 string "Marigold" 1

Thank you contents_type!

In the initial part we introcuded the contents_type field. Let’s see how it prevents any error whilst using the Ticket constructor:

$ octez-client normalize data 'Ticket "KT1FHqsvc7vRS3u54L66DdMX4gb6QKqxJ1JW" string "Marigold" 1' of type 'ticket unit' --unparsing-mode Optimized

Warning:

                 This is NOT the Tezos Mainnet.

           Do NOT use your fundraiser keys on this network.

At (unshown) location 0, Type unit is not compatible with type string.
At (unshown) location 0, Type unit is not compatible with type string.
Fatal error:
  ill-typed data expression

In the previous command, we try to normalize a Ticket "KT1FHqsvc7vRS3u54L66DdMX4gb6QKqxJ1JW" string "Marigold" 1 which is obviously a ticket string, but we say the node that its type is ticket unit. We are blocked.

Let’s see if we try to normalize Ticket "KT1FHqsvc7vRS3u54L66DdMX4gb6QKqxJ1JW" unit "Marigold" 1 saying it is a ticket unit:

$ octez-client normalize data 'Ticket "KT1FHqsvc7vRS3u54L66DdMX4gb6QKqxJ1JW" unit "Marigold" 1' of type 'ticket unit' --unparsing-mode Optimized

Warning:

                 This is NOT the Tezos Mainnet.

           Do NOT use your fundraiser keys on this network.

At (unshown) location 3, value "Marigold" is invalid for type unit.
At (unshown) location 3, unexpected string, only a primitive
can be used here.
Fatal error:
  ill-typed data expression

End to end test scenario

To illustrate the new UX, let’s explain an end-to-end test scenario we have in the Tezos codebase:

Tests that an implicit account can send a single ticket to originated using the [Transfer] manager operation.

In details, the test scenario does:

  1. Setup: The test initializes by setting up the Tezos node and client, enabling direct ticket spending to facilitate ticket transfers.

  2. Ticket Deposit: Tickets are deposited into an implicit account. This involves originating a contract referred to as ticketer, which serves as the destination for ticket deposits.

  3. Ticket Transfer: The test ensures that the implicit account holds the deposited ticket. This step validates the successful transfer of tickets to the implicit account.

  4. Originating Contract: A new contract, known as bag, is originated on the Tezos blockchain. This contract is designed to store tickets that are sent to its designated entry point called save.

  5. Ticket Transfer to Originated Contract: The test executes a transfer operation to move tickets from the implicit account to the newly originated contract (bag). This operation is facilitated by the Tezos transaction manager.

  6. Assertion: Assertions are performed to validate the ticket balances after the transfer. The test ensures that the ticket balance in the implicit account is reduced to zero while confirming that the bag contract now holds the transferred ticket.

Detailed commands run for this test scenario

Here, we will only focus on the commands useful to transfer the ticket and verify the balance.

As explained in the previous section, before the tranfer, we have to

  1. setup the node,
  2. originate the contract,
  3. deposit the initial ticket.

The initial deposit:

$ octez-client --endpoint http://127.0.0.1:62659 \ 
--base-dir /tmp/tezt-36584/1/client1 \ 
get ticket balance for bootstrap1 \
with ticketer KT1XU7JNShJpKXuutHacmsQXdd1EmfXozEb3 \
and type string and content '"Ticket"'

1

Now, we can tranfer the ticket from bootstrap1 (implicit account) to KT198aGhEPaTusD75SSD8qVpQGzkp3tYqacq (smart contract)!

Transfer the ticket!
$ octez-client --endpoint http://127.0.0.1:62659 \ 
--base-dir /tmp/tezt-36584/1/client1 --wait none \
transfer 0 from bootstrap1 to KT198aGhEPaTusD75SSD8qVpQGzkp3tYqacq \ 
--burn-cap 1 --entrypoint save \
--arg 'Ticket "KT1XU7JNShJpKXuutHacmsQXdd1EmfXozEb3" string "Ticket" 1'
Check the balance!

We have two balances to check to ensure the transfer had been successfully and well done:

  1. bootstrap1 must have exactly 0 ticket with content "Ticket",
  2. KT198aGhEPaTusD75SSD8qVpQGzkp3tYqacq must have exactly one ticket with content "Ticket"
$ octez-client --endpoint http://127.0.0.1:62659 \
--base-dir /tmp/tezt-36584/1/client1 \
get ticket balance for bootstrap1 \
with ticketer KT1XU7JNShJpKXuutHacmsQXdd1EmfXozEb3 \
and type string and content '"Ticket"'

0

:tada: First balance is perfect, let’s see the second one…

$ octez-client --endpoint http://127.0.0.1:62659 \
--base-dir /tmp/tezt-36584/1/client1 \
get ticket balance for KT198aGhEPaTusD75SSD8qVpQGzkp3tYqacq \
with ticketer KT1XU7JNShJpKXuutHacmsQXdd1EmfXozEb3 \
and type string and content '"Ticket"'

1

:tada: Yes!!!

Conclusion

In this blog post, we have introduced two new features for tickets:

  1. Transfer tickets from user account to smart contract
  2. New Michelson Ticket constructor

Thanks to these two new features, a whole new era is ready for tickets!

7 Likes