The functionality of a WAVES account only allows you to verify that the transaction released from it was actually sent from this account
Smart-accont forbids some types of transactions
Each Ride script should start with directives These directives tell the compiler that:
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ASSET #-}
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
let LIMIT = 1500
let START_TRANSFER = 0
# "height" is built-in variable. Blockchain height at the script execution time
func checkBlockHeight() = height >= START_TRANSFER
match tx {
case t: SetScriptTransaction => false
case ttx: TransferTransaction => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) && ttx.amount <= LIMIT && checkBlockHeight()
case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}
It's much easier than you imagine. Say "Hello World" in WAVES Network Insert this script into your project
{-# STDLIB_VERSION 6 #-}
{-# CONTENT_TYPE EXPRESSION #-}
{-# SCRIPT_TYPE ASSET #-}
let LIMIT = 1500
let START_TRANSFER = 0
# "height" is built-in variable. Blockchain height at the script execution time
func checkBlockHeight() = height >= START_TRANSFER
match tx {
case t: SetScriptTransaction => false
case ttx: TransferTransaction => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey) && ttx.amount <= LIMIT && checkBlockHeight()
case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}
Deploy your script into Waves Network
During deployment you'll need to create your account if you still haven't it
// 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
describe('Account-script test suite', async function () {
const wvs = 10 ** 8;
let accontScript;
this.timeout(100000);
before(async function () {
// Генерируется какое-то количество аккаунтов с указанным балансом
await setupAccounts({
user: 1 * wvs,
secondUser: 1 * wvs
});
//Deployment is carried out by the transaction of installing the script on the account
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
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
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
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');
})
})
//An example of a function call from dApp
import com.wavesplatform.crypto.Crypto;
import com.wavesplatform.transactions.DataTransaction;
import com.wavesplatform.transactions.SetScriptTransaction;
import com.wavesplatform.transactions.TransferTransaction;
import com.wavesplatform.transactions.account.PrivateKey;
import com.wavesplatform.transactions.common.Amount;
import com.wavesplatform.transactions.common.Base64String;
import com.wavesplatform.transactions.data.BooleanEntry;
import com.wavesplatform.transactions.data.DataEntry;
import com.wavesplatform.transactions.data.IntegerEntry;
import com.wavesplatform.transactions.data.StringEntry;
import com.wavesplatform.wavesj.Node;
import com.wavesplatform.wavesj.Profile;
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;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class AccountScriptTest {
static PrivateKey user;
static PrivateKey secondUser;
static Node node;
static Base64String accountScript;
static int errorCode = 307;
// 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");
@BeforeClass
public static void before() throws NodeException, IOException {
node = new Node(Profile.LOCAL);
// A certain number of accounts with the specified balance is generated
user = PrivateKey.fromSeed(Crypto.getRandomSeedBytes());
TransferTransaction transfer1 = TransferTransaction.builder(
user.address(), Amount.of(1_00_000_000)
).getSignedWith(faucet);
node.broadcast(transfer1);
node.waitForTransaction(transfer1.id());
secondUser = PrivateKey.fromSeed(Crypto.getRandomSeedBytes());
TransferTransaction transfer2 = TransferTransaction.builder(
secondUser.address(), Amount.of(1_00_000_000)
).getSignedWith(faucet);
node.broadcast(transfer2);
node.waitForTransaction(transfer2.id());
// Deployment is carried out by the transaction of installing the script on the account
String source = Files.readString(new File("src/test/resources/account-script.ride"));
accountScript = node.compileScript(source).script();
SetScriptTransaction ssTx = SetScriptTransaction.builder(accountScript).getSignedWith(user);
node.broadcast(ssTx);
node.waitForTransaction(ssTx.id());
SetScriptTransaction ssTx2 = SetScriptTransaction.builder(accountScript).getSignedWith(secondUser);
node.broadcast(ssTx2);
node.waitForTransaction(ssTx2.id());
System.out.println("Script has been set");
}
@Test
public void should_transfer_less_than_1500() throws IOException, NodeException {
// Transfer WAVES
TransferTransaction transferTx = TransferTransaction.builder(
secondUser.address(), Amount.of(1000)
).getSignedWith(user);
// Get account effective balance
long userBalanceBefore = node.getBalance(user.address());
long secondUserBalanceBefore = node.getBalance(secondUser.address());
node.broadcast(transferTx);
node.waitForTransaction(transferTx.id());
long userBalanceAfter = node.getBalance(user.address());
long secondUserBalanceAfter = node.getBalance(secondUser.address());
// Compare user balances
Assert.assertTrue(userBalanceBefore > userBalanceAfter);
Assert.assertTrue(secondUserBalanceBefore < secondUserBalanceAfter);
Assert.assertTrue(secondUserBalanceBefore < secondUserBalanceAfter);
}
@Test
public void should_not_transfer_more_than_1500() throws NodeException, IOException {
// Trying to make a transfer with an amount that exceeds the limit
TransferTransaction transferTx = TransferTransaction.builder(
secondUser.address(), Amount.of(2000)
).getSignedWith(user);
try {
node.broadcast(transferTx);
} catch (NodeException nodeException) {
Assert.assertEquals(errorCode, nodeException.getErrorCode());
}
}
@Test
public void should_not_set_script_again() throws NodeException, IOException {
// Trying to change the script to a new one
SetScriptTransaction ssTx = SetScriptTransaction.builder(accountScript).getSignedWith(user);
try {
node.broadcast(ssTx);
} catch (NodeException nodeException) {
Assert.assertEquals(errorCode, nodeException.getErrorCode());
}
}
@Test
public void should_store_data() throws NodeException, IOException {
// Make records to the state as a data transaction to check the standard behavior of the account for other types of transactions
DataTransaction dataTx = DataTransaction.builder(
IntegerEntry.as("integerVal", 1),
BooleanEntry.as("booleanVal", true),
StringEntry.as("stringVal", "Lorem ipsum dolor sit amet")
).getSignedWith(user);
node.broadcast(dataTx);
node.waitForTransaction(dataTx.id());
List<DataEntry> store = node.getData(user.address());
Assert.assertEquals(3, store.size());
Map<String, DataEntry> dataEntryMap = store.stream().collect(Collectors.toMap(DataEntry::key, item -> item));
Assert.assertEquals(1L, dataEntryMap.get("integerVal").valueAsObject());
Assert.assertEquals(true, dataEntryMap.get("booleanVal").valueAsObject());
Assert.assertEquals("Lorem ipsum dolor sit amet", dataEntryMap.get("stringVal").valueAsObject());
}
}
You've been created your first dApp successfully. It has been published on the Waves.