contract: ct_2BUHXzVNrELMiWKQ64saw1fvwir7wV5rnh5b28AK8Hf8Epr6qV

Contract source code
The code as it had been recorded in the contract create transaction. This is not being validated if it matches the bytecode.
include "List.aes"
include "Pair.aes"
namespace Worker =
  type package_code = string

  record package = {
    daily_cap : int,
    price : int}

  record aggregated_package = {
    daily_cap : int,
    count : int}

  record worker = {
    daily_cap : int,
    can_withdraw_payout : bool,
    packages : map(package_code, aggregated_package),
    joined_pool_tmst : int}

  record transfer_packs =
    {
     worker : address,
     packages_to_move : list(string * int),
     new_address : address
     }

  datatype approvable_action = Transfer(transfer_packs)

  function new_package(price : int, cap : int) : package =
    {daily_cap = cap,
     price = price}

  function claim(ps : list(package_code * (package * int)), joined_tmst : int) : worker =
    let daily_cap = daily_cap_from_packs_list(ps) 
    let packs : map(package_code, aggregated_package) =
      List.foldl(
        (accum, t) =>
          let pack_id = Pair.fst(t)
          let (pack, cnt) = Pair.snd(t)
          let val =
            switch(Map.lookup(pack_id, accum))
              None => {daily_cap = pack.daily_cap, count = cnt}
              Some(v) => v{count = v.count + cnt}
          accum{[pack_id] = val},
        {},
        ps)
    {daily_cap = daily_cap,
     can_withdraw_payout = false,
     packages = packs,
     joined_pool_tmst = joined_tmst}

  function split_packages(w : worker, split : transfer_packs) : worker * worker =
    let (packages_left, packages_collected) =
      List.foldl(
        (accum, p) =>
          let accum_left = Pair.fst(accum)
          let accum_collected = Pair.snd(accum)
          let code = Pair.fst(p)
          let count = Pair.snd(p)
          switch(Map.lookup(code, accum_left))
            None => abort("Does not own enough packages")
            Some(owned_packs) =>
              let left_packs = owned_packs.count - count 
              require(left_packs > -1, "Does not own enough packages")
              (accum_left{[code] = owned_packs{count = left_packs}}, (code, owned_packs{count = count}) :: accum_collected),
        (w.packages, []),
        split.packages_to_move)
    let daily_cap_delta = List.sum(List.map((t) => Pair.snd(t).daily_cap * Pair.snd(t).count, packages_collected))
    let new_w = {daily_cap = daily_cap_delta,
                 can_withdraw_payout = false,
                 packages = Map.from_list(packages_collected),
                 joined_pool_tmst = Chain.block_height}
    require(Map.size(new_w.packages) == List.length(packages_collected), "Do not split counts of the same package code")
    (w{daily_cap = w.daily_cap - daily_cap_delta, packages = packages_left}, new_w)

  function merge_workers(w1 : worker, w2: worker) =
    let packages =
      List.foldl(
        (accum, t) =>
          let code = Pair.fst(t)
          let aggr_pack = Pair.snd(t)
          let updated_pack =
            switch(Map.lookup(code, accum))
              None => aggr_pack
              Some(p) => p{count = p.count + aggr_pack.count}
          accum{[code] = updated_pack},
        w1.packages,
        Map.to_list(w2.packages))
    // if one of them is allowed to withdraw, so is the resulting new account
    let oldest_tmst =
      switch(w1.joined_pool_tmst < w2.joined_pool_tmst)
        true => w1.joined_pool_tmst
        false => w2.joined_pool_tmst
    let can_withdraw_payout = w1.can_withdraw_payout || w2.can_withdraw_payout
    let daily_cap = daily_cap_from_packages(packages) 
    {daily_cap = daily_cap,
     can_withdraw_payout = can_withdraw_payout,
     packages = packages,
     joined_pool_tmst = oldest_tmst}

  function daily_cap_from_packs_list(ps : list(package_code * (package * int))) =
    List.sum(List.map((t) => Pair.fst(Pair.snd(t)).daily_cap * Pair.snd(Pair.snd(t)), ps))
                
  function daily_cap_from_packages(ps : map(package_code, aggregated_package)) =
    List.sum(List.map((t) => Pair.snd(t).daily_cap * Pair.snd(t).count, Map.to_list(ps)))





contract interface Data =
  stateful entrypoint set_hive : (address) => unit
  stateful entrypoint add : (address, Worker.worker) => unit
  stateful entrypoint remove : (address) => unit
  payable stateful entrypoint give_rewards : (list(address * int)) => unit
  entrypoint balance : (address) => int
  entrypoint assert_is_payable : (address) => unit
  stateful entrypoint payout : () => unit
  stateful entrypoint payout_without_payable_check : (address) => unit
  entrypoint all_balances : () => list(address * int)
  entrypoint all_daily_caps : () => list(address * int)
  entrypoint all : () => list(address)
  entrypoint all_detailed : () => map(address, Worker.worker)
  entrypoint member : (address) => bool
  entrypoint get : (address) => Worker.worker
  stateful entrypoint rename : (address, address) => unit
  stateful entrypoint make_payable : (address) => unit
  stateful entrypoint make_non_payable : (address) => unit
  stateful entrypoint split_packages : (Worker.transfer_packs) => unit
  entrypoint all_balances_and_daily_caps : () => list(address * int * int)

contract interface Hive =
  entrypoint leader : () => address
  stateful entrypoint enroll : (address, Worker.worker) => unit
  entrypoint member : (address) => bool
  stateful entrypoint remove : (address) => unit
  entrypoint get : (address) => Worker.worker
  stateful entrypoint set_locked : (bool) => unit
  stateful entrypoint set_leader : (address) => unit
  entrypoint can_be_destroyed : () => bool
  entrypoint info : () => address * string * address * string * string * string * string * list(string)
  stateful entrypoint make_payable : (address) => unit
  stateful entrypoint make_non_payable : (address) => unit
  entrypoint assert_worker_is_payable : (address) => unit
  stateful entrypoint force_payout : (address) => unit
  stateful entrypoint change_worker_address : (address, address) => unit
  entrypoint balance : (address) => int
  stateful entrypoint set_data_ct : (Data) => unit
  stateful entrypoint move_data_and_coins_to_new_hive : (Hive) => Data
  payable entrypoint receive_coins : () => unit 
  stateful entrypoint evacuate_coins : (int, address) => unit
  stateful entrypoint split_packages : (Worker.transfer_packs) => unit


include "Set.aes"
include "List.aes"
include "Pair.aes"

main contract HiveInstance:Hive =
  datatype hive_status = OPEN | LOCKED | MIGRATED

  datatype event
    = Enroll(address)
    | Remove(address)

  record state =
    { 
      status : hive_status,
      leader : address,
      main_contract : address,
      connect_addresses : Set.set(string), // IP addresses and ports
      leader_name : string,
      leader_url: string,
      leader_avatar_url : string,
      leader_description : string,
      data_ct : Data
    }

  entrypoint init(main_contract : address, leader : address, data_ct : Data) =
    {status = OPEN,
     leader = leader,
     main_contract = main_contract,
     connect_addresses = Set.new(),
     leader_name = "",
     leader_url = "",
     leader_avatar_url = "",
     leader_description = "",
     data_ct = data_ct}

  entrypoint status() =
    switch(state.status)
      OPEN => "open"
      LOCKED => "locked"
      MIGRATED => "migrated"
  
  entrypoint info() =
    (Contract.address, status(), state.leader, state.leader_name, state.leader_url, state.leader_avatar_url, state.leader_description, Set.to_list(state.connect_addresses))

  entrypoint data_contract() =
    state.data_ct
  
  entrypoint main_contract() =
    state.main_contract
  
  entrypoint can_be_destroyed() : bool =
    (empty() && state.status == LOCKED && Contract.balance == 0) || state.status == MIGRATED

  entrypoint leader() =
    state.leader
  
  entrypoint workers() =
    state.data_ct.all()

  entrypoint workers_detailed() =
    state.data_ct.all_detailed()
    
  entrypoint worker_balances() : list(address * int) =
    state.data_ct.all_balances()

  entrypoint worker_daily_caps() : list(address * int) =
    state.data_ct.all_daily_caps()

  entrypoint worker_balances_and_caps() : list(address * int * int) =
    state.data_ct.all_balances_and_daily_caps()

  entrypoint empty() : bool =
     List.is_empty(workers())

  stateful entrypoint enroll(worker_address : address, worker : Worker.worker) =
    assert_caller_is_main_contract()
    require(state.status == OPEN, "Hive is locked, can not join it")
    Chain.event(Enroll(worker_address))
    add_worker(worker_address, worker)

  entrypoint member(worker_address : address) : bool =
    state.data_ct.member(worker_address)


  /* deletes a worker from the hive if present. Currently the accumulated coins remain in
     hive. */
  stateful entrypoint remove(worker_address : address) =
    assert_caller_is_main_contract()
    Chain.event(Remove(worker_address))
    state.data_ct.remove(worker_address)
    
  entrypoint get(worker_address : address) =
    state.data_ct.get(worker_address)
  
  // this can overwrite a MIGRATED state
  stateful entrypoint set_locked(val : bool) =
    assert_caller_is_main_contract()
    let s =
      switch(val)
        true => LOCKED 
        false => OPEN
    put(state{status = s})

  stateful entrypoint set_leader(new_leader : address) =
    assert_caller_is_main_contract()
    put(state{leader = new_leader})

  stateful entrypoint add_connect_address(conn_address : string) =
    assert_leader()
    put(state{connect_addresses = Set.insert(conn_address, state.connect_addresses)})

  stateful entrypoint rm_connect_address(conn_address : string) =
    assert_leader()
    put(state{connect_addresses = Set.delete(conn_address, state.connect_addresses)})

  stateful entrypoint set_name(name : string) =
    assert_leader()
    put(state{leader_name = name})
    
  stateful entrypoint set_url(url : string) =
    assert_leader()
    put(state{leader_url = url})

  stateful entrypoint set_avatar_url(avatar_url : string) =
    assert_leader()
    put(state{leader_avatar_url = avatar_url})

  stateful entrypoint set_description(description : string) =
    assert_leader()
    put(state{leader_description = description})
    
  /* NB: does not take into account one's daily limits! */
  stateful entrypoint simply_reward_work(amounts : list(address * int), _ : string) = 
    assert_leader()
    let total_reward = List.sum(List.map((t) => Pair.snd(t), amounts))
    require(Contract.balance >= total_reward, "Not enough GAJU for that reward")
    state.data_ct.give_rewards(amounts, value = total_reward)

  payable entrypoint receive_coins() =
    ()

  // for Eureka
  entrypoint balance(addr : address) =
    state.data_ct.balance(addr)

  entrypoint daily_cap(addr : address) =
    let worker = get(addr)
    worker.daily_cap

  stateful entrypoint payout() : unit =
    state.data_ct.payout()

  stateful entrypoint force_payout(worker_addr : address) =
    assert_caller_is_main_contract() 
    state.data_ct.payout_without_payable_check(worker_addr)

  stateful entrypoint change_worker_address(old_addr : address, new_addr : address) =
    assert_caller_is_main_contract() 
    state.data_ct.rename(old_addr, new_addr)

  stateful entrypoint evacuate_coins(amount : int, safeheaven : address) =
    assert_caller_is_main_contract() 
    Chain.spend(safeheaven, amount)

  stateful entrypoint make_payable(worker_address : address) =
    assert_caller_is_main_contract() 
    state.data_ct.make_payable(worker_address)

  stateful entrypoint make_non_payable(worker_address : address) =
    assert_caller_is_main_contract() 
    state.data_ct.make_non_payable(worker_address)

  stateful entrypoint set_data_ct(data_ct : Data) =
    assert_caller_is_main_contract() 
    put(state{data_ct = data_ct})

  stateful entrypoint move_data_and_coins_to_new_hive(new_hive : Hive) =
    assert_caller_is_main_contract() 
    state.data_ct.set_hive(new_hive.address)
    new_hive.receive_coins(value = Contract.balance)
    put(state{status = MIGRATED})
    state.data_ct

  entrypoint assert_worker_is_payable(worker_address : address) =
    state.data_ct.assert_is_payable(worker_address)

  stateful entrypoint split_packages(split : Worker.transfer_packs) =
    assert_caller_is_main_contract() 
    state.data_ct.split_packages(split)

  // private functions 
  function assert_caller_is_main_contract() =
    require(Call.caller == state.main_contract, "Call it through the main hive contract")

  function assert_leader() =
    require(Call.origin == state.leader, "Must be called by the leader")

  stateful function add_worker(worker_address, worker) =
    state.data_ct.add(worker_address, worker)