Contract - OLD-FPC
Address:
ID:

SUMMARY
FULL NAMEFixed Payout Contract - AAPL 2018-11-09 - $200
STATUSInactive
TRANSACTIONS0
LAST ACTIVATION BLOCK45,412
BALANCES
ASSETAMOUNT
11
(* NAME_START:Fixed Payout Contract - AAPL 2018-11-09 - $200:NAME_END *)

open Zen.Base
open Zen.Cost
open Zen.Data
open Zen.Types

module Asset = Zen.Asset
module CR = Zen.ContractResult
module Dict  = Zen.Dictionary
module Hash = Zen.Hash
module RT = Zen.ResultT
module TX = Zen.TxSkeleton
module U64 = FStar.UInt64
module Wallet = Zen.Wallet

val oracleContractID: s:string {FStar.String.length s = 72}
let oracleContractID = "00000000ca055cc0af4d25ea1c8bbbf41444aadd68a168558397516b2f64727d87e72f97"

val ticker: s: string{FStar.String.length s <= 4}
let ticker = "AAPL"

let strike = 200000UL // USD price multiplied by 1000

let time = 1541797200UL

type messageParams = {
    price: U64.t;
    returnAddress: lock
}

val getReturnAddress: option (Dict.t data) -> option lock `cost` 71
let getReturnAddress dict = // 5
    dict >!= Dict.tryFind "returnAddress" // 64
         >?= tryLock // 2

val getPrice: option (Dict.t data) -> option U64.t `cost` 71
let getPrice dict = // 5
    dict >!= Dict.tryFind "Price" // 64
         >?= tryU64 // 2

val getParams: option (Dict.t data) -> result messageParams `cost` 161
let getParams dict = // 19
    let! msgPrice = getPrice dict in // 71
    let! returnAddress = getReturnAddress dict in // 71
    match msgPrice, returnAddress with
    | Some msgPrice, Some returnAddress ->
        RT.ok ({ price=msgPrice; returnAddress=returnAddress })
    | None, _ ->
        RT.failw "Could not parse Price from messageBody"
    | _, None ->
        RT.failw "Could not parse returnAddress from messageBody"

val hashData: U64.t -> hash `cost` 812
let hashData price = let open Hash in // 36
    let! timeHash = updateU64 time empty // 48
                    >>= finalize in // 20
    let! tickerHash = begin updateString ticker empty
                      |> inc (6 * (4 - FStar.String.length ticker)) //24
                      >>= finalize //20
                      end <: hash `cost` 44 in //20
    let! priceHash = updateU64 price empty // 48
                     >>= finalize in // 20

    updateHash timeHash empty // 192
    >>= updateHash tickerHash // 192
    >>= updateHash priceHash // 192
    >>= finalize // 20

val buyTx: txSkeleton -> contractId -> lock -> U64.t -> CR.t `cost` 483
let buyTx tx contractID returnAddress amount = // 32
    let! bullToken = Asset.fromSubtypeString contractID "Bull" in // 64
    let! bearToken = Asset.fromSubtypeString contractID "Bear" in // 64
    // lock all available ZP to this contract
    TX.lockToContract Asset.zenAsset amount contractID tx // 64
    // mint an equivalent amount of bull and bear tokens
    >>= TX.mint amount bullToken // 64
    >>= TX.mint amount bearToken // 64
    // lock bull and bear tokens to the returnAddress
    >>= TX.lockToAddress bullToken amount returnAddress // 64
    >>= TX.lockToAddress bearToken amount returnAddress // 64
    >>= CR.ofTxSkel // 3

val buy: txSkeleton -> contractId -> messageBody: option data -> CR.t `cost` 718
let buy tx contractID messageBody = // 32
    let! returnAddress = messageBody >!= tryDict // 4
                                     >>= getReturnAddress in // 71
    let! oracleContractID = Zen.ContractId.parse oracleContractID in //64
    let! amount = TX.getAvailableTokens Asset.zenAsset tx in // 64
    match returnAddress, oracleContractID with
    | Some returnAddress, Some oracleContractID ->
        if amount <> 0UL then
            buyTx tx contractID returnAddress amount // 483
        else
            RT.incFailw 483 "Cannot buy with 0ZP in txSkeleton"
    | None, _ ->
        RT.incFailw 483 "Could not parse returnAddress from messageBody"
    | _, None ->
        RT.incFailw 483 "Could not parse oracleContractID. This contract will be unusable. Please redeploy this contract with a valid oracleContractID."

val oracleMessage: U64.t -> Dict.t data -> contractId -> message `cost` 892
let oracleMessage price dict oracleContractID = // 16
    let! hash = hashData price in // 812
    let! dict = Dict.add "Hash" (Hash hash) dict in // 64
    ret ({ recipient=oracleContractID;
           command="Verify";
           body=Some(Collection (Dict dict)) })

// invokes the oracle to validate inclusion of the message
val invokeOracle:
    U64.t
    -> option (Dict.t data)
    -> option txSkeleton
    -> CR.t `cost` 987
let invokeOracle price dict tx = // 31
    let! oracleContractID = Zen.ContractId.parse oracleContractID in // 64
    match dict, oracleContractID, tx with
    | Some dict, Some oracleContractID, Some tx ->
        let! msg = oracleMessage price dict oracleContractID in // 892
        RT.ok ({ tx=tx; message=Some msg; state=NoChange })
    | None, _, _ ->
        RT.incFailw 892 "Something went wrong! messageBody should not be empty"
    | _, None, _ ->
        RT.incFailw 892 "Something went wrong! could not parse oracleContractID"
    | _, _, None ->
        RT.incFailw 892 "Could not construct tx from wallet"

val redeemTx:
    asset
    -> txSkeleton
    -> contractId
    -> messageParams
    -> option (Dict.t data)
    -> w: wallet
    -> CR.t `cost` (Wallet.size w * 128 + 1395)
let redeemTx contractAsset tx contractID messageParams dict wallet = // 24
    // amount of the contract asset received
    let! amount = TX.getAvailableTokens contractAsset tx in // 64
    // destroy the contract asset received
    begin TX.destroy amount contractAsset tx // 64
    // send the same amount of ZP to the returnAddress
    >>= TX.lockToAddress Asset.zenAsset amount messageParams.returnAddress // 64
    >>= TX.fromWallet Asset.zenAsset amount contractID wallet // 128 * size wallet + 192
    <: (option txSkeleton `cost` (Wallet.size wallet * 128 + 320))
    end
    // check with the oracle
    >>= invokeOracle messageParams.price dict // 987

val redeem:
    txSkeleton
    -> contractId
    -> messageBody: option data
    -> wallet: wallet
    -> CR.t `cost` (Wallet.size wallet * 128 + 1652)
let redeem tx contractID messageBody wallet = let open U64 in // 28
    let! dict = messageBody >!= tryDict in // 4
    let! messageParams = getParams dict in // 161
    match messageParams with
    | OK messageParams -> begin
        let! winningAsset = begin //64
            if messageParams.price >=^ strike
            then Asset.fromSubtypeString contractID "Bull"
            else Asset.fromSubtypeString contractID "Bear"
            end in
        redeemTx winningAsset tx contractID messageParams dict wallet // Wallet.size w * 128 + 1395
        end <: CR.t `cost` (Wallet.size wallet * 128 + 1459)
    | ERR msg ->
        RT.incFailw (Wallet.size wallet * 128 + 1459) msg
    | _ ->
        RT.incFailw (Wallet.size wallet * 128 + 1459) "Something went wrong! unexpected error"

let main (tx: txSkeleton) _ (contractID: contractId) (command: string) _
         (messageBody: option data) (wallet: wallet) _
         : CR.t `cost` ( match command with
                         | "Buy" -> 725
                         | "Redeem" -> Wallet.size wallet * 128 + 1659
                         | _ -> 7 ) = // 7
    match command with
    | "Buy" ->
        buy tx contractID messageBody
        <: (CR.t `cost` ( match command with
                          | "Buy" -> 718
                          | "Redeem" -> Wallet.size wallet * 128 + 1652
                          | _ -> 0 ))
    | "Redeem" ->
        redeem tx contractID messageBody wallet
    | _ ->
        RT.failw "Invalid command"

let cf _ _ (command: string) _ _ (wallet: wallet) _ : nat `cost` 9 = // 9
    match command with
    | "Buy" -> ret 725
    | "Redeem" -> ret (Wallet.size wallet * 128 + 1659)
    | _ -> ret 7