[
"autonomous agent",
{
"bounce_fees": {
"base": 10000
},
"init": "{
$HOUSE_CUT = 0.1; //We will take this cut on the debut of an NFT in our platform
$ARTIST_CUT = 0.01; //The artist will get 1% from every resale of their NFT
$RESALE_CUT = 0.01; //1%
$HALF_HOUR = 1800; //30m = 1800s
$extendDeadLine = $NFT=>$NFT.soldAt <= timestamp + $HALF_HOUR?$NFT || {soldAt: $NFT.soldAt + $HALF_HOUR}: $NFT; //Extend the auction if there is a bid less than half an hour before the auction is closed
/*
** Strips $NFT of computed fields to reduce AA's storage size
*/
$stripToken = $NFT=>{
delete($NFT, "isBanned");
delete($NFT, "issuer");
delete($NFT, "cap");
delete($NFT, "mintedAt");
delete($NFT, "duration"); //For foreign NFTs
return $NFT;
};
if (NOT exists(trigger.data["method"]))
bounce("method field is mandatory");
}",
"getters": "{
$_maecenasCurve = $x=>69069420 * $x^2;
/*
** How many copies of a given NFT were initially minted wether they were sold or not
*/
$mintedUnits = $NFT=>{
if (NOT var[$NFT])
bounce("NFT not found");
if (NOT asset[$NFT].exists)
bounce("That NFT does not exist");
return asset[$NFT].cap;
};
/*
** All the information about the given NFT
*/
$tokenInfo = $NFT=>{
if (NOT var[$NFT])
bounce("NFT not found");
return var[$NFT] ||
{
isBanned: typeof(data_feed[[oracles=this_address, feed_name=$NFT, ifnone=false, ifseveral="last", type="string"]]) == "string" AND data_feed[[oracles=this_address, feed_name=$NFT, ifnone=false, ifseveral="last", type="string"]] == "REVOKED",
issuer: asset[$NFT].definer_address,
cap: unit[$NFT].messages[[.app="asset"]].payload.cap,
mintedAt: unit[$NFT].timestamp
};
};
/*
** How many bytes have maecenas invested in a given artist
*/
$investedInArtist = $artist=>{
$x0 = var["artist_" || $artist]["supply"];
return 11511570 * $x0.supply * (1 + $x0.supply) * (1 + 2 * $x0.supply);
};
/*
** How many bytes have an investor accrued from the given artist
*/
$accrued = ($investor, $artist)=>floor(var["artist_" || $artist]["share"] * var["maecenas_" || $investor || "_" || $artist]/var["artist_" || $artist]["supply"], 0);
/*
** Percentage of the artist's earnings that is being shared with their maecenas
*/
$artistSharePercentage = $artist=>var["artist_" || $artist]["sharePercent"];
/*
** Useful artist information for the maecenas
*/
$artistInfo = $artist=>var["artist_" || $artist];
/*
** How many copies of the NFT were sold, bounces if we did not mint the NFT
*/
$circulatingSupply = $NFT=>{
if (data_feed[[oracles=this_address, feed_name=$NFT, ifnone=false, ifseveral="last", type="string"]] == "REVOKED")
bounce("That NFT was revoked");
if ($tokenInfo($NFT).issuer != this_address)
bounce("That NFT was not minted by us, thus we don't know how many of them are out in the wild");
return var[$NFT]["unitsSold"];
};
}",
"messages": {
"cases": [
{
"if": "{
NOT var["owner"] //The owner has not been set
}",
"messages": [
{
"app": "state",
"state": "{
var["owner"] = "
IUU43O7TS2TBYKAPGKUARDZHOTAE275A"; //Set the Owner to my address
var["locked"] = 0; //Bytes locked in bids are not withdrawable by the Owner
}"
}
]
},
{
"if": "{
trigger.address == var["owner"]
AND (trigger.data["method"] == "mint"
OR trigger.data["method"] == "payout"
OR trigger.data["method"] == "approve"
OR trigger.data["method"] == "revoke"
OR trigger.data["method"] == "transferOwnership")
}",
"messages": {
"cases": [
{
"if": "{
trigger.data["method"] == "mint"
}",
"init": "{
if (NOT exists(trigger.data["amount"]))
bounce("amount field is mandatory");
if (NOT is_valid_amount(trigger.data["amount"]))
bounce("The amount of NFT copies to mint is not valid");
if (NOT exists(trigger.data["ipfs"]))
bounce("ipfs field is mandatory");
if (NOT exists(trigger.data["seller"]))
bounce("seller field is mandatory");
if (NOT is_valid_address(trigger.data["seller"]))
bounce("The seller address is not valid");
if (NOT exists(trigger.data["endTime"]))
bounce("endTime field is mandatory");
if (NOT is_integer(trigger.data["endTime"]))
bounce("endTime field is not a timestamp");
if (trigger.data["endTime"] <= timestamp)
bounce("The endTime cannot be set in the past");
if (trigger.data["endTime"] - timestamp > 2628000)
bounce("You cannot set the end time in more than 30 days");
}",
"messages": {
"cases": [
{
"if": "{
trigger.data["amount"] == 1
}",
"messages": [
{
"app": "asset",
"payload": {
"cap": 1,
"is_private": false,
"is_transferrable": true,
"auto_destroy": false,
"fixed_denominations": false,
"issued_by_definer_only": true,
"cosigned_by_definer": false,
"spender_attested": false
}
},
{
"app": "state",
"state": "{
var[response_unit] = {
bid: 0, //Max current bid (a NFT can have this field because it was resold. Check for price field instead)
by: trigger.data["seller"],
at: timestamp, //Max bid time
ipfs: trigger.data["ipfs"], //Hash of the content
author: trigger.data["seller"], //The original seller
soldAt: trigger.data["endTime"], //End of the auction
soldBy: trigger.data["seller"] //Who is currently selling the NFT
};
}"
}
]
},
{
"init": "{
if (NOT exists(trigger.data["price"]))
bounce("price field is mandatory if you intend to sell more than one NFT copy");
if (trigger.data["price"] < 20000)
bounce("The minium price is 20000 bytes");
if (NOT is_valid_amount(trigger.data["price"]))
bounce("price is not valid");
}",
"messages": [
{
"app": "asset",
"payload": {
"cap": "{trigger.data["amount"]}",
"is_private": false,
"is_transferrable": true,
"auto_destroy": false,
"fixed_denominations": false,
"issued_by_definer_only": true,
"cosigned_by_definer": false,
"spender_attested": false
}
},
{
"app": "state",
"state": "{
var[response_unit] = {
price: trigger.data["price"], //Sale price (this field is only present if the first sale was a fixed price one)
at: timestamp, //Max bid time
ipfs: trigger.data["ipfs"], //Hash of the content
unitsSold: 0,
author: trigger.data["seller"], //The original seller
soldAt: trigger.data["endTime"], //End of the auction
soldBy: trigger.data["seller"] //Who is currently selling the NFT
};
}"
}
]
}
]
}
},
{
"if": "{
trigger.data["method"] == "payout"
}",
"init": "{
$payout = balance["base"] - var["locked"] - storage_size - 200000; //Always keep at least storage_size + 200.000 bytes to pay for txs
if ($payout <= storage_size + 200000)
bounce("There are not enough funds to withdraw");
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$payout}"
}
]
}
}
]
},
{
"if": "{
trigger.data["method"] == "approve"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$NFT = var[trigger.data["NFT"]];
if (NOT $NFT)
bounce("That NFT is not known by this AA");
if (NOT $NFT.pendingAproval)
bounce("That NFT is not pendingAproval");
}",
"messages": [
{
"app": "state",
"state": "{
var[trigger.data["NFT"]] ||= {pendingAproval: '', duration: '', foreign: true, bid: 20000, by: $NFT.soldBy, at: timestamp, soldAt: timestamp + $NFT.duration};
}"
}
]
},
{
"if": "{
trigger.data["method"] == "revoke"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
}",
"messages": [
{
"app": "data_feed",
"payload": {
"{trigger.data["NFT"]}": "REVOKED"
}
}
]
},
{
"if": "{
trigger.data["method"] == "transferOwnership"
}",
"init": "{
if (NOT exists(trigger.data["newOwner"]))
bounce("newOwner field is mandatory");
if (NOT is_valid_address(trigger.data["newOwner"]))
bounce("newOwner field is not a valid address");
}",
"messages": [
{
"app": "state",
"state": "{
var["owner"] = trigger.data["newOwner"];//Ownership is transferred
}"
}
]
}
]
}
},
{
"if": "{
trigger.data["method"] == "BUY"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$NFT = $tokenInfo(trigger.data["NFT"]);
$isAuction = $NFT.bid AND typeof($NFT.bid) == "number";
if ($isAuction)
$hasBidders = (NOT $NFT.by)?false:($NFT.soldBy != $NFT.by);//Booleans cannot be compared
if (NOT $NFT)
bounce("This NFT was not minted by us nor we listed it for sale");
if (timestamp > $NFT.soldAt){
if ($isAuction){
if ($hasBidders) //You can still buy it if there were no bidders
bounce("The auction is over");
}
else
bounce("The auction is already over");
}
}",
"messages": {
"cases": [
{
"if": "{
$isAuction
}",
"init": "{
if (trigger.output[[asset=base]].amount < 20000)
bounce("The minimum bid is 20000 bytes");
if (trigger.output[[asset=base]].amount <= $NFT.bid)
bounce("Your bid is lower or equal to the last bid. You must increase it");
if ($NFT.soldBy == trigger.address)
bounce("You cannot bid on your own auction");
}",
"messages": [
{
"app": "payment",
"if": "{
$NFT.by != $NFT.soldBy
}",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$NFT.by}",
"amount": "{$NFT.bid - 10000}"
}
]
}
},
{
"app": "state",
"state": "{
var["locked"] += trigger.output[[asset=base]].amount - ($hasBidders?$NFT.bid:0); //Increase non-withdrawable funds by the difference between previous and last bet
$NFT.bid = trigger.output[[asset=base]].amount;
$NFT.by = trigger.address;
var[trigger.data["NFT"]] ||= $stripToken($extendDeadLine($NFT));
}"
}
]
},
{
"init": "{
if (NOT asset[trigger.data["NFT"]].exists)
bounce("That asset does not exists");
if (asset[trigger.data["NFT"]].definer_address != this_address)
bounce("We have not listed that asset for sale");
if ($NFT.unitsSold == $NFT.cap)
bounce("All NFT copies have been already sold");
if (trigger.output[[asset=base]].amount < $NFT.price)
bounce("Your payment is lower than the NFT price");
$hasMaecenas = var["artist_" || $NFT.author]["supply"] > 0;
if ($hasMaecenas)
$artist = $artistInfo($NFT.author);
}",
"messages": [
{
"app": "payment",
"if": "{
floor($NFT.price * (1-$HOUSE_CUT) - 10000, 0) > 0
}",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$NFT.soldBy}",
"amount": "{floor($NFT.price * (1-$HOUSE_CUT - $hasMaecenas ? $artist.sharePercent : 0) - 10000, 0)}"
}
]
}
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{trigger.address}",
"amount": 1
}
]
}
},
{
"app": "state",
"state": "{
var[trigger.data["NFT"]] ||= {unitsSold: $NFT.unitsSold + 1};
}"
}
]
}
]
}
},
{
"if": "{
trigger.data["method"] == "CLAIM"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$NFT = var[trigger.data["NFT"]];
if (timestamp <= $NFT.soldAt)
bounce("The auction is not over yet");
if ($NFT.bid == 0)
bounce("That auction was already claimed");
$hasMaecenas = var["artist_" || $NFT.author]["supply"] > 0;
if ($hasMaecenas)
$artist = $artistInfo($NFT.author);
}",
"messages": {
"cases": [
{
"if": "{//NFT was first sold
$NFT.soldBy == $NFT.author
}",
"messages": [
{
"app": "payment",
"if": "{
floor($NFT.bid * (1 - $HOUSE_CUT - $hasMaecenas ? $artist.sharePercent : 0) - 10000, 0) > 0 //enough to be worth paying
AND $NFT.soldBy != $NFT.by //there is at least a bid
}",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$NFT.soldBy}",
"amount": "{floor($NFT.bid * (1-$HOUSE_CUT - $hasMaecenas ? $artist.sharePercent: 0) - 10000, 0)}"
}
]
}
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{$NFT.by}",
"amount": 1
}
]
}
},
{
"app": "state",
"state": "{ //Reset bid and by fields
var["locked"] -= $NFT.bid; //These bytes are no longer locked as the buyer has already withdrawn them
var[trigger.data["NFT"]] ||= {bid: 0, by: ''}; //Prepare the state for the next auction
if ($hasMaecenas){
$maecenasShare = floor($NFT.bid * $artist.sharePercent, 0);
var["locked"] += $maecenasShare;
var["artist_" || $NFT.author] ||= {share: $artist.share + $maecenasShare};
}
}"
}
]
},
{
"messages": [
{
"app": "payment",
"if": "{
$NFT.soldBy != $NFT.by //If we don't do this check the seller could steal bytes from the AA if there were no bids
}",
"payload": {
"asset": "base",
"outputs": {
"cases": [
{
"if": "{
floor($NFT.bid * $ARTIST_CUT, 0) - 10000 > 0 //It is worth to pay the artist
AND is_valid_address($NFT.author)
}",
"outputs": [
{
"address": "{$NFT.soldBy}",
"amount": "{floor($NFT.bid * (1-$ARTIST_CUT-$RESALE_CUT) - 10000, 0)}"
},
{
"address": "{$NFT.author}",
"amount": "{floor($NFT.bid * $ARTIST_CUT, 0) - 10000}"
}
]
},
{
"outputs": [
{
"address": "{$NFT.soldBy}",
"amount": "{floor($NFT.bid * (1-$ARTIST_CUT-$RESALE_CUT) - 10000, 0)}"
}
]
}
]
}
}
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{$NFT.by}",
"amount": 1
}
]
}
},
{
"app": "state",
"state": "{
var["locked"] -= $NFT.bid;
}"
}
]
}
]
}
},
{
"if": "{//[PUBLIC] Resell a NFT
trigger.data["method"] == "SELL"
}",
"init": "{
//Parameter validation
if (NOT exists(trigger.data["endTime"]))
bounce("endTime field is mandatory");
if (trigger.data["endTime"] < timestamp)
bounce("You cannot set the end time in the past");
if (trigger.data["endTime"] - timestamp > 2628000)
bounce("You cannot set the end time in more than 30 days");
if (NOT exists(trigger.data["initialPrice"]))
bounce("initialPrice field is mandatory");
if (NOT is_valid_amount(trigger.data["initialPrice"]))
bounce("initialPrice must be a valid amount");
if (trigger.data["initialPrice"] < 20000)
bounce("The minimum initialPrice is 20000 bytes");
//NFT validation
if (trigger.output[[asset!="base"]].asset == "ambigous")
bounce("You cannot send more than one NFT type at a time");
if (NOT var[trigger.output[[asset!="base"]].asset])
bounce("You need to send an actual NFT but you are sending only bytes");
$NFT = $tokenInfo(trigger.output[[asset!="base"]].asset);
if (NOT $NFT)
bounce("This NFT was not minted by us nor we listed it for sale");
if (trigger.output[[asset!="base"]].amount!=1)
bounce("You cannot auction more than 1 copy of an NFT at a time");
if ($NFT.isBanned)
bounce("We revoked the trading of that token probably due to copyright reasons");
if (timestamp < $NFT.soldAt)
bounce("A copy of that NFT is already being auctioned. The auction is expected to end at " || timestamp_to_string($NFT.soldAt) || " UTC but may be delayed depending on the number of bids");
else if ($NFT.bid AND typeof($NFT.bid) == "number" AND $NFT.bid != 0)
bounce("A copy of that NFT is already being auctioned. The auction has already ended but it has not been claimed. You can close it yourself even if you are not the max bidder");
}",
"messages": [
{
"app": "state",
"state": "{
$NFT.bid = trigger.data["initialPrice"];
$NFT.by = trigger.address;
$NFT.soldBy = trigger.address;
$NFT.at = timestamp;
$NFT.soldAt = trigger.data["endTime"];
var[trigger.output[[asset!="base"]].asset] ||= $stripToken($NFT);
}"
}
]
},
{
"if": "{
trigger.data["method"] == "PRELIST"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
if (NOT asset[trigger.data["NFT"]].exists)
bounce("That NFT does not exist within the Obyte network");
if (NOT asset[trigger.data["NFT"]].is_transferrable)
bounce("That NFT is not tradeable");
if (NOT exists(trigger.data["ipfs"]))
bounce("ipfs field is mandatory");
if (NOT exists(trigger.data["seller"]))
bounce("seller field is mandatory");
if (NOT is_valid_address(trigger.data["seller"]))
bounce("The seller address is not valid");
if (NOT exists(trigger.data["duration"]))
bounce("duration field is mandatory. Enter the auction duration in seconds");
if (NOT is_integer(trigger.data["duration"]))
bounce("duration field is not a valid amount of seconds");
if (trigger.data["duration"] > 2628000)
bounce("The auction cannot last more than 30 days");
}",
"messages": [
{
"app": "state",
"state": "{
var[trigger.data["NFT"]] = {
ipfs: trigger.data["ipfs"], //Hash of the content
author: 'foreign', //The original seller is locked to foreign for foreign NFTs
soldBy: trigger.data["seller"],
duration: trigger.data["duration"] //Duration of the auction in seconds
};
}"
}
]
},
{
"if": "{
trigger.data["method"] == "ENABLE_INVESTING"
}",
"init": "{
if (var["artist_" || trigger.address])
bounce("You are already accepting investments");
if (NOT exists(trigger.data["share"]))
bounce("share field is mandatory");
if (NOT is_integer(trigger.data["share"]) OR trigger.data["share"] < 1 OR trigger.data["share"] > 50)
bounce("share must be an integer between 1 - 50");
}",
"messages": [
{
"app": "state",
"state": "{
var["artist_" || trigger.address] = {
supply: 0,
share: 0,
sharePercent: trigger.data["share"]/100
};
}"
}
]
},
{
"if": "{
trigger.data["method"] == "DIVEST"
}",
"init": "{
if (NOT exists(trigger.data["artist"]))
bounce("artist field is mandatory");
if (NOT is_valid_address(trigger.data["artist"]))
bounce("artist must be a valid address");
if (NOT var["artist_" || trigger.data["artist"]])
bounce("That artist has never accepted maecenas");
if (NOT trigger.data["amount"])
bounce("amount field is mandatory");
if (NOT is_integer(trigger.data["amount"] OR trigger.data["amount"] <= 0))
bounce("amount must be an integer > 0");
if (trigger.data["amount"] > var["maecenas_" || trigger.address || "_" || trigger.data["artist"]])
bounce("You only have " || var["maecenas_" || trigger.address || "_" || trigger.data["artist"]] || " shares of that artist");
if (NOT var["maecenas_" || trigger.address || "_" || trigger.data["artist"]])
bounce("You never funded that artist");
$artist = $artistInfo(trigger.data["artist"]);
$yourShare = floor($artist.share * trigger.data["amount"]/$artist.supply, 0);
if ($yourShare < 20000)
bounce("It is not worth divesting your share");
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.data["artist"]}",
"amount": "{$yourShare - 10000}"
}
]
}
},
{
"app": "state",
"state": "{
var["artist_" || trigger.data["artist"]] ||= {
share: var["artist_" || trigger.data["artist"]]["share"] - $yourShare, //Decrease the share by the amount just divested
supply: var["artist_" || trigger.data["artist"]]["supply"] - trigger.data["amount"] //Decrease the supply by the shares used to divest
};
var["maecenas_" || trigger.address || "_" || trigger.data["artist"]] -= trigger.data["amount"];
var["locked"] -= $yourShare;
}"
}
]
},
{
"if": "{
trigger.data["method"] == "INVEST"
}",
"init": "{
if (NOT exists(trigger.data["artist"]))
bounce("artist field is mandatory");
if (NOT is_valid_address(trigger.data["artist"]))
bounce("artist field is not a valid address");
if (NOT var["artist_" || trigger.data["artist"]])
bounce("That artist is not accepting maecenas");
if (NOT exists(trigger.data["amount"]))
bounce("amount field is mandatory");
if (NOT is_integer(trigger.data["amount"]) OR trigger.data["amount"] <= 0)
bounce("amount must be a positive integer");
$artist = $artistInfo(trigger.data["artist"]);
$x0 = $artist.supply;
$x1 = $artist.supply + trigger.data["amount"];
$upToFirstToken = 11511570 * $x0 * (1 + $x0) * (1 + 2 * $x0);
$upToTargetPurchase = 11511570 * $x1 * (1 + $x1) * (1 + 2 * $x1);
$totalCost = round($upToTargetPurchase - $upToFirstToken, 0);
if (trigger.output[[asset="base"]].amount < $totalCost)
bounce("Your payment is not enough for that purchase. You need to send " || $totalCost - trigger.output[[asset="base"]].amount || " bytes more or reduce the amount of maecenas tokens you are buying");
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.data["artist"]}",
"amount": "{$totalCost - 10000}"
}
]
}
},
{
"app": "state",
"state": "{
var["artist_" || trigger.data["artist"]] ||= {supply: $artist.supply + trigger.data["amount"]};
if (var["maecenas_" || trigger.address || "_" || trigger.data["artist"]])
var["maecenas_" || trigger.address || "_" || trigger.data["artist"]] += trigger.data["amount"];
else
var["maecenas_" || trigger.address || "_" || trigger.data["artist"]] = trigger.data["amount"];
}"
}
]
}
]
}
}
]