Unit ID
curfwo3TPVcLynF5NWpYjJk+3Weo35JlMndJ/El/AE4=
Received
29.04.2021 16:31:56
Confirmation delay (full node)
5 minutes 26 seconds
Confirmation delay (light node)
7 minutes 34 seconds
Messages
Definition
Definition: [ "autonomous agent", { "bounce_fees": { "base": 10000 }, "init": "{ $HOUSE_CUT = 0.03; //We charge a 3% fee on all sales $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 I can withdraw without bouncing $owner = var["owner"]; $oracle = var["oracle"]; //Used for GBYTE_USD price }", "messages": { "cases": [ { "if": "{ NOT $owner //The owner has not been set }", "messages": [ { "app": "state", "state": "{ var["owner"] = "IUU43O7TS2TBYKAPGKUARDZHOTAE275A"; var["oracle"] = "F4KHJUCLJKY4JV7M5F754LAJX4EB7M4N"; }" } ] }, { "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 == "setOracle" OR $method == "setHelper" OR $method == "removeHelper") }", "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 == "setOracle" }", "init": "{ if (NOT exists(trigger.data["oracle"])) bounce("oracle field is mandatory"); if (NOT is_valid_address(trigger.data["oracle"])) bounce("Invalid oracle address"); if (NOT data_feed[[oracles=trigger.data["oracle"], feed_name="GBYTE_USD", ifseveral="last"]]) bounce("That oracle is not posting a GBYTE_USD datafeed"); }", "messages": [ { "app": "state", "state": "{ var["oracle"] = trigger.data["oracle"]; }" } ] }, { "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 == "removeHelper" }", "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": "{ $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 NFT copies to mint is not valid"); $price = trigger.data["priceUSD"] OTHERWISE trigger.data["price"]; $isHelper = trigger.data["helper_" || trigger.address]; if (NOT $price OR $price < 0) //Check that priceUSD is > 0 bounce("price or priceUSD field must be filled and it must be a positive number"); if (NOT exists(trigger.data["priceUSD"])){ //It is priced in bytes if ($price < 20000) bounce("The minium price is 20000 bytes"); if (NOT is_valid_amount($price)) bounce("price is not valid"); } if (exists(trigger.data["maxPeriods"])){ 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 $isHelper){ //These NFTs will be listed for sale so we need metadata about them if (NOT exists(trigger.data["ipfs"])) //meta.json bounce("ipfs field is mandatory"); if (NOT exists(trigger.data["title"])) bounce("title field is mandatory"); 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 NFT } 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"); } $isTransferrable = exists(trigger.data["validity"]) //Timed licenses are NOT transferrable ? false : trigger.data["transferrable"]; }", "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.title OTHERWISE "Untitled")|| " license"}", "author": "{$pendingNaming.author}", "ipfs": "{$pendingNaming.ipfs}", "type": "LIC", "decimals": 0 } }, { "app": "state", "state": "{ if (trigger.address == $owner OR $isHelper){ var["NFT_" || response_unit] = { price: trigger.data["price"] OTHERWISE false, priceUSD: trigger.data["priceUSD"] 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 }; var["pendingNaming"] = response_unit; } else{ var["FREE_" || response_unit] = { price: trigger.data["price"] OTHERWISE false, priceUSD: trigger.data["priceUSD"] OTHERWISE false, author: trigger.address, validity: trigger.data["validity"], maxPeriods: trigger.data["maxPeriods"], soldAt: trigger.data["soldAt"] OTHERWISE false, onSale: true }; } }" } ] }, { "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.title OTHERWISE "Untitled")|| " license"}", "author": "{$pendingNaming.author}", "ipfs": "{$pendingNaming.ipfs}", "type": "LIC", "decimals": 0 } }, { "app": "state", "state": "{ if (trigger.address == $owner OR $isHelper){ var["NFT_" || response_unit] = { price: trigger.data["price"] OTHERWISE false, priceUSD: trigger.data["priceUSD"] 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 }; var["pendingNaming"] = response_unit; } else{ var["FREE_" || response_unit] = { price: trigger.data["price"] OTHERWISE false, priceUSD: trigger.data["priceUSD"] OTHERWISE false, author: trigger.address, validity: trigger.data["validity"], maxPeriods: trigger.data["maxPeriods"], onSale: true }; } }" } ] } ] } }, { "if": "{ $method == "END_SALE" }", "init": "{ if (NOT exists(trigger.data["NFT"])) bounce("NFT field is mandatory"); $NFT = var["NFT_" || trigger.data["NFT"]]; if (NOT $NFT) bounce("That NFT is not known by this AA"); if (trigger.address != $NFT.author) //The owner can also stops sales (as we expect authors losing their private keys) bounce("You cannot stop a sale not created by yourself"); }", "messages": [ { "app": "state", "state": "{ var["NFT_" || trigger.data["NFT"]] ||= {onSale: false}; }" } ] }, { "if": "{ trigger.data["method"] == "BUY" }", "init": "{ $NFT = var["NFT_" || trigger.data["NFT"]]; $previousExpiry = var[trigger.address || "_" || trigger.data["NFT"]] OTHERWISE timestamp; 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("That license is not on sale"); if (exists(trigger.data["periods"])){ if (NOT is_integer(trigger.data["periods"])) 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; $expectedAmount = $periods * ($NFT.price ? $NFT.price : round($NFT.priceUSD * 1 / data_feed[[oracles=$oracle, feed_name="GBYTE_USD", ifseveral="last"]] * $BYTES_IN_GBYTE, 0)); 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 - $HOUSE_CUT), 0) > 0 }", "payload": { "asset": "base", "outputs": [ { "address": "{$NFT.author}", "amount": "{floor($expectedAmount * (1 - $HOUSE_CUT), 0)}" } ] } }, { "app": "payment", "payload": { "asset": "{trigger.data['NFT']}", "outputs": [ { "address": "{trigger.address}", "amount": 1 } ] } }, { "app": "data", "if": "{ $pendingNaming }", "payload": { "asset": "{var["recordToData"]}", "name": "{($pendingNaming.title 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 > 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": "{ if ($isRegisteredNFT){ var[($isRegisteredNFT ? "NFT_" : "FREE_") || trigger.data["NFT"]] ||= { //Dirty trick in key calculation to save complexity price: exists(trigger.data["price"]) ? $price : false, priceUSD: exists(trigger.data["priceUSD"]) ? $price : false, }; } }" } ] }, { "if": "{ $method == "REDEEM" }", "init": "{ if (NOT exists(trigger.data["spack"])) bounce("You need to provide a signed package"); $spack = json_parse(trigger.data["spack"]); if (NOT is_valid_signed_package($spack, $spack.authors[0].address)) bounce("The signed message signature is invalid"); if (NOT $spack.signed_message.NFT) bounce("The signer of this message did not include a NFT field"); $NFT = var["NFT_" || $spack.signed_message.NFT]; if (NOT $NFT) bounce("That license does not exist"); //Check if sale is over and expire was set in the signed message. Otherwise the NFT can be redeemed even if the sale was over. if ($spack.signed_message.expireDate){ if ($spack.signed_message.expireDate < timestamp) bounce("That license is no longer claimable"); } $signedAmount = $spack.signed_message.amount OTHERWISE 1; $unit = unit[$spack.signed_message.NFT]; $cap = $unit.messages[[.app="asset"]].payload.cap; if ($NFT.author != $spack.authors[0].address) bounce("The signer of that message is not the author of that license"); }", "messages": [ { "app": "payment", "payload": { "asset": "{$spack.signed_message.NFT}", "outputs": [ { "address": "{trigger.address}", "amount": "{$NFT.signedAmount}" } ] } }, { "app": "state", "state": "{ if ($NFT.validity){ var[trigger.address || "_" || $spack.signed_message.NFT] = (var[trigger.address || "_" || $spack.signed_message.NFT] OTHERWISE 0) + $NFT.signedAmount * $NFT.validity; } }" } ] } ] } } ]
Technical information
Fees:
16,219 bytes
(452 headers, 15767 payload)
Level:1984791
Witnessed level:1984784
Main chain index:1976817
Latest included mc index:1976816
Status:stable/confirmed/final