[
"autonomous agent",
{
"bounce_fees": {
"base": 10000
},
"init": "{\r
$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.\r
$FREEMARKET_CUT = 0.01; //We take 1% on every foreign NFT trade\r
$ARTIST_CUT = 0.01; //The artist will get 1% from every resale of their NFT\r
$INVESTMENT_CUT = 0.01;//We take a 1% cut from all maecenas investments\r
$HALF_HOUR = 1800; //30m = 1800s. An auction is extended by 1800s every time there is a new bid near the deadline\r
$RESERVE_AMOUNT = 200000; //We will always keep > 200.000 in the AA\r
$MIN_BID_INCREMENT = 0.0001;\r
\r
$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\r
/*\r
** Strips $NFT of computed fields to reduce AA's storage size\r
*/\r
$stripToken = $NFT=>{\r
delete($NFT, "isBanned");\r
delete($NFT, "issuer");\r
delete($NFT, "cap");\r
delete($NFT, "mintedAt");\r
delete($NFT, "duration"); //For foreign NFTs\r
delete($NFT, "meta");\r
return $NFT;\r
};\r
\r
if (NOT trigger.data["method"])\r
bounce("method field is mandatory");\r
\r
$method = trigger.data["method"];\r
\r
$spendableFunds = balance["base"] - var["locked"] - storage_size - $RESERVE_AMOUNT;\r
$owner = var["owner"];\r
$calculateSharesToBuy = ($price, $pool, $myShare)=>ln($price / ($pool * $myShare)) / ln($myShare);\r
$_maecenasCurve = $x=>6969420 * $x^2;\r
}",
"getters": "{\r
$saleInfo = $NFT=>{\r
$unit = unit[$NFT];\r
$info = var["NFT_" || $NFT];\r
\r
if (NOT $info)\r
bounce("That NFT is not for sale or does not exist");\r
\r
return $info || {\r
cap: $unit.messages[[.app="asset"]].payload.cap,\r
mintedAt: $unit.timestamp\r
};\r
};\r
/*\r
** All the artist information\r
*/\r
$artistInfo = $artist=>var["artist_" || $artist];\r
$profile = $artist=>var["profile_" || $artist];\r
}",
"messages": {
"cases": [
{
"if": "{\r
NOT $owner //The owner has not been set\r
}",
"messages": [
{
"app": "state",
"state": "{\r
var["owner"] = "
IUU43O7TS2TBYKAPGKUARDZHOTAE275A"; //Set the Owner to my address\r
var["locked"] = 0; //Bytes locked in bids are not withdrawable by the Owner\r
}"
}
]
},
{
"if": "{\r
trigger.address == $owner\r
AND ($method == "payout"\r
OR $method == "approve"\r
OR $method == "transferOwnership"\r
OR $method == "setHelper"\r
OR $method == "verifyProfile")\r
}",
"messages": {
"cases": [
{
"if": "{\r
$method == "payout"\r
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}"
}
]
}
}
]
},
{
"if": "{\r
$method == "approve"\r
}",
"init": "{\r
if (NOT trigger.data["NFT"])\r
bounce("NFT field is mandatory");\r
\r
$NFT = var["pending_" || trigger.data["NFT"]];\r
\r
if (NOT $NFT)\r
bounce("That NFT is not known by this AA");\r
}",
"messages": [
{
"app": "state",
"state": "{\r
$isFreeNFT = var["free_" || trigger.data["NFT"]];\r
if ($isFreeNFT)\r
var["free_" || trigger.data["NFT"]] = false; //Delete previous data so it is no longer tradable in the free market\r
\r
var["SOLD_" || trigger.data["NFT"]] = {\r
author: "foreign",\r
ipfs: $NFT.ipfs,\r
type: $NFT.type\r
};\r
\r
var["pending_" || trigger.data["NFT"]] = false; //Delete from the pendingApproval list\r
}"
}
]
},
{
"if": "{\r
$method == "verifyProfile"\r
}",
"init": "{\r
if (NOT trigger.data["artist"])\r
bounce("artist field is mandatory");\r
}",
"messages": [
{
"app": "attestation",
"payload": "{var["profile_" || trigger.data["artist"]]}"
},
{
"app": "state",
"state": "{\r
var["profile_" || trigger.data["artist"]] ||= {verified: true};\r
}"
}
]
},
{
"if": "{\r
$method == "transferOwnership"\r
}",
"init": "{\r
if (NOT trigger.data["newOwner"])\r
bounce("newOwner field is mandatory");\r
if (NOT is_valid_address(trigger.data["newOwner"]))\r
bounce("newOwner field is not a valid address");\r
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$spendableFunds}"
}
]
}
},
{
"app": "state",
"state": "{\r
var["owner"] = trigger.data["newOwner"];//Ownership is transferred\r
}"
}
]
},
{
"if": "{\r
$method == "setHelper"\r
}",
"init": "{\r
if (NOT trigger.data["helper"])\r
bounce("helper field is mandatory");\r
}",
"messages": [
{
"app": "state",
"state": "{\r
var["helper"] = trigger.data["helper"];\r
}"
}
]
}
]
}
},
{
"if": "{\r
(trigger.address == $owner OR trigger.address == var["helper"])\r
AND $method == "revoke"\r
}",
"init": "{\r
if (NOT trigger.data["NFT"])\r
bounce("NFT field is mandatory");\r
}",
"messages": [
{
"app": "data_feed",
"payload": {
"{trigger.data["NFT"]}": "REVOKED"
}
}
]
},
{
"if": "{\r
$method == "MINT"\r
}",
"init": "{\r
if (NOT trigger.data["amount"])\r
bounce("amount field is mandatory");\r
if (NOT is_valid_amount(trigger.data["amount"]))\r
bounce("The amount of NFT copies to mint is not valid");\r
\r
if (NOT trigger.data["ipfs"])\r
bounce("ipfs field is mandatory");\r
\r
if (NOT trigger.data["type"])\r
bounce("type field is mandatory");\r
\r
if (NOT is_valid_address(trigger.data["seller"]))\r
bounce("The seller address is not valid");\r
\r
if (NOT trigger.data["endTime"])\r
bounce("endTime field is mandatory");\r
\r
if (trigger.data["endTime"] <= timestamp)\r
bounce("The endTime cannot be set in the past");\r
\r
if ($owner != trigger.address){ //The owner reserves the right to post sales with longer deadlines (e.g: for licenses)\r
if (trigger.data["endTime"] - timestamp > 2628000)\r
bounce("You cannot set the end time in more than 30 days in the future");\r
}\r
\r
$seller = (trigger.address == $owner) //Only the owner can list NFTs in behalf of others\r
? trigger.data["seller"]\r
: trigger.address;\r
\r
$key = ((trigger.address == $owner)\r
? "NFT_"\r
: "FREE_");\r
}",
"messages": {
"cases": [
{
"if": "{\r
trigger.data["amount"] == 1\r
}",
"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": "data",
"if": "{\r
var["recordToData"]\r
}",
"payload": {
"asset": "{var["recordToData"]}",
"name": "{trigger.data["title"] OTHERWISE "Untitled"}",
"decimals": 0
}
},
{
"app": "state",
"state": "{\r
var[$key || response_unit] = {\r
bid: 20000, //Max current bid (a NFT can have this field because it was resold. Check for price field instead)\r
bids: 0,\r
type: trigger.data["type"],\r
by: trigger.data["seller"],\r
at: timestamp, //Max bid time\r
ipfs: trigger.data["ipfs"], //Hash of the content\r
author: $seller, //The original seller\r
soldAt: trigger.data["endTime"], //End of the auction\r
soldBy: $seller //Who is currently selling the NFT\r
};\r
\r
if (typeof(trigger.data["meta"]) == "object")\r
var["meta_" || response_unit] = trigger.data["meta"];\r
\r
var["recordToData"] = response_unit;\r
}"
}
]
},
{
"init": "{\r
$price = trigger.data["price"];\r
if (NOT $price)\r
bounce("price field is mandatory if you intend to sell more than one NFT copy");\r
if ($price < 20000)\r
bounce("The minium price is 20000 bytes");\r
if (NOT is_valid_amount($price))\r
bounce("price is not valid");\r
}",
"messages": [
{
"app": "asset",
"if": "{\r
trigger.data["amount"] == 0 //Uncapped asset, no more units can be issued after endTime\r
}",
"payload": {
"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": "asset",
"if": "{\r
trigger.data["amount"] > 0\r
}",
"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": "{\r
var[$key || response_unit] = {\r
price: $price, //Sale price (this field is only present if the first sale was a fixed price one)\r
type: trigger.data["type"],\r
at: timestamp, //Max bid time\r
ipfs: trigger.data["ipfs"], //Hash of the content\r
unitsSold: 0,\r
author: $seller, //The original seller\r
soldAt: trigger.data["endTime"], //End of the auction\r
soldBy: $seller //Who is currently selling the NFT\r
};\r
if (typeof(trigger.data["meta"]) == "object")\r
var["meta_" || response_unit] = trigger.data["meta"];\r
}"
}
]
}
]
}
},
{
"if": "{\r
$method == "BUY"\r
OR $method == "DIVEST"\r
OR $method == "CLAIM"\r
}",
"init": "{\r
$NFT = exists(trigger.data["NFT"])\r
? $saleInfo(trigger.data["NFT"])\r
: false;\r
\r
\r
$artist = $method == "DIVEST"\r
? $artistInfo(trigger.data["artist"])\r
: $artistInfo($NFT.author);\r
\r
\r
if ($method == "BUY"){ //Validate buy params\r
if (NOT exists(trigger.data["NFT"]))\r
bounce("NFT field is mandatory");\r
\r
$isAuction = $NFT.bid ? true : false; //It can only NOT be an auction if it is first sold and thus $NFT.bid does not exist\r
\r
if ($isAuction)\r
$hasBidders = ($NFT.soldBy != $NFT.by) ? true : false; //Booleans cannot be compared\r
\r
if ($isAuction AND NOT $NFT.by)\r
bounce("There are no copies of that NFT listed for sale at the moment");\r
\r
if (timestamp > $NFT.soldAt){\r
if ($isAuction){\r
if ($hasBidders) //You can still buy it if there were no bidders\r
bounce("The auction is over");\r
}\r
else{\r
bounce("The sale is already over");\r
}\r
}\r
}\r
\r
else if ($method == "DIVEST"){ //Validate divest params\r
if (NOT trigger.data["artist"])\r
bounce("artist field is mandatory");\r
if (NOT is_valid_address(trigger.data["artist"]))\r
bounce("artist's address is invalid");\r
\r
if (NOT $artist)\r
bounce("That artist never accepted maecenas");\r
\r
if (NOT exists(trigger.data["amount"]))\r
bounce("amount field is mandatory");\r
if (NOT is_integer(trigger.data["amount"]) OR trigger.data["amount"] <= 0)\r
bounce("amount must be an integer > 0");\r
\r
$investedAmount = var["maecenas_" || trigger.address || "_" || trigger.data["artist"]];\r
\r
if (NOT $investedAmount)\r
bounce("You have no shares of that artist");\r
\r
if (trigger.data["amount"] > $investedAmount)\r
bounce("You only have " || $investedAmount || " shares of that artist");\r
\r
$yourShare = floor($artist.share * trigger.data["amount"] / $artist.supply, 0);\r
\r
if ($yourShare < 20000)\r
bounce("It is not worth divesting your share");\r
}\r
\r
else if ($method == "CLAIM"){ //Validate claim params\r
if (NOT trigger.data["NFT"])\r
bounce("NFT field is mandatory");\r
\r
$isAuction = $NFT.bid ? true : false;\r
\r
if (NOT $isAuction)\r
bounce("You cannot claim a non-auction sale");\r
\r
$hasBidders = ($NFT.soldBy != $NFT.by) ? true : false;\r
\r
if (timestamp <= $NFT.soldAt)\r
bounce("The auction is not over yet");\r
\r
if ($NFT.bid == 0)\r
bounce("That auction was already claimed");\r
}\r
\r
if ($artist AND (($isAuction AND $hasBidders) OR (NOT $isAuction))){\r
$allowsMaecenas = ($artist.sharePercent >= 0.01) ? true : false;\r
$hasMaecenas = $allowsMaecenas\r
? $artist.supply > 0\r
? true\r
: false\r
: false;\r
\r
\r
if ($allowsMaecenas){ \r
if ($method == "BUY")\r
$maecenasShare = $artist.sharePercent * trigger.output[[asset="base"]].amount;\r
else if ($method == "CLAIM" AND $hasBidders)\r
$maecenasShare = $artist.sharePercent * $NFT.bid;\r
else\r
$maecenasShare = 0;\r
\r
$sharesInThePool = ($method != "DIVEST")\r
? $artist.supply\r
: ($artist.supply - trigger.data["amount"]); //If divesting we need to substract divested shares\r
\r
if ($method == "DIVEST")\r
$p = $artist.share - floor((trigger.data["amount"] / $artist.supply) * $artist.share, 0); //Current share minus what was just divested\r
else\r
$p = $artist.share + $maecenasShare; //Pool value after the sale\r
\r
$k = 1 / ($sharesInThePool + 1); //The fraction of the pool 1 share represents\r
$price = $_maecenasCurve($sharesInThePool + 1); //Price of buying one share\r
\r
\r
if ($method == "BUY"){\r
if ($NFT.soldBy == $NFT.author){ //We do not need to check for auction as we do not invest in those until the claim phase\r
$totalSpendable = floor(\r
$spendableFunds\r
+ $price * $INVESTMENT_CUT //What I save from investment cut as it is paid to myself\r
+ floor(trigger.output[[asset="base"]].amount * $HOUSE_CUT, 0) //What I got as cut from the triggering tx\r
, 0);\r
}\r
else{\r
$totalSpendable = floor(\r
$spendableFunds\r
+ $price * $INVESTMENT_CUT //What I save from investment cut as it is paid to myself\r
- trigger.output[[asset="base"]].amount //Cannot spend the trigering tx as it will be locked\r
, 0);\r
}\r
}\r
else if ($method == "CLAIM"){\r
$totalSpendable = floor(\r
$spendableFunds //Initial amount\r
+ $price * $INVESTMENT_CUT //What I save from investment cut\r
+ $NFT.bid * (1 - $HOUSE_CUT) //What I get from house cut\r
, 0);\r
}\r
else{\r
$totalSpendable = floor($spendableFunds + $price * $INVESTMENT_CUT, 0);\r
}\r
\r
$sharesToBuy = $sharesInThePool == 0 ? 0 : ceil($calculateSharesToBuy($price, $p, $k), 0);//Can be 0 and I would still afford 1 share if profitable\r
\r
if ($k == 1){\r
$sharesICanAfford = $price < $totalSpendable ? 1 : 0;\r
}\r
else {\r
if ($totalSpendable > $price AND $sharesToBuy >= 1){\r
$sharesICanAfford = floor($totalSpendable / ($price * $sharesToBuy * (1 - $INVESTMENT_CUT)), 0); //Do not buy more shares than what I can afford\r
}\r
else{\r
$sharesICanAfford = 0;\r
}\r
}\r
}\r
\r
if ($allowsMaecenas AND $sharesICanAfford > 0){\r
$buyShares = true;\r
}\r
else{\r
$buyShares = false;\r
}\r
\r
$shareIWillfinallyBuy = min($sharesToBuy, $sharesICanAfford); //Maybe I can afford more shares than what is profitable to buy\r
$myProfit = floor($buyShares ? $p - $p * $k^$shareIWillfinallyBuy : 0, 0); //0 if no shares are bought\r
$poolFinalValue = $p - $myProfit; //Not actual profit but what I got out of the pool\r
}\r
else {\r
$allowsMaecenas = false;\r
$buyShares = false;\r
$hasMaecenas = false;\r
$maecenasShare = 0;\r
$myProfit = 0;\r
$poolFinalValue = 0;\r
}\r
}",
"messages": {
"cases": [
{
"if": "{\r
trigger.data["method"] == "BUY"\r
}",
"messages": {
"cases": [
{
"if": "{\r
$isAuction\r
}",
"init": "{\r
if (trigger.output[[asset="base"]].amount < 20000)\r
bounce("The minimum bid is 20000 bytes");\r
if (trigger.output[[asset="base"]].amount <= $NFT.bid * (1 + $MIN_BID_INCREMENT))\r
bounce("Your bid must be at least " || $NFT.bid * (1 + $MIN_BID_INCREMENT) || " (0.01% higher than the current)");\r
\r
if ($NFT.soldBy == trigger.address)\r
bounce("You cannot bid on your own auction");\r
}",
"messages": [
{
"app": "payment",
"if": "{\r
$NFT.by != $NFT.soldBy //If we don't check for this the artists could steal bytes from the AA\r
}",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$NFT.by}",
"amount": "{$NFT.bid - 10000}"
}
]
}
},
{
"app": "state",
"state": "{\r
var["locked"] += trigger.output[[asset="base"]].amount - ($hasBidders ? $NFT.bid : 0); //Increase non-withdrawable funds by the difference between previous and last bid\r
$NFT.bid = trigger.output[[asset="base"]].amount;\r
$NFT.at = timestamp;\r
$NFT.bids = $NFT.bids + 1;\r
$NFT.by = trigger.address;\r
var["NFT_" || trigger.data["NFT"]] ||= $stripToken($extendDeadLine($NFT));\r
}"
}
]
},
{
"init": "{\r
if ($NFT.unitsSold == $NFT.cap AND $NFT.cap != 0) //We need to make an additional check for uncapped assets\r
bounce("All NFT copies have been already sold");\r
\r
if (trigger.output[[asset="base"]].amount < $NFT.price)\r
bounce("Your payment is lower than the NFT price. You have to send " || $NFT.price || " bytes");\r
}",
"messages": [
{
"app": "payment",
"if": "{\r
floor($NFT.price * (1 - $HOUSE_CUT - ($allowsMaecenas ? (($hasMaecenas OR $buyShares) ? $artist.sharePercent : 0) : 0)) - 10000, 0) > 0\r
}",
"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": "{\r
var["NFT_" || trigger.data["NFT"]] ||= {unitsSold: $NFT.unitsSold + 1};\r
if ($hasMaecenas){\r
var["locked"] += $maecenasShare - $myProfit;\r
var["artist_" || $NFT.author] ||= {share: $poolFinalValue};\r
}\r
if ($NFT.unitsSold + 1 == $NFT.cap){\r
var["NFT_" || trigger.data["NFT"]] = false; //Delist it from sale\r
var["SOLD_" || trigger.data["NFT"]] = $stripToken($NFT); //Add it to meta\r
}\r
}"
}
]
}
]
}
},
{
"if": "{\r
trigger.data["method"] == "CLAIM"\r
}",
"messages": {
"cases": [
{
"if": "{//NFT was first sold\r
$NFT.soldBy == $NFT.author\r
}",
"init": "{\r
if ($hasMaecenas OR $buyShares){\r
$paymentFromShares = $shareIWillfinallyBuy * round($price * (1 - $INVESTMENT_CUT), 0);\r
$sellerPayout = floor($NFT.bid * (1 - $HOUSE_CUT - $artist.sharePercent) - 10000 + $paymentFromShares, 0);\r
}\r
else {\r
$sellerPayout = floor($NFT.bid * (1 - $HOUSE_CUT) - 10000, 0);\r
}\r
}",
"messages": [
{
"app": "payment",
"if": "{\r
$sellerPayout > 0 //enough to be worth paying\r
AND $NFT.soldBy != $NFT.by //there is at least a bid\r
}",
"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": "{\r
$decrement = $hasMaecenas //My share does not play a role here\r
? $NFT.bid - $maecenasShare //If it had maecenas the maecenas share remains locked\r
: $NFT.bid; //If it does not have maecenas all bytes are unlocked\r
\r
if ($NFT.bid > 20000) //If it had no bids you don't need to reduce locked amount\r
var["locked"] -= $decrement; //These bytes are no longer locked as the artist is getting them in this same unit\r
\r
var["SOLD_" || trigger.data["NFT"]] = $stripToken($NFT); //Save for the next auction\r
var["NFT_" || trigger.data["NFT"]] = false; //No longer listed for sale\r
\r
if ($hasMaecenas AND $NFT.bid > 20000)\r
var["artist_" || $NFT.author] ||= {share: $artist.share + $maecenasShare}; //Increase the artist pool value\r
}"
}
]
},
{
"messages": [
{
"app": "payment",
"if": "{\r
$NFT.soldBy != $NFT.by //If we don't do this check the seller could steal bytes from the AA if there were no bids\r
}",
"payload": {
"asset": "base",
"outputs": {
"cases": [
{
"if": "{\r
floor($NFT.bid * $ARTIST_CUT, 0) - 10000 > 0 //It is worth paying the original artist\r
}",
"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": "{\r
var["locked"] -= $NFT.bid;\r
var["SOLD_" || trigger.data["NFT"]] = $stripToken($NFT); //Save for the next auction\r
var["NFT_" || trigger.data["NFT"]] = false;\r
}"
}
]
}
]
}
},
{
"if": "{\r
trigger.data["method"] == "DIVEST"\r
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$yourShare - 10000}"
}
]
}
},
{
"app": "state",
"state": "{\r
var["artist_" || trigger.data["artist"]] ||= {\r
share: $artist.share - $yourShare, //Decrease the share by the amount just divested\r
supply: $artist.supply - trigger.data["amount"] //Decrease the supply by the shares used to divest\r
};\r
\r
var["maecenas_" || trigger.address || "_" || trigger.data["artist"]] -= trigger.data["amount"];\r
var["locked"] -= ($yourShare + $myProfit);\r
}"
}
]
}
]
}
},
{
"if": "{//[PUBLIC] Resell a NFT\r
$method == "SELL"\r
}",
"init": "{\r
//Parameter validation\r
if (NOT exists(trigger.data["endTime"]))\r
bounce("endTime field is mandatory");\r
if (trigger.data["endTime"] < timestamp)\r
bounce("You cannot set the end time in the past");\r
if (trigger.data["endTime"] > timestamp + 2628000)\r
bounce("You cannot set the end time in more than 30 days");\r
\r
if (NOT exists(trigger.data["initialPrice"]))\r
bounce("initialPrice field is mandatory");\r
if (NOT is_valid_amount(trigger.data["initialPrice"]))\r
bounce("initialPrice must be a valid amount");\r
if (trigger.data["initialPrice"] < 20000)\r
bounce("The minimum initialPrice is 20000 bytes");\r
\r
//NFT validation\r
if (trigger.output[[asset!="base"]].asset == "ambigous")\r
bounce("You cannot send more than one NFT type at a time");\r
\r
$feedData = data_feed[[oracles=this_address, feed_name=trigger.output[[asset!="base"]].asset, ifnone=false, ifseveral="last", type="string"]];\r
\r
$NFT = var["SOLD_" || trigger.output[[asset!="base"]].asset] || {isBanned: $feedData == "REVOKED" ? true : false}; //[We need $NFT.isBanned]This already checks for $NFT existence\r
\r
if (NOT $NFT)\r
bounce("NFT not found");\r
\r
$sale = var["NFT_" || trigger.output[[asset!="base"]].asset];\r
\r
if (trigger.output[[asset!="base"]].amount != 1)\r
bounce("You cannot auction more than 1 copy of an NFT at a time");\r
\r
if ($NFT.isBanned)\r
bounce("We revoked the trading of that token probably due to copyright reasons");\r
\r
if ($sale)\r
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");\r
}",
"messages": [
{
"app": "state",
"state": "{\r
$NFT.bid = trigger.data["initialPrice"];\r
$NFT.bids = 0;\r
$NFT.by = trigger.address;\r
$NFT.soldBy = trigger.address;\r
$NFT.at = timestamp;\r
$NFT.soldAt = trigger.data["endTime"];\r
var["NFT_" || trigger.output[[asset!="base"]].asset] = $stripToken($NFT);\r
}"
}
]
},
{
"if": "{\r
$method == "PRELIST"\r
}",
"init": "{\r
if (NOT trigger.data["NFT"])\r
bounce("NFT field is mandatory");\r
if (NOT asset[trigger.data["NFT"]].exists)\r
bounce("That NFT does not exist within the Obyte network");\r
\r
$isFreeNFT = var["FREE_" || trigger.data["NFT"]];\r
\r
if (NOT $isFreeNFT){//If the NFT was not listed in the free marked we need to know more about it\r
if (NOT trigger.data["ipfs"])\r
bounce("ipfs field is mandatory");\r
\r
if (NOT trigger.data["type"])\r
bounce("type field is required");\r
}\r
}",
"messages": [
{
"app": "state",
"state": "{\r
if ($isFreeNFT){\r
var["pending_" || trigger.data["NFT"]] = $isFreeNFT;\r
}\r
else{\r
var["pending_" || trigger.data["NFT"]] = { //We save it as pending approval until we certify the provided info is valid\r
ipfs: trigger.data["ipfs"],\r
type: trigger.data["type"]\r
};\r
}\r
}"
}
]
},
{
"if": "{\r
$method == "ENABLE_INVESTING"\r
}",
"init": "{\r
if (var["artist_" || trigger.address])\r
bounce("You are already accepting maecenas");\r
\r
if (NOT trigger.data["share"])\r
bounce("share field is mandatory");\r
\r
if (NOT is_integer(trigger.data["share"]) OR trigger.data["share"] < 1 OR trigger.data["share"] > 50)\r
bounce("share must be an integer between 1 - 50");\r
if (exists(trigger.data["selfBuy"])){\r
if (NOT is_integer(trigger.data["selfBuy"]))\r
bounce("selfBuy field must be an integer");\r
if (trigger.data["selfBuy"] < 0)\r
bounce("selfBuy must be a positive integer");\r
\r
$cost = (1161570 * trigger.data["selfBuy"] * (1 + trigger.data["selfBuy"]) * (1 + 2 * trigger.data["selfBuy"])) * $INVESTMENT_CUT;\r
\r
if (trigger.output[[asset="base"]].amount < $cost)\r
bounce("You need to send at least " || $cost || " bytes");\r
}\r
}",
"messages": [
{
"app": "state",
"state": "{\r
var["artist_" || trigger.address] = {\r
supply: trigger.data["selfBuy"] OTHERWISE 0,\r
share: 0,\r
sharePercent: trigger.data["share"] / 100\r
};\r
if (trigger.data["selfBuy"])\r
var["maecenas_" || trigger.address || "_" || trigger.address] = trigger.data["selfBuy"];\r
}"
}
]
},
{
"if": "{\r
$method == "DISABLE_INVESTING"\r
}",
"init": "{\r
$info = $artistInfo(trigger.address);\r
\r
if (NOT $info)\r
bounce("You are already not accepting maecenas");\r
\r
if ($info.supply > 0)\r
bounce("You cannot stop accepting maecenas right now as you already have some");\r
}",
"messages": [
{
"app": "state",
"state": "{\r
var["artist_" || trigger.address] = false;\r
}"
}
]
},
{
"if": "{\r
$method == "INVEST"\r
}",
"init": "{\r
\r
if (NOT trigger.data["artist"])\r
bounce("artist field is mandatory");\r
\r
$artist = $artistInfo(trigger.data["artist"]);\r
\r
if (NOT $artist)\r
bounce("That artist is not accepting maecenas");\r
\r
if (NOT trigger.data["amount"])\r
bounce("amount field is mandatory");\r
if (NOT is_integer(trigger.data["amount"]) OR trigger.data["amount"] <= 0)\r
bounce("amount must be a positive integer");\r
\r
\r
$x0 = $artist.supply;\r
$x1 = $artist.supply + trigger.data["amount"];\r
\r
\r
$upToFirstToken = 1161570 * $x0 * (1 + $x0) * (1 + 2 * $x0);\r
$upToTargetPurchase = 1161570 * $x1 * (1 + $x1) * (1 + 2 * $x1);\r
\r
/* DEBUG\r
$upToFirstToken = 10000/3 * $x0 * (1 + $x0) * (1 + 2 * $x0);\r
$upToTargetPurchase = 10000/3 * $x1 * (1 + $x1) * (1 + 2 * $x1);\r
*/\r
$totalCost = round($upToTargetPurchase - $upToFirstToken, 0);\r
\r
if (trigger.output[[asset="base"]].amount < $totalCost)\r
bounce("Your payment is not enough for that purchase");\r
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.data["artist"]}",
"amount": "{floor($totalCost * (1 - $INVESTMENT_CUT) - 10000, 0)}"
}
]
}
},
{
"app": "state",
"state": "{\r
var["artist_" || trigger.data["artist"]] ||= {supply: $artist.supply + trigger.data["amount"]};\r
\r
if (var["maecenas_" || trigger.address || "_" || trigger.data["artist"]])\r
var["maecenas_" || trigger.address || "_" || trigger.data["artist"]] += trigger.data["amount"];\r
else\r
var["maecenas_" || trigger.address || "_" || trigger.data["artist"]] = trigger.data["amount"];\r
}"
}
]
},
{
"if": "{\r
$method == "PROFILE"\r
}",
"init": "{\r
$prof = $profile(trigger.address);\r
\r
if ($prof.verified)\r
bounce("You have to contact support to modify a verified profile. We do this to prevent already verified accounts from impersonating another artist");\r
\r
$profileObj = $prof || trigger.data || {verified: false};\r
delete($profileObj, "method");\r
}",
"messages": [
{
"app": "state",
"state": "{\r
var["profile_" || trigger.address] = $profileObj;\r
}"
}
]
},
{
"if": "{\r
$method == "SET_META"\r
}",
"init": "{\r
if (NOT exists(trigger.data["NFT"]))\r
bounce("NFT field is mandatory");\r
\r
$NFT = var["SOLD_" || trigger.data["NFT"]];\r
\r
if (NOT $NFT)\r
bounce("NFT not found");\r
\r
if ($NFT.author != trigger.address)\r
bounce("You can only set the metadata of NFTs minted by yourself");\r
\r
if (var["meta_" || trigger.data["NFT"]])\r
bounce("That NFT meta was already set");\r
}",
"messages": [
{
"app": "state",
"state": "{\r
var["meta_" || trigger.data["NFT"]] = trigger.data;\r
}"
}
]
},
{
"if": "{\r
$method == "CHANGE_PRICE"\r
}",
"init": "{\r
if (NOT trigger.data["NFT"])\r
bounce("NFT field is mandatory");\r
\r
$NFT = var["NFT_" || trigger.data["NFT"]];\r
\r
if (NOT $NFT)\r
bounce("NFT not found");\r
\r
if (NOT exists(trigger.data["price"]))\r
bounce("price field is mandatory");\r
if (NOT is_integer(trigger.data["price"]))\r
bounce("price must be an integer");\r
if (NOT is_valid_amount(trigger.data["price"]))\r
bounce("price is invalid");\r
\r
if ($NFT.bid)\r
bounce("You cannot change an auction price!");\r
}",
"messages": [
{
"app": "state",
"state": "{\r
var["NFT_" || trigger.data["NFT"]] ||= {price: trigger.data["price"]};\r
}"
}
]
},
{
"if": "{\r
$method == "FREESELL"\r
}",
"init": "{\r
if (trigger.output[[asset!="base"]].asset == "ambigous")\r
bounce("You cannot send more than 1 NFT type per transaction");\r
if (trigger.output[[asset!="base"]].amount != 1)\r
bounce("You cannot sell more than 1 NFT unit at a time");\r
\r
$isApproved = var["NFT_" || trigger.output[[asset!="base"]].asset] OTHERWISE var["SOLD_" || trigger.output[[asset!="base"]].asset];\r
\r
if ($isApproved)\r
bounce("That NFT is already verified, thus it cannot be traded in the free market");\r
\r
$feedData = data_feed[[oracles=this_address, feed_name=$isApproved, ifnone="false", ifseveral="last", type="string"]];\r
$isBanned = $feedData == "REVOKED" ? true : false;\r
\r
if ($isBanned)\r
bounce("We have revoked the trading of that token. Probably due to copyrights");\r
\r
$NFT = var["FREE_" || trigger.output[[asset!="base"]].asset];\r
\r
if ($NFT.soldAt < timestamp)\r
bounce("A copy of that NFT is already being auctioned. It is expected to end at " || timestamp_to_string($NFT.soldAt) || " UTC");\r
\r
\r
if (NOT $NFT){ //We do not know about the NFT\r
if(NOT exists(trigger.data["ipfs"]))\r
bounce("ipfs field is required as we do not know which file that NFT represents");\r
}\r
\r
if (NOT exists(trigger.data["endTime"]))\r
bounce("endTime field is mandatory");\r
if (NOT is_integer(trigger.data["endTime"]))\r
bounce("endTime must be integer");\r
if (trigger.data["endTime"] < timestamp)\r
bounce("endTime cannot be set in the past");\r
if (trigger.data["endTime"] > timestamp + 2628000)\r
bounce("endTime cannot be set in more than a month in the future");\r
\r
if (exists(trigger.data["price"])){ //Is normal sale\r
if (NOT is_valid_amount(trigger.data["price"]))\r
bounce("price is invalid");\r
else\r
$toCreate = {price: trigger.data["price"]};\r
}\r
else{ //Is auction\r
$toCreate = {bid: 20000, by: trigger.address, at: timestamp};\r
}\r
}",
"messages": [
{
"app": "state",
"state": "{\r
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"]};\r
}"
}
]
},
{
"if": "{\r
$method == "FREEBUY"\r
}",
"init": "{\r
if (NOT trigger.data["NFT"])\r
bounce("NFT field is mandatory");\r
if (NOT exists(trigger.data["NFT"]))\r
bounce("NFT field is mandatory");\r
\r
$NFT = var["FREE_" || trigger.data["NFT"]];\r
\r
if (NOT $NFT)\r
bounce("That NFT does not exist");\r
\r
if (trigger.address == $NFT.soldBy)\r
bounce("You cannot buy/bid on your own NFTs");\r
\r
if (trigger.output[[asset="base"]].amount < $NFT.bid * (1 + $MIN_BID_INCREMENT))\r
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%");\r
}",
"messages": [
{
"app": "state",
"state": "{\r
var["FREE_" || trigger.data["NFT"]] ||= {bid: trigger.output[[asset="base"]].amount, at: timestamp, by: trigger.address, bids: $NFT.bids + 1};\r
var["locked"] += trigger.output[[asset="base"]].amount;\r
}"
}
]
},
{
"if": "{\r
$method == "FREECLAIM"\r
}",
"init": "{\r
if (NOT trigger.data["NFT"])\r
bounce("NFT field is mandatory");\r
\r
$NFT = var["FREE_" || trigger.data["NFT"]];\r
\r
if (NOT $NFT)\r
bounce("NFT not found");\r
\r
if ($NFT.soldAt < timestamp)\r
bounce("The auction/sale is not over yet");\r
\r
$isAuction = $NFT.bid ? true : false;\r
}",
"messages": [
{
"if": "{\r
$NFT.by != $NFT.soldBy\r
}",
"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": "{\r
var["FREE_" || trigger.data["NFT"]] = false;\r
var["FREESOLD_" || trigger.data["NFT"]] = $stripToken($NFT);\r
}"
}
]
}
]
}
}
]