[
"autonomous agent",
{
"bounce_fees": {
"base": 10000
},
"init": "{
$HOUSE_CUT = var["FEE"]; //We will take this cut on the debut of an NFT in our platform. We charge 0.1% for foreign NFTs.
$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 = "
NQ2FQP24F354R2IXAKMW7CRCVH4MTIIY";
$CURRENCY_REGISTRY = "
J5NLMOCZUL5JLNDA6PYZDGYSRB7VH7FJ";
$MAX_ROYALTY = var[$USER_REGISTRY_ADDRESS]["maxRoyalty"];
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"];
$helper = var["helper"];
}",
"getters": "{
$saleInfo = ($NFT, $seller)=>{
$unit = unit[$NFT];
$info = var["FREE_" || $NFT || "_" || $seller] OTHERWISE var["NFT_" || $NFT || "_" || $seller];
if (NOT $info)
bounce("That NFT is not for sale or does not exist");
return $info;
};
$fetchNFT = $asset => {
$unitInfo = unit[$asset];
$parentUnit = unit[$unitInfo.parent_units[0]];
if (NOT $unitInfo.messages)
bounce("Bad unit");
$schema = $unitInfo.authors[0].authentifiers
? false
: $unitInfo.authors[0].address;
if (NOT $schema)
bounce("We can only accept NFTs defined with a schema");
$definedBy = $unitInfo.authors[0].authentifiers
? $unitInfo.authors[0].address //Is a person
: $parentUnit.authors[0].address; //Is a person or an AA (they can also define NFTs)
$metadata = $parentUnit.messages[[.app='data']].payload;
$isAccepted = var["
NQ2FQP24F354R2IXAKMW7CRCVH4MTIIY"]["SCHEMA_" || $schema];
if (NOT $isAccepted)
bounce("We do not accept NFTs with that schema");
$assetMetadata = $unitInfo.messages[[.app='asset']].payload;
return {asset: $assetMetadata, meta: $metadata, author: $definedBy};
};
}",
"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
var["FEE"] = 0.1; //10% first sale fee
}"
}
]
},
{
"if": "{
trigger.address == $owner
AND ($method == "payout"
OR $method == "transferOwnership"
OR $method == "reduceFee"
OR $method == "addSchema"
OR $method == "setHelper")
}",
"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 == "reduceFee"}",
"init": "{
if (NOT exists(trigger.data["fee"]))
bounce("fee field is mandatory");
if (trigger.data["fee"] < 0)
bounce("Are you drunk?");
if (trigger.data["fee"] >= $HOUSE_CUT)
bounce("fee must be lower than the current one");
}",
"messages": [
{
"app": "state",
"state": "{
var["FEE"] = trigger.data["fee"];
}"
}
]
},
{
"if": "{$method == "addSchema"}",
"init": "{
if (NOT trigger.data["schema"])
bounce("schema field is mandatory");
if (NOT is_aa(trigger.data["schema"]))
bounce("schema is not the address of an autonomous agent");
}",
"messages": [
{
"app": "state",
"state": "{
var["SCHEMA_" || trigger.data["schema"]] = true;
}"
}
]
}
]
}
},
{
"if": "{
(trigger.address == $owner OR trigger.address == $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 == "BUY"}",
"init": "{
if (NOT trigger.data["address"])
bounce("address field is mandatory");
if (NOT is_valid_address(trigger.data["address"]))
bounce("address field is an invalid address");
if (NOT trigger.data["NFT"])
bounce("NFT field is mandatory");
if (trigger.address == trigger.data["address"])
bounce("You are buying your own NFT. Claim it instead in order to avoid fees");
$sale = $saleInfo(trigger.data["NFT"], trigger.data["address"]);
$nft = $fetchNFT(trigger.data["NFT"]);
$artist = $USER_REGISTRY_ADDRESS.$artistInfoOf($nft.author);
$amount = $sale.isAuction
? 1 //1 lot not 1 unit
: trigger.data["amount"] OTHERWISE 1;
$pricePerUnit = $sale.currency
? $CURRENCY_REGISTRY.$convert($sale.price, $sale.currency)
: $sale.price;
$fullPrice = $amount * $pricePerUnit;
$cut = round($fullPrice * $HOUSE_CUT, 0);
$toAuthor = round($fullPrice * (min($nft.meta["royalty"] OTHERWISE 0, $MAX_ROYALTY))/100, 0);
response["meta"] = $nft.meta;
response["toAuthor"] = $toAuthor;
$toSeller = $fullPrice - $cut - $toAuthor;
}",
"messages": [
{
"if": "{NOT $sale.auction}",
"init": "{
if (exists(trigger.data["amount"])){
if (NOT is_integer(trigger.data["amount"]))
bounce("amount field must be an integer");
if (trigger.data["amount"] < 1)
bounce("amount field must be at least 1");
if (trigger.data["amount"] > $sale.amount)
bounce("That user is selling only " || $sale.amount || " units");
}
if ($sale.currency)
response["rate"] = $CURRENCY_REGISTRY.$getExchangeRate($sale.currency);
response["realPrice"] = $pricePerUnit;
response["total"] = $fullPrice;
if (trigger.output[[asset="base"]].amount < $fullPrice)
bounce("Your payment is lower than the NFT price. You have to send " || $fullPrice || " bytes");
}",
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$sale.soldBy}",
"amount": "{$toSeller}"
},
{
"if": "{$toAuthor > 5000}",
"address": "{$nft.author}",
"amount": "{$toAuthor}"
}
]
}
},
{
"if": "{NOT $sale.auction}",
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$amount}"
}
]
}
},
{
"app": "state",
"state": "{
if ($sale.auction) {
if (trigger.output[[asset="base"]].amount < 20000)
bounce("The minimum bid is 20000 bytes");
if (trigger.output[[asset="base"]].amount <= $sale.price * (1 + $MIN_BID_INCREMENT))
bounce("Your bid must be at least " || ceil($sale.price * (1 + $MIN_BID_INCREMENT), 0) || " (0.01% higher than the current)");
if ($sale.soldBy == trigger.address)
bounce("You cannot bid on your own auction");
if (timestamp > $sale.endTime AND $sale.bids) //You can still bid on an expired auction if it had no bids. This will reawake the bid for 30 mins if the NFT was created by an attested user. Otherwise you will be able to claim the auction right away after bidding.
bounce("The auction is over");
var["locked"] += trigger.output[[asset="base"]].amount - ($sale.by ? $sale.price : 0); //Increase non-withdrawable funds by the difference between previous and last bid
$saleState = $sale || {
price: trigger.output[[asset="base"]].amount,
at: timestamp,
bids: $sale.bids + 1,
by: trigger.address,
endTime: $sale.endTime + $HALF_HOUR
};
}
else {
$newAmount = $sale.amount - $amount;
$saleState = $newAmount == 0 ? false : $sale || {amount: $newAmount, at: timestamp};
}
$key = var["FREE_" || trigger.data["NFT"] || "_" || trigger.data["address"]]
? "FREE_"
: "NFT_";
$fullKey = $key || trigger.data["NFT"] || "_" || trigger.data["address"];
var[$fullKey] = $saleState;
}"
}
]
},
{
"if": "{$method == "CLAIM"}",
"init": "{
$sale = $saleInfo(trigger.data["NFT"], trigger.address);
if (NOT trigger.data["NFT"])
bounce("NFT field is mandatory");
$nft = $fetchNFT(trigger.data["NFT"]);
$artist = $USER_REGISTRY_ADDRESS.$artistInfoOf($nft.author);
if ($sale.auction AND timestamp < $sale.endTime)
bounce("The auction is not over yet");
}",
"messages": {
"cases": [
{
"if": "{$sale.auction}",
"messages": [
{
"app": "payment",
"if": "{$sale.bids != 0}",
"init": "{
$share = $sale.price * (1 - $HOUSE_CUT);
if ($nft.meta["royalty"] > 0 AND $sale.bids > 0){
$royalty = $share * min($nft.meta["royalty"], $MAX_ROYALTY) / 100;
$outputs = [
{//Royalty
address: $nft.author,
amount: floor($royalty, 0) //Percentage of the whole bid, no fees are charged
},
{//Seller payment
address: $sale.soldBy,
amount: floor($share - $royalty, 0) //We take the house cut then royalty cut
}
];
}
else{
$outputs = [
{//Seller payment
address: $sale.soldBy,
amount: floor($share, 0)
}
];
}
$payload = {outputs: $outputs};
}",
"payload": "{$payload}"
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{$sale.by OTHERWISE $sale.soldBy}",
"amount": "{$sale.amount}"
}
]
}
},
{
"app": "state",
"state": "{
if ($sale.bids > 0) //If it had no bids you don't need to reduce locked amount
var["locked"] -= $sale.price;
if (var["FREE_" || trigger.data["NFT"] || "_" || $sale.soldBy])
var["FREE_" || trigger.data["NFT"] || "_" || $sale.soldBy] = false;
else
var["NFT_" || trigger.data["NFT"] || "_" || $sale.solBy] = false; //No longer listed for sale
}"
}
]
},
{
"if": "{NOT $sale.auction}",
"init": "{
if (trigger.address != $sale.soldBy)
bounce("You cannot claim other people's sales");
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{$sale.soldBy}",
"amount": "{$sale.amount}"
}
]
}
},
{
"app": "state",
"state": "{
if (var["FREE_" || trigger.data["NFT"] || "_" || $sale.soldBy])
var["FREE_" || trigger.data["NFT"] || "_" || $sale.soldBy] = false;
else
var["NFT_" || trigger.data["NFT"] || "_" || $sale.soldBy] = false;
}"
}
]
}
]
}
},
{
"if": "{$method == "REDEEM"}",
"init": "{
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");
$sale = $saleInfo($spack.signed_message["NFT"], $spack.authors[0]["address"]);
if (NOT $sale)
bounce("That NFT is not listed for sale");
if ($sale.auction)
bounce("You cannot claim a NFT that is being auctioned");
//Check if expire is set in the signed message
if ($spack.signed_message["expireDate"]){
if ($spack.signed_message["expireDate"] < timestamp)
bounce("That code is no longer redeemable");
}
//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;
if ($signedAmount > $sale.amount)
$amount = $sale.amount;
else
$amount = $signedAmount;
//All copies were sold
if ($amount == 0)
bounce("All copies of that NFT have been already sold");
}",
"messages": {
"cases": [
{
"messages": [
{
"app": "payment",
"payload": {
"asset": "{$spack.signed_message["NFT"]}",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$amount}"
}
]
}
},
{
"app": "state",
"state": "{
$key = var["FREE_" || $spack.signed_message["NFT"] || $spack.authors[0]["address"]]
? "FREE_" || $spack.signed_message["NFT"] || $spack.authors[0]["address"]
: "NFT_" || $spack.signed_message["NFT"] || $spack.authors[0]["address"];
var[$key] = ($sale.amount - $amount > 0)
? $sale || {amount: $sale.amount - $amount}
: false;
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 == "CHANGE_PRICE"}",
"init": "{
if (NOT trigger.data["NFT"])
bounce("NFT field is mandatory");
$sale = $saleInfo(trigger.data["NFT"], trigger.address);
if (NOT $sale)
bounce("Sale not found");
if (NOT exists(trigger.data["price"]))
bounce("price field is mandatory");
if (NOT exists(trigger.data["currency"])){ //Priced in bytes, we cannot allow decimals
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");
}
else{//User wants price to be foreign asset based, let's check if we support the asset
if (NOT $CURRENCY_REGISTRY.$getCurrency(trigger.data["currency"]))
bounce("You cannot set the price in that currency");
}
if ($sale.auction)
bounce("You cannot change an auction price!");
}",
"messages": [
{
"app": "state",
"state": "{
if (var["NFT_" || trigger.data["NFT"] || "_" || trigger.address])
var["NFT_" || trigger.data["NFT"] || "_" || trigger.address] ||= {price: trigger.data["price"], soldBy: trigger.address, currency: trigger.data["currency"] OTHERWISE false};
else
var["FREE_" || trigger.data["NFT"] || "_" || trigger.address] ||= {price: trigger.data["price"], soldBy: trigger.address, currency: trigger.data["currency"] OTHERWISE false};
}"
}
]
},
{
"if": "{$method == "SELL"}",
"init": "{
if (trigger.output[[asset!="base"]].asset == "ambigous")
bounce("You can only send a single NFT at a time");
$NFT = $fetchNFT(trigger.output[[asset!="base"]].asset);
$prof = $USER_REGISTRY_ADDRESS.$profileOf($NFT.author);
$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");
if (NOT trigger.data["price"])
bounce("price field is mandatory");
if (trigger.data["currency"]){
if (NOT $CURRENCY_REGISTRY.$getCurrency(trigger.data["currency"]))
bounce("We do not support that currency");
}
else {
if (NOT is_integer(trigger.data["price"]))
bounce("Price must be an integer");
if (NOT is_valid_amount(trigger.data["price"]))
bounce("Price is not a valid amount");
if (trigger.data["price"] * trigger.output[[asset!="base"]].amount < 20000)
bounce("The minimum sale price is 20.000 bytes");
}
if (trigger.data["auction"]){
if (NOT exists(trigger.data["endTime"]))
bounce("If you want to hold an auction you have to specify the endTime");
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");
}
}",
"messages": [
{
"app": "state",
"state": "{
$key = ($prof.verified ? "NFT_" : "FREE_") || trigger.output[[asset!="base"]].asset || "_" || trigger.address;
var[$key] = {
price: trigger.data["price"],
amount: trigger.output[[asset!="base"]].amount,
endTime: trigger.data["auction"]
? trigger.data["endTime"]
: false,
soldBy: trigger.address,
bids: trigger.data["auction"]
? 0
: false,
auction: trigger.data["auction"]
? true
: false,
redeem: trigger.data["redeem"]
? true
: false,
currency: trigger.data["auction"]
? false
: trigger.data["currency"] OTHERWISE false
};
}"
}
]
}
]
}
}
]