[
"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.01; //We take 1% on every non-verified 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;//Bids must be at least 0.01% higher than the previous one
$ONE_MONTH = 2592000;
$USER_REGISTRY_ADDRESS = "
FN6NFHCLR4KRIQDGMBQSPUFYZHPIPANR";
$CURRENCY_REGISTRY = "
JXBRQF274QMZ3K6K276RM35TI4UFQAUL";
$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, "cap");
delete($NFT, "mintedAt");
return $NFT;
};
if (NOT trigger.data["method"])
bounce("method field is mandatory");
$method = trigger.data["method"];
$spendableFunds = balance["base"] - var["locked"] - storage_size - $RESERVE_AMOUNT;
$owner = var["owner"];
$calculateSharesToBuy = ($price, $pool, $myShare)=>ln($price / ($pool * $myShare)) / ln($myShare);
$_maecenasCurve = $x=>6969420 * $x^2;
}",
"getters": "{
$saleInfo = $NFT=>{
$unit = unit[$NFT];
$info = var["FREE_" || $NFT] OTHERWISE 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
};
};
/*
** Example of a meta getter to enable platform interop
*/
$getNFTInfo = $NFT=>{
$unit = unit[$NFT];
/*
** Info should have at least 'author' field. 'ipfs' and 'type' field are also recommended.
*/
$info = var["FREE_" || $NFT] OTHERWISE var["NFT_" || $NFT];
$transferrable = $unit.messages[[.app="asset"]]["payload"]["transferrable"];
return $info || {
cap: $unit.messages[[.app="asset"]]["payload"]["cap"],
mintedAt: $unit.timestamp,
transferrable: $transferrable
};
};
}",
"messages": {
"cases": [
{
"if": "{
NOT $owner
}",
"messages": [
{
"app": "state",
"state": "{
var["owner"] = "
IUU43O7TS2TBYKAPGKUARDZHOTAE275A"; //Set the Owner to my address
var["helper"] = "
IUU43O7TS2TBYKAPGKUARDZHOTAE275A";
var["locked"] = 0; //Bytes locked in bids are not withdrawable by the Owner
}"
}
]
},
{
"if": "{
trigger.address == $owner
AND ($method == "payout"
OR $method == "transferOwnership"
OR $method == "setHelper"
OR $method == "verifyProfile")
}",
"messages": {
"cases": [
{
"if": "{
$method == "payout"
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$spendableFunds}"
}
]
}
}
]
},
{
"if": "{
$method == "transferOwnership"
}",
"init": "{
if (NOT 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",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$spendableFunds}"
}
]
}
},
{
"app": "state",
"state": "{
var["owner"] = trigger.data["newOwner"];//Ownership is transferred
}"
}
]
},
{
"if": "{
$method == "setHelper"
}",
"init": "{
if (NOT trigger.data["helper"])
bounce("helper field is mandatory");
}",
"messages": [
{
"app": "state",
"state": "{
var["helper"] = trigger.data["helper"];
}"
}
]
}
]
}
},
{
"if": "{
$method == "REGISTER"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("That NFT does not exist");
$definer = unit[trigger.data["NFT"]].authors[0];
if (NOT $definer)
bounce("NFT field is not a known asset");
$meta = var[$definer]["NFT"]; //The NFT asset must be defined by the AA that holds its metadata
if (NOT $meta)
bounce("That NFT platform does not hold its metadata in a format compatible with ours");
}",
"messages": [
{
"app": "state",
"state": "{
var["FREE_" || trigger.data["NFT"]] = $meta;
}"
}
]
},
{
"if": "{
(trigger.address == $owner OR trigger.address == var["helper"])
AND $method == "revoke"
}",
"init": "{
if (NOT trigger.data["NFT"])
bounce("NFT field is mandatory");
}",
"messages": [
{
"app": "data_feed",
"payload": {
"{trigger.data["NFT"]}": "REVOKED"
}
}
]
},
{
"if": "{
$method == "MINT"
}",
"init": "{
if (NOT trigger.data["amount"])
bounce("amount field is mandatory");
if (NOT is_valid_amount(trigger.data["amount"]) AND trigger.data["amount"] != 0)
bounce("The amount of NFT copies to mint is not valid");
if (NOT trigger.data["type"])
bounce("type field is mandatory");
if (trigger.data["endTime"]){
if (trigger.data["endTime"] <= timestamp)
bounce("The endTime cannot be set in the past");
}
if ($owner != trigger.address){ //The owner reserves the right to post sales with longer deadlines
if (trigger.data["endTime"] - timestamp > 2628000) //A bit more than 30 days
bounce("You cannot set the end time in more than 30 days in the future");
}
if (NOT trigger.data["title"])
bounce("title field is mandatory");
if (NOT trigger.data["ipfs"])
bounce("ipfs field is mandatory");
if (trigger.data["royalty"]){
if (NOT is_integer(trigger.data["royalty"]))
bounce("royalty must be an integer");
if (trigger.data["royalty"] < 0 OR trigger.data["royalty"] > 40)
bounce("royalty must be in the interval 0-40");
}
if (exists(trigger.data["inflation"])){
if (NOT exists(trigger.data["batchSize"]))
bounce("batchSize field is mandatory");
if (NOT is_integer(trigger.data["batchSize"]))
bounce("batchSize must be integer");
if (trigger.data["batchSize"] <= 0)
bounce("batchSize must be an extrictly positive integer");
$initialIssue = trigger.data["amount"] OTHERWISE 0;
if (exists(trigger.data["cap"])){
if (NOT is_integer(trigger.data["cap"]))
bounce("cap must be an integer");
else if (trigger.data["cap"] <= 0)
bounce("cap must be a positive integer");
else if (trigger.data["cap"] > 9e15)
bounce("cap must be at most 9e15");
else if (NOT is_integer((trigger.data["cap"] - $initialIssue) / trigger.data["batchSize"]))
bounce("(cap - amount) / batchsize must be an integer");
}
if (NOT exists(trigger.data["period"]))
bounce("period field is mandatory");
if (NOT is_integer(trigger.data["period"]))
bounce("period must be an integer");
if (trigger.data["period"] < 0)
bounce("period must be an extrictly positive integer");
if (trigger.data["autoSell"]){
if (trigger.data["batchSize"] > 1){
if (NOT exists(trigger.data["mintPrice"]))
bounce("You cannot set autoSell without also setting mintPrice");
if (exists(trigger.data["mintCurrency"]))
if (NOT $CURRENCY_REGISTRY.$getCurrency(trigger.data["mintCurrency"]))
bounce("That currency is not supported");
}
//We are here if you set autoSell for an auction (batch size = 1)
}
else {
if (exists(trigger.data["mintPrice"]))
bounce("You cannot set mintPrice without autoSell");
if (exists(trigger.data["mintCurrency"]))
bounce("You cannot set mintCurrency without autoSell");
}
$supplyOpts = {
batchSize: trigger.data["batchSize"],
period: trigger.data["period"],
initialMint: trigger.data["amount"] OTHERWISE 0,
autoSell: trigger.data["autoSell"] ? true : false, //Otherwise only the owner can unlock the newly minted supply
lastClaim: timestamp,
issueEpoch: 0,
mintPrice: trigger.data["mintPrice"] OTHERWISE false,
mintCurrency: trigger.data["mintCurrency"] OTHERWISE false
};
}
$seller = trigger.address == $owner
? trigger.data["seller"] OTHERWISE trigger.address
: trigger.address;
if (trigger.data["seller"] AND NOT is_valid_address(trigger.data["seller"])) //The owner has to specify who sells the NFT
bounce("The seller address is not valid");
$prof = $USER_REGISTRY_ADDRESS.$profileOf($seller);
response["a1"] = $prof;
$key = $prof.verified
? "NFT_"
: "FREE_";
}",
"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": "data",
"if": "{
var["recordToData"]
}",
"payload": {
"asset": "{var["recordToData"]}",
"name": "{trigger.data["title"] OTHERWISE "Untitled"}",
"author": "{$seller}",
"ipfs": "{trigger.data["ipfs"]}",
"type": "{trigger.data["type"]}",
"decimals": 0
}
},
{
"app": "state",
"state": "{
var[$key || response_unit] = {
bid: 20000, //Max current bid (a NFT can have this field because it was resold. Check for price field instead)
bids: 0,
type: trigger.data["type"],
by: $seller,
royalty: trigger.data["royalty"] OTHERWISE 0,
at: timestamp, //Max bid time
ipfs: trigger.data["ipfs"], //Hash of the content
author: $seller, //The original seller
soldAt: trigger.data["endTime"] OTHERWISE timestamp + $ONE_MONTH, //End of the auction
soldBy: $seller, //Who is currently selling the NFT
supplyOpts: $supplyOpts OTHERWISE false
};
var["recordToData"] = response_unit;
}"
}
]
},
{
"init": "{
$price = trigger.data["price"];
if (NOT $price)
bounce("price field is mandatory if you intend to sell more than one NFT copy");
if ($price < 20000)
bounce("The minium price is 20000 bytes");
if (NOT is_valid_amount($price))
bounce("price is not valid");
}",
"messages": [
{
"app": "asset",
"if": "{
trigger.data["amount"] == 0 //Uncapped asset, no more units can be issued after endTime
}",
"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": "{
trigger.data["amount"] > 0
}",
"payload": {
"cap": "{trigger.data["inflation"] ? trigger.data["cap"] OTHERWISE 9e15 : 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[$key || response_unit] = {
price: $price, //Sale price (this field is only present if the first sale was a fixed price one)
type: trigger.data["type"],
at: timestamp,
royalty: trigger.data["royalty"] OTHERWISE 0, //Max bid time
ipfs: trigger.data["ipfs"], //Hash of the content
unitsSold: 0,
author: $seller, //The original seller
soldAt: trigger.data["endTime"] OTHERWISE timestamp + $ONE_MONTH, //End of the auction
soldBy: $seller, //Who is currently selling the NFT
redeem: trigger.data["redeem"] ? true : false,
supplyOpts: $supplyOpts OTHERWISE false
};
}"
}
]
}
]
}
},
{
"if": "{
$method == "BUY"
OR $method == "CLAIM"
}",
"init": "{
$NFT = $saleInfo(trigger.data["NFT"]);
$artist = $USER_REGISTRY_ADDRESS.$artistInfoOf($NFT.author);
if ($method == "BUY"){ //Validate buy params
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
//Inflationary NFT
if ($NFT.supplyOpts){
if ($NFT.unitsSold >= $NFT.supplyOpts["initialMint"] + $NFT.supplyOpts["period"] * $NFT.supplyOpts["issueEpoch"]){ //All available supply is sold, lets check if a new batch can be issued and put for sale
//Check if all units are already sold
if ($NFT.unitsSold == $NFT.supplyOpts["initialMint"] + $NFT.supplyOpts["batchSize"] * $NFT.supplyOpts["issueEpoch"]){
if ($NFT.supplyOpts["autoSell"]){ //Check if a new batch is ready for sale
if ($NFT.supplyOpts["lastClaim"] > timestamp - $NFT.supplyOpts["period"])
bounce("There are no copies of that NFT listed for sale");
}
else{ //AutoSell is disabled
bounce("Only the issuer can redeem new copies");
}
//All copies have been sold and only the author can claim new batches
}
else{
if ($NFT.supplyOpts["mintCurrency"])//Priced in foreing currenncy
$NFT.price = $CURRENCY_REGISTRY.$getExchangeRate($NFT.supplyOpts["currency"]) * $NFT.supplyOpts.mintPrice;
else//Priced in Bytes
$NFT.price = $NFT.supplyOpts["mintPrice"];
$isAuction = $NFT.supplyOpts["batchSize"] == 1 ? true : false;
}
}
else{
$isInflationaryButSoldAsNormal = true;
}
}
else
$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 (NOT $NFT.supplyOpts OR $isInflationaryButSoldAsNormal){
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 ($method == "CLAIM"){ //Validate claim params
if (NOT 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.claimed)
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 ($method == "BUY")
$maecenasShare = floor($artist.sharePercent * trigger.output[[asset="base"]].amount, 0);
else if ($method == "CLAIM" AND $hasBidders)
$maecenasShare = floor($artist.sharePercent * $NFT.bid, 0);
else
$maecenasShare = 0;
$sharesInThePool = ($method != "DIVEST")
? $artist.supply
: ($artist.supply - trigger.data["amount"]); //If divesting we need to substract divested shares
if ($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 ($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 ($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 * (1 + $MIN_BID_INCREMENT))
bounce("Your bid 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;
if (var["FREE_" || trigger.data["NFT"]])//We do not extend the deadline of freemarket NFTs
var["FREE_" || trigger.data["NFT"]] ||= $stripToken($NFT);
else
var["NFT_" || trigger.data["NFT"]] ||= $stripToken($extendDeadLine($NFT));
}"
}
]
},
{
"init": "{
if ($NFT.unitsSold == $NFT.cap AND $NFT.cap != 0) //We need to make an additional check for uncapped assets
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": {
"cases": [
{
"if": "{
$hasMaecenas
}",
"outputs": [
{
"address": "{$NFT.soldBy}",
"amount": "{floor($NFT.price * (1 - $HOUSE_CUT - ($allowsMaecenas ? (($hasMaecenas OR $buyShares) ? $artist.sharePercent : 0) : 0)) - 10000, 0)}"
},
{
"address": "{$USER_REGISTRY_ADDRESS}",
"amount": "{$poolFinalValue - $artist.share}"
}
]
},
{
"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
}
]
}
},
{
"if": "{
$hasMaecenas
}",
"app": "data",
"payload": {
"share": "{$poolFinalValue - $artist.share}"
}
},
{
"app": "state",
"state": "{
if ($NFT.soldBy == $NFT.author){ //Only increase the count if sold by the author
if (var["FREE_" || trigger.data["NFT"]])
var["FREE_" || trigger.data["NFT"]] ||= {unitsSold: $NFT.unitsSold + 1};
else
var["NFT_" || trigger.data["NFT"]] ||= {unitsSold: $NFT.unitsSold + 1};
//Check if all units were just sold
if ($NFT.unitsSold + 1 == $NFT.cap AND $NFT.cap != 0){
if (var["FREE_" || trigger.data["NFT"]])
var["FREE_" || trigger.data["NFT"]] ||= {bid: 0, price: false, soldAt: timestamp}; //All units are sold, let's adjust soldAt so it can be already resold
else
var["NFT_" || trigger.data["NFT"]] ||= {bid: 0, price: false, soldAt: timestamp}; //Prepare for auction and delist it from normal sale
}
}
//if ($hasMaecenas){
// var["locked"] += $maecenasShare - $myProfit;
// var["artist_" || $NFT.author] ||= {share: $poolFinalValue};
//}
}"
}
]
}
]
}
},
{
"if": "{
trigger.data["method"] == "CLAIM"
}",
"messages": {
"cases": [
{
"if": "{
$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
}
]
}
},
{
"if": "{
$hasMaecenas AND $NFT.bid > 20000 //If it does not have maecenas but accepts them pool is not increased
}",
"app": "data",
"payload": {
"share": "{$maecenasShare}"
}
},
{
"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
if (var["FREE_" || trigger.data["NFT"]])
var["FREE_" || trigger.data["NFT"]] ||= {claimed: true};
else
var["NFT_" || trigger.data["NFT"]] ||= {claimed: true}; //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
}",
"init": "{
$share = $NFT.bid * (1 - $HOUSE_CUT);
if ($NFT.royalty > 0 AND $NFT.soldBy != $NFT.by){
$royalty = $share * $NFT.royalty / 100;
$outputs = [
{//Royalty
address: $NFT.author,
amount: floor($royalty, 0) //Percentage of the whole bid, no fees are charged
},
{//Seller payment
address: $NFT.soldBy,
amount: floor($share - $royalty, 0) //We take the house cut then royalty cut
}
];
}
else{
$outputs = [
{//Seller payment
address: $NFT.soldBy,
amount: floor($share, 0)
}
];
}
$payload = {outputs: $outputs};
}",
"payload": "{$payload}"
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{$NFT.by}",
"amount": 1
}
]
}
},
{
"app": "state",
"state": "{
var["locked"] -= $NFT.bid;
if (var["FREE_" || trigger.data["NFT"]])
var["FREE_" || trigger.data["NFT"]] ||= {claimed: true};
else
var["NFT_" || trigger.data["NFT"]] ||= {claimed: true}; //No longer listed for sale
}"
}
]
}
]
}
}
]
}
},
{
"if": "{
$method == "REDEEM"
}",
"init": "{
if (exists(trigger.data["NFT"])){//Redeeming an inflationary NFT
$NFT = $saleInfo(trigger.data["NFT"]);
if ($NFT.author != trigger.address) //Check the author is the one redeeming the batch
bounce("Only the NFT author can redeem the batch");
if ($NFT.supplyOpts["autoSell"])
bounce("You cannot redeem a batch that is supposed to be automatically listed for sale");
if ($NFT.lastBatchDate > timestamp - $NFT.supplyOpts["period"])
bounce("You cannot redeem a new batch batch yet");
}
else{
if (NOT exists(trigger.data["signed_message"]))
bounce("signed_message field is mandatory");
if (NOT exists(trigger.data["authors"]))
bounce('authors field is mandatory');
if (NOT exists(trigger.data["meta"]))
bounce("meta field is mandatory");
$spack = {
signed_message: trigger.data["signed_message"],
authors: trigger.data["authors"]
} || trigger.data["meta"];
if (NOT is_valid_signed_package($spack, $spack.authors[0]["address"]))
bounce("The signed message signature is invalid");
if (NOT $spack.signed_message["serial"])
bounce("The signed message does not contains a serial field");
if (NOT $spack.signed_message["NFT"])
bounce("The signer of this message did not include a NFT field");
//Check if the code has already been redeemed
if (var["CODE_" || $spack.signed_message["NFT"] || "_" || sha256($spack.signed_message["serial"])])
bounce("That code was already redeemed");
$NFT = $saleInfo($spack.signed_message["NFT"]);
if (NOT $NFT)
bounce("That NFT does not exist");
//Prevent author from stealing their NFTs from other people
if ($NFT.soldBy != $NFT.author)
bounce("You cannot claim an NFT that is not being sold by their author");
if ($NFT.author != $spack.authors[0]["address"])
bounce("The signer of that message is not the author of that NFT");
//NFTs have to be created with redeemable: true to be redeemable
if (NOT $NFT.redeem)
bounce("That NFT is not redeemable");
//Check if sale is over and expire was set in the signed message
if ($spack.signed_message["expireDate"])
if ($NFT.soldAt < timestamp OR $spack.signed_message["expireDate"] < timestamp)
bounce("That NFT is no longer claimable");
//Check if the NFT is address-locked
if ($spack.signed_message["claimer"])
if ($spack.signed_message["claimer"] != trigger.address)
bounce("You cannot claim that NFT from that address");
$signedAmount = $spack.signed_message["amount"] OTHERWISE 1;
//Uncapped asset, we can issue whatever amount was signed
if (NOT $NFT.cap){
$amount = $signedAmount;
}
//If it is going over the cap we need to reduce the claimed amount to match the asset cap
else {
if ($NFT.unitsSold + $signedAmount > $NFT.cap)
$amount = $NFT.cap - $NFT.unitsSold;
else
$amount = $signedAmount;
}
//All copies were issued
if ($amount == 0)
bounce("All copies of that NFT have been already minted");
}
}",
"messages": {
"cases": [
{
"if": "{
trigger.data["NFT"]
}",
"messages": [
{
"app": "state",
"state": "{
if (var["FREE_" || trigger.data["NFT"]])
var["FREE_" || trigger.data["NFT"]] ||= {lastBatchDate: timestamp};
else
var["NFT_" || trigger.data["NFT"]] ||= {lastBatchDate: timestamp};
}"
}
]
},
{
"messages": [
{
"app": "payment",
"payload": {
"asset": "{$spack.signed_message["NFT"]}",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$amount}"
}
]
}
},
{
"app": "state",
"state": "{
if (var["FREE_" || $spack.signed_message["NFT"]])
var["FREE_" || $spack.signed_message["NFT"]] ||= {unitsSold: $NFT.unitsSold + $amount};
else
var["NFT_" || $spack.signed_message["NFT"]] ||= {unitsSold: $NFT.unitsSold + $amount};
var["CODE_" || $spack.signed_message["NFT"] || "_" || sha256($spack.signed_message["serial"], 'base64')] = true; //Hash the serial to prevent the issuer from sending too big serials to bloat state size
}"
}
]
}
]
}
},
{
"if": "{
$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");
//Check that the NFT is not revoked
$feedData = data_feed[[oracles=this_address, feed_name=trigger.output[[asset!="base"]].asset, ifnone="OK", ifseveral="last", type="string"]];
if ($feedData == "REVOKED")
bounce("We revoked the trading of that token probably due to copyright reasons");
$NFT = $saleInfo(trigger.output[[asset!="base"]].asset) OTHERWISE {};
if ($NFT.supplyOpts)
bounce("You cannot resell inflationary supply NFTs here");
if (NOT $NFT)
bounce("NFT not found");
if (trigger.output[[asset!="base"]].amount != 1)
bounce("You cannot auction more than 1 copy of an NFT at a time");
if ($NFT.soldAt >= timestamp)
bounce("A copy of that NFT is already being sold. The sale is expected to end at " || timestamp_to_string($NFT.soldAt) || " UTC but may be delayed depending on the number of bids");
if ($NFT.bid AND NOT $NFT.claimed)
bounce("That NFT was auctioned but the payment has not been claimed yet. You can force the saller to claim it.");
}",
"messages": [
{
"app": "state",
"state": "{
$NFT.bid = trigger.data["initialPrice"];
$NFT.bids = 0;
$NFT.by = trigger.address;
$NFT.soldBy = trigger.address;
$NFT.at = timestamp;
$NFT.claimed = false;
$NFT.soldAt = trigger.data["endTime"];
if (var["FREE_" || trigger.output[[asset!="base"]].asset])
var["FREE_" || trigger.output[[asset!="base"]].asset] = $stripToken($NFT) || {claimed: false};
else
var["NFT_" || trigger.output[[asset!="base"]].asset] = $stripToken($NFT) || {claimed: false};
}"
}
]
},
{
"if": "{
$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 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 (trigger.data["price"] < 20000)
bounce("The minimum price is 20000 bytes");
if ($NFT.bid)
bounce("You cannot change an auction price!");
}",
"messages": [
{
"app": "state",
"state": "{
var["NFT_" || trigger.data["NFT"]] ||= {price: trigger.data["price"]};
}"
}
]
}
]
}
}
]