Nem用のwalletであるRaccoon Walletをスマホにインストールし、ウォレットのアドレスを取得します。
RaccoonWallet
https://apps.apple.com/jp/app/raccoonwallet/id1437034062
NEM WalletからRaccoon Walletのアドレスへ、mosaicを送信します

Raccoon Wallet側で受け取ったトランザクションを確認

おおおお、なんか凄い
これをプログラムでやりたい。出来ればUI付きで
随机应变 ABCD: Always Be Coding and … : хороший
Nem用のwalletであるRaccoon Walletをスマホにインストールし、ウォレットのアドレスを取得します。
RaccoonWallet
https://apps.apple.com/jp/app/raccoonwallet/id1437034062
NEM WalletからRaccoon Walletのアドレスへ、mosaicを送信します

Raccoon Wallet側で受け取ったトランザクションを確認

おおおお、なんか凄い
これをプログラムでやりたい。出来ればUI付きで
$ mkdir greeter-dapp
$ truffle unbox react
$ truffle develop
truffle(develop)> compile
truffle(develop)> migrate
$ cp ../greeter/contracts/Greeter.sol contracts/
2_deploy_greeter.js
var GreeterContract = artifacts.require("./Greeter.sol");
module.exports = function(deployer) {
deployer.deploy(GreeterContract);
};
client/src/App.js
import React, { Component } from "react";
import GreeterContract from "./contracts/Greeter.json";
import getWeb3 from "./getWeb3";
import "./App.css";
class App extends Component {
state = { greeting: '', web3: null, accounts: null, contract: null };
componentDidMount = async () => {
try {
// Get network provider and web3 instance.
const web3 = await getWeb3();
// Use web3 to get the user's accounts.
const accounts = await web3.eth.getAccounts();
// Get the contract instance.
const networkId = await web3.eth.net.getId();
const deployedNetwork = GreeterContract.networks[networkId];
const instance = new web3.eth.Contract(
GreeterContract.abi,
deployedNetwork && deployedNetwork.address,
);
// Set web3, accounts, and contract to the state, and then proceed with an
// example of interacting with the contract's methods.
this.setState({ web3, accounts, contract: instance }, this.runExample);
} catch (error) {
// Catch any errors for any of the above operations.
alert(
`Failed to load web3, accounts, or contract. Check console for details.`,
);
console.error(error);
}
};
runExample = async () => {
const { accounts, contract } = this.state;
const response = await contract.methods.greet().call();
this.setState({greeting: response})
};
render() {
if (!this.state.web3) {
return <div>Loading Web3, accounts, and contract...</div>;
}
return (
<div className="App">
<h1>Greeter</h1>
{this.state.greeting}
<form>
<label>
New Greeting:
<input type="text" value={this.state.greeting} onChange={this.handleGreetingChange}>
</label>
</form>
<button onClick={this.formSubmitHandler}> Submit </button>
</div>
);
}
}
export default App;
うーむ、ようわからん
Web3はイーサリアムも操作できるようにするJSライブラリ
HTTPまたはIPC接続を使ってイサーリアムに接続するためのJSON-RPCラッパー
フロントエンド <-> Web3 <-> ブロックチェーン
### Web3のメソッド
getAccounts()
getBlockNumber()
getBalance()
sendTransaction()
web3.setProvider(provider) とする
web3ではPromiseを頻繁に使用する
metamaskはwe3を使っているサイトと簡単にやり取りができるブラウザ拡張
send
call(view/pure関数)
fundraiser_factory_test.js
const FundraiserFactoryContract = artifacts.require("FundraiserFactory");
const FundraiserContract = artifacts.require("Fundraiser");
contract("FundraiserFactory: deployment", ()=> {
it("has been deployed", async() => {
const fundraiserFactory = FundraiserFactoryContract.deployed();
assert(fundraiserFactory, "fundraiser factory was not deployed");
})
});
contract("FundraiserFactory: createFundraiser", (accounts) => {
let fundraiserFactory;
const name = "Beneficiary Name";
const url = "beneficiaryname.org";
const imageURL = "https://placekitten.com/600/350"
const description = "Beneficiary Description"
const beneficiary = accounts[1];
it("increments the fundraisersCount", async() => {
fundraiserFactory = await FundraiserFactoryContract.deployed();
const currentFundraisersCount = fundraiserFactory.fundraisersCount();
await fundraiserFactory.createFundraiser(
name,
url,
imageURL,
description,
beneficiary
);
const newFundraisersCount = await fundraiserFactory.fundraisersCount();
// assert.equal(
// newFundraisersCount - currentFundraisersCount,
// 1,
// "should increment by 1"
// );
});
it("emits the FundraiserCreated event", async() => {
fundraiserFactory = await FundraiserFactoryContract.deployed();
const tx = await fundraiserFactory.createFundraiser(
name,
url,
imageURL,
description,
beneficiary
);
const expectedEvent = "FundraiserCreated";
const actualEvent = tx.logs[0].event;
assert.equal(actualEvent, expectedEvent, "event should match");
})
});
contract("FundraiserFactory: fundraisers", (accounts) => {
async function createFundraiserFactory(fundraiserCount, accounts){
const factory = await FundraiserFactoryContract.new();
await addFundraisers(factory, fundraiserCount, accounts);
return factory;
}
async function addFundraisers(factory, count, accounts){
const name = "Beneficiary";
const lowerCaseName = name.toLowerCase();
const beneficiary = accounts[1];
for (let i = 0; i < count; i++){
await factory.createFundraiser(
`${name} ${i}`,
`${lowerCaseName}${i}.com`,
`${lowerCaseName}${i}.png`,
`Description for ${name} ${i}`,
beneficiary
);
}
}
describe("when fundraisers collection is empty", () => {
it("returns an empty collection", async() => {
const factory = await createFundraiserFactory(0, accounts);
const fundraisers = await factory.fundraisers(10, 0);
assert.equal(
fundraisers.length,
0,
"collection should be empty"
);
});
});
describe("varying limits", async() => {
let factory;
beforeEach(async() => {
factory = await createFundraiserFactory(30, accounts);
});
it("returns 10 results when limit requested is 10", async()=> {
const fundraisers = await factory.fundraisers(10, 0);
assert.equal(
fundraisers.length,
10,
"results size should be 10"
);
});
xit("returns 20 results when limit requested is 20", async() => {
const fundraisers = await factory.fundraisers(20, 0);
assert.equal(
fundraisers.length,
20,
"results size should be 20"
);
});
xit("returns 20 results when limit requested is 30", async() => {
const fundraisers = await factory.fundraisers(30, 0);
assert.equal(
fundraisers.length,
20,
"results size should be 20"
);
});
});
describe("varying offset", () => {
let factory;
beforeEach(async() => {
factory = await createFundraiserFactory(10, accounts);
});
it("contains the fundraiser with the appropriate offset", async() => {
const fundraisers = await factory.fundraisers(1, 0);
const fundraiser = await FundraiserContract.at(fundraisers[0]);
const name = await fundraiser.name();
assert.ok(await name.includes(0), `${name} did not include the offset`);
});
it("contains the fundraiser with the appropriate offset", async() => {
const fundraisers = await factory.fundraisers(1, 7);
const fundraiser = await FundraiserContract.at(fundraisers[0]);
const name = await fundraiser.name();
assert.ok(await name.includes(7), `${name} did not include the offset`);
})
});
describe("boundary conditions", ()=> {
let factory;
beforeEach(async() => {
factory = await createFundraiserFactory(10, accounts);
});
it("raises out of bounds error", async () => {
try {
await factory.fundraisers(1, 11);
assert.fail("error was not raised")
} catch(err) {
const expected = "offset out of bounds";
assert.ok(err.message.includes(expected), `${err.message}`);
}
});
it("adjusts return size to prevent out of bounds error", async() => {
try {
const fundraisers = await factory.fundraisers(10, 5);
assert.equal(
fundraisers.length,
5,
"collection adjusted"
);
} catch(err) {
assert.fail("limit and offset exceeded bounds");
}
})
})
})
contracts
pragma solidity >0.4.23 <=0.8.0;
import "./Fundraiser.sol";
contract FundraiserFactory {
uint256 constant maxLimit = 20;
Fundraiser[] private _fundraisers;
event FundraiserCreated(Fundraiser indexed fundraiser, address indexed owner);
function createFundraiser(
string memory name,
string memory url,
string memory imageURL,
string memory description,
address payable beneficiary
)
public {
Fundraiser fundraiser = new Fundraiser(
name,
url,
imageURL,
description,
beneficiary,
msg.sender
);
_fundraisers.push(fundraiser);
emit FundraiserCreated(fundraiser, fundraiser.owner());
}
function fundraisersCount() public view returns(uint256) {
return _fundraisers.length;
}
function fundraisers(uint256 limit, uint256 offset) public view returns(Fundraiser[] memory coll){
require(offset <= fundraisersCount(), "offset out of bounds");
uint256 size = fundraisersCount() - offset;
size = size < limit ? size : limit;
size = size < maxLimit ? size : maxLimit;
coll = new Fundraiser[](size);
for(uint256 i = 0; i < size; i++){
coll[i] = _fundraisers[offset + i];
}
return coll;
}
}
関数とその機能を覚えていけば、行けそうな気がして来た
$ mkdir fundraiser
$ cd fundraiser
$ truffle unbox react
$ tree
$ rm contracts/SimpleStorage.sol \
migrations/2_deploy_contracts.js \
test/*
$ touch contracts/Fundraiser.sol test/fundraiser_test.js
受取人の名前、Webサイト、受取人のアドレス、トップページのカードに使う画像URL、受取人に関する説明、管理人またはownerのアドレス
fundraiser_test.js
const FundraiserContract = artifacts.require("Fundraiser");
contract("Fundraiser", accounts => {
let fundraiser;
const name = "Beneficiary Name";
const url = "beneficiaryname.org";
const imageURL = "https://placekitten.com/600/350";
const description = "Beneficiary description";
const beneficiary = accounts[1];
const owner = accounts[0];
beforeEach(async() => {
fundraiser = await FundraiserContract.new(
name,
url,
imageURL,
description,
beneficiary,
owner
);
});
describe("initialization", () => {
it("gets the beneficiary name", async() => {
const actual = await fundraiser.name();
assert.equal(actual, name, "names should match");
});
it("gets the beneficiary url", async() => {
const actual = await fundraiser.url();
assert.equal(actual, url, "url should match");
});
it("gets the beneficiary image url", async() => {
const actual = await fundraiser.imageURL();
assert.equal(actual, imageURL, "imageURL should match");
});
it("gets the beneficiary description", async() => {
const actual = await fundraiser.description();
assert.equal(actual, description, "description should match");
});
it("gets the beneficiary", async() => {
const actual = await fundraiser.beneficiary();
assert.equal(actual, beneficiary, "beneficiary addresses should match");
});
it("gets the owner", async() => {
const actual = await fundraiser.owner();
assert.equal(actual, owner, "bios should match");
})
});
describe("setBeneficiary", () => {
const newBeneficiary = accounts[2];
it("updated beneficiary when called by owner account", async() => {
await fundraiser.setBeneficiary(newBeneficiary, {from: owner});
const actualBeneficiary = await fundraiser.beneficiary();
assert.equal(actualBeneficiary, newBeneficiary,
"beneficiaries should match");
})
it("throws an error when called from a non-owner account", async() => {
try {
await fundraiser.setBeneficiary(newBeneficiary, {from: accounts[3]});
assert.fail("withdraw was not restricted to owners")
} catch(err) {
const expectedError = "Ownable: caller is not the owner"
const actualError = err.reason;
assert.equal(actualError, expectedError, "should not be permitted")
}
})
});
describe("making donations", () => {
const value = web3.utils.toWei('0.0289');
const donor = accounts[2];
it("increases myDonationsCount", async() => {
const currentDonationsCount = await fundraiser.myDonationsCount(
{from: donor}
);
await fundraiser.donate({from: donor, value});
const newDonationsCount = await fundraiser.myDonationsCount(
{from: donor}
);
assert.equal(
1,
newDonationsCount - currentDonationsCount,
"myDonationsCount should increment by 1"
);
});
it("includes donation in myDonations", async() => {
await fundraiser.donate({from: donor, value});
const {values, dates} = await fundraiser.myDonations(
{from: donor}
);
assert.equal(
value,
values[0],
"values should match"
);
assert(dates[0], "date should be present");
});
it("increases the totalDonations amaount", async() => {
const currentTotalDonations = await fundraiser.totalDonations();
await fundraiser.donate({from: donor, value});
const newTotalDonations = await fundraiser.totalDonations();
const diff = newTotalDonations - currentTotalDonations;
assert.equal(
diff,
value,
"difference should match the donation value"
);
});
it("increases donationsCount", async() => {
const currentDonationsCount = await fundraiser.donationsCount();
await fundraiser.donate({from: donor, value});
const newDonationsCount = await fundraiser.donationsCount();
assert.equal(
1,
newDonationsCount - currentDonationsCount,
"donationsCount should increment by 1"
)
})
it("emits the DonationReceived event", async() => {
const tx = await fundraiser.donate({from: donor, value});
const expectedEvent = "DonationReceived";
const actualEvent = tx.logs[0].event;
assert.equal(actualEvent, expectedEvent, "events should match");
})
});
describe("withdrawing funds", ()=> {
beforeEach(async() => {
await fundraiser.donate(
{from: accounts[2], value: web3.utils.toWei('0.1')}
);
});
describe("access controls", () => {
it("throws an error when called from a non-owner account", async() => {
try {
await fundraiser.withdraw({from: accounts[3]});
assert.fail("withdraw was not restricted to owners");
} catch(err) {
const expectedError = "Ownable: caller is not the owner";
const actualError = err.reason;
assert.equal(actualError, expectedError, "should not be permitted");
}
});
it("permits the owner to call the function", async() => {
try {
await fundraiser.withdraw({from: owner});
assert(true, "no errors were thrown");
} catch(err) {
assert.fail("should not have thrown an error");
}
})
});
it("transfers balance to beneficiary", async() => {
const currentContractBalance = await web3.eth.getBalance(fundraiser.address);
const currentBeneficiaryBalance = await web3.eth.getBalance(beneficiary);
await fundraiser.withdraw({from: owner});
const newContractBalance = await web3.eth.getBalance(fundraiser.address);
const newBeneficiaryBalance = await web3.eth.getBalance(beneficiary);
const beneficiaryDifference = newBeneficiaryBalance - currentBeneficiaryBalance;
assert.equal(
newContractBalance,
0,
"contract should have a 0 balance"
);
assert.equal(
beneficiaryDifference,
currentContractBalance,
"beneficiary should receive all the funds"
);
});
it("emit Withdraw event", async()=>{
const tx = await fundraiser.withdraw({from: owner});
const expectedEvent = "Withdraw";
const actualEvent = tx.logs[0].event;
assert.equal(
actualEvent,
expectedEvent,
"events should match"
);
});
});
describe("fallback function", () => {
const value = web3.utils.toWei('0.0289');
it("increases the totalDonation amount", async() => {
const currentTotalDonations = await fundraiser.totalDonations();
await web3.eth.sendTransaction(
{to: fundraiser.address, from: accounts[9], value}
);
const newTotalDonations = await fundraiser.totalDonations();
const diff = newTotalDonations - currentTotalDonations;
assert.equal(
diff,
value,
"difference should match the donation value"
);
});
it("increase donationsCount", async() => {
const currentDonationsCount = await fundraiser.donationsCount();
await web3.eth.sendTransaction(
{to: fundraiser.address, from: accounts[9], value}
);
const newDonationsCount = await fundraiser.donationsCount();
assert.equal(
1,
newDonationsCount - currentDonationsCount,
"donationsCount should increment by 1"
);
});
});
});
Fundraiser.sol
pragma solidity >0.4.23 <=0.8.0;
import "openzeppelin-solidity/contracts/access/Ownable.sol";
import "openzeppelin-solidity/contracts/utils/math/SafeMath.sol";
contract Fundraiser is Ownable{
using SafeMath for uint256;
uint256 public totalDonations;
uint256 public donationsCount;
event DonationReceived(address indexed donor, uint256 value);
event Withdraw(uint256 amount);
struct Donation {
uint256 value;
uint256 date;
}
mapping(address => Donation[]) private _donations;
string public name;
string public url;
string public imageURL;
string public description;
address payable public beneficiary;
constructor(
string memory _name,
string memory _url,
string memory _imageURL,
string memory _description,
address payable _beneficiary,
address _custodian
)
public {
name = _name;
url = _url;
imageURL = _imageURL;
description = _description;
beneficiary = _beneficiary;
transferOwnership(_custodian);
}
function setBeneficiary(address payable _beneficiary) public onlyOwner {
beneficiary = _beneficiary;
}
function myDonationsCount() public view returns(uint256){
return _donations[msg.sender].length;
}
function donate() public payable {
Donation memory donation = Donation({
value: msg.value,
date: block.timestamp
});
_donations[msg.sender].push(donation);
totalDonations = totalDonations.add(msg.value);
donationsCount++;
emit DonationReceived(msg.sender, msg.value);
}
function myDonations() public view returns(
uint256[] memory values,
uint256[] memory dates
){
uint256 count = myDonationsCount();
values = new uint256[](count);
dates = new uint256[](count);
for (uint256 i = 0; i < count; i++){
Donation storage donation = _donations[msg.sender][i];
values[i] = donation.value;
dates[i] = donation.date;
}
return (values, dates);
}
function withdraw() public onlyOwner {
uint256 balance = address(this).balance;
beneficiary.transfer(balance);
emit Withdraw(balance);
}
fallback () external payable {
totalDonations = totalDonations.add(msg.value);
donationsCount++;
}
}
address型とaddress payable型の二つのアドレスがある
少しずつ理解してきました
NEMのサイトに飛びます
https://discord.com/invite/xymcity
これはdiscordのチャットですね


Symbol
https://jp.symbolplatform.com/
Nemplatform wallet
これか

import してログインする

namespace : 独自トークンを機能させるための場所
mosaic : トークの詳細設定(名称、初期供給量、小数点)
早速NEM Walletにnemを送ってみます。120NEMが必要ということなので、124NEM送る
NEMはBlock chainに比べて、blockが作られる時間が早い
https://explorer.nemtool.com/
すると、アカウントに124NEMが入っている!

### namespace
create namespaceでcapitalcoinとします
すると、100.15NEM引かれて、Dashboardでnamespaceが作られます
### Mosaics
namespaceが作られると、mosaicsの作成ができるようになります。
Mosaic name: hpscript
Description: Hpscript
Initial Supply: 100000000
Total Supply: 100000000


### Mosaicの送信
もにゃに送信できるらしいが、もにゃ側でエラーになる…
発行は簡単にできるが、使い方がイマイチやな
infuraに登録する
https://infura.io/
プロジェクトのエンドポイントをコピー
セキュリティとプライバーシーでMETAMASKのMNEMONICを取得する
$ export MNEMONIC=””
$ export INFURA_PROJECT_ID=””
$ npm install truffle-hdwallet-provider –save-dev
tuffle-config.js
L ネットワーク設定
const HDWalletProvider = require('truffle-hdwallet-provider');
rinkeby: {
provider: () => {
const mnemonic = process.env["MNEMONIC"]
const project_id = process.env["INFURA_PROJECT_ID"]
return new HDWalletProvider(
mnemonic,
`https://rinkeby.infura.io/v3/${project_id}`
);
},
network_id: "*"
}

$ truffle migrate –network rinkeby
Compiling your contracts…
===========================
> Everything is up to date, there is nothing to compile.
Network up to date.
Error: err: insufficient funds for gas * price + value: address 0x71c9f1D5bE00173ae7B774bDBE2112cdD03C5C92 have 0 want 134439500000000000 (supplied gas 6721975)
gasがないとダメって書かれてるな..
まあ なんとなく仕組みはわかった
faucetでイーサを取得する
https://goerli-faucet.slock.it/

エラーになるな… 何故だろう
$ npm install truffle-hdwallet-provider –save-dev
$ export MNEMONIC=”<ハーモニック>”
truffle-config.js
const HDWalletProvider = require('@truffle/hdwallet-provider');
goerli: {
provider: () => {
const mnemonic = process.env["MNEMONIC"]
return new HDWalletProvider(mnemonic, "http://192.168.34.10:8545")
},
network_id: "*",
}
$ parity –chain=goerli
$ truffle migrate –network goerli
$ cd client
$ npm run start
なんか かなり難しいな…
コントラクトをデプロイする3種類の方法
– Ganacheにデプロイ(ローカルブロックチェーン)
– Parityを使ってGoerliテストネットワークにコントラクをデプロイ
– Infuraを使ってRinkebyテストネットワークにデプロイ
デプロイはtruffle migrateを使う
$ truffle compile
build/contractsの下にファイルが作成される
アプリケーションバイナリインターフェイスがABI
bytecodeはコンパイルした結果
$ cp -r hoscdev/chapter-5/greeter/client greeter/client
truffle-config.js
module.exports = {
contracts_build_directory: "./client/src/contracts",
}
$ truffle compile
$ cd client
$ npm install
$ npm run start
http://192.168.33.10:3000/

### Ganacha-CLI
どうやらローカルからGanachaは使えないみたいなので
$ curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add –
$ echo “deb https://dl.yarnpkg.com/debian/ stable main” | sudo tee /etc/apt/sources.list.d/yarn.list
$ sudo apt update && sudo apt install yarn
$ yarn –version
1.22.15
$ yarn add ganache-cli
error jsdom@19.0.0: The engine “node” is incompatible with this module. Expected version “>=12”. Got “10.24.1”
truffle-config.jsをローカルに落としてもう一度やってみる

ganacheのMNEMONICをコピーする
Metamaskを一旦Chrome extensionから解除、再設定して、mnemonicを入力する
$ truffle migrate –network development
Compiling your contracts…
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations…
======================
> Network name: ‘development’
> Network id: 5777
> Block gas limit: 6721975 (0x6691b7)
1_initial_migration.js
======================
Replacing ‘Migrations’
———————-
> transaction hash: 0x569e2101661c8336ea93eabe22e32ce7edd755f2ec231db1df269c699141fa9e
> Blocks: 0 Seconds: 0
> contract address: 0x9d1cB4d7659Ff924A9F929AD0abD072CA0ABC103
> block number: 1
> block timestamp: 1639442039
> account: 0xf6C3c58654B7005C200587f552980CaE09b0B7d1
> balance: 99.99615706
> gas used: 192147 (0x2ee93)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00384294 ETH
> Saving migration to chain.
> Saving artifacts
————————————-
> Total cost: 0.00384294 ETH
2_deploy_greeter.js
===================
Replacing ‘Greeter’
——————-
> transaction hash: 0x7903ecd10332145b40bf013d79b5e00846f4c77a1c5bdf480624e36200c41957
> Blocks: 0 Seconds: 0
> contract address: 0xC9A75Fc25626AB7907783F0440BD4e6fed30A54D
> block number: 3
> block timestamp: 1639442040
> account: 0xf6C3c58654B7005C200587f552980CaE09b0B7d1
> balance: 99.9888452
> gas used: 323255 (0x4eeb7)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.0064651 ETH
> Saving migration to chain.
> Saving artifacts
————————————-
> Total cost: 0.0064651 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.01030804 ETH
うーん、概念がよくわからん
MetaMaskでインポートphraseでログインしても入ってないんだよな..
Solidityでは、入出力などはアクセスできない 関数のみ
const GreeterContract = artifacts.require("Greeter");
contract("Greeter", () => {
it("has been deployed successfully", async() => {
const greeter = await GreeterContract.deployed();
assert(greeter, "contract was not deployed");
});
describe("greet()", () => {
it("returns 'Hello World!'", async() => {
const greeter = await GreeterContract.deployed();
const expected = "Hello World!"
const actual = await greeter.greet();
assert.equal(actual, expected, "Greeted with 'Hello World!'")
})
})
});
コントラクトに関数を追加
contract Greeter {
function greet() external pure returns(string memory){
return "Hello World!";
}
}
$ truffle test
Contract: Greeter
✓ has been deployed successfully
greet()
✓ returns ‘Hello World!’
2 passing (78ms)
状態を変更しない関数は pure または view
### Contractを動的にする
greeter_test.js
contract("Greeter: update greeting", ()=> {
describe("setGreeting(string)", ()=> {
it("sets greeting to passed in string", async() => {
const greeter = await GreeterContract.deployed();
const expected = "Hi there!";
await greeter.setGreeting(expected);
const actual = await greeter.greet();
assert.equal(actual, expected, "greeting was not updated");
});
});
});
Greeter.sol
contract Greeter {
string private _greeting = "Hello World!";
function greet() external view returns(string memory){
return _greeting;
}
function setGreeting(string calldata greeting) external {
_greeting = greeting;
}
}
テストにパスする
### GreeterをOwnableにする
geter関数ownerを呼び出すことで誰が所有者か確認する
contract("Greeter", () => {
it("has been deployed successfully", async() => {
const greeter = await GreeterContract.deployed();
assert(greeter, "contract was not deployed");
});
describe("greet()", () => {
it("returns 'Hello World!'", async() => {
const greeter = await GreeterContract.deployed();
const expected = "Hello World!"
const actual = await greeter.greet();
assert.equal(actual, expected, "Greeted with 'Hello World!'")
})
})
describe("owner()", () => {
it("returns the address of the owner", async()=> {
const greeter = await GreeterContract.deployed();
const owner = await greeter.owner();
assert(owner, "the current owner");
})
})
});
contract Greeter {
string private _greeting = "Hello World!";
address private _owner;
function greet() external view returns(string memory){
return _greeting;
}
function setGreeting(string calldata greeting) external {
_greeting = greeting;
}
function owner() public view returns(address){
return _owner;
}
}
所有者のアドレスがデプロイ元のアドレスと同じであることをテストしたい
truffleではaccounts変数を使ってアカウントにアクセスできる
contract("Greeter", (accounts) => {
it("has been deployed successfully", async() => {
const greeter = await GreeterContract.deployed();
assert(greeter, "contract was not deployed");
});
describe("greet()", () => {
it("returns 'Hello World!'", async() => {
const greeter = await GreeterContract.deployed();
const expected = "Hello World!"
const actual = await greeter.greet();
assert.equal(actual, expected, "Greeted with 'Hello World!'")
})
})
describe("owner()", () => {
it("returns the address of the owner", async()=> {
const greeter = await GreeterContract.deployed();
const owner = await greeter.owner();
assert(owner, "the current owner");
});
it("matches the address that originally deployed the contract", async() => {
const greeter = await GreeterContract.deployed();
const owner = await greeter.owner();
const expected = accounts[0];
assert.equal(owner, expected, "matches address used to deploy contract");
})
})
});
contract Greeter {
string private _greeting = "Hello World!";
address private _owner;
constructor() public {
_owner = msg.sender;
}
function greet() external view returns(string memory){
return _greeting;
}
function setGreeting(string calldata greeting) external {
_greeting = greeting;
}
function owner() public view returns(address){
return _owner;
}
}
$ npm install openzeppelin-solidity
変更前
pragma solidity >= 0.4.0 < 0.7.0;
contract Greeter {
string private _greeting = "Hello World!";
address private _owner;
constructor() public {
_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;
}
}
後
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;
}
}
tuffle-config.js
compilers: {
solc: {
version: "0.6.0", // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
// settings: { // See the solidity docs for advice about optimization and evmVersion
// optimizer: {
// enabled: false,
// runs: 200
// },
// evmVersion: "byzantium"
// }
}
}
うーむ、普通にやっとるが、レベル高いな…