Raw File
ticket_transfer.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com>                *)
(* Copyright (c) 2022 Margiold <contact@marigold.dev>                        *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

open Alpha_context

let parse_ticket ~consume_deserialization_gas ~ticketer ~contents ~ty ctxt =
  let open Lwt_result_syntax in
  let*? ty, ctxt =
    Script.force_decode_in_context ~consume_deserialization_gas ctxt ty
  in
  let*? contents, ctxt =
    Script.force_decode_in_context ~consume_deserialization_gas ctxt contents
  in
  let*? Ex_comparable_ty contents_type, ctxt =
    Script_ir_translator.parse_comparable_ty ctxt (Micheline.root ty)
  in
  let* contents, ctxt =
    Script_ir_translator.parse_comparable_data
      ctxt
      contents_type
      (Micheline.root contents)
  in
  let token = Ticket_token.Ex_token {ticketer; contents_type; contents} in
  return (ctxt, token)

let parse_ticket_and_operation ~consume_deserialization_gas ~ticketer ~contents
    ~ty ~sender ~destination ~entrypoint ~amount ctxt =
  let open Lwt_result_syntax in
  let* ( ctxt,
         (Ticket_token.Ex_token {contents; contents_type; ticketer} as token) )
      =
    parse_ticket ~consume_deserialization_gas ~ticketer ~contents ~ty ctxt
  in
  let*? ticket_ty =
    Script_typed_ir.ticket_t Micheline.dummy_location contents_type
  in
  let ticket = Script_typed_ir.{ticketer; contents; amount} in
  let* unparsed_parameters, ctxt =
    Script_ir_translator.unparse_data ctxt Optimized ticket_ty ticket
  in
  let*? ctxt, nonce = fresh_internal_nonce ctxt in
  let op =
    Script_typed_ir.Internal_operation
      {
        sender;
        nonce;
        operation =
          Transaction_to_smart_contract
            {
              amount = Tez.zero;
              unparsed_parameters;
              destination;
              entrypoint;
              location = Micheline.dummy_location;
              parameters_ty = ticket_ty;
              parameters = ticket;
            };
      }
  in
  return (ctxt, token, op)

let transfer_ticket_with_hashes ctxt ~sender_hash ~dst_hash
    (qty : Ticket_amount.t) =
  let qty = Script_int.(to_zint (qty :> n num)) in
  let open Lwt_result_syntax in
  let* sender_storage_diff, ctxt =
    Ticket_balance.adjust_balance ctxt sender_hash ~delta:(Z.neg qty)
  in
  let* dst_storage_diff, ctxt =
    Ticket_balance.adjust_balance ctxt dst_hash ~delta:qty
  in
  let* diff, ctxt =
    Ticket_balance.adjust_storage_space
      ctxt
      ~storage_diff:(Z.add sender_storage_diff dst_storage_diff)
  in
  return (ctxt, diff)

let transfer_ticket ctxt ~sender ~dst ex_token qty =
  let open Lwt_result_syntax in
  let* sender_hash, ctxt =
    Ticket_balance_key.of_ex_token ctxt ~owner:sender ex_token
  in
  let* dst_hash, ctxt =
    Ticket_balance_key.of_ex_token ctxt ~owner:dst ex_token
  in
  transfer_ticket_with_hashes ctxt ~sender_hash ~dst_hash qty

let transfer_tickets_in_parameters ctxt parameter parameter_ty ~source ~dst =
  let open Lwt_result_syntax in
  let*? has_tickets, ctxt = Ticket_scanner.type_has_tickets ctxt parameter_ty in
  let* tickets, ctxt =
    Ticket_scanner.tickets_of_value
      ~include_lazy:true
      ctxt
      has_tickets
      parameter
  in
  let* ctxt, ticket_receipt, paid_storage_diff =
    List.fold_left_es
      (fun (ctxt, ticket_receipt_acc, paid_storage_diff_acc) ticket ->
        let ticket_token, amount =
          Ticket_scanner.ex_token_and_amount_of_ex_ticket ticket
        in
        let* ctxt, paid_storage_diff =
          transfer_ticket ctxt ~sender:source ~dst ticket_token amount
        in
        let* ticket_token, ctxt =
          Ticket_token_unparser.unparse ctxt ticket_token
        in
        let amount = Script_int.(to_zint (amount :> n num)) in
        let ticket_receipt_item =
          Ticket_receipt.
            {
              ticket_token;
              updates = [{account = source; amount = Z.neg amount}];
              (* We only handle negation from the [source] in this receipt.
                 The addition to [dst] will be taken into account in the receipts
                 generated from the subsequent contract execution. *)
            }
        in
        return
          ( ctxt,
            ticket_receipt_item :: ticket_receipt_acc,
            Z.add paid_storage_diff_acc paid_storage_diff ))
      (ctxt, [], Z.zero)
      tickets
  in
  return (ctxt, ticket_receipt, paid_storage_diff)
back to top