In the two previous articles we discussed using smart accounts and smart assets for running auctions and creating customer loyalty programs , as well as facilitating transparency for financial instruments.
Today, we will look at some specific use cases for smart assets, including asset freezing and restricting transactions for certain addresses.
Smart assets allow Waves users to apply scripts to tokens in much the same way as they can apply scripts to smart accounts. Whenever a transaction for a smart asset is created, it is validated by the script before being confirmed by the blockchain.
Smart assets differ from smart accounts in the following ways:
- In a smart asset’s code, proofs cannot be checked (as discussed in the first article)
- In a smart account’s code, an ExchangeTransaction can only be checked if your account is a matcher account. Otherwise, only an order can be checked. In a smart asset’s code, you cannot check an order, but you can check an ExchangeTransaction and, if necessary, extract an order from it.
- Unlike a smart account, a smart asset doesn’t have a state, but we still have access to account states from the script.
Smart assets help to substantially simplify the process of writing contracts, making implementation of many use cases more concise and elegant.
Asset Freezing
To freeze assets until a certain block height, targetHeight, is reached, simply define this value in the following smart asset’s script:
let targetHeight = 1500000
height >= targetHeight
height is a language function that returns the current height
Using a certain matcher
To define a certain matcher, you can assign the matcher address as the sender value in the following smart asset’s script:
match tx
case t : ExchangeTransaction =>
t.sender == addressFromString("3PJaDyprvekvPXPuAtxrapacuDJopgJRaU3")
case _ => true
Recipient whitelist
To allow transfer of tokens only to specific accounts — to create a “whitelist” — you can use a smart asset with the following script, which checks for the presence of an address on the list:
match tx t.recipient == trustedRecipient3
case _ => false
For purposes of security and provable completion of language, the list doesn’t complete implementation of the iterator. Therefore, it is defined as a set of specific elements.
Recipient blacklist
Similarly, to prohibit transfer of tokens to specific accounts, you can create a blacklist. Exactly the same smart asset script will be used, but it will check an address for absence from the blacklist:
match tx
case t : TransferTransaction =>
let bannedRecipient1 = addressFromString("3P6ms9EotRX8JwSrebeTXYVnzpsGCrKWLv4")
let bannedRecipient2 = addressFromString("3PLZcCJyYQnfWfzhKXRA4rteCQC9J1ewf5K")
let bannedRecipient3 = addressFromString("3PHrS6VNPRtUD8MHkfkmELavL8JnGtSq5sx")
t.recipient != bannedRecipient1 && t.recipient != bannedRecipient2 && t.recipient != bannedRecipient3
case _ => false
Transferring by issuer permission
Using a smart asset, you can also add the option of transferring the smart asset only with its issuer’s permission (commitment/debt label). The issuer expresses their consent by placing the transaction’s ID in their account’s state:
match tx
case t : TransferTransaction =>
let issuer = extract(addressFromString("3P6ms9EotRX8JwSrebeTXYVnzpsGCrKWLv4"))
#checking that the issuer's state contains the current transaction's ID
isDefined(getInteger(issuer, toBase58String(t.id)))
case _ => false
Asset tradable only against a certain currency
A smart asset can allow trading solely against specific coins. For instance, to ensure the smart asset trades only with bitcoin, the following code can be used:
let BTCId = base58'8LQW8f7P5d5PZM7GtZEBgaqRPGSzS3DfPuiXrURJ4AJS'
match tx
case t : ExchangeTransaction =>
t.sellOrder.assetPair.priceAsset == BTCId
Trading with prices from an oracle
In a smart asset’s script, you can set permission for trading only at a price fixed in the trusted oracle’s state:
let oracle = Address(base58'3PLNmokt22NrSiNvCLvwMUP84LCMJqbXwAD')
let assetId = toBase58String(base58'oWgJN6YGZFtZrV8BWQ1PGktZikgg7jzGmtm16Ktyvjd')
match tx MassTransferTransaction => false
case e: ExchangeTransaction =>
#checking that trading is at the price fixed in the oracle's state for this asset
let correctPrice = e.price == extract(getInteger(oracle, assetId))
#checking that the trading is in exchange for WAVES
let correctPriceAsset = !isDefined(e.sellOrder.assetPair.priceAsset)
correctPrice && correctPriceAsset
case _ => true
Here, we face a special situation while checking the ID of the asset against which our smart asset trades. If the asset’s ID is not defined, it is WAVES by default. In the script, we check that trading is to be against WAVES.
Fixed price raise
You can set a fixed price for a smart asset, which can be raised in steps by a specific proportion. This is an example of a script for a smart asset traded with a fixed price that is raised by 5% every 1,000 blocks:
let startPrice = 10
let startHeight = 1000
let interval = 1000
#by what percentage the price is increased in one step
let raise = 5
match tx MassTransferTransaction => false
case e: ExchangeTransaction =>
e.price == startPrice + ((height - startHeight) / interval) * (100 + raise) / 100
&& !isDefined(e.sellOrder.assetPair.priceAsset)
case _ => true
Interval trading
A script can also be applied to allow trading of a smart asset that is limited to a predetermined time frame. This is an example of such a script:
let startHeight = 10000
let interval = 44000
let limit = 1500
match tx MassTransferTransaction
In the script, we check that no more than “limit” of intervals has elapsed since the start of trading (startHeight). The interval length equals the number of blocks defined in the interval field.
Join Waves Community
Read Waves News channel
Follow Waves Twitter
Subscribe to Waves Subreddit
Waves Smart Asset Applications: Whitelists, Blacklists and Interval Trading was originally published in Waves Platform on Medium, where people are continuing the conversation by highlighting and responding to this story.