Raw File
client_proto_contracts.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com>     *)
(*                                                                           *)
(* 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 Protocol
open Alpha_context

module Contract_entity = struct
  include Contract (* t, Compare, encoding *)

  let of_source s =
    Contract.of_b58check s |> Environment.wrap_tzresult
    |> record_trace_eval (fun () -> error_of_fmt "bad contract notation")
    |> Lwt.return

  let to_source s =
    let open Lwt_result_syntax in
    return (Contract.to_b58check s)

  let name = "contract"
end

module Raw_contract_alias = Client_aliases.Alias (Contract_entity)

module Originated_contract_alias = struct
  let find cctxt s =
    let open Lwt_result_syntax in
    let* contract_opt = Raw_contract_alias.find_opt cctxt s in
    match contract_opt with
    | Some (Contract.Originated v) -> return v
    | Some (Implicit _) -> failwith "contract %s is an implicit account" s
    | None -> failwith "no contract named %s" s

  let of_literal s =
    let open Lwt_result_syntax in
    let*! contract_opt = Contract_entity.of_source s in
    match contract_opt with
    | Ok (Contract.Originated v) -> return v
    | Ok (Implicit _) -> failwith "contract %s is an implicit account" s
    | Error _ as err -> Lwt.return err

  let find_destination cctxt s =
    let open Lwt_result_syntax in
    match String.split ~limit:1 ':' s with
    | ["alias"; alias] -> find cctxt alias
    | ["text"; text] -> of_literal text
    | _ -> (
        let*! contract_hash_opt = of_literal s in
        match contract_hash_opt with
        | Ok _ as ok_v -> Lwt.return ok_v
        | Error k_errs -> (
            let*! contract_hash_opt = find cctxt s in
            Lwt.return
            @@
            match contract_hash_opt with
            | Ok _ as ok_v -> ok_v
            | Error c_errs -> Error (c_errs @ k_errs)))

  let destination_parameter () =
    Tezos_clic.parameter
      ~autocomplete:Raw_contract_alias.autocomplete
      find_destination

  let destination_param ?(name = "dst") ?(desc = "destination contract") next =
    let desc =
      String.concat
        "\n"
        [
          desc;
          "Can be a literal or an alias (autodetected in order).\n\
           Use 'text:<literal>' or 'alias:<name>' to force.";
        ]
    in
    Tezos_clic.param ~name ~desc (destination_parameter ()) next

  let destination_arg ?(name = "dst") ?(doc = "destination contract") () =
    let doc =
      String.concat
        "\n"
        [
          doc;
          "Can be a literal or an alias (autodetected in order).\n\
           Use 'text:<literal>' or 'alias:<name>' to force.";
        ]
    in
    Tezos_clic.arg ~long:name ~doc ~placeholder:name (destination_parameter ())
end

module Contract_alias = struct
  let find cctxt s =
    let open Lwt_result_syntax in
    let* contract_opt = Raw_contract_alias.find_opt cctxt s in
    match contract_opt with
    | Some v -> return v
    | None -> (
        let* pkh_opt = Client_keys.Public_key_hash.find_opt cctxt s in
        match pkh_opt with
        | Some v -> return (Contract.Implicit v)
        | None -> failwith "no contract or key named %s" s)

  let find_key cctxt name =
    let open Lwt_result_syntax in
    let* v = Client_keys.Public_key_hash.find cctxt name in
    return (Contract.Implicit v)

  let rev_find cctxt (c : Contract.t) =
    let open Lwt_result_syntax in
    match c with
    | Implicit hash -> (
        let* str_opt = Client_keys.Public_key_hash.rev_find cctxt hash in
        match str_opt with
        | Some name -> return_some ("key:" ^ name)
        | None -> return_none)
    | Originated _ -> Raw_contract_alias.rev_find cctxt c

  let get_contract cctxt s =
    match String.split ~limit:1 ':' s with
    | ["key"; key] -> find_key cctxt key
    | _ -> find cctxt s

  let autocomplete cctxt =
    let open Lwt_result_syntax in
    let* keys = Client_keys.Public_key_hash.autocomplete cctxt in
    let* contracts = Raw_contract_alias.autocomplete cctxt in
    return (List.map (( ^ ) "key:") keys @ contracts)

  let alias_param ?(name = "name") ?(desc = "existing contract alias") next =
    let desc =
      desc ^ "\n"
      ^ "Can be a contract alias or a key alias (autodetected in order).\n\
         Use 'key:<name>' to force the later."
    in
    Tezos_clic.(param ~name ~desc (parameter ~autocomplete get_contract) next)

  let find_destination cctxt s =
    let open Lwt_result_syntax in
    match String.split ~limit:1 ':' s with
    | ["alias"; alias] -> find cctxt alias
    | ["key"; text] ->
        let* v = Client_keys.Public_key_hash.find cctxt text in
        return (Contract.Implicit v)
    | ["text"; text] -> Contract_entity.of_source text
    | _ -> (
        let*! contract_opt = Contract_entity.of_source s in
        match contract_opt with
        | Ok v -> return v
        | Error c_errs -> (
            let*! contract_opt = find cctxt s in
            match contract_opt with
            | Ok v -> return v
            | Error k_errs -> Lwt.return_error (c_errs @ k_errs)))

  let destination_parameter () =
    let open Lwt_result_syntax in
    Tezos_clic.parameter
      ~autocomplete:(fun cctxt ->
        let* list1 = autocomplete cctxt in
        let* list2 = Client_keys.Public_key_hash.autocomplete cctxt in
        return (list1 @ list2))
      find_destination

  let destination_param ?(name = "dst") ?(desc = "destination contract") next =
    let desc =
      String.concat
        "\n"
        [
          desc;
          "Can be a literal, an alias, or a key (autodetected in order).\n\
           Use 'text:<literal>', 'alias:<name>', 'key:<key>' to force.";
        ]
    in
    Tezos_clic.param ~name ~desc (destination_parameter ()) next

  let destination_arg ?(name = "dst") ?(doc = "destination contract") () =
    let doc =
      String.concat
        "\n"
        [
          doc;
          "Can be a literal, an alias, or a key (autodetected in order).\n\
           Use 'text:<literal>', 'alias:<name>', 'key:<key>' to force.";
        ]
    in
    Tezos_clic.arg ~long:name ~doc ~placeholder:name (destination_parameter ())

  let name cctxt contract =
    let open Lwt_result_syntax in
    let* str_opt = rev_find cctxt contract in
    match str_opt with
    | None -> return (Contract.to_b58check contract)
    | Some name -> return name
end

let list_contracts cctxt =
  let open Lwt_result_syntax in
  let* raw_contracts = Raw_contract_alias.load cctxt in
  let contracts = List.map (fun (n, v) -> ("", n, v)) raw_contracts in
  let* keys = Client_keys.Public_key_hash.load cctxt in
  (* List accounts (implicit contracts of identities) *)
  let* accounts =
    List.map_es
      (fun (n, v) ->
        let* mem = Raw_contract_alias.mem cctxt n in
        let p = if mem then "key:" else "" in
        let v' = Contract.Implicit v in
        return (p, n, v'))
      keys
  in
  return (contracts @ accounts)

let get_delegate cctxt ~chain ~block source =
  Alpha_services.Contract.delegate_opt cctxt (chain, block) source
back to top