[
"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. We charge 0.1% for foreign NFTs.
$FREEMARKET_CUT = 0.001; //We take 0.1% on every foreign NFT trade
$ARTIST_CUT = 0.01; //The artist will get 1% from every resale of their NFT
$INVESTMENT_CUT = 0.01;//We take a 1% cut from all maecenas investments
$HALF_HOUR = 1800; //30m = 1800s. An auction is extended by 1800s every time there is a new bid near the deadline
$RESERVE_AMOUNT = 200000; //We will always keep > 200.000 in the AA
$MIN_BID_INCREMENT = 0.0001;
$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
delete($NFT, "meta");
return $NFT;
};
if (NOT exists(trigger.data["method"]))
bounce("method field is mandatory");
$spendableFunds = balance["base"] - var["locked"] - storage_size - $RESERVE_AMOUNT;
$owner = var["owner"];
$calculateSharesToBuy = ($price, $pool, $myShare)=>ln($price / ($pool * $myShare)) / ln($myShare);
}",
"getters": "{
$_maecenasCurve = $x=>6969420 * $x^2;
//DEBUG $_maecenasCurve = $x=>20000*$x^2;
// TOKEN RELATED GETTERS
/*
** All the information about the given NFT sale
*/
$tokenInfo = $NFT=>{
$unit = unit[$NFT];
$tok = var[$NFT];
if (NOT $tok)
bounce("NFT not found");
$feedData = data_feed[[oracles=this_address, feed_name=$NFT, ifnone="false", ifseveral="last", type="string"]];
return $tok ||
{
isBanned: $feedData == "REVOKED" ? true : false,
cap: $unit.messages[[.app="asset"]].payload.cap,
mintedAt: $unit.timestamp,
issuer: asset[$NFT].definer_address,
meta: var["meta_" || $NFT]
};
};
$saleInfo = $NFT=>{
$unit = unit[$NFT];
$info = var["NFT_" || $NFT];
if (NOT $info)
bounce("That NFT is not for sale or does not exist");
return $info || {
cap: $unit.messages[[.app="asset"]].payload.cap,
mintedAt: $unit.timestamp
};
};
/*
** How many copies of the NFT were sold, bounces if we did not mint the NFT
*/
$circulatingSupply = $NFT=>{
$info = $tokenInfo($NFT);
if ($info.isBanned)
bounce("That NFT was revoked");
if ($info.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 $info.unitsSold OTHERWISE 1;
};
//ARTIST AND MAECENAS RELATED GETTERS
/*
** All the artist information
*/
$artistInfo = $artist=>var["artist_" || $artist];
$profile = $artist=>var["profile_" || $artist];
/*
** How many BYTES have maecenas invested in a given artist
*/
$totalArtistInvestment = $artist=>{
if (NOT is_valid_address($artist))
bounce("artist address is invalid");
$artistInf = $artistInfo($artist);
if (typeof($artistInf) == "boolean")
bounce("That artist does not accept maecenas");
return 11511570 * $artistInf.supply * (1 + $artistInf.supply) * (1 + 2 * $artistInf.supply);
};
/*
** How many BYTES does the next share of the given artist costs
*/
$artistSharePrice = $artist=>{
$artistInf = var["artist_" || $artist];
if (NOT $artistInf)
bounce("That artist does not accept investments");
return $_maecenasCurve($artistInf.supply);
};
/*
** How many BYTES does it take to buy $shares amount of the given artist shares
*/
$artistSharesPrice = ($artist, $shares)=>{
if (NOT is_integer($shares))
bounce("shares must be an integer");
if ($shares <= 0)
bounce("shares must be a positive integer");
if (NOT is_valid_address($artist))
bounce("artist address is invalid");
$artistInf = $artistInfo($artist);
if (NOT $artistInf)
bounce("That artist does not accept maecenas");
$x0 = $artistInf.supply;
$x1 = $artistInf.supply + $shares;
$upToFirstToken = 1161570 * $x0 * (1 + $x0) * (1 + 2 * $x0);
$upToTargetPurchase = 1161570 * $x1 * (1 + $x1) * (1 + 2 * $x1);
return round($upToTargetPurchase - $upToFirstToken, 0);
};
/*
** How many SHARES of the given artist has the given maecenas
*/
$maecenasArtistShares = ($maecenas, $artist)=>{
if (NOT is_valid_address($artist))
bounce("artist address is invalid");
if (NOT is_valid_address($maecenas))
bounce("maecenas address is invalid");
return var["maecenas_" || $maecenas || "_" || $artist] OTHERWISE 0;
};
/*
** How many BYTES have an investor accrued from the given artist
*/
$maecenasEarnings = ($maecenas, $artist)=>{
if (NOT is_valid_address($artist))
bounce("artist address is invalid");
if (NOT is_valid_address($maecenas))
bounce("maecenas address is invalid");
$artistInf = $artistInfo($artist);
return floor($artistInf.share * $maecenasArtistShares($maecenas) / $artistInf.supply, 0);
};
}",
"messages": {
"cases": [
{
"if": "{
NOT $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 == $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"
OR trigger.data["method"] == "verifyProfile")
}",
"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 in the future");
}",
"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["NFT_" || response_unit] = {
bid: 20000, //Max current bid (a NFT can have this field because it was resold. Check for price field instead)
bids: 0,
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["NFT_" || 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": "{
if ($spendableFunds <= 20000)
bounce("There are not enought funds to withdraw");
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}"
}
]
}
}
]
},
{
"if": "{
trigger.data["method"] == "approve"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$NFT = var["pending_" || trigger.data["NFT"]];
if (NOT $NFT)
bounce("That NFT is not known by this AA");
}",
"messages": [
{
"app": "state",
"state": "{
$isFreeNFT = var["free_" || trigger.data["NFT"]];
if ($isFreeNFT)
var["free_" || trigger.data["NFT"]] = false; //Delete previous data so it is no longer tradable in the free market
var["SOLD_" || trigger.data["NFT"]] = {
author: "foreign",
ipfs: $NFT.ipfs
};
var["pending_" || trigger.data["NFT"]] = false; //Delete from the pendingApproval list
}"
}
]
},
{
"if": "{
trigger.data["method"] == "verifyProfile"
}",
"init": "{
if (NOT exists(trigger.data["artist"]))
bounce("artist field is mandatory");
}",
"messages": [
{
"app": "state",
"state": "{
var["profile_" || trigger.data["artist"]] ||= {verified: true};
}"
}
]
},
{
"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": "payment",
"if": "{
$spendableFunds > 20000
}",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$spendableFunds}"
}
]
}
},
{
"app": "state",
"state": "{
var["owner"] = trigger.data["newOwner"];//Ownership is transferred
}"
}
]
}
]
}
},
{
"if": "{
trigger.data["method"] == "BUY"
OR trigger.data["method"] == "DIVEST"
OR trigger.data["method"] == "CLAIM"
}",
"init": "{
$NFT = exists(trigger.data["NFT"])
? $saleInfo(trigger.data["NFT"])
: false;
$artist = trigger.data["method"] == "DIVEST"
? $artistInfo(trigger.data["artist"])
: $artistInfo($NFT.author);
if (trigger.data["method"] == "BUY"){ //Validate buy params
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$isAuction = $NFT.bid ? true : false; //It can only NOT be an auction if it is first sold and thus $NFT.bid does not exist
if ($isAuction)
$hasBidders = ($NFT.soldBy != $NFT.by) ? true : false; //Booleans cannot be compared
if ($isAuction AND NOT $NFT.by)
bounce("There are no copies of that NFT listed for sale at the moment");
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 sale is already over");
}
}
}
else if (trigger.data["method"] == "DIVEST"){ //Validate divest params
if (NOT exists(trigger.data["artist"]))
bounce("artist field is mandatory");
if (NOT is_valid_address(trigger.data["artist"]))
bounce("artist's address is invalid");
if (NOT $artist)
bounce("That artist never accepted 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 an integer > 0");
$investedAmount = var["maecenas_" || trigger.address || "_" || trigger.data["artist"]];
if (NOT $investedAmount)
bounce("You have no shares of that artist");
if (trigger.data["amount"] > $investedAmount)
bounce("You only have " || $investedAmount || " shares of that artist");
$yourShare = floor($artist.share * trigger.data["amount"] / $artist.supply, 0);
if ($yourShare < 20000)
bounce("It is not worth divesting your share");
}
else if (trigger.data["method"] == "CLAIM"){ //Validate claim params
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$isAuction = $NFT.bid ? true : false;
if (NOT $isAuction)
bounce("You cannot claim a non-auction sale");
$hasBidders = ($NFT.soldBy != $NFT.by) ? true : false;
if (timestamp <= $NFT.soldAt)
bounce("The auction is not over yet");
if ($NFT.bid == 0)
bounce("That auction was already claimed");
}
if ($artist AND (($isAuction AND $hasBidders) OR (NOT $isAuction))){
$allowsMaecenas = ($artist.sharePercent >= 0.01) ? true : false;
$hasMaecenas = $allowsMaecenas
? $artist.supply > 0
? true
: false
: false;
if ($allowsMaecenas){
if (trigger.data["method"] == "BUY")
$maecenasShare = $artist.sharePercent * trigger.output[[asset="base"]].amount;
else if (trigger.data["method"] == "CLAIM" AND $hasBidders)
$maecenasShare = $artist.sharePercent * $NFT.bid;
else
$maecenasShare = 0;
$sharesInThePool = (trigger.data["method"] != "DIVEST")
? $artist.supply
: ($artist.supply - trigger.data["amount"]); //If divesting we need to substract divested shares
if (trigger.data["method"] == "DIVEST")
$p = $artist.share - floor((trigger.data["amount"] / $artist.supply) * $artist.share, 0); //Current share minus what was just divested
else
$p = $artist.share + $maecenasShare; //Pool value after the sale
$k = 1 / ($sharesInThePool + 1); //The fraction of the pool 1 share represents
$price = $_maecenasCurve($sharesInThePool + 1); //Price of buying one share
if (trigger.data["method"] == "BUY"){
if ($NFT.soldBy == $NFT.author){ //We do not need to check for auction as we do not invest in those until the claim phase
$totalSpendable = floor(
$spendableFunds
+ $price * $INVESTMENT_CUT //What I save from investment cut as it is paid to myself
+ floor(trigger.output[[asset="base"]].amount * $HOUSE_CUT, 0) //What I got as cut from the triggering tx
, 0);
}
else{
$totalSpendable = floor(
$spendableFunds
+ $price * $INVESTMENT_CUT //What I save from investment cut as it is paid to myself
- trigger.output[[asset="base"]].amount //Cannot spend the trigering tx as it will be locked
, 0);
}
}
else if (trigger.data["method"] == "CLAIM"){
$totalSpendable = floor(
$spendableFunds //Initial amount
+ $price * $INVESTMENT_CUT //What I save from investment cut
+ $NFT.bid * (1 - $HOUSE_CUT) //What I get from house cut
, 0);
}
else{
$totalSpendable = floor($spendableFunds + $price * $INVESTMENT_CUT, 0);
}
$sharesToBuy = $sharesInThePool == 0 ? 0 : ceil($calculateSharesToBuy($price, $p, $k), 0);//Can be 0 and I would still afford 1 share if profitable
if ($k == 1){
$sharesICanAfford = $price < $totalSpendable ? 1 : 0;
}
else {
if ($totalSpendable > $price AND $sharesToBuy >= 1){
$sharesICanAfford = floor($totalSpendable / ($price * $sharesToBuy * (1 - $INVESTMENT_CUT)), 0); //Do not buy more shares than what I can afford
}
else{
$sharesICanAfford = 0;
}
}
}
if ($allowsMaecenas AND $sharesICanAfford > 0){
$buyShares = true;
}
else{
$buyShares = false;
}
$shareIWillfinallyBuy = min($sharesToBuy, $sharesICanAfford); //Maybe I can afford more shares than what is profitable to buy
$myProfit = floor($buyShares ? $p - $p * $k^$shareIWillfinallyBuy : 0, 0); //0 if no shares are bought
$poolFinalValue = $p - $myProfit; //Not actual profit but what I got out of the pool
}
else {
$allowsMaecenas = false;
$buyShares = false;
$hasMaecenas = false;
$maecenasShare = 0;
$myProfit = 0;
$poolFinalValue = 0;
}
}",
"messages": {
"cases": [
{
"if": "{
trigger.data["method"] == "BUY"
}",
"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 (trigger.output[[asset="base"]].amount <= $NFT.bid * (1 + $MIN_BID_INCREMENT))
bounce("Your bis must be at least " || $NFT.bid * (1 + $MIN_BID_INCREMENT) || " (0.01% higher than the current)");
if ($NFT.soldBy == trigger.address)
bounce("You cannot bid on your own auction");
}",
"messages": [
{
"app": "payment",
"if": "{
$NFT.by != $NFT.soldBy //If we don't check for this the artists could steal bytes from the AA
}",
"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 bid
$NFT.bid = trigger.output[[asset="base"]].amount;
$NFT.at = timestamp;
$NFT.bids = $NFT.bids + 1;
$NFT.by = trigger.address;
var["NFT_" || trigger.data["NFT"]] ||= $stripToken($extendDeadLine($NFT));
}"
}
]
},
{
"init": "{
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. You have to send " || $NFT.price || " bytes");
}",
"messages": [
{
"app": "payment",
"if": "{
floor($NFT.price * (1 - $HOUSE_CUT - ($allowsMaecenas ? (($hasMaecenas OR $buyShares) ? $artist.sharePercent : 0) : 0)) - 10000, 0) > 0
}",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$NFT.soldBy}",
"amount": "{floor($NFT.price * (1 - $HOUSE_CUT - ($allowsMaecenas ? (($hasMaecenas OR $buyShares) ? $artist.sharePercent : 0) : 0)) - 10000, 0)}"
}
]
}
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{trigger.address}",
"amount": 1
}
]
}
},
{
"app": "state",
"state": "{
var["NFT_" || trigger.data["NFT"]] ||= {unitsSold: $NFT.unitsSold + 1};
if ($hasMaecenas){
var["locked"] += $maecenasShare - $myProfit;
var["artist_" || $NFT.author] ||= {share: $poolFinalValue};
}
if ($NFT.unitsSold + 1 == $NFT.cap){
var["NFT_" || trigger.data["NFT"]] = false; //Delist it from sale
var["SOLD_" || trigger.data["NFT"]] = $stripToken($NFT); //Add it to meta
}
}"
}
]
}
]
}
},
{
"if": "{
trigger.data["method"] == "CLAIM"
}",
"messages": {
"cases": [
{
"if": "{//NFT was first sold
$NFT.soldBy == $NFT.author
}",
"init": "{
if ($hasMaecenas OR $buyShares){
$paymentFromShares = $shareIWillfinallyBuy * round($price * (1 - $INVESTMENT_CUT), 0);
$sellerPayout = floor($NFT.bid * (1 - $HOUSE_CUT - $artist.sharePercent) - 10000 + $paymentFromShares, 0);
}
else {
$sellerPayout = floor($NFT.bid * (1 - $HOUSE_CUT) - 10000, 0);
}
}",
"messages": [
{
"app": "payment",
"if": "{
$sellerPayout > 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": "{$sellerPayout}"
}
]
}
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{$NFT.by}",
"amount": 1
}
]
}
},
{
"app": "state",
"state": "{
$decrement = $hasMaecenas //My share does not play a role here
? $NFT.bid - $maecenasShare //If it had maecenas the maecenas share remains locked
: $NFT.bid; //If it does not have maecenas all bytes are unlocked
if ($NFT.bid > 20000) //If it had no bids you don't need to reduce locked amount
var["locked"] -= $decrement; //These bytes are no longer locked as the artist is getting them in this same unit
var["SOLD_" || trigger.data["NFT"]] = $stripToken($NFT); //Save for the next auction
var["NFT_" || trigger.data["NFT"]] = false; //No longer listed for sale
if ($hasMaecenas AND $NFT.bid > 20000)
var["artist_" || $NFT.author] ||= {share: $artist.share + $maecenasShare}; //Increase the artist pool value
}"
}
]
},
{
"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 paying the original artist
}",
"outputs": [
{
"address": "{$NFT.soldBy}",
"amount": "{floor($NFT.bid * (1 - $ARTIST_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) - 10000, 0)}"
}
]
}
]
}
}
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{$NFT.by}",
"amount": 1
}
]
}
},
{
"app": "state",
"state": "{
var["locked"] -= $NFT.bid;
var["SOLD_" || trigger.data["NFT"]] = $stripToken($NFT); //Save for the next auction
var["NFT_" || trigger.data["NFT"]] = false;
}"
}
]
}
]
}
},
{
"if": "{
trigger.data["method"] == "DIVEST"
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$yourShare - 10000}"
}
]
}
},
{
"app": "state",
"state": "{
var["artist_" || trigger.data["artist"]] ||= {
share: $artist.share - $yourShare, //Decrease the share by the amount just divested
supply: $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 + $myProfit);
}"
}
]
}
]
}
},
{
"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");
$feedData = data_feed[[oracles=this_address, feed_name=trigger.output[[asset!="base"]].asset, ifnone=false, ifseveral="last", type="string"]];
$NFT = var["SOLD_" || trigger.output[[asset!="base"]].asset] || {isBanned: $feedData == "REVOKED" ? true : false}; //[We need $NFT.isBanned]This already checks for $NFT existence
if (NOT $NFT)
bounce("NFT not found");
$sale = var["NFT_" || trigger.output[[asset!="base"]].asset];
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 ($sale)
bounce("A copy of that NFT is already being auctioned. The auction is expected to end at " || timestamp_to_string($sale.soldAt) || " UTC but may be delayed depending on the number of bids");
}",
"messages": [
{
"app": "state",
"state": "{
$NFT.bid = trigger.data["initialPrice"];
$NFT.bids = 0;
$NFT.by = trigger.address;
$NFT.soldBy = trigger.address;
$NFT.at = timestamp;
$NFT.soldAt = trigger.data["endTime"];
var["NFT_" || 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 exists(trigger.data["ipfs"]))
bounce("ipfs 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"] < 0)
bounce("You cannot set a negative duration");
if (trigger.data["duration"] > 2628000)
bounce("The auction cannot last more than 30 days");
}",
"messages": [
{
"app": "state",
"state": "{
var["pending_" || trigger.data["NFT"]] = {
ipfs: trigger.data["ipfs"] //Hash of the content
};
}"
}
]
},
{
"if": "{
trigger.data["method"] == "ENABLE_INVESTING"
}",
"init": "{
if (var["artist_" || trigger.address])
bounce("You are already accepting maecenas");
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"] == "DISABLE_INVESTING"
}",
"init": "{
$info = $artistInfo(trigger.address);
if (NOT $info)
bounce("You are already not accepting maecenas");
if ($info.supply > 0)
bounce("You cannot stop accepting maecenas right now as you already have some");
}",
"messages": [
{
"app": "state",
"state": "{
var["artist_" || trigger.address] = false;
}"
}
]
},
{
"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 = 1161570 * $x0 * (1 + $x0) * (1 + 2 * $x0);
$upToTargetPurchase = 1161570 * $x1 * (1 + $x1) * (1 + 2 * $x1);
/* DEBUG
$upToFirstToken = 10000/3 * $x0 * (1 + $x0) * (1 + 2 * $x0);
$upToTargetPurchase = 10000/3 * $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 || " bytes" || (trigger.data["amount"] > 1 ? " or reduce the amount of maecenas tokens you are buying" : ""));
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.data["artist"]}",
"amount": "{floor($totalCost * (1 - $INVESTMENT_CUT) - 10000, 0)}"
}
]
}
},
{
"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"];
}"
}
]
},
{
"if": "{
trigger.data["method"] == "PROFILE"
}",
"init": "{
$prof = $profile(trigger.address);
if ($prof.verified)
bounce("You have to contact support to modify a verified profile. We do this to prevent already verified accounts from impersonating another artist");
$profileObj = $prof || trigger.data || {verified: false};
delete($profileObj, "method");
}",
"messages": [
{
"app": "state",
"state": "{
var["profile_" || trigger.address] = $profileObj;
}"
}
]
},
{
"if": "{
trigger.data["method"] == "SET_META"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$NFT = var["SOLD_" || trigger.data["NFT"]];
if (NOT $NFT)
bounce("NFT not found");
if ($NFT.author != trigger.address)
bounce("You can only set the metadata of NFTs minted by yourself");
}",
"messages": [
{
"app": "state",
"state": "{
var["meta_" || trigger.data["NFT"]] = trigger.data;
}"
}
]
},
{
"if": "{
trigger.data["method"] == "CHANGE_PRICE"
}",
"init": "{
if (NOT trigger.data["NFT"])
bounce("NFT field is mandatory");
$NFT = var["NFT_" || trigger.data["NFT"]];
if (NOT $NFT)
bounce("NFT not found");
if (NOT exists(trigger.data["price"]))
bounce("price field is mandatory");
if (NOT is_integer(trigger.data["price"]))
bounce("price must be an integer");
if (NOT is_valid_amount(trigger.data["price"]))
bounce("price is invalid");
if ($NFT.bid)
bounce("You cannot change an auction price!");
}",
"messages": [
{
"app": "state",
"state": "{
var["NFT_" || trigger.data["NFT"]] ||= {price: trigger.data["price"]};
}"
}
]
},
{
"if": "{
trigger.data["method"] == "FREESELL"
}",
"init": "{
if (trigger.output[[asset!="base"]].asset == "ambigous")
bounce("You cannot send more than 1 NFT type per transaction");
if (trigger.output[[asset!="base"]].amount != 1)
bounce("You cannot sell more than 1 NFT unit at a time");
$isApproved = var["NFT_" || trigger.output[[asset!="base"]].asset] OTHERWISE var["SOLD_" || trigger.output[[asset!="base"]].asset];
if ($isApproved)
bounce("That NFT is already verified, thus it cannot be traded in the free market");
$feedData = data_feed[[oracles=this_address, feed_name=$isApproved, ifnone="false", ifseveral="last", type="string"]];
$isBanned = $feedData == "REVOKED" ? true : false;
if ($isBanned)
bounce("We have revoked the trading of that token. Probably due to copyrights");
$NFT = var["FREE_" || trigger.output[[asset!="base"]].asset];
if ($NFT.soldAt < timestamp)
bounce("A copy of that NFT is already being auctioned. It is expected to end at " || timestamp_to_string($NFT.soldAt) || " UTC");
if (NOT $NFT){ //We do not know about the NFT
if(NOT exists(trigger.data["ipfs"]))
bounce("ipfs field is required as we do not know which file that NFT represents");
}
if (NOT exists(trigger.data["endTime"]))
bounce("endTime field is mandatory");
if (NOT is_integer(trigger.data["endTime"]))
bounce("endTime must be integer");
if (trigger.data["endTime"] < timestamp)
bounce("endTime cannot be set in the past");
if (trigger.data["endTime"] > timestamp + 2628000)
bounce("endTime cannot be set in more than a month in the future");
if (exists(trigger.data["price"])){ //Is normal sale
if (NOT is_valid_amount(trigger.data["price"]))
bounce("price is invalid");
else
$toCreate = {price: trigger.data["price"]};
}
else{ //Is auction
$toCreate = {bid: 20000, by: trigger.address, at: timestamp};
}
}",
"messages": [
{
"app": "state",
"state": "{
var["FREE_" || trigger.output[[asset!="base"]].asset] = $toCreate || {ipfs: (NOT $NFT) ? trigger.data["ipfs"] : $NFT.ipfs, bids: 0, author: 'foreign', soldBy: trigger.address, soldAt: trigger.data["endTime"]};
}"
}
]
},
{
"if": "{
trigger.data["method"] == "FREEBUY"
}",
"init": "{
if (NOT trigger.data["NFT"])
bounce("NFT field is mandatory");
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$NFT = var["FREE_" || trigger.data["NFT"]];
if (NOT $NFT)
bounce("That NFT does not exist");
if (trigger.address == $NFT.soldBy)
bounce("You cannot buy/bid on your own NFTs");
if (trigger.output[[asset="base"]].amount < $NFT.bid * (1 + $MIN_BID_INCREMENT))
bounce("You have to send at least " || $NFT.bid * (1 + $MIN_BID_INCREMENT) || " bytes as you need to increment the current bet by at least 0.01%");
}",
"messages": [
{
"app": "state",
"state": "{
var["FREE_" || trigger.data["NFT"]] ||= {bid: trigger.output[[asset="base"]].amount, at: timestamp, by: trigger.address, bids: $NFT.bids + 1};
var["locked"] += trigger.output[[asset="base"]].amount;
}"
}
]
},
{
"if": "{
trigger.data["method"] == "FREECLAIM"
}",
"init": "{
if (NOT trigger.data["NFT"])
bounce("NFT field is mandatory");
$NFT = var["FREE_" || trigger.data["NFT"]];
if (NOT $NFT)
bounce("NFT not found");
if ($NFT.soldAt < timestamp)
bounce("The auction/sale is not over yet");
$isAuction = $NFT.bid ? true : false;
}",
"messages": [
{
"if": "{
$NFT.by != $NFT.soldBy
}",
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$NFT.soldBy}",
"amount": "{$NFT.price * (1 - $FREEMARKET_CUT)}"
}
]
}
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data["NFT"]}",
"outputs": [
{
"address": "{$NFT.by}",
"amount": 1
}
]
}
},
{
"app": "state",
"state": "{
var["FREE_" || trigger.data["NFT"]] = false;
var["FREESOLD_" || trigger.data["NFT"]] = $stripToken($NFT);
}"
}
]
}
]
}
}
]