[
"autonomous agent",
{
"bounce_fees": {
"base": 10000
},
"init": "{
$BYTES_IN_GBYTE = 1000000000;
if (NOT exists(trigger.data["method"]))
bounce("method field is mandatory");
$method = trigger.data["method"];
$spendableFunds = balance["base"] - storage_size; //The max amount the owner can withdraw without bouncing due to not enough funds
$owner = var["owner"];
$isHelper = var["helper_" || trigger.address];
$oracle = var["oracle"]; //Used for GBYTE_USD price
/*
** Provides information about the given license
*/
$getNFTInfo = $NFT=>{
$unit = unit[$NFT];
$info = var["FREE_" || $NFT] OTHERWISE var["NFT_" || $NFT];
$transferrable = $unit.messages[[.app="asset"]]["payload"]["transferrable"];
if (NOT $transferrable)//Do not return info about non transferrable NFTs as they cannot get listed in other platforms
return false;
return $info || {
cap: $unit.messages[[.app="asset"]]["payload"]["cap"],
mintedAt: $unit.timestamp
};
};
$isValidPlainLicense = ($spack, $issuer)=>{
if (is_valid_signed_package($spack, $issuer))
return $spack.signed_message["expireDate"] < timestamp;
};
$FEE = var["FEE"];
}",
"messages": {
"cases": [
{
"if": "{
NOT $owner //The owner has not been set
}",
"messages": [
{
"app": "state",
"state": "{
var["owner"] = "
IUU43O7TS2TBYKAPGKUARDZHOTAE275A";
var["CURRENCY_USD"] = {
oracle1: "
F4KHJUCLJKY4JV7M5F754LAJX4EB7M4N",
feed1: "GBYTE_USD",
oracle2: false,
feed2: false
};
var["FEE"] = 0.03;
}"
}
]
},
{
"if": "{
trigger.address == $owner
AND ($method == "payout" //We also check the method so the Owner can also act as an user
OR $method == "stopSale"
OR $method == "transferOwnership"
OR $method == "setHelper"
OR $method == "addCurrency"
OR $method == "delCurrency"
OR $method == "reduceFee"
OR $method == "delHelper")
}",
"messages": {
"cases": [
{
"if": "{
$method == "payout"
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$spendableFunds}"
}
]
}
}
]
},
{
"if": "{
$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",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$spendableFunds}"
}
]
}
},
{
"app": "state",
"state": "{
var["owner"] = trigger.data["newOwner"];
}"
}
]
},
{
"if": "{
$method == "reduceFee"
}",
"init": "{
if (NOT exists(trigger.data["fee"]))
bounce("fee field is mandatory");
if (trigger.data["fee"] <= 0)
bounce("Fee must be an strictly positive value");
if (trigger.data["fee"] > $FEE)
bounce("You can only reduce the current fee");
}",
"messages": [
{
"app": "state",
"state": "{
var["FEE"] = trigger.data["fee"];
}"
}
]
},
{
"if": "{
$method == "addCurrency"
}",
"init": "{
if (NOT exists(trigger.data["ticker"]))
bounce("ticker field is mandatory");
if (NOT exists(trigger.data["oracle1"]))
bounce("oracle1 field is mandatory");
if (NOT is_valid_address(trigger.data["oracle1"]))
bounce("oracle1 address is invalid");
if (NOT exists(trigger.data["feed1"]))
bounce("feed1 field is mandatory");
if (exists(trigger.data["oracle2"])){
if (NOT is_valid_address(trigger.data["oracle2"]))
bounce("oracle2 address is invalid");
if (NOT exists(trigger.data["oracle1"]))
bounce("If you provided oracle2 you have to provide oracle1");
if (NOT exists(trigger.data["feed2"]))
bounce("If you provided oracle2 you have to provide feed2");
if (NOT data_feed[[oracles=trigger.data["oracle2"], feed_name=trigger.data["feed2"], ifseveral="last", ifnone=false]])
bounce("oracle2 does not post that feed");
}
if (NOT data_feed[[oracles=trigger.data["oracle1"], feed_name=trigger.data["feed1"], ifseveral="last", ifnone=false]])
bounce("oracle1 does not post that feed");
}",
"messages": [
{
"app": "state",
"state": "{
var["CURRENCY_" || trigger.data["ticker"]] = {
oracle1: trigger.data["oracle1"],
feed1: trigger.data["feed1"],
oracle2: trigger.data["oracle2"],
feed2: trigger.data["feed2"]
};
}"
}
]
},
{
"if": "{
$method == "delCurrency"
}",
"init": "{
if (NOT exists(trigger.data["ticker"]))
bounce("ticker field is mandatory");
if (NOT var[trigger.data["ticker"]])
bounce("That currency is unknown for the AA");
}",
"messages": [
{
"app": "state",
"state": "{
var["CURRENCY_" || trigger.data["ticker"]] = false;
}"
}
]
},
{
"if": "{
$method == "setHelper"
}",
"init": "{
if (NOT exists(trigger.data["helper"]))
bounce("helper field is mandatory");
if (NOT is_valid_address(trigger.data["helper"]))
bounce("helper address is invalid");
}",
"messages": [
{
"app": "state",
"state": "{
var["helper_" || trigger.data["helper"]] = true;
}"
}
]
},
{
"if": "{
$method == "delHelper"
}",
"init": "{
if (NOT exists(trigger.data["helper"]))
bounce("helper field is mandatory");
if (NOT is_valid_address(trigger.data["helper"]))
bounce("helper address is invalid");
}",
"messages": [
{
"app": "state",
"state": "{
var["helper_" || trigger.data["helper"]] = false;
}"
}
]
}
]
}
},
{
"if": "{
(trigger.address == $owner OR $isHelper)
AND $method == "trust"
}",
"init": "{
if (NOT trigger.data["user"])
bounce("user field is mandatory");
if (NOT is_valid_address(trigger.data["user"]))
bounce("user field is an invalid address");
}",
"messages": [
{
"app": "state",
"state": "{
var["trusted_" || trigger.data["user"]] = true;
}"
}
]
},
{
"if": "{
(trigger.address == $owner OR $isHelper)
AND $method == "untrust"
}",
"init": "{
if (NOT trigger.data["user"])
bounce("user field is mandatory");
if (NOT is_valid_address(trigger.data["user"]))
bounce("user field is an invalid address");
}",
"messages": [
{
"app": "state",
"state": "{
var["trusted_" || trigger.data["user"]] = false;
}"
}
]
},
{
"if": "{
(trigger.address == $owner OR $isHelper)
AND $method == "untrust"
}",
"init": "{
if (NOT trigger.data["NFT"])
bounce("NFT field is mandatory");
}",
"messages": [
{
"app": "state",
"state": "{
var["FREE_" || trigger.data["NFT"]] = var["NFT_" || trigger.data["NFT"]];
var["NFT_" || trigger.data["NFT"]] = false;
}"
}
]
},
{
"if": "{
$method == "MINT"
}",
"init": "{
if (NOT exists(trigger.data["amount"]))
bounce("amount field is mandatory. Set it to 0 for unlimited");
if ((NOT is_valid_amount(trigger.data["amount"])) AND trigger.data["amount"] != 0) //amount=0 => uncapped
bounce("The amount of license copies to mint is not valid");
if (NOT exists(trigger.data["price"]))
bounce("price field is mandatory");
$priceCurrency = trigger.data["priceCurrency"] OTHERWISE false;
$price = trigger.data["price"]; //Price in the selected currency
$isTrusted = var["trusted_" || trigger.address];
if ($price <= 0) //Check that price in any currency is > 0
bounce("price field must be filled and it must be a positive number");
if (NOT exists(trigger.data["priceCurrency"])){ //It is priced in bytes
if ($price < 20000) //It not worth to sell for under 20KB
bounce("The minium price is 20.000 bytes");
if (NOT is_valid_amount($price))
bounce("price is not valid");
}
else{
if (NOT var["CURRENCY_" || trigger.data["priceCurrency"]])
bounce("That currency is currently not supported");
}
if (exists(trigger.data["maxPeriods"])){ //Periods are discrete to better represent how licenses currently work IRL (sellers may be interested in not selling continous periods)
if (NOT is_integer(trigger.data["maxPeriods"]))
bounce("maxPeriods must be an integer");
if (trigger.data["maxPeriods"] <= 0)
bounce("maxPeriods must be positive");
if (NOT exists(trigger.data["validity"]))
bounce("If you set maxPeriods you have to set validity too");
}
if (exists(trigger.data["validity"])){
if (NOT is_integer(trigger.data["validity"]))
bounce("validity must be an integer");
if (trigger.data["validity"] <= 0)
bounce("You cannot make a license valid for 0s or less");
}
if (trigger.address == $owner OR $isTrusted){ //These licenses will be listed for sale so we need metadata about them
if (NOT exists(trigger.data["ipfs"])) //meta.json IPFS CID
bounce("ipfs field is mandatory");
if (NOT exists(trigger.data["title"]))
bounce("title field is mandatory");
if (length(trigger.data["title"]) > 512) //Prevent locking AA funds by sending max length titles
bounce("The license title must be at most 512 characters long");
if (exists(trigger.data["ticker"])){
if (length(trigger.data["ticker"] < 128)) //Prevent locking AA funds by sending max length names
bounce("The length of the license ticker must be at most 128 characters long");
}
if (trigger.address == $owner){ //Only the owner can set a third party as the seller to prevent approved sellers to troll another sellers by creating licensed on their name
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");
}
$pendingNaming = var["NFT_" || var["pendingNaming"]]; //Previosly issued license
}
if (exists(trigger.data["soldAt"])){
if (NOT is_integer(trigger.data["soldAt"]))
bounce("soldAt must be an integer");
if (trigger.data["soldAt"] <= timestamp)
bounce("soldAt cannot be set in the past");
}
if (exists(trigger.data["tranferrable"])){
$isTransferrable = exists(trigger.data["validity"]) //Timed licenses are not transferrable
? false
: trigger.data["tranferrable"] == "true"
? true
: false;
}
else{
$isTransferrable = false;
}
}",
"messages": {
"cases": [
{
"if": "{
trigger.data["amount"]
}",
"messages": [
{
"app": "asset",
"payload": {
"cap": "{trigger.data["amount"]}",
"is_private": false,
"is_transferrable": "{$isTransferrable}",
"auto_destroy": false,
"fixed_denominations": false,
"issued_by_definer_only": true,
"cosigned_by_definer": false,
"spender_attested": false
}
},
{
"app": "data",
"if": "{
$pendingNaming
}",
"payload": {
"asset": "{var["recordToData"]}",
"name": "{($pendingNaming.ticker OTHERWISE "Untitled")|| " license"}",
"author": "{$pendingNaming.author}",
"ipfs": "{$pendingNaming.ipfs}",
"type": "LIC",
"decimals": 0
}
},
{
"app": "state",
"state": "{
if (trigger.address == $owner OR $isTrusted){
var["NFT_" || response_unit] = {
price: trigger.data["price"] OTHERWISE false,
priceCurrency: trigger.data["priceCurrency"] OTHERWISE false,
title: trigger.data["title"],
ipfs: trigger.data["ipfs"],
author: trigger.data["seller"],
validity: trigger.data["validity"] OTHERWISE false,
maxPeriods: trigger.data["maxPeriods"] OTHERWISE false,
soldAt: trigger.data["soldAt"] OTHERWISE false,
onSale: true,
type: 'LIC'
};
var["pendingNaming"] = response_unit;
}
else{ //Non trusted NFTs are unnamed (i.e: we never post a data app with metainfo)
var["FREE_" || response_unit] = {
price: trigger.data["price"] OTHERWISE false,
priceCurrency: trigger.data["priceCurrency"] OTHERWISE false,
author: trigger.address,
validity: trigger.data["validity"],
maxPeriods: trigger.data["maxPeriods"],
soldAt: trigger.data["soldAt"] OTHERWISE false,
onSale: true,
type: 'LIC'
};
}
}"
}
]
},
{
"messages": [
{
"app": "asset",
"payload": {
"is_private": false,
"is_transferrable": "{$isTransferrable}",
"auto_destroy": false,
"fixed_denominations": false,
"issued_by_definer_only": true,
"cosigned_by_definer": false,
"spender_attested": false
}
},
{
"app": "data",
"if": "{
$pendingNaming
}",
"payload": {
"asset": "{var["recordToData"]}",
"name": "{($pendingNaming.ticker OTHERWISE "Untitled")|| " license"}",
"author": "{$pendingNaming.author}",
"ipfs": "{$pendingNaming.ipfs}",
"type": "LIC",
"decimals": 0
}
},
{
"app": "state",
"state": "{
if (trigger.address == $owner OR $isTrusted){
var["NFT_" || response_unit] = {
price: trigger.data["price"] OTHERWISE false,
priceCurrency: trigger.data["priceCurrency"] OTHERWISE false,
title: trigger.data["title"],
ipfs: trigger.data["ipfs"],
author: trigger.data["seller"],
validity: trigger.data["validity"] OTHERWISE false,
maxPeriods: trigger.data["maxPeriods"] OTHERWISE false,
onSale: true,
type: 'LIC'
};
var["pendingNaming"] = response_unit;
}
else{
var["FREE_" || response_unit] = {
price: trigger.data["price"] OTHERWISE false,
priceCurrency: trigger.data["priceCurrency"] OTHERWISE false,
author: trigger.address,
validity: trigger.data["validity"],
maxPeriods: trigger.data["maxPeriods"],
onSale: true,
type: 'LIC'
};
}
}"
}
]
}
]
}
},
{
"if": "{
$method == "END_SALE"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$NFT = var["NFT_" || trigger.data["NFT"]] OTHERWISE var["FREE_" || trigger.data["NFT"]];
if (NOT $NFT)
bounce("That NFT is not known by this AA");
if (trigger.address != $NFT.author)
bounce("You cannot stop a sale not created by yourself");
}",
"messages": [
{
"app": "state",
"state": "{
if (var["NFT_" || trigger.data["NFT"]])
var["NFT_" || trigger.data["NFT"]] ||= {onSale: false};
else
var["FREE_" || trigger.data["NFT"]] ||= {onSale: false};
}"
}
]
},
{
"if": "{
trigger.data["method"] == "BUY"
}",
"init": "{
$NFT = var["NFT_" || trigger.data["NFT"]] OTHERWISE var["FREE_" || trigger.data["NFT"]];
$previousExpiry = var[trigger.address || "_" || trigger.data["NFT"]] OTHERWISE timestamp;
if (NOT $NFT)
bounce("NFT field is mandatory");
if ($NFT.soldAt){
if ($NFT.soldAt < timestamp)
bounce("That license sale period ended on " || timestamp_to_string($NFT.soldAt) || " UTC");
}
if (NOT $NFT.onSale)
bounce("The owner of that license cancelled its sale. No more copies will be ever sold.");
if (exists(trigger.data["periods"])){
if (NOT is_integer(trigger.data["periods"])) //Periods are discrete to better represent how licensing work right now
bounce("periods must be an integer");
if ($NFT.validity AND $NFT.maxPeriods){ //If it is a timed license with a maximum amount of periods check if the purchase does not exceed max purchasable time
if ($previousExpiry + trigger.data["periods"] * $NFT.validity > timestamp + ($NFT.maxPeriods OTHERWISE 1) * $NFT.validity)
bounce("You cannot buy that many periods in advance. You have to wait until your license is closer to expiry date");
}
}
$periods = trigger.data["periods"] OTHERWISE 1;
if ($NFT.priceCurrency){ //Not priced in bytes, lets calculate the equivalent value in bytes
$currency = var["CURRENCY_" || $NFT.priceCurrency];
$multiplier = $currency.feed2
? 1 / data_feed[[oracles=$currency.oracle2, feed_name=$currency.feed2, ifseveral="last"]] * data_feed[[oracles=$currency.oracle1, feed_name=$currency.feed1, ifseveral="last"]]
: data_feed[[oracles=$currency.oracle1, feed_name=$currency.feed1, ifseveral="last"]];
}
$expectedAmount = $NFT.priceCurrency
? round($NFT.price * 1 / $multiplier * $BYTES_IN_GBYTE, 0)
: $NFT.price;
response["rate"] = $multiplier / $BYTES_IN_GBYTE; //Price per byte
if (trigger.output[[asset="base"]].amount < $expectedAmount)
bounce("Your payment is lower than the NFT price. You have to send " || $expectedAmount || " bytes");
$pendingNaming = var["NFT_" || var["recordToData"]];
}",
"messages": [
{
"app": "payment",
"if": "{
floor($expectedAmount * (1 - $FEE), 0) > 0
}",
"payload": {
"asset": "base",
"outputs": [
{
"address": "{$NFT.author}",
"amount": "{floor($expectedAmount * (1 - $FEE), 0)}"
}
]
}
},
{
"app": "payment",
"payload": {
"asset": "{trigger.data['NFT']}",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$periods}"
}
]
}
},
{
"app": "data",
"if": "{
$pendingNaming
}",
"payload": {
"asset": "{var["recordToData"]}",
"name": "{($pendingNaming.ticker OTHERWISE "Untitled") || " license"}",
"author": "{$pendingNaming.author}",
"ipfs": "{$pendingNaming.ipfs}",
"type": "LIC",
"decimals": 0
}
},
{
"app": "state",
"state": "{
if ($pendingNaming){
var["recordToData"] = false;
}
if ($NFT.validity){ //It is a timed license, lets save expiry date for the buyer
var[trigger.address || "_" || trigger.data["NFT"]] = $previousExpiry + $NFT.validity * $periods; //Add the just purchased time to the subscription
}
}"
}
]
},
{
"if": "{
$method == "CHANGE_PRICE"
}",
"init": "{
if (NOT exists(trigger.data["NFT"]))
bounce("NFT field is mandatory");
$NFT = var["NFT_" || trigger.data["NFT"]] OTHERWISE var["FREE_" || trigger.data["NFT"]];
$isRegisteredNFT = var["NFT_" || trigger.data["NFT"]];
if (NOT $NFT)
bounce("NFT not found");
if ($NFT.author != trigger.address)
bounce("You can only change the price of your own licenses!");
if (exists(trigger.data["price"])){
if (NOT is_integer(trigger.data["price"]))
bounce("price must be an integer");
if (NOT is_valid_amount(trigger.data["price"]) OR trigger.data["price"] < 20000)
bounce("price must be at least 20.000 bytes");
if (exists(trigger.data["priceUSD"]))
bounce("You cannot set price and priceUSD at the same time");
}
if (NOT exists(trigger.data["price"]) AND NOT exists(trigger.data["priceUSD"]))
bounce("You have to provide either price or priceUSD field");
$price = trigger.data["price"] OTHERWISE trigger.data["priceUSD"];
}",
"messages": [
{
"app": "state",
"state": "{
var[($isRegisteredNFT ? "NFT_" : "FREE_") || trigger.data["NFT"]] ||= {
price: exists(trigger.data["price"]) ? $price : false,
priceUSD: exists(trigger.data["priceUSD"]) ? $price : 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 ($spack.signed_message["amount"])
if (NOT is_integer($spack.signed_message["amount"]))
bounce("You cannot redeem fractional license periods");
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");
//Check for address-bound codes
if ($spack.signed_message["claimer"])
if ($spack.signed_message["claimer"] != trigger.address)
bounce("You cannot claim that NFT from that address");
$NFT = var["NFT_" || $spack.signed_message.NFT] OTHERWISE var["FREE_" || $spack.signed_message.NFT];
if (NOT $NFT)
bounce("That license does not exist");
if ($spack.signed_message["expireDate"]){ //Check if the code has expired
if ($NFT.soldAt){ //The license sale had an expiration date. Let's check if it is expired
if ($NFT.soldAt < timestamp)
bounce("The sale of that license has already ended. You cannot claim a copy of it");
}
if ($spack.signed_message["expireDate"] < timestamp){
bounce("That NFT is no longer claimable");
}
}
$signedAmount = $spack.signed_message["amount"] OTHERWISE 1; //A code can grant more than 1 license period
if ($NFT.author != $spack.authors[0].address)
bounce("The signer of that message is not the issuer of that license");
//Uncapped asset, we can issue whatever amount was signed
if (NOT $NFT.cap){
$periods = $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)
$periods = $NFT.cap - $NFT.unitsSold;
else
$periods = $signedAmount;
}
//All copies were issued
if ($periods == 0)
bounce("All copies of that NFT have been already minted");
}",
"messages": [
{
"app": "payment",
"payload": {
"asset": "{$spack.signed_message["NFT"]}",
"outputs": [
{
"address": "{trigger.address}",
"amount": "{$periods}"
}
]
}
},
{
"app": "state",
"state": "{
if ($NFT.validity){
var[trigger.address || "_" || $spack.signed_message["NFT"]] = (var[trigger.address || "_" || $spack.signed_message["NFT"]] OTHERWISE 0) + $periods * $NFT.validity;
}
var["CODE_" || $spack.signed_message["NFT"] || "_" || sha256($spack.signed_message["serial"])] = true; //Mark as used
}"
}
]
}
]
}
}
]