Files
gsc/cli/test-data/gsc2_tokens_to_ast/gym.aes
T
Peter Harpending eff77fff6b wip gsc
2026-06-01 21:01:20 -07:00

146 lines
5.2 KiB
Plaintext

/**
* Example gym subscription contract
*
* Copyright (C) 2025, QPQ AG. All Rights Reserved.
*
* Owner can
* - run "tick" to collect monthly fees -> spent to owner
* - unsubscribe anyone
*
* Anyone can
* - subscribe for a period of N months (debit system - all money paid up front)
* - extend their subscription for K months
* - cancel their subscription and withdraw any future money
*
* Owner must issue new contract if he wants to change monthly fee; otherwise
* data structure is too fucking complicated.
*/
include "List.aes"
payable contract Gym =
type pucks = int
type n_months = int
record state = {owner : address,
members : map(address, n_months),
monthly_fee : pucks}
// SFP accessors
entrypoint
get_state : () => state
get_state() = state
stateful entrypoint
put_state : state => unit
put_state(s) = put(s)
entrypoint
is_member : address => bool
is_member(whomst) = Map.member(whomst, state.members)
entrypoint
owner : () => address
owner() = state.owner
entrypoint
members : () => map(address, pucks)
members() = state.members
entrypoint
monthly_fee : () => pucks
monthly_fee() = state.monthly_fee
// actual things
entrypoint
init : (pucks) => state
init(monthly_rate) =
require(monthly_rate >= 0, "monthly rate must be non-negative")
{owner = Call.caller,
members = {},
monthly_fee = monthly_rate}
//------------------------------------------------
// OWNER ENTRYPOINTS: tick/ban
//------------------------------------------------
stateful entrypoint
tick : () => unit
tick() | Call.caller != state.owner =
abort("you are not allowed to do ticks!")
tick() =
// collect fees
// 1 monthly charge per member
let total_charge = state.monthly_fee * Map.size(state.members)
Chain.spend(state.owner, total_charge)
// update members list (clean out everyone with just 1 month left)
let old_members_list : list(address * n_months) = Map.to_list(state.members)
let new_members_list : list(address * n_months) = List.foldl(deduct_month, [], old_members_list)
let new_members : map(address, n_months) = Map.from_list(new_members_list)
let new_state : state = state{members = new_members}
put(new_state)
function
deduct_month : (list(address*n_months), address*n_months) => list(address*n_months)
// n>1 months left -> subtract a month but keep in membership list
deduct_month(acc, (patron, months_left)) | months_left > 1 =
(patron, months_left-1) :: acc
// only 1 month left -> remove from membership list
deduct_month(acc, (patron, 1)) =
acc
stateful entrypoint
ban : (address) => unit
ban(_) | Call.caller != state.owner =
abort("you are not allowed to ban people!")
ban(patron) =
refund(patron)
//------------------------------------------------
// PUBLIC ENTRYPOINTS: subscribe/cancel
//------------------------------------------------
payable stateful entrypoint
subscribe : (n_months) => unit
subscribe(n) | n < 1 =
abort("must subscribe for at least 1 month")
subscribe(n) | Call.value < n*state.monthly_fee =
abort("not enough money to subscribe for that many months!")
subscribe(n) =
let charge: pucks = n * state.monthly_fee
// call will be successful
// refund caller extra money
let extra_money: pucks = Call.value - charge
Chain.spend(Call.caller, extra_money)
// update membership
let old_months : n_months = Map.lookup_default(Call.caller, state.members, 0)
let new_months : n_months = old_months + n
let new_members : map(address, n_months) = state.members{[Call.caller] = new_months}
let new_state : state = state{members = new_members}
put(new_state)
stateful entrypoint
cancel : () => unit
cancel() =
refund(Call.caller)
//------------------------------------------------
// INTERNAL: refund people
//------------------------------------------------
stateful function
refund : (address) => unit
refund(patron) =
let patron_months : n_months =
switch (Map.lookup(patron, state.members))
None => abort("already not a member!")
Some(remaining_months) => remaining_months
let patron_balance : pucks = patron_months * state.monthly_fee
// update membership
let new_members : map(address, n_months) = Map.delete(patron, state.members)
let new_state : state = state{members = new_members}
put(new_state)
// refund balance to patron
Chain.spend(patron, patron_balance)