Echo tutorial
A tutorial on how to build, test and deploy a simple interactable smart contract in Acala EVM+.
This is an example that builds upon the hello-world example with additional functionalities, like support for events, interactable public functions and private variables. As the hello-world already contains an example on how to build the project, we will only focus on building the smart contract, test and deploy scripts. For the setup and naming, replace the hello-world with echo. Let's jump into it!
NOTE: You can refer to the complete code of this tutorial at https://github.com/AcalaNetwork/truffle-tutorials/tree/master/echo
In this tutorial we will be adding a simple smart contract that has a public variable called
echo
, which stores the latest string passed to a public function. Every time the echo
variable is changed, the event, containing the latest value as well as the number of time it was changed, is emitted.Your empty smart contract should look like this:
pragma solidity =0.8.9;
contract Echo{
}
echo
variable, used to store the string passed to it, is placed at the beginning of the example. Its visibility should be set to public, so that the compiler builds a getter function for it. echoCount
variable is used to count the number of times the echo
variable is changed. Additionally we will have a NewEcho
event that will be emitted every time the echo
variable is changed and it will contain the new value as well as the number of times the echo
variable is changed. The content of the smart contract, including these two variables and the event, looks like this: string public echo;
uint echoCount;
event NewEcho(string message, uint count);
The
constructor
function can set the initial value of the echo
variable. Let's set it to Deployed successfully!
, to signal that the smart contract is ready to use: constructor() {
echo = "Deployed successfully!";
}
The last thing to add is a function that allows us to change the value of the
echo
variable. The function should assign the new value to the echo
variable, increment the echoCount
, emit the NewEcho
event and return the input string. Let's call this function scream()
as it will cause an echo: function scream(string memory message) public returns(string memory){
echo = message;
echoCount += 1;
emit NewEcho(message, echoCount);
return message;
}
This concludes our
Echo
smart contract.pragma solidity =0.8.9;
contract Echo{
string public echo;
uint echoCount;
event NewEcho(string message, uint count);
constructor() {
echo = "Deployed successfully!";
}
function scream(string memory message) public returns(string memory){
echo = message;
echoCount += 1;
emit NewEcho(message, echoCount);
return message;
}
}
As the Echo smart contract is ready to be compiled, we can use the
yarn build
command (like we did in the hello-world) to compile the smart contract, which will create the build
directory and contain the compiled smart contract.Your test file should be called
x_echo.js
and the empty test along with the import statement should look like this:const Echo = artifacts.require("Echo");
/*
* uncomment accounts to access the test accounts made available by the
* Ethereum client
* See docs: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-javascript
*/
contract("Echo", function (/* accounts */) {
});
To prepare for the testing, we have to define
instance
global variable. The instance
will store the deployed Echo smart contract. Let's assign it a value in the beforeEach
action: let instance;
beforeEach("setup development environment", async function () {
instance = await Echo.deployed();
});
Our test will be split into two sections,
Deployment
and Operation
: describe("Deployment", function () {
});
describe("Operation", function () {
});
Within
Deployment
describe block we will ensure that the test suite works as expected and validate that the echo
variable is set to Deployed successfully!
: it("should assert true", async function () {
return assert.isTrue(true);
});
it("returns the right value after the contract is deployed", async function() {
const echo = await instance.echo();
expect(echo).to.equal("Deployed successfully!");
});
We can now add the following test cases to our describe block:
- 1.The contract should update the
echo
variable whenscream()
is called. - 2.When the
echo
variable is changed, theNewEcho
should be emitted. - 3.The
echoCount
should be incremented when new string is saved to theecho
variable.
The test cases of the
Operation
describe block should look like this: it("should update the echo variable", async function () {
await instance.scream("Hello World!");
expect(await instance.echo()).to.equal("Hello World!");
});
it("shold emit NewEcho event", async function () {
const response = await instance.scream("Hello World!");
expect(response.logs[0].event).to.equal("NewEcho");
});
it("should increment echo counter in the NewEcho event", async function () {
const initialResponse = await instance.scream("Hello World!");
const finalResponse = await instance.scream("Goodbye World!");
expect(finalResponse.logs[0].args.message).to.equal("Goodbye World!");
expect(finalResponse.logs[0].args.count.toNumber()).to.equal(initialResponse.logs[0].args.count.toNumber() + 1);
});
With that, our test is ready to be run.
const Echo = artifacts.require("Echo");
/*
* uncomment accounts to access the test accounts made available by the
* Ethereum client
* See docs: https://www.trufflesuite.com/docs/truffle/testing/writing-tests-in-javascript
*/
contract("Echo", function (/* accounts */) {
let instance;
beforeEach("setup development environment", async function () {
instance = await Echo.deployed();
});
describe("Deployment", function () {
it("should assert true", async function () {
return assert.isTrue(true);
});
it("returns the right value after the contract is deployed", async function() {
const echo = await instance.echo();
expect(echo).to.equal("Deployed successfully!");
});
});
describe("Operation", function () {
it("should update the echo variable", async function () {
await instance.scream("Hello World!");
expect(await instance.echo()).to.equal("Hello World!");
});
it("shold emit NewEcho event", async function () {
const response = await instance.scream("Hello World!");
expect(response.logs[0].event).to.equal("NewEcho");
});
it("should increment echo counter in the NewEcho event", async function () {
const initialResponse = await instance.scream("Hello World!");
const finalResponse = await instance.scream("Goodbye World!");
expect(finalResponse.logs[0].args.message).to.equal("Goodbye World!");
expect(finalResponse.logs[0].args.count.toNumber()).to.equal(initialResponse.logs[0].args.count.toNumber() + 1);
});
});
});
NOTE: You need to add a deployment script for
Echo
before you can run the tests.When you run the test with (for example)
yarn test-mandala
, your tests should pass with the following output:yarn test-mandala
yarn run v1.22.15
warning ../../../../../package.json: No license field
$ truffle test --network mandala
Using network 'mandala'.
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Deploying Echo
Echo deployed at: 0xf80A32A835F79D7787E8a8ee5721D0fEaFd78108
Contract: Echo
Deployment
✓ should assert true
✓ returns the right value after the contract is deployed (1144ms)
Operation
✓ should update the echo variable (5123ms)
✓ shold emit NewEcho event (9937ms)
✓ should increment echo counter in the NewEcho event (15529ms)
5 passing (41s)
✨ Done in 82.05s.
This deployment script will deploy the contract and output its address.
Within the
x_echo.js
we will import the Echo
smart contract and have the blank migration ready. We do this by placing the following code within the file:const Echo = artifacts.require("Echo");
module.exports = async function (deployer) {
};
Within the script, we first log the
Deploying Echo
to the console, to signal the start of the deployment, then we deploy it and log its address to the console: console.log("Deploying Echo");
await deployer.deploy(Echo);
console.log("Echo deployed at:", Echo.address);
Running the
yarn deploy-mandala
script should return the following output:yarn deploy-mandala
yarn run v1.22.15
warning ../../../../../package.json: No license field
$ truffle migrate --network mandala
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'mandala'
> Network id: 595
> Block gas limit: 15000000 (0xe4e1c0)
1_initial_migration.js
======================
Replacing 'Migrations'
----------------------
> transaction hash: 0xa2f826fac3b11aba0b1b19226716f4cc38a9a959679baf3435fd9710586b9cb4
> Blocks: 1 Seconds: 4
> contract address: 0xc374eB17f665914c714Ac4cdC8AF3a3474228cc5
> block number: 203
> block timestamp: 1638183162016
> account: 0x75E480dB528101a381Ce68544611C169Ad7EB342
> balance: 9999996.085190716982
> gas used: 0 (0x0)
> gas price: 0.000000001 gwei
> value sent: 0 ETH
> total cost: 0 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0 ETH
1637792775_echo.js
==================
Deploying Echo
Replacing 'Echo'
----------------
> transaction hash: 0x4e6d554743d48596d482f9713c025895b437823d66e34af2e43258d1e3663f78
> Blocks: 1 Seconds: 4
> contract address: 0x2eA9df3bABe04451c9C3B06a2c844587c59d9C37
> block number: 205
> block timestamp: 1638183174001
> account: 0x75E480dB528101a381Ce68544611C169Ad7EB342
> balance: 9999994.702387153408
> gas used: 0 (0x0)
> gas price: 0.000000001 gwei
> value sent: 0 ETH
> total cost: 0 ETH
Echo deployed at: 0x2eA9df3bABe04451c9C3B06a2c844587c59d9C37
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0 ETH
✨ Done in 52.94s.
We have built upon the first example and added a smart contract with more functionalities and tested all of them. The tests were more detailed and covered more examples. We also ensured that we can interact with the smart contract and that we can modify its storage. We can compile smart contract
yarn build
, test it with yarn test
or yarn test-mandala
and deploy it with yarn deploy
or yarn deploy-mandala
.Last modified 7mo ago