123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322(*****************************************************************************)(* *)(* Open Source License *)(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)(* Copyright (c) 2020-2022 Nomadic Labs <contact@nomadic-labs.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. *)(* *)(*****************************************************************************)letid="tez"letname="mutez"openCompare.Int64(* invariant: positive *)typerepr=ttypet=Tez_tagofrepr[@@ocaml.unboxed]letwrapt=Tez_tagt[@@ocaml.inlinealways]typeerror+=|Addition_overflowoft*t(* `Temporary *)|Subtraction_underflowoft*t(* `Temporary *)|Multiplication_overflowoft*int64(* `Temporary *)|Negative_multiplicatoroft*int64(* `Temporary *)|Invalid_divisoroft*int64(* `Temporary *)letzero=Tez_tag0L(* all other constant are defined from the value of one micro tez *)letone_mutez=Tez_tag1Lletmax_mutez=Tez_tagInt64.max_intletmul_int(Tez_tagtez)i=Tez_tag(Int64.multezi)letone_cent=mul_intone_mutez10_000Lletfifty_cents=mul_intone_cent50L(* 1 tez = 100 cents = 1_000_000 mutez *)letone=mul_intone_cent100Lletof_strings=lettriplets=function|hd::tl->letlen=String.lengthhdinCompare.Int.(len<=3&&len>0&&List.for_all(funs->String.lengths=3)tl)|[]->falseinletintegerss=triplets(String.split_on_char','s)inletdecimalss=letl=String.split_on_char','sinifCompare.List_length_with.(l>2)thenfalseelsetriplets(List.revl)inletparseleftright=letremove_commass=String.concat""(String.split_on_char','s)inletpad_to_sixs=letlen=String.lengthsinString.init6(funi->ifCompare.Int.(i<len)thens.[i]else'0')inletprepared=remove_commasleft^pad_to_six(remove_commasright)inOption.mapwrap(Int64.of_string_optprepared)inmatchString.split_on_char'.'swith|[left;right]->ifString.containss','thenifintegersleft&&decimalsrightthenparseleftrightelseNoneelseifCompare.Int.(String.lengthright>0)&&Compare.Int.(String.lengthright<=6)thenparseleftrightelseNone|[left]->if(not(String.containss','))||integersleftthenparseleft""elseNone|_->Noneletppppf(Tez_tagamount)=letmult_int=1_000_000Linletrecleftppfamount=letd,r=(Int64.divamount1000L,Int64.remamount1000L)inifCompare.Int64.(d>0L)thenFormat.fprintfppf"%a%03Ld"leftdrelseFormat.fprintfppf"%Ld"rinletrightppfamount=lettripletppfv=ifCompare.Int.(vmod10>0)thenFormat.fprintfppf"%03d"velseifCompare.Int.(vmod100>0)thenFormat.fprintfppf"%02d"(v/10)elseFormat.fprintfppf"%d"(v/100)inlethi,lo=(amount/1000,amountmod1000)inifCompare.Int.(lo=0)thenFormat.fprintfppf"%a"triplethielseFormat.fprintfppf"%03d%a"hitripletloinletints,decs=(Int64.divamountmult_int,Int64.(to_int(remamountmult_int)))inleftppfints;ifCompare.Int.(decs>0)thenFormat.fprintfppf".%a"rightdecsletto_stringt=Format.asprintf"%a"pptlet(-?)tez1tez2=letopenResult_syntaxinlet(Tez_tagt1)=tez1inlet(Tez_tagt2)=tez2inift2<=t1thenreturn(Tez_tag(Int64.subt1t2))elsetzfail(Subtraction_underflow(tez1,tez2))letsub_opt(Tez_tagt1)(Tez_tagt2)=ift2<=t1thenSome(Tez_tag(Int64.subt1t2))elseNonelet(+?)tez1tez2=letopenResult_syntaxinlet(Tez_tagt1)=tez1inlet(Tez_tagt2)=tez2inlett=Int64.addt1t2inift<t1thentzfail(Addition_overflow(tez1,tez2))elsereturn(Tez_tagt)let(*?)tezm=letopenResult_syntaxinlet(Tez_tagt)=tezinifm<0Lthentzfail(Negative_multiplicator(tez,m))elseifm=0Lthenreturn(Tez_tag0L)elseift>Int64.(divmax_intm)thentzfail(Multiplication_overflow(tez,m))elsereturn(Tez_tag(Int64.multm))let(/?)tezd=letopenResult_syntaxinlet(Tez_tagt)=tezinifd<=0Lthentzfail(Invalid_divisor(tez,d))elsereturn(Tez_tag(Int64.divtd))letdiv2(Tez_tagt)=Tez_tag(Int64.divt2L)letmul_exntm=matcht*?Int64.of_intmwithOkv->v|Error_->invalid_arg"mul_exn"letdiv_exntd=matcht/?Int64.of_intdwithOkv->v|Error_->invalid_arg"div_exn"letmul_ratio~roundingtez~num~den=letopenResult_syntaxinlet(Tez_tagt)=tezinifnum<0Lthentzfail(Negative_multiplicator(tez,num))elseifden<=0Lthentzfail(Invalid_divisor(tez,den))elseifnum=0Lthenreturnzeroelseletnumerator=Z.(mul(of_int64t)(of_int64num))inletdenominator=Z.of_int64deninletz=matchroundingwith|`Down->Z.divnumeratordenominator|`Up->Z.cdivnumeratordenominatorinifZ.fits_int64zthenreturn(Tez_tag(Z.to_int64z))elsetzfail(Multiplication_overflow(tez,num))letmul_percentage~rounding=letz100=Z.of_int100infun(Tez_tagt)(percentage:Int_percentage.t)->(* Guaranteed to produce no errors by the invariants on {!Int_percentage.t}. *)letdiv'=matchroundingwith`Down->Z.div|`Up->Z.cdivinTez_tagZ.(to_int64(div'(mul(of_int64t)(of_int(percentage:>int)))z100))letof_mutezt=ift<0LthenNoneelseSome(Tez_tagt)letof_mutez_exnx=matchof_mutezxwithNone->invalid_arg"Tez.of_mutez"|Somev->vletto_mutez(Tez_tagt)=tletencoding=letopenData_encodinginletdecode(Tez_tagt)=Z.of_int64tinletencode=Json.wrap_error(funi->Tez_tag(Z.to_int64i))inData_encoding.defname(check_size10(convdecodeencoden))letbalance_update_encoding=letopenData_encodinginconv(function|`Creditedv->to_mutezv|`Debitedv->Int64.neg(to_mutezv))(Json.wrap_error@@funv->ifCompare.Int64.(v<0L)then`Debited(Tez_tag(Int64.negv))else`Credited(Tez_tagv))int64let()=letopenData_encodinginregister_error_kind`Temporary~id:(id^".addition_overflow")~title:("Overflowing "^id^" addition")~pp:(funppf(opa,opb)->Format.fprintfppf"Overflowing addition of %a %s and %a %s"ppopaidppopbid)~description:("An addition of two "^id^" amounts overflowed")(obj1(req"amounts"(tup2encodingencoding)))(functionAddition_overflow(a,b)->Some(a,b)|_->None)(fun(a,b)->Addition_overflow(a,b));register_error_kind`Temporary~id:(id^".subtraction_underflow")~title:("Underflowing "^id^" subtraction")~pp:(funppf(opa,opb)->Format.fprintfppf"Underflowing subtraction of %a %s and %a %s"ppopaidppopbid)~description:("A subtraction of two "^id^" amounts underflowed (i.e., would have led to a negative amount)")(obj1(req"amounts"(tup2encodingencoding)))(functionSubtraction_underflow(a,b)->Some(a,b)|_->None)(fun(a,b)->Subtraction_underflow(a,b));register_error_kind`Temporary~id:(id^".multiplication_overflow")~title:("Overflowing "^id^" multiplication")~pp:(funppf(opa,opb)->Format.fprintfppf"Overflowing multiplication of %a %s and %Ld"ppopaidopb)~description:("A multiplication of a "^id^" amount by an integer overflowed")(obj2(req"amount"encoding)(req"multiplicator"int64))(functionMultiplication_overflow(a,b)->Some(a,b)|_->None)(fun(a,b)->Multiplication_overflow(a,b));register_error_kind`Temporary~id:(id^".negative_multiplicator")~title:("Negative "^id^" multiplicator")~pp:(funppf(opa,opb)->Format.fprintfppf"Multiplication of %a %s by negative integer %Ld"ppopaidopb)~description:("Multiplication of a "^id^" amount by a negative integer")(obj2(req"amount"encoding)(req"multiplicator"int64))(functionNegative_multiplicator(a,b)->Some(a,b)|_->None)(fun(a,b)->Negative_multiplicator(a,b));register_error_kind`Temporary~id:(id^".invalid_divisor")~title:("Invalid "^id^" divisor")~pp:(funppf(opa,opb)->Format.fprintfppf"Division of %a %s by non positive integer %Ld"ppopaidopb)~description:("Multiplication of a "^id^" amount by a non positive integer")(obj2(req"amount"encoding)(req"divisor"int64))(functionInvalid_divisor(a,b)->Some(a,b)|_->None)(fun(a,b)->Invalid_divisor(a,b))letcompare(Tez_tagx)(Tez_tagy)=comparexylet(=)(Tez_tagx)(Tez_tagy)=x=ylet(<>)(Tez_tagx)(Tez_tagy)=x<>ylet(<)(Tez_tagx)(Tez_tagy)=x<ylet(>)(Tez_tagx)(Tez_tagy)=x>ylet(<=)(Tez_tagx)(Tez_tagy)=x<=ylet(>=)(Tez_tagx)(Tez_tagy)=x>=yletequal(Tez_tagx)(Tez_tagy)=equalxyletmax(Tez_tagx)(Tez_tagy)=Tez_tag(maxxy)letmin(Tez_tagx)(Tez_tagy)=Tez_tag(minxy)