Originally
I have copied the code from the following book.
Hands-On Smart Contract Development with Solidity and Ethereum
Environment
- Windows 11
- wsl2
- Ubuntu 18.04
- nvm : 0.39.0
- node : 16.13.2
- npm : 8.1.2
- Truffle v5.4.30 (core: 5.4.30)
- Solidity - 0.8.11 (solc-js)
- Node v16.13.2
- Web3.js v1.5.3
Prepare
wsl2
https://docs.microsoft.com/en-us/windows/wsl/install
open ubuntu on wsl.
nvm
https://github.com/nvm-sh/nvm
Install Truffle
command
# sometime you should install gcc and package before.
# sudo apt install g++-7 libstdc++-7-dev
nvm install 16.13.2 # DO NOT USE >=17.0.0
nvm use 16.13.2
npm i -g truffle # DO NOT USE sudo
Create Project and Greeter Contract
command
mkdir greeter
cd greeter
truffle init
truffle create contract Greeter
truffle create test Greeter
truffle create migration Greeter
Install prettier for vscode
command
npm install --save-dev prettier prettier-plugin-solidity
code --install-extension esbenp.prettier-vscode
code --install-extension JuanBlanco.solidity
.vscode/settings.json
{
"editor.formatOnSave": true,
"solidity.formatter": "prettier",
"[solidity]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
prettier.config.js
module.exports = {
overrides: [
{
files: "*.sol",
options: {
printWidth: 80,
tabWidth: 2,
useTabs: false,
singleQuote: false,
bracketSpacing: false,
explicitTypes: "always",
},
},
],
};
Edit Migration File
Edit migration file. It may in ./migraionts
folder
migrations/12345678_greeter.js
const Greeter = artifacts.require("Greeter");
module.exports = function(_deployer) {
_deployer.deploy(Greeter);
};
Run First Test
command
truffle test
# Using network 'test'.
#Compiling your contracts...
#===========================
#> Compiling ./contracts/Greeter.sol
#> Compiling ./contracts/Migrations.sol
#> Compilation warnings encountered:
#> Artifacts written to /tmp/test--2064-vQs8uxlslsyw
#> Compiled successfully using:
# - solc: 0.8.11+commit.d7f03943.Emscripten.clang
#
# Contract: Greeter
# ✓ should assert true
#
# 1 passing (20ms)
If you have some compile errors, please read this article.
Hello World!
contracts/Greeter.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
// remove constructor
contract Greeter {
function greet() external pure returns(string memory){
return "Hello, World!";
}
}
test/greeter.js
const Greeter = artifacts.require("Greeter");
contract("Greeter", function (/* accounts */) {
it("should assert true", async function () {
await Greeter.deployed();
return assert.isTrue(true);
});
describe("greet()", () => {
it("returns 'Hello, World!'", async () => {
const greeter = await Greeter.deployed();
const expected = "Hello, World!";
const actual = await greeter.greet();
assert.equal(actual, expected, "greeted with'Hello, World!'");
})
})
});
command
truffle test
# Contract: Greeter
# ✓ should assert true
# greet()
# ✓ returns 'Hello, World!'
#
# 2 passing (31ms)
Update Message
So far it has only returned a fixed "Hello World!", but I will try to update the message.
contracts/Greeter.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract Greeter {
string private _greeting = "Hello, World!";
function greet() external view returns (string memory) {
return _greeting;
}
function setGreeting(string calldata greeting) external {
_greeting = greeting;
}
}
test/greeter.js
const Greeter = artifacts.require("Greeter");
contract("Greeter", function (/* accounts */) {
it("should assert true", async function () {
await Greeter.deployed();
return assert.isTrue(true);
});
describe("greet()", () => {
it("returns 'Hello, World!'", async () => {
const greeter = await Greeter.deployed();
const expected = "Hello, World!";
const actual = await greeter.greet();
assert.equal(actual, expected, "greeted with 'Hello, World!'");
});
});
});
contract("Greeter: update greeting", function (/* accounts */) {
it("sets greeting to passed in string", async function () {
const greeter = await Greeter.deployed();
const expected = "Hi there!";
await greeter.setGreeting(expected);
const actual = await greeter.greet();
assert.equal(actual, expected, "greeing updated'");
});
});
command
truffle test
# Contract: Greeter
# ✓ should assert true
# greet()
# ✓ returns 'Hello, World!'
# Contract: Greeter: update greeting
# ✓ sets greeting to passed in string (1060ms)```
# 3 passing (1s)
Check Ownership
Ensure that only the Owner can update messages.
contracts/Greeter.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract Greeter {
string private _greeting = "Hello, World!";
address private _owner;
constructor() {
_owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == _owner, "Ownable: caller is not the owner");
_;
}
function greet() external view returns (string memory) {
return _greeting;
}
function setGreeting(string calldata greeting) external onlyOwner {
_greeting = greeting;
}
function owner() public view returns (address) {
return _owner;
}
}
test/greeter.js
const Greeter = artifacts.require("Greeter");
contract("Greeter: update greeting", (accounts) => {
describe("when message is sent by the owner", () => {
it("sets greeting is sent by the owner", async () => {
const greeter = await Greeter.deployed();
const expected = "The owner changed the mssage";
await greeter.setGreeting(expected);
const actual = await greeter.greet();
assert.equal(actual, expected, "greeting updated");
});
});
describe("when message is sent by another account", () => {
it("does not set the greeting", async () => {
const greeter = await Greeter.deployed();
const expected = await greeter.greet();
try {
await greeter.setGreeting("Not the owner", { from: accounts[1] });
} catch (err) {
const errorMessage = "Ownable: caller is not the owner";
assert.equal(err.reason, errorMessage, "greeting should not update");
return;
}
assert(false, "greeting shoud not update");
});
});
});
command
truffle test
# Contract: Greeter: update greeting
# when message is sent by the owner
# ✓ sets greeting is sent by the owner (1066ms)
# when message is sent by another account
# ✓ does not set the greeting (1083ms)
# 2 passing (2s)
Use OpenZeppelin
Use OpenZeppelin module.
command
npm init -y
npm i openzeppelin-solidity
contracts/Greeter.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import "openzeppelin-solidity/contracts/access/Ownable.sol";
contract Greeter is Ownable {
string private _greeting = "Hello, World!";
function greet() external view returns (string memory) {
return _greeting;
}
function setGreeting(string calldata greeting) external onlyOwner {
_greeting = greeting;
}
}