# 區塊鏈 16,23,30-Sep-2022, ###### tags: `blockchain` ### jamboard ## https://jamboard.google.com/d/1s5ImoKlKkBXVWlap3UAz3SANHHAIth0VQbnU6QQc2rU/edit?usp=sharing ## 下載ganache的戰機 ### https://trufflesuite.com/ganache/ ### `10:30-10:40` break ## https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn ## https://metamask.io/ ## https://github.com/ipfs/ipfs ## https://blockcast.it/2022/03/21/what-to-expect-after-the-ethereum-merge/ ## 0x ba c5 2F 94 C4 67 85 07 88 4F 26 50 c9 10 43 e0 f3 3F 42 42 ## https://github.com/ethereum/solidity ## http://remix.ethereum.org/ ![](https://i.imgur.com/RTGpBB5.png) # 4_Demo1.sol ```solidity= // SPDX-License-Identifier: GPL-3.0 pragma solidity = 0.6.8; contract Demo1 { } ``` ![](https://i.imgur.com/dzLYZNs.png) # Demo1.sol ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo1 { string public message; constructor(string memory initMessage) { message = initMessage; } function setMessage(string memory newMessage) public { message = newMessage; } function getMessage() public view returns(string memory) { return message; } } ``` ![](https://i.imgur.com/mhgycwj.png) # break 3:40-3:50 # https://iancoleman.io/bip39/#english # Demo2 ```solidity= // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo2 { function getrResult(uint a, uint b) public pure returns (string memory){ return integerToString(a+b); } function integerToString(uint i) internal pure returns(string memory){ if (i==0){ return "0"; } // 算出有幾位數 uint j=i; uint len; while (j!=0){ len++; j/=10; } bytes memory bstr = new bytes(len); uint k = len-1; while(i != 0){ bstr[k--]=bytes1(uint8(48+(i%10))); i /= 10; } return string(bstr); } } ``` ![](https://i.imgur.com/XDedJj7.png) # Demo3 ```solidity= // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo3 { function arrayDemo1() public pure returns (uint256){ uint len= 7; uint[] memory a = new uint[](len); return a.length; } } ``` # download node.JS if not ## https://nodejs.org/en/ # download visual studio code if not ## https://code.visualstudio.com/ ## 16.16, 16.17 or 18.X 都可以 ## 打開http://remix.ethereum.org/ # 6_demo3.sol ```solidity! // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo3 { function arrayDemo1() public pure returns (uint256){ uint len= 7; uint[] memory a = new uint[](len); return a.length; } function arrayDemo2(uint length) public pure returns(uint256) { uint[] memory a = new uint[](length); return a.length; } function arrayDemo3() public pure returns(uint256) { uint[7] memory a; return a.length; } function arrayDemo4(uint index) public pure returns(uint256) { uint[] memory a = new uint[](7); a[6]=7; a[5]=6; a[4]=5; a[3]=4; a[2]=3; a[1]=2; a[0]=1; return a[index]; } function arrayDemo5(uint index) public pure returns(uint256) { uint[7] memory a; a[6]=7; a[5]=6; a[4]=5; a[3]=4; a[2]=3; a[1]=2; a[0]=1; return a[index]; } function arrayDemo6() public pure returns(uint[7] memory) { uint[7] memory a; a[6]=7; a[5]=6; a[4]=5; a[3]=4; a[2]=3; a[1]=2; a[0]=1; return a; } function arrayDemo7() public pure returns(uint[] memory) { uint[] memory a = new uint[](7); a[6]=7; a[5]=6; a[4]=5; a[3]=4; a[2]=3; a[1]=2; a[0]=1; return a; } } ``` ![](https://i.imgur.com/eqAxaSM.png) # 7_Demo4.sol ```solidity! // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo4 { uint256[] members1 = new uint256[](5); function arrayTest1() public { members1[0]=2; members1[1]=4; members1[2]=6; members1[3]=8; members1[4]=10; } function arrayTest2() public view returns(uint256[] memory){ return members1; } function arrayTest3(uint d) public { members1.push(d); } } ``` # 8_Demo5.sol ```solidity! // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo5 { enum StarbuckSize {SHORT, TALL, GRANDE, VENTI, TRENTA} StarbuckSize size; StarbuckSize constant defaultSize=StarbuckSize.TALL; StarbuckSize size2 = StarbuckSize.GRANDE; function getSize1() public view returns (StarbuckSize) { return size; } function getSize2() public pure returns (StarbuckSize) { return defaultSize; } function getSize3() public view returns (StarbuckSize) { return size2; } function setSize1(StarbuckSize s)public { size = s; } // function setSize2(StarbuckSize s) public { // defaultSize = s; // } function getSize4() public view returns (uint8){ return uint8(size); } } ``` ![](https://i.imgur.com/kBpLs3l.png) # 9_Demo6.sol ```solidity! // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo7 { mapping(address=>uint)public balances; function updateBalance(uint newBalance) public { balances[msg.sender] = newBalance; } function getBalance() public view returns (uint) { return balances[address(this)]; } function setContractBalance(uint v) public { balances[address(this)]=v; } } ``` ![](https://i.imgur.com/8QVmLVo.png) ![](https://i.imgur.com/fOmeJLD.png) ![](https://i.imgur.com/tdVNrjW.png) # 11_demo8.sol ```solidity= // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo8{ function getMultiReturn() public pure returns(uint256 product, uint256 sum) { uint a=3; uint b=5; return (a*b, a+b); } function getMultiReturn2() public pure returns(uint256 product, uint256 sum){ uint a=3; uint b=5; product = a*b; sum = a+b; } } ``` # 12_demo9.sol ```solidity! // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; contract Demo9 { address owner; constructor() { owner = msg.sender; } modifier onlyOwner { require(msg.sender==owner); _; } modifier costs(uint256 price) { if (msg.value >= price) { _; } } } contract Register is Demo9 { mapping(address => bool) public registeredAddress; uint public price; constructor(uint initPrice) { price = initPrice; } function register() public payable costs(price) { registeredAddress[msg.sender] = true; } function changePrice(uint256 _price) public onlyOwner { price = _price; } } ``` ![](https://i.imgur.com/zDfjLLa.png) ![](https://i.imgur.com/4rOEFJS.png) ![](https://i.imgur.com/j2CqD4J.png) ![](https://i.imgur.com/UmeaDug.png) ![](https://i.imgur.com/hudATy6.png) ![](https://i.imgur.com/ftLEk8p.png) # 13_Demo10.sol ```solidity= // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.7.4; pragma experimental ABIEncoderV2; contract Demo10 { string[] public myArray; constructor() { myArray.push("Hi"); myArray.push("Hello world"); } function getArray() public view returns (string[] memory) { return myArray; } } ``` # truffle install # https://github.com/git-for-windows/git/releases/download/v2.37.3.windows.1/Git-2.37.3-64-bit.exe ``` npm uninstall -g truffle npm uninstall -g ganache-cli npm install -g truffle npm install -g npm@8.19.2 npm list -g npm install -g ganache-cli truffle --version ganache-cli --version ``` # 參考的結果是 ``` C:\Users\user>truffle --version Truffle v5.5.31 (core: 5.5.31) Ganache v7.4.3 Solidity v0.5.16 (solc-js) Node v16.16.0 Web3.js v1.7.4 C:\Users\user>ganache-cli --version Ganache CLI v6.12.2 (ganache-core: 2.13.2) ``` ``` code . ``` ctrl+k,o # https://github.com/github/gitignore/blob/main/Node.gitignore # Extensions * Name: Git Graph Id: mhutchie.git-graph Description: View a Git Graph of your repository, and perform Git actions from the graph. Version: 1.30.0 Publisher: mhutchie VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph * Name: solidity Id: JuanBlanco.solidity Description: Ethereum Solidity Language for Visual Studio Code Version: 0.0.140 Publisher: Juan Blanco VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity ![](https://i.imgur.com/cT7s9rR.png) ``` npm init -y truffle init -y ``` # template的樣本 ``` Try our scaffold commands to get started: $ truffle create contract YourContractName # scaffold a contract $ truffle create test YourTestName # scaffold a test http://trufflesuite.com/docs ``` # https://trufflesuite.com/ganache/ ```javascript! compilers: { solc: { version: "0.5.16", // 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" } } }, ``` # 完整的 ```javascript= /** * Use this file to configure your truffle project. It's seeded with some * common settings for different networks and features like migrations, * compilation, and testing. Uncomment the ones you need or modify * them to suit your project as necessary. * * More information about configuration can be found at: * * https://trufflesuite.com/docs/truffle/reference/configuration * * Hands-off deployment with Infura * -------------------------------- * * Do you have a complex application that requires lots of transactions to deploy? * Use this approach to make deployment a breeze 🏖️: * * Infura deployment needs a wallet provider (like @truffle/hdwallet-provider) * to sign transactions before they're sent to a remote public node. * Infura accounts are available for free at 🔍: https://infura.io/register * * You'll need a mnemonic - the twelve word phrase the wallet uses to generate * public/private key pairs. You can store your secrets 🤐 in a .env file. * In your project root, run `$ npm install dotenv`. * Create .env (which should be .gitignored) and declare your MNEMONIC * and Infura PROJECT_ID variables inside. * For example, your .env file will have the following structure: * * MNEMONIC = <Your 12 phrase mnemonic> * PROJECT_ID = <Your Infura project id> * * Deployment with Truffle Dashboard (Recommended for best security practice) * -------------------------------------------------------------------------- * * Are you concerned about security and minimizing rekt status 🤔? * Use this method for best security: * * Truffle Dashboard lets you review transactions in detail, and leverages * MetaMask for signing, so there's no need to copy-paste your mnemonic. * More details can be found at 🔎: * * https://trufflesuite.com/docs/truffle/getting-started/using-the-truffle-dashboard/ */ // require('dotenv').config(); // const { MNEMONIC, PROJECT_ID } = process.env; // const HDWalletProvider = require('@truffle/hdwallet-provider'); module.exports = { /** * Networks define how you connect to your ethereum client and let you set the * defaults web3 uses to send transactions. If you don't specify one truffle * will spin up a managed Ganache instance for you on port 9545 when you * run `develop` or `test`. You can ask a truffle command to use a specific * network from the command line, e.g * * $ truffle test --network <network-name> */ networks: { // Useful for testing. The `development` name is special - truffle uses it by default // if it's defined here and no other network is specified at the command line. // You should run a client (like ganache, geth, or parity) in a separate terminal // tab if you use this network and you must also set the `host`, `port` and `network_id` // options below to some value. // development: { host: "127.0.0.1", // Localhost (default: none) port: 7545, // Standard Ethereum port (default: none) network_id: "*", // Any network (default: none) }, // // An additional network, but with some advanced options… // advanced: { // port: 8777, // Custom port // network_id: 1342, // Custom network // gas: 8500000, // Gas sent with each transaction (default: ~6700000) // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) // from: <address>, // Account to send transactions from (default: accounts[0]) // websocket: true // Enable EventEmitter interface for web3 (default: false) // }, // // Useful for deploying to a public network. // Note: It's important to wrap the provider as a function to ensure truffle uses a new provider every time. // goerli: { // provider: () => new HDWalletProvider(MNEMONIC, `https://goerli.infura.io/v3/${PROJECT_ID}`), // network_id: 5, // Goerli's id // confirmations: 2, // # of confirmations to wait between deployments. (default: 0) // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) // }, // // Useful for private networks // private: { // provider: () => new HDWalletProvider(MNEMONIC, `https://network.io`), // network_id: 2111, // This network is yours, in the cloud. // production: true // Treats this network as if it was a public net. (default: false) // } }, // Set default mocha options here, use special reporters, etc. mocha: { // timeout: 100000 }, // Configure your compilers compilers: { solc: { version: "0.5.16", // 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" } } }, // Truffle DB is currently disabled by default; to enable it, change enabled: // false to enabled: true. The default storage location can also be // overridden by specifying the adapter settings, as shown in the commented code below. // // NOTE: It is not possible to migrate your contracts to truffle DB and you should // make a backup of your artifacts to a safe location before enabling this feature. // // After you backed up your artifacts you can utilize db by running migrate as follows: // $ truffle migrate --reset --compile-all // // db: { // enabled: false, // host: "127.0.0.1", // adapter: { // name: "sqlite", // settings: { // directory: ".db" // } // } // } }; ``` # lab1.sol ```solidity= pragma solidity ^0.5.16; contract GMR { address public manager; constructor() public { manager = msg.sender; } } ``` # build with command line ``` truffle compile ``` ![](https://i.imgur.com/KbcLaJW.png) ![](https://i.imgur.com/gS7g1XY.png) ``` truffle create migration GMR ``` # 1_g_m_r.js ```javascript= const GMR = artifacts.require("GMR") module.exports = function (_deployer) { _deployer.deploy(GMR); }; ``` # 部署 ``` truffle deploy ``` # 檢視solcjs # 進入到contracts的目錄 ``` solcjs --version solcjs --abi lab1.sol ``` # mkdir lab2_solidity_test ``` copy lab1_solidity_intro\.gitignore lab2_solidity_test ``` # 打開vs code後 ``` npm init -y truffle init -y ``` # Call.sol ```solidity pragma solidity ^0.5.16; contract Chat { string public message; constructor (string memory initMessage) public { message = initMessage; } function setMessage(string memory newMessage) public { message = newMessage; } function getMessage() public view returns (string memory) { return message; } } ``` # 2_call.js ```javascript= const Chat = artifacts.require("Chat"); module.exports = function (_deployer) { _deployer.deploy(Chat, "Hi, I am truffle") }; ``` # npm install mocha # .\Room.js ```javascript= class Room { constructor() { console.log("**創建了一個Room**") } open() { return "door open, welcome" } close() { return "door close,bye bye" } } module.exports = Room ``` # .\test\Room.test.js https://mochajs.org/ ```javascript const Room = require("../Room") const assert = require("assert") it("this is my first test", () => { console.log("要執行第一個測試~!") }) it("this is my second test", () => { console.log("這是我的第二個測試") }) ``` # node_modules\.bin\mocha test ```javascript= "scripts": { "test": "mocha" }, ``` # npm run test # alt+shift+F 排列程式碼 # BC.test.js ```javascript it("寫在其它的檔的測試", () => { console.log("test something else...") }) ``` # Room.test.js ```javascript const Room = require("../Room") const assert = require("assert") before("準備工作", () => { console.log("準備資料") }) beforeEach("派送資料", () => { console.log("[p]指派該份資料到測試") }) afterEach("收集資料", () => { console.log("[c]收集資料並且清理環境") }) after("最後收尾", () => { console.log("最後收尾") }) it("this is my first test", () => { console.log("要執行第一個測試~!") }) it("this is my second test", () => { console.log("這是我的第二個測試") }) describe("測試Room的這個類別", () => { let r1; before("prepare r1",()=>{ r1 = new Room(); }) it("test1, 測試門會開", () => { assert.strictEqual(r1.open(), "door open, welcome") }) it("test2, 測試門會關", () => { assert.strictEqual(r1.close(), "door close,bye bye") }) }) ``` # day2的程式碼請見: ## https://drive.google.com/file/d/1VwJMoBEAs91iqLl9OkZILDBLYvHbHQ_7/view?usp=sharing # day3 ``` cd lab2_solidity_test ``` ## npm run test ## node_modules\.bin\mocha test ## 執行某一個特定的檔案 ### `npm run test -- test\BC.test.js` # `my.test.web3.js` ```javascript= describe("test web3 alone", () => { it("get web3 environment", () => { }) }) ``` ## npm install 連至區塊鏈需要的內容 ``` npm install web3 npm install ganache-cli ``` ## 修改my.test.web3.js ```javascript const assert = require("assert") const ganache = require("ganache-cli") const Web3 = require("web3") const web3 = new Web3(ganache.provider()) beforeEach(() => { //console.log("web3=", web3) web3.eth.getAccounts().then((accounts)=>{ console.log("帳號這時候才來") console.log(accounts) }) //console.log("result=", result) }) describe("test web3 alone", () => { it("get web3 environment", () => { console.log("執行測試") }) }) ``` ## npm run test -- test\my.web3.test.js # my2.web3.test.js ```javascript const assert = require("assert") const ganache = require("ganache-cli") const Web3 = require("web3") const web3 = new Web3(ganache.provider()) let accounts; beforeEach(async () => { accounts = await web3.eth.getAccounts(); }) describe("test web3 alone", () => { it("get web3 environment", () => { console.log("執行測試") console.log("取得第一個帳號", accounts[0]) }) }) ``` ## npm run test -- test\my2.web3.test.js ### npm install chai chai-as-promised ## contract.test.js ```javascript= const { default: Web3 } = require("web3") contract("測試合約", accounts => { it("1.取得帳戶", () => { console.log("第一個帳戶是:", accounts[0]) }) it("2.取得餘額", async () => { // 直接送一個web3 const balance = await web3.eth.getBalance(accounts[0]); console.log("餘額是:", balance) }) }) ``` ## truffle test -- .\test\contract.test.js ## !! ## 修改Call.sol ==> Chat.sol ```javascript= const Chat = artifacts.require("../contracts/Chat.sol") let chat; beforeEach(async () => { chat = await Chat.deployed() console.log("部署好合約:") }) contract("與合約互動", accounts => { it("取得初始化的值", async () => { console.log("作測試") const message = await chat.getMessage(); console.log("合約初始值是:", message) }) it("設值再把值取回", async () => { await chat.setMessage("修改到其它值") const m1 = await chat.getMessage(); console.log("修改完的結果是", m1) const m2 = await chat.message(); console.log("使用變數生成的函數結果是", m2) }) }) ``` ``` mkdir lab3_metacoin cd lab3_metacoin truffle unbox metacoin ``` # README.md ``` truffle test .\test\TestMetaCoin.sol ``` ![](https://i.imgur.com/EM8zbYj.png) ![](https://i.imgur.com/j0dbQBC.png) ![](https://i.imgur.com/tX7PdLs.png) ```javascript= development: { host: "127.0.0.1", // Localhost (default: none) port: 7545, // Standard Ethereum port (default: none) network_id: "5777", // Any network (default: none) }, ``` # commands ``` accounts accounts[0] ``` ![](https://i.imgur.com/cGu7ifw.png) ``` web3 web3.eth ``` ``` deploy let instance = await MetaCoin.deployed() typeof instance let accounts = await web3.eth.getAccounts() accounts[0] let balance = await instance.getBalance(accounts[0]) balance balance+balance balance.toNumber()+balance.toNumber() let balanceInEther = await instance.getBalanceInEth(accounts[0]) typeof balanceInEther balanceInEther.toNumber() balanceInEther.toNumber()*5 instance.sendCoin(accounts[1],500) let b1 = await instance.getBalance(accounts[0]) b1.toNumber() let b2 = await instance.getBalance(accounts[1]) b2.toNumber() ``` # https://drive.google.com/file/d/1yvxxHuBTgjD6INyZbhz3q4w45I_uDnAx/view?usp=sharing ```solidity pragma solidity ^0.5.16; contract GMR { address public manager; address[] public players; constructor() public { manager = msg.sender; } function enterGame() public payable { players.push(msg.sender); } } ``` ![](https://i.imgur.com/aMtKtfW.png) ![](https://i.imgur.com/SEuD2y3.png) # 修改程式加上限制 ```javascript= pragma solidity ^0.5.16; contract GMR { address public manager; address[] public players; constructor() public { manager = msg.sender; } function enterGame() public payable { require(msg.value>0.01 ether); players.push(msg.sender); } } ``` ![](https://i.imgur.com/CfcYhFI.png) # https://eth-converter.com/ ![](https://i.imgur.com/uZunKTJ.png) ```solidity pragma solidity ^0.5.16; contract GMR { address public manager; address payable[] public players; constructor() public { manager = msg.sender; } function enterGame() public payable { require(msg.value>0.01 ether); players.push(msg.sender); } function getContractBalance() public view returns (uint) { return address(this).balance; } modifier restricted() { require(msg.sender==manager); _; } function payMoneyToPlayer() public restricted { //require(msg.sender==manager); uint winnerIndex = chooseByTime(); players[winnerIndex].transfer(getContractBalance()); players = new address payable[](0); } function chooseByTime() private view returns (uint) { uint result = block.timestamp%players.length; return result; } function getCurrentPlayers() public view returns (address payable[] memory) { return players; } } ``` ![](https://i.imgur.com/kgqEzQ4.png) # npm install mocha chai chai-as-promised ## GMR.test.js ```javascript const GMR = artifacts.require("../contracts/GMR.sol"); let gmr; beforeEach(() => { }) contract("lab1 GMR testing:", accounts => { it("部署完檢查帳號", () => { console.log("accounts=",accounts[0]); }) }) ``` # npm list -g ![](https://i.imgur.com/SbPh9sd.png) ```javascript= test: { host: "127.0.0.1", // Localhost (default: none) port: 8545, // Standard Ethereum port (default: none) network_id: "*", // Any network (default: none) }, development: { host: "127.0.0.1", // Localhost (default: none) port: 7545, // Standard Ethereum port (default: none) network_id: "*", // Any network (default: none) }, ``` ## `truffle test --network test test\GMR.test.js` ```javascript const GMR = artifacts.require("../contracts/GMR.sol"); const assert = require("assert"); let gmr; beforeEach(async () => { //gmr = await GMR.deployed(); gmr = await GMR.new(); console.log("GMR is fresh deployed"); }) contract("lab1 GMR testing:", accounts => { it("部署完檢查合約地址", () => { //console.log("accounts=", accounts[0]); assert.ok(gmr.address); }) it("管理者應該是第一個帳戶", async () => { console.log("accounts[0]=", accounts[0]); const manager = await gmr.manager(); console.log("manager=", manager); assert.strictEqual(accounts[0], manager); }) it("進入遊戲", async () => { await gmr.enterGame({ from: accounts[0], value: web3.utils.toWei("0.02", "ether") }); const players = await gmr.getCurrentPlayers(); console.log("目前使用者是:", players); assert.strictEqual(players[0], accounts[0]); assert.strictEqual(1, players.length); }) it("多筆玩家", async () => { const joiningPlayers = [accounts[1], accounts[2], accounts[3], accounts[4]]; for (let p of joiningPlayers) { await gmr.enterGame({ from: p, value: web3.utils.toWei("0.02", "ether") }) } const players = await gmr.getCurrentPlayers(); console.log("目前使用者是:", players); assert.strictEqual(4, players.length); assert.strictEqual(accounts[1], players[0]); assert.strictEqual(accounts[2], players[1]); assert.strictEqual(accounts[3], players[2]); assert.strictEqual(accounts[4], players[3]); }) }) ``` # GMR.fail.test.js ```javascript const GMR = artifacts.require("../contracts/GMR.sol"); //const assert = require("assert"); let gmr; beforeEach(async () => { gmr = await GMR.deployed(); //gmr = await GMR.new(); console.log("GMR is fresh deployed"); }) contract("GMR不正確時的檢查", accounts => { it("入場費不夠", async () => { try { await gmr.enterGame({ from: accounts[0], value: 300 }) assert(false); } catch (err) { assert.isAbove(err.message.search("VM Exception while processing transaction"), -1, "應該要發生的是VM的錯誤"); } }) }) ``` ![](https://i.imgur.com/6RHcZx2.png) ## mkdir lab4_cac ## cd lab4_cac ## copy ..\lab1_solidity_intro\.gitignore . ## code . ``` npm init -y truffle init -y git init ``` # cac.sol ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.5.16; contract CAC { address public manager; uint256 public minimumFund; address[] public approvers; constructor(uint256 minimum) public { manager = msg.sender; minimumFund = minimum; } function join() public payable { require(msg.value > minimumFund); approvers.push(msg.sender); } } ``` # 1_init_cac.js ```javascript const CAC = artifacts.require("CAC") module.exports = function(deployer){ deployer.deploy(CAC, 500); } ``` ```javascript development: { host: "127.0.0.1", // Localhost (default: none) port: 9545, // Standard Ethereum port (default: none) network_id: "*", // Any network (default: none) }, ``` # truffle develop ## truffle deploy ## truffle develop --network development ## deploy ## deploy --reset ``` let cac1 = await CAC.deployed() typeof cac1 cac1 cac1.manager() await cac1.manager() cac1.minimumFund() await cac1.minimumFund() let cac1_mf = await cac1.minimumFund() cac1_mf.toNumber() (await cac1.minimumFund()).toNumber() ``` ![](https://i.imgur.com/SbZklPP.png) ## day3 完成的code # https://drive.google.com/file/d/1L7A7GL-kL-7mvNLlhdpzqY0xrxLs7Dzu/view?usp=sharing # Day4開始 ## 使用LAB4_CAC ## 使用truffle-cli ## truffle develop ### 在truffle> ``` deploy accounts let cac1 = await CAC.deployed() (await cac1.minimumFund()).toNumber() ``` ## not enough ``` cac1.join({from:accounts[1], value:450}) ``` ## enough ``` cac1.join({from:accounts[1], value:550}) cac1.approvers(1) accounts[1] ``` ## CAC.sol ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.5.16; contract CAC { struct SpendingRequest { string description; uint value; address vendorAccount; bool complete; } SpendingRequest[] public requests; address public manager; uint256 public minimumFund; address[] public approvers; modifier restricted() { require(msg.sender == manager); _; } constructor(uint256 minimum) public { manager = msg.sender; minimumFund = minimum; } function join() public payable { require(msg.value > minimumFund); approvers.push(msg.sender); } } ``` # http://remix.ethereum.org/ ## 16_Demo12.sol ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.7.4; contract Demo13 { int[] public myArray; constructor() { myArray.push(100); } function addSomething() public { myArray.push(200); myArray.push(300); int[] storage myArray2 = myArray; myArray2[0]=555; } } ``` ## 19_Demo15.sol ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.7.4; contract Demo14 { int[] public myArray; constructor() { myArray.push(100); } function addSomething() public { myArray.push(200); myArray.push(300); int[] memory myArray2 = myArray; myArray2[0]=555; } } ``` ## CAC.sol ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.5.16; contract CAC { struct SpendingRequest { string description; uint value; address vendorAccount; bool complete; } SpendingRequest[] public requests; address public manager; uint256 public minimumFund; address[] public approvers; function createRequest(string memory des, uint value, address vendor) public restricted { SpendingRequest memory req = SpendingRequest({ description:des, value:value, vendorAccount:vendor, complete:false }); requests.push(req); } modifier restricted() { require(msg.sender == manager); _; } constructor(uint256 minimum) public { manager = msg.sender; minimumFund = minimum; } function join() public payable { require(msg.value > minimumFund); approvers.push(msg.sender); } } ``` ![](https://i.imgur.com/fEGV0eh.png) ``` migrate --reset ``` ``` cac1.createRequest() cac1 = await CAC.deployed() cac1.createRequest("wash outer wall",1000,accounts[0]) cac1.requests(0) cac1.createRequest("wash outer wall",1000,accounts[0],{from:accounts[1]}) ``` # CAC.sol ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.5.16; contract CAC { struct SpendingRequest { string description; uint value; address vendorAccount; bool complete; // change to mapping uint approvalCount; mapping(address=>bool) approvedUsers; } SpendingRequest[] public requests; address public manager; uint256 public minimumFund; // change to mapping mapping(address=>bool) public approvers; uint public approversCount; function createRequest(string memory des, uint value, address vendor) public restricted { SpendingRequest memory req = SpendingRequest({ description:des, value:value, vendorAccount:vendor, complete:false, approvalCount:0 }); requests.push(req); } modifier restricted() { require(msg.sender == manager); _; } constructor(uint256 minimum) public { manager = msg.sender; minimumFund = minimum; } function join() public payable { require(msg.value > minimumFund); //approvers.push(msg.sender); approvers[msg.sender] = true; } } ``` # 重新檢驗 ``` migrate --reset cac1 = await CAC.deployed() cac1.createRequest("wash outer wall",1000,accounts[0]) cac1.requests(0) cac1.createRequest("wash outer wall",1000,accounts[0],{from:accounts[1]}) ``` ![](https://i.imgur.com/1EedFiV.png) # CAC.sol ## 相容於0.76版 ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.7.4; contract CAC { struct SpendingRequest { string description; uint256 value; address vendorAccount; bool complete; // change to mapping uint256 approvalCount; mapping(address => bool) approvedUsers; } uint256 numRequests; mapping(uint256 => SpendingRequest) public requests; address public manager; uint256 public minimumFund; // change to mapping mapping(address => bool) public approvers; uint256 public approversCount; function createRequest( string memory des, uint256 value, address vendor ) public restricted { SpendingRequest storage spendingRequest = requests[numRequests++]; spendingRequest.description = des; spendingRequest.value = value; spendingRequest.vendorAccount = vendor; } modifier restricted() { require(msg.sender == manager); _; } constructor(uint256 minimum) { manager = msg.sender; minimumFund = minimum; } function join() public payable { require(msg.value > minimumFund); //approvers.push(msg.sender); approvers[msg.sender] = true; approversCount++; } function approvalRequest(uint256 index) public { SpendingRequest storage request = requests[index]; require(approvers[msg.sender]); require(!request.approvedUsers[msg.sender]); request.approvalCount++; request.approvedUsers[msg.sender] = true; } } ``` ``` compile migrate --reset cac1 = await CAC.deployed() cac1.createRequest("wash outer wall",1000,accounts[9]) cac1.requests(0) ``` ![](https://i.imgur.com/cbM8Bb7.png) ![](https://i.imgur.com/sc2YsFb.png) ![](https://i.imgur.com/naEI4p1.png) ![](https://i.imgur.com/0rSPU1q.png) ![](https://i.imgur.com/iIdktdd.png) ![](https://i.imgur.com/H6bvJHd.png) ``` npm install mocha chai chai-as-promised ``` ## package.json ```json "scripts": { "test": "mocha" }, ``` ## truffle develop ```javascript= const assert = require("chai").assert; const CAC = artifacts.require("CAC.sol"); let cac const VM_ERROR_STRING = "VM Exception while processing transaction:" beforeEach(async () => { //cac = await CAC.deployed(); cac = await CAC.new(500); }) contract("test CAC", (accounts) => { it("contract exist", async () => { assert(cac != null, "array should not be null") //console.log("cac address=", cac.address) }) it("get minimum fund", async () => { const minimumFund = await cac.minimumFund(); const v = minimumFund.toNumber(); //console.log("type is", typeof v); // strict equal === // equal == assert.strictEqual(v, 500, "minimum fund should be 500"); }) it("get manager", async () => { const mgr = await cac.manager(); console.log("mgr", typeof mgr); assert.strictEqual(mgr, accounts[0], "manager should be first account"); }) it("join, make request, and approve", async () => { await cac.join({ from: accounts[0], value: 550 }); await cac.createRequest("clean floor", 1000, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); }) it("join, make request, and approve twice", async () => { await cac.join({ from: accounts[0], value: 550 }); await cac.join({ from: accounts[1], value: 550 }); await cac.join({ from: accounts[2], value: 550 }); await cac.createRequest("clean floor", 1000, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); try { await cac.approvalRequest(0, { from: accounts[0] }); assert(false) } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) it("not enough fee", async () => { try { await cac.join({ from: accounts[0], value: 450 }); assert(false) } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) it("not approvers", async () => { await cac.createRequest("clean floor", 1000, accounts[9]); try{ await cac.approvalRequest(0, { from: accounts[5] }); assert(false) } catch(e){ assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) }) ``` # CAC (with payable vendor) ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.7.4; contract CAC { struct SpendingRequest { string description; uint256 value; address payable vendorAccount; bool complete; // change to mapping uint256 approvalCount; mapping(address => bool) approvedUsers; } uint256 numRequests; mapping(uint256 => SpendingRequest) public requests; address public manager; uint256 public minimumFund; // change to mapping mapping(address => bool) public approvers; uint256 public approversCount; function createRequest( string memory des, uint256 value, address payable vendor ) public restricted { SpendingRequest storage spendingRequest = requests[numRequests++]; spendingRequest.description = des; spendingRequest.value = value; spendingRequest.vendorAccount = vendor; } modifier restricted() { require(msg.sender == manager); _; } constructor(uint256 minimum) { manager = msg.sender; minimumFund = minimum; } function join() public payable { require(msg.value > minimumFund); //approvers.push(msg.sender); approvers[msg.sender] = true; approversCount++; } function approvalRequest(uint256 index) public { SpendingRequest storage request = requests[index]; require(approvers[msg.sender]); require(!request.approvedUsers[msg.sender]); request.approvalCount++; request.approvedUsers[msg.sender] = true; } function executeRequest(uint256 index) public restricted { SpendingRequest storage request = requests[index]; require(!request.complete); require(request.approvalCount > (approversCount / 2)); request.vendorAccount.transfer(request.value); request.complete = true; } } ``` # CAC2.test.js ```javascript= const assert = require("chai").assert; const CAC = artifacts.require("CAC.sol"); let cac const VM_ERROR_STRING = "VM Exception while processing transaction:" beforeEach(async () => { //cac = await CAC.deployed(); cac = await CAC.new(500); }) contract("test more CAC", (accounts) => { it("付款失敗", async () => { await cac.join({ from: accounts[0], value: 1000 }); await cac.join({ from: accounts[1], value: 1000 }); await cac.join({ from: accounts[2], value: 1000 }); await cac.createRequest("clean floor", 888, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); try { await cac.executeRequest(0); assert(false); } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) it("取得帳戶餘額", async () => { const amount1 = await web3.eth.getBalance(accounts[0]); console.log("餘額是", typeof amount1, amount1); const value = BigInt(amount1); console.log("value=", typeof value, value); console.log("轉成bigint可以作加減", value + 100n); console.log("轉成bigint可以作加減", value - 100n); }) it("付款成功", async () => { await cac.join({ from: accounts[0], value: 1000 }); await cac.join({ from: accounts[1], value: 1000 }); await cac.join({ from: accounts[2], value: 1000 }); await cac.createRequest("clean floor", 888, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); await cac.approvalRequest(0, { from: accounts[1] }); const origBalance = await web3.eth.getBalance(accounts[9]); console.log("原本accounts[9]的餘額是", origBalance); await cac.executeRequest(0); const newBalance = await web3.eth.getBalance(accounts[9]); console.log("新accounts[9]的餘額是", newBalance); const remaining = BigInt(newBalance) - BigInt(origBalance); console.log("值是:", remaining); assert.strictEqual(BigInt(888), remaining, `值應該是${remaining}`); }) }) ``` ## 修改完的CAC.sol ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.7.4; contract CAC { struct SpendingRequest { string description; uint256 value; address payable vendorAccount; bool complete; // change to mapping uint256 approvalCount; mapping(address => bool) approvedUsers; } uint256 numRequests; mapping(uint256 => SpendingRequest) public requests; address public manager; uint256 public minimumFund; // change to mapping mapping(address => bool) public approvers; uint256 public approversCount; function createRequest( string memory des, uint256 value, address payable vendor ) public restricted { SpendingRequest storage spendingRequest = requests[numRequests++]; spendingRequest.description = des; spendingRequest.value = value; spendingRequest.vendorAccount = vendor; } modifier restricted() { require(msg.sender == manager); _; } constructor(uint256 minimum,address creator) { manager = creator; minimumFund = minimum; } function join() public payable { require(msg.value > minimumFund); //approvers.push(msg.sender); approvers[msg.sender] = true; approversCount++; } function approvalRequest(uint256 index) public { SpendingRequest storage request = requests[index]; require(approvers[msg.sender]); require(!request.approvedUsers[msg.sender]); request.approvalCount++; request.approvedUsers[msg.sender] = true; } function executeRequest(uint256 index) public restricted { SpendingRequest storage request = requests[index]; require(!request.complete); require(request.approvalCount > (approversCount / 2)); request.vendorAccount.transfer(request.value); request.complete = true; } } ``` ## CommitteeCreator.sol ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.7.4; import "./CAC.sol"; contract CommitteeCreator { CAC[] public deployedCommittee; function createCommittee(uint256 minimum) public { CAC c = new CAC(minimum, msg.sender); deployedCommittee.push(c); } function getDeployedCommittee() public view returns(CAC[] memory){ return deployedCommittee; } } ``` ![](https://i.imgur.com/dO4RSpF.png) ![](https://i.imgur.com/Wyq4HZU.png) ![](https://i.imgur.com/edWHm2c.png) ![](https://i.imgur.com/pXdyoea.png) ![](https://i.imgur.com/y32WvPw.png) # `1_initial_creator.js` ```javascript! const cc = artifacts.require("CommitteeCreator") module.exports = function(deployer){ deployer.deploy(cc); } ``` ## 在truffle> ``` deploy --reset let cc = await CommitteeCreator.deployed() cc.address cc ``` ## 更新的CAC.test.js ```javascript const assert = require("chai").assert; const CAC = artifacts.require("CAC.sol"); let cac const VM_ERROR_STRING = "VM Exception while processing transaction:" contract("test CAC", (accounts) => { beforeEach(async () => { //cac = await CAC.deployed(); cac = await CAC.new(500, accounts[0]); }) it("contract exist", async () => { assert(cac != null, "array should not be null") //console.log("cac address=", cac.address) }) it("get minimum fund", async () => { const minimumFund = await cac.minimumFund(); const v = minimumFund.toNumber(); //console.log("type is", typeof v); // strict equal === // equal == assert.strictEqual(v, 500, "minimum fund should be 500"); }) it("get manager", async () => { const mgr = await cac.manager(); console.log("mgr", typeof mgr); assert.strictEqual(mgr, accounts[0], "manager should be first account"); }) it("join, make request, and approve", async () => { await cac.join({ from: accounts[0], value: 550 }); await cac.createRequest("clean floor", 1000, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); }) it("join, make request, and approve twice", async () => { await cac.join({ from: accounts[0], value: 550 }); await cac.join({ from: accounts[1], value: 550 }); await cac.join({ from: accounts[2], value: 550 }); await cac.createRequest("clean floor", 1000, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); try { await cac.approvalRequest(0, { from: accounts[0] }); assert(false) } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) it("not enough fee", async () => { try { await cac.join({ from: accounts[0], value: 450 }); assert(false) } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) it("not approvers", async () => { await cac.createRequest("clean floor", 1000, accounts[9]); try { await cac.approvalRequest(0, { from: accounts[5] }); assert(false) } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) }) ``` ## 更新過的CAC2.test.js ```javascript! const assert = require("chai").assert; const CAC = artifacts.require("CAC.sol"); let cac const VM_ERROR_STRING = "VM Exception while processing transaction:" contract("test more CAC", (accounts) => { beforeEach(async () => { //cac = await CAC.deployed(); cac = await CAC.new(500,accounts[0]); }) it("付款失敗", async () => { await cac.join({ from: accounts[0], value: 1000 }); await cac.join({ from: accounts[1], value: 1000 }); await cac.join({ from: accounts[2], value: 1000 }); await cac.createRequest("clean floor", 888, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); try { await cac.executeRequest(0); assert(false); } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) it("取得帳戶餘額", async () => { const amount1 = await web3.eth.getBalance(accounts[0]); console.log("餘額是", typeof amount1, amount1); const value = BigInt(amount1); console.log("value=", typeof value, value); console.log("轉成bigint可以作加減", value + 100n); console.log("轉成bigint可以作加減", value - 100n); }) it("付款成功", async () => { await cac.join({ from: accounts[0], value: 1000 }); await cac.join({ from: accounts[1], value: 1000 }); await cac.join({ from: accounts[2], value: 1000 }); await cac.createRequest("clean floor", 888, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); await cac.approvalRequest(0, { from: accounts[1] }); const origBalance = await web3.eth.getBalance(accounts[9]); console.log("原本accounts[9]的餘額是", origBalance); await cac.executeRequest(0); const newBalance = await web3.eth.getBalance(accounts[9]); console.log("新accounts[9]的餘額是", newBalance); const remaining = BigInt(newBalance) - BigInt(origBalance); console.log("值是:", remaining); assert.strictEqual(BigInt(888), remaining, `值應該是${remaining}`); }) }) ``` ## `CC.test.js` ```javascript! const CommitteeCreator = artifacts.require("CommitteeCreator.sol"); const CAC = artifacts.require("CAC.sol"); const assert = require("chai").assert; require('chai').use(require("chai-as-promised")).should(); let cc; beforeEach(async () => { cc = await CommitteeCreator.new(); console.log("cc部署完成") }) contract("Committee Creator測試", accounts => { it("用第一個帳號創建應該是第一個帳號作管理者", async () => { await cc.createCommittee(500); const cac1Addr = await cc.deployedCommittee(0); console.log(`cac的實例會在${cac1Addr}`) const allArray = await cc.getDeployedCommittee(); console.log(`array在${allArray}`) const cac1 = await CAC.at(cac1Addr) const manager = await cac1.manager(); assert.strictEqual(manager, accounts[0], "管理應該是第一個帳號") }) it("用第二個帳號創建應該是第二個帳號作管理者", async () => { await cc.createCommittee(500, { from: accounts[1] }); const cac1Addr = await cc.deployedCommittee(0); const cac = await CAC.at(cac1Addr) const manager = await cac.manager(); assert.strictEqual(manager, accounts[1], "管理應該是第個二帳號") }) }) ``` ## CC2.test.js ```javascript! const assert = require("chai").assert; const CAC = artifacts.require("CAC.sol"); const CommitteeCreator = artifacts.require("CommitteeCreator.sol"); let cac let cc const VM_ERROR_STRING = "VM Exception while processing transaction:" beforeEach(async () => { cc = await CommitteeCreator.new(); console.log("cc部署完成") }) contract("test CAC", (accounts) => { beforeEach(async () => { //cac = await CAC.new(500, accounts[0]); await cc.createCommittee(500, { from: accounts[0] }) const cac1Addr = await cc.deployedCommittee(0); cac = await CAC.at(cac1Addr); }) it("contract exist", async () => { assert(cac != null, "array should not be null") //console.log("cac address=", cac.address) }) it("get minimum fund", async () => { const minimumFund = await cac.minimumFund(); const v = minimumFund.toNumber(); //console.log("type is", typeof v); // strict equal === // equal == assert.strictEqual(v, 500, "minimum fund should be 500"); }) it("get manager", async () => { const mgr = await cac.manager(); console.log("mgr", typeof mgr); assert.strictEqual(mgr, accounts[0], "manager should be first account"); }) it("join, make request, and approve", async () => { await cac.join({ from: accounts[0], value: 550 }); await cac.createRequest("clean floor", 1000, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); }) it("join, make request, and approve twice", async () => { await cac.join({ from: accounts[0], value: 550 }); await cac.join({ from: accounts[1], value: 550 }); await cac.join({ from: accounts[2], value: 550 }); await cac.createRequest("clean floor", 1000, accounts[9]); await cac.approvalRequest(0, { from: accounts[0] }); try { await cac.approvalRequest(0, { from: accounts[0] }); assert(false) } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) it("not enough fee", async () => { try { await cac.join({ from: accounts[0], value: 450 }); assert(false) } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) it("not approvers", async () => { await cac.createRequest("clean floor", 1000, accounts[9]); try { await cac.approvalRequest(0, { from: accounts[5] }); assert(false) } catch (e) { assert.isAbove(e.message.search(VM_ERROR_STRING), -1, "VM exception should happen") } }) }) ``` ## 使用 ``` truffle develop deploy --reset let cc =await CommitteeCreator.deployed() cc.createCommittee(500) let cacAddr1 = await cc.deployedCommittee(0) let inst1 = await CAC.at(cacAddr1) inst1.manager() accounts[0] inst1.minimumFund() (await inst1.minimumFund()).toNumber() ``` # 所有的程式碼 1. vs code: * https://drive.google.com/file/d/1gwoYAXI_kmCr1zknd4LtlzXRD-d3JvSd/view?usp=sharing 1. remix code: * https://drive.google.com/file/d/1UAgEi_MI_OX8I3TzXsiOzGUoSbyI4Jpw/view?usp=sharing