Unit ID
Cuab6V09Eqh1LTANOmNuR0ufIthHFg15Qt/IR3N1GAU=
Received
03.06.2021 17:10:19
Confirmation delay (full node)
3 minutes 33 seconds
Confirmation delay (light node)
5 minutes 30 seconds
Messages
Definition
Definition: [ "autonomous agent", { "bounce_fees": { "base": 10000 }, "init": "{ $BYTES_IN_GBYTE = 1000000000; $USER_REGISTRY_ADDRESS = "4APCELFBBFQU7UF46RWOQVZPQ5Z67IBH"; $CURRENCY_REGISTRY = "AO46ELAIDT2LX2RXVVABIABXNBKHRSCE"; 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]; /* ** 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["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 == "reduceFee" OR $method == "untrust" OR $method == "delHelper") }", "messages": { "cases": [ { "if": "{ $method == "payout" }", "messages": [ { "app": "payment", "payload": { "asset": "base", "outputs": [ { "address": "{trigger.address}", "amount": "{$spendableFunds}" } ] } } ] }, { "if": "{ $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": "{ $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 == "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["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"); $currency = $CURRENCY_REGISTRY.$getCurrency(trigger.data["currency"]); $profile = $USER_REGISTRY_ADDRESS.$profileOf(trigger.address); if (trigger.data["currency"]){ if (NOT $currency) bounce("The provided currency is not supported"); } $price = trigger.data["currency"] //Price in the selected currency ? $CURRENCY_REGISTRY.$convert(trigger.data["price"], trigger.data["currency"]) : trigger.data["price"]; if ($price <= 0) //Check that price in any currency is > 0 bounce("price field must be higher than 0"); if (NOT exists(trigger.data["currency"])){ //It is priced in bytes if ($price < 20000) //It not worth to sell for under 20KB also it might be an user error trying to price in GBYTES bounce("The minium price is 20.000 bytes"); if (NOT is_valid_amount($price)) bounce("price is not valid"); } 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 $profile.isLicensor){ //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 $profile.isLicensor){ var["NFT_" || response_unit] = { price: trigger.data["price"] OTHERWISE false, currency: trigger.data["currency"] 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, currency: trigger.data["currency"] 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 $profile.isLicensor){ var["NFT_" || response_unit] = { price: trigger.data["price"] OTHERWISE false, currency: trigger.data["currency"] 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 = $USER_REGISTRY_ADDRESS.$licenseExpirationOf(trigger.address, trigger.data["NFT"]); 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"); } $periods = trigger.data["periods"] OTHERWISE 1; if ($NFT.currency) //Not priced in bytes, lets calculate the equivalent value in bytes by doing fiat=>USD=>byte if there is no direct conversion available $expectedAmount = $CURRENCY_REGISTRY.$convert($NFT.price, $NFT.currency) * $periods; else $expectedAmount = $NFT.price * $periods; if ($NFT.maxPeriods){ $remainingPeriods = ceil((($USER_REGISTRY_ADDRESS.$licenseExpirationOf(trigger.address, trigger.data["NFT"]) OTHERWISE timestamp) - timestamp) / $NFT.validity, 0); if ($remainingPeriods + $periods > $NFT.maxPeriods) bounce("You cannot buy that many periods in advance. Please try again when your license is closer to expiry"); } response["rate"] = $CURRENCY_REGISTRY.$getExchangeRate($NFT.currency) / $BYTES_IN_GBYTE; //Price per byte response["price"] = $expectedAmount; response["currency"] = $NFT.currency; 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)}" }, { "if": "{$NFT.validity}", "address": "{$USER_REGISTRY_ADDRESS}", "amount": 1 } ] } }, { "app": "payment", "payload": { "asset": "{trigger.data['NFT']}", "outputs": [ { "address": "{trigger.address}", "amount": "{$periods}" } ] } }, { "app": "data", "if": "{ $pendingNaming OR $NFT.validity }", "init": "{ $payload = ($pendingNaming?{ asset: var["recordToData"], name: ($pendingNaming.ticker OTHERWISE "Untitled") || " license", author: $pendingNaming.author, ipfs: $pendingNaming.ipfs, type: "LIC", decimals: 0 } : {}) || ($NFT.validity? {NFT: trigger.data["NFT"], time: $NFT.validity * $periods, method: "addTime"} : {}); }", "payload": "{$payload}" }, { "app": "state", "state": "{ if ($pendingNaming) var["recordToData"] = false; }" } ] }, { "if": "{ $method == "CHANGE_PRICE" }", "init": "{ if (NOT exists(trigger.data["NFT"])) bounce("NFT field is mandatory"); $currency = $CURRENCY_REGISTRY.$getCurrency(trigger.data["currency"]); if (NOT exists(trigger.data["price"])) bounce("price field is mandatory"); if (NOT exists(trigger.data["currency"])){ if (trigger.data["price"] < 20000) bounce("price must be greater than 20.000 bytes"); } else{ if (NOT $currency) bounce("That currency is unsupported"); } $price = trigger.data["currency"] ? $CURRENCY_REGISTRY.$convert(trigger.data["price"], trigger.data["currency"]) : trigger.data["price"]; //Price in the selected currency if ($price <= 0) //Check that price in any currency is > 0 bounce("price field must be higher than 0"); $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!"); }", "messages": [ { "app": "state", "state": "{ var[($isRegisteredNFT ? "NFT_" : "FREE_") || trigger.data["NFT"]] ||= { price: $price, currency: trigger.data["currency"] OTHERWISE 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}" }, { "if": "{$NFT.validity}", "address": "{$USER_REGISTRY_ADDRESS}", "amount": 1 } ] } }, { "if": "{ $NFT.validity }", "app": "data", "payload": { "NFT": "{$spack.signed_message["NFT"]}", "time": "{$signedAmount * $NFT.validity}" } }, { "app": "state", "state": "{ var["CODE_" || $spack.signed_message["NFT"] || "_" || sha256($spack.signed_message["serial"])] = true; //Mark as used }" } ] } ] } } ]
Technical information
Fees:
21,506 bytes
(452 headers, 21054 payload)
Level:2031474
Witnessed level:2031465
Main chain index:2023482
Latest included mc index:2023481
Status:stable/confirmed/final