1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// Copyright Judica, Inc 2021
//
// This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! Put Contract
use super::*;
use std::sync::Arc;
/// Put Contracts pay out as the price goes down.
pub struct Put<'a> {
    /// The # of units
    amount: Amount,
    /// The strike with ONE_UNIT precision (bitcoin per symbol)
    strike_x_one_unit: u64,
    operator_api: &'a dyn apis::OperatorApi,
    user_api: &'a dyn apis::UserApi,
    symbol: Symbol,
    /// whether we are buying or selling the put
    buying: bool,
    ctx: Context,
}

const ONE_UNIT: u64 = 10_000;
impl<'a> TryFrom<Put<'a>> for GenericBetArguments<'a> {
    type Error = CompilationError;
    fn try_from(mut v: Put<'a>) -> Result<Self, Self::Error> {
        let key = v.operator_api.get_key();
        let user = v.user_api.get_key();
        let mut outcomes = vec![];
        let strike = v.strike_x_one_unit;
        let max_amount_bitcoin = v.amount * strike;
        // Increment 1 dollar per step
        let mut strike_ctx = v.ctx.derive_str(Arc::new("strike".into()))?;
        for price in (0..=strike).step_by(ONE_UNIT as usize) {
            let mut profit = Amount::from_sat(strike) - Amount::from_sat(price);
            let mut refund = max_amount_bitcoin - profit;
            if v.buying {
                std::mem::swap(&mut profit, &mut refund);
            }
            outcomes.push((
                price as i64,
                strike_ctx
                    .derive_num(price as u64)?
                    .template()
                    .add_output(profit, &v.user_api.receive_payment(profit), None)?
                    .add_output(refund, &v.operator_api.receive_payment(refund), None)?
                    .into(),
            ));
        }
        // Now that the schedule is constructed, build a contract
        Ok(GenericBetArguments {
            // must send max amount for the contract to be valid!
            amount: max_amount_bitcoin,
            outcomes,
            oracle: v.operator_api.get_oracle(),
            cooperate: Clause::And(vec![key, user]),
            symbol: v.symbol,
        })
    }
}

impl<'a> TryFrom<Put<'a>> for GenericBet {
    type Error = CompilationError;
    fn try_from(v: Put<'a>) -> Result<Self, Self::Error> {
        Ok(GenericBetArguments::try_from(v)?.into())
    }
}