/* * Aegora Base Contract * * Author: Craig Everett * Copyright: Tsuriai Corporation (2022) * License: GPLv3 * Version: 1 * * This is the base contract for the market. * It is responsible for: * - A library of valid contracts by type (sales offers, auctions, etc.) * - Managing the authorized key list of market masters * - Providing a single known endpoint to discover deployed, active contracts * - Managing the lifecycle of a contract * * Lifecycle of a sales offer: * 1. The seller calls AegoraBase.post_sale() to create his SalesOffer * 2. Sale proceeds * IF it is succesful, the contract calls AegoraBase.close() * IF it is revoked by seller, the contract calls AegoraBase.close() * IF it times out or is killed by the maester, one calls AegoraBase.kill() * * Calls to this contract can come from one of five sources: * 1. The Maester (essentially the administrator) * - init(template : SalesOffer // Posts the initial contract * maester : address // with a reference to a template * tsuriai : address) * - update_template(contract : SalesOffer) // Update the template address * - epstein(id : bool) // Force a contract to close * 2. Sellers * - post_sale(id : int, price : int) // Create a new sale contract clone * 3. The Aegora site (or anyone really, but mainly useful for the site backend) * - lookup(id) // Looks up a sale contract's address * - template() // Looks up the current sales contract template * 4. A sales contract * - close(id : int) // A closing sale contract may call to remove itself from the active index * 5. The Tsuriai master Key * - update_maester(key : address) // Change the Maester key for operational reasons */ @compiler == 7.0.1 payable contract interface SalesOffer = entrypoint init : (address, address, address, int, address, int, int) => void payable entrypoint do_a_backflip : () => bool contract AegoraBase = record state = {contracts : map(int, SalesOffer), template : SalesOffer, maester : address, tsuriai : address} stateful entrypoint init(template : SalesOffer, maester : address, tsuriai : address) : state = {contracts = {}, template = template, maester = maester, tsuriai = tsuriai} public entrypoint template() : SalesOffer = state.template public stateful entrypoint update_template(source : SalesOffer) : unit = require(Call.caller == state.maester, "Nuh, uh uh! You didn't say the magic word!") put(state{template = source}) public entrypoint lookup(id: int) : SalesOffer = switch(Map.lookup(id, state.contracts)) Some(target) => target None => abort("Bad ID!") public stateful entrypoint post_sale(agent : address, portion : int, seller : address, id: int, price: int) : SalesOffer = require(price > 0, "You cannot pay someone to buy things, Mr. Keynes.") require(!Map.member(id, state.contracts), "People don't think it be like it is, but it do.") switch(Chain.clone(ref = state.template, protected = true, state.tsuriai, Contract.address, agent, portion, seller, id, price)) Some(posted) => put(state{contracts = state.contracts{[id] = posted}}) posted None => abort("Bad sale!") public stateful entrypoint close(id: int) : bool = switch(Map.lookup(id, state.contracts)) Some(target) => require(target.address == Call.caller, "Bad caller") put(state{contracts = Map.delete(id, state.contracts)}) true None => false public stateful entrypoint update_maester(key: address) : unit = require(Call.caller == state.tsuriai, "Nuh, uh uh! You didn't say the magic word!") put(state{maester = key}) public stateful entrypoint epstein(id: int) : bool = require(Call.caller == state.maester, "C'mon, man! You only got the husband and son!") switch(Map.lookup(id, state.contracts)) Some(target) => put(state{contracts = Map.delete(id, state.contracts)}) target.do_a_backflip() true None => false