logo
back
back
Back

Using dApps, Smart account and smart asset, you can implement various blockchain-empowered applications: gaming and gambling, DeFi, digital identity, supply chains, and many others

Create Smart AssetSmart AssetSmart Asset

The functionality of a WAVES account only allows you to verify that the transaction released from it was actually sent from this account

More info
gradient
img
img

Create your first Smart asset in one step in WAVES IDE

Smart-asset without burning

arrow
plus

Create your first Smart Asset

Step 1

Directives

Each Ride script should start with directives These directives tell the compiler that:

< The script uses Standard library version 5 >
< Type of the script is Smart Account >
< The script will be assigned to an account >
ride
Copy
1
2
3
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ASSET #-}

Auxiliary Definitions and boolean expressions

After the directives, you can define auxiliary variables and functions. The expression checks transactions involving the asset for compliance with the specified conditions. If the conditions are not met, the transaction is denied

ride
Copy
1
2
3
4
match tx {
    case t : SetAssetScriptTransaction | BurnTransaction => throw("You can`t set new script or burn token")
    case _ => true
}

Add script into your project

It's much easier than you imagine. Say "Hello World" in WAVES Network Insert this script into your project

New project IDE
ride
Copy
1
2
3
4
5
6
7
8
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ASSET #-}

match tx {
    case t : SetAssetScriptTransaction | BurnTransaction => throw("You can`t set new script or burn token")
    case _ => true
}
arrow
plus

Deploy

Step 2

deploy
deploy
deploy

Deploy your script into Waves Network

deploy
deploy
deploy

During deployment you'll need to create your account if you still haven't it

arrow
plus

Run test

Step 3

// Run test with help of Surfboard

// In the before section, we perform all the actions that we need to test the Smart account and certain number of accounts with the specified balance is generated

ride
Copy
1
2
3
4
5
6
7
8
9
10
11
describe('Smart-asset test suite', async function () {
    const wvs = 10 ** 8;
    let curtAssetId;

    this.timeout(100000);

before(async function () {
    await setupAccounts({
        curt: 10 * wvs
    });
});

//Deployment is carried out by the transaction of installing the script on the account

ride
Copy
1
2
3
4
5
6
7
8
9
10
11
    accontScript = compile(file('account-script.ride'));
const ssTx = setScript({ script: accontScript }, accounts.user);
    await broadcast(ssTx);
    await waitForTx(ssTx.id);

    const ssTx2 = setScript({ script: accontScript }, accounts.secondUser);
    await broadcast(ssTx2);
    await waitForTx(ssTx2.id);

    console.log('Script has been set')
});

//Each section of it() serves to test some functionality
// Transfer WAVES

ride
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
it('Should transfer less than 1500', async function () {
    const transferTx = transfer({ amount: 1000, recipient: address(accounts.secondUser), fee: 1000000 }, accounts.user);

    // Get account effective balance
    const userBalanceBefore = await balance(address(accounts.user));
    const secondUserBalanceBefore = await balance(address(accounts.secondUser));

    await broadcast(transferTx);
    await waitForTx(transferTx.id);

    const userBalanceAfter = await balance(address(accounts.user));
    const secondUserBalanceAfter = await balance(address(accounts.secondUser));

    expect(userBalanceBefore).to.be.gt(userBalanceAfter);
    expect(secondUserBalanceBefore).to.be.lt(secondUserBalanceAfter);
})

// Trying to make a transfer with an amount that exceeds the limit

ride
Copy
1
2
3
4
5
6
7
8
9
10
11
it('Should not transfer more than 1000', async function () {
    const transferTx = transfer({ amount: 2000, recipient: address(accounts.secondUser), fee: 1000000 }, accounts.user);

    expect(broadcast(transferTx)).to.be.rejectedWith("");
})

it('Should not set script again', async function () {
    const ssTx = setScript({ script: accontScript, fee: 1500000 }, accounts.user);

    expect(broadcast(ssTx)).to.be.rejectedWith("");
})

// Make records to the state as a data transaction to check the standard behavior of the account for other types of transactions

ride
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
it('Should store data', async function () {
    const records = [
        { key: 'integerVal', value: 1 },
        { key: 'booleanVal', value: true },
        { key: 'stringVal', value: 'Lorem ipsum dolor sit amet' }
    ]

    const dataTx = data({ data: records, fee: 1000000 }, accounts.user);
    await broadcast(dataTx);
    await waitForTx(dataTx.id);

    const store = await accountData(address(accounts.user));

    expect(store["integerVal"].value).to.be.equal(1);
    expect(store["booleanVal"].value).to.be.equal(true);
    expect(store["stringVal"].value).to.be.equal('Lorem ipsum dolor sit amet');
  })
})

Also you can run test by using InvokeScript transaction

//An example of a function call from dApp

java
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import com.wavesplatform.crypto.Crypto;
import com.wavesplatform.transactions.BurnTransaction;
import com.wavesplatform.transactions.IssueTransaction;
import com.wavesplatform.transactions.SetAssetScriptTransaction;
import com.wavesplatform.transactions.TransferTransaction;
import com.wavesplatform.transactions.account.PrivateKey;
import com.wavesplatform.transactions.common.Amount;
import com.wavesplatform.transactions.common.AssetId;
import com.wavesplatform.transactions.common.Base64String;
import com.wavesplatform.wavesj.Node;
import com.wavesplatform.wavesj.Profile;
import com.wavesplatform.wavesj.ScriptInfo;
import com.wavesplatform.wavesj.exceptions.NodeException;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.web3j.utils.Files;

import java.io.File;
import java.io.IOException;

public class AssetScriptTest {

    static Node node;

    static PrivateKey curt;

    // additional info can be found - https://hub.docker.com/r/wavesplatform/waves-private-node
    static PrivateKey faucet = PrivateKey.fromSeed("waves private node seed with waves tokens");

    static int errorCode = 306;

    // In the before section, we perform all the actions that we need to test scripts
    @BeforeClass
    public static void before() throws NodeException, IOException {
        node = new Node(Profile.LOCAL);

        // A certain number of accounts with the specified balance is generated
        curt = PrivateKey.fromSeed(Crypto.getRandomSeedBytes());
        TransferTransaction transfer1 = TransferTransaction.builder(
            curt.address(), Amount.of(10_00_000_000)
        ).getSignedWith(faucet);
        node.broadcast(transfer1);
        node.waitForTransaction(transfer1.id());
    }

    @Test
    public void test() throws NodeException, IOException {
        //Should issue smart-asset with quantity 100_000_000'

        // Specify the parameters of the smart asset (at this stage, a stub is installed as a script,
        // which is equal "true")
        IssueTransaction issueTx = IssueTransaction.builder(
            "mySmartToken",
            100_000_000,
            8
        )
        .isReissuable(true)
        .description("some desc")
        .script(new Base64String("AQa3b8tH"))
        .getSignedWith(curt);

        AssetId curtAssetId = node.broadcast(issueTx).assetId();
        node.waitForTransaction(issueTx.id());

        // Check the balance of the token on the account - all the tokens of the issuing account
        long total = node.getAssetBalance(curt.address(), curtAssetId);
        Assert.assertEquals(issueTx.quantity(), total);

        // Should set real script and try to change script

        // Compiling the Smart Asset Script
        String source = Files.readString(new File("src/test/resources/smart-asset-script.ride"));
        ScriptInfo assetScript = node.compileScript(source);

        // Install the script on the smart asset (already allowed to do this)
        SetAssetScriptTransaction sasTx = SetAssetScriptTransaction.builder(
            curtAssetId, assetScript.script()
        ).getSignedWith(curt);
        node.broadcast(sasTx);
        node.waitForTransaction(sasTx.id());

        // Try to change the smart asset script again, get an errornode.waitForTransaction(sasTx.id());
        SetAssetScriptTransaction sasTx2 = SetAssetScriptTransaction.builder(
            curtAssetId, assetScript.script()
        ).getSignedWith(curt);
        try {
            node.broadcast(sasTx2);
        } catch (NodeException nodeException) {
            Assert.assertEquals(errorCode, nodeException.getErrorCode());
        }

        //Should try burn some curtAsset

        // Trying to burn a certain number of tokens - get an error, because the script forbids from such actions with the asset
        BurnTransaction bTx = BurnTransaction.builder(Amount.of(100, curtAssetId))
            .extraFee(400000)
            .getSignedWith(curt);
        try {
            node.broadcast(bTx);
        } catch (NodeException nodeException) {
            Assert.assertEquals(errorCode, nodeException.getErrorCode());
        }
    }
}

Congragulations!

You've been created your first dApp successfully. It has been published on the Waves.

Still have questions?

Lets

Keep in touchKeep in touchKeep in touch

Learn about new infrastructure products, libraries, and new solutions for easy blockchain work