###### tags: `Solidity` # Walter部份 [TOC] ### 流程圖 1. 流程及Block圖, [Miro](https://miro.com/app/board/uXjVOKr4eqE=/?invite_link_id=991086201686) 2. 所有流程含UI/UX wireframe, [Web](http://aisw.in/134/flow/form-wizard.html) ### MongoDB 教學 1.control ``` - db.createCollection('nfactory-backend') - show dbs - use nfactory-backend - db.getName() - db.getCollectionNames() - db.db.getCollectionNames("nfactory-backend").drop - db.dropDatabase() ``` 2.CRUD - [安裝、基礎CRUD、外部輸入資料、Data Type](https://medium.com/@mingjiehsu/mongodb-學習筆記-一-安裝-基礎crud-外部輸入資料-data-type-1169000cf02c) - [MongoDB supports many datatypes](https://www.tutorialspoint.com/mongodb/mongodb_datatype.htm) - [MongoDB Schema 設計指南 ](https://blog.toright.com/posts/4483/mongodb-schema-設計指南.html) ### Landing page 設定 config.js ```json { "CONTRACT_ADDRESS": "0x827acb09a2dc20e39c9aad7f7190d9bc53534192", "SCAN_LINK": "https://polygonscan.com/token/0x827acb09a2dc20e39c9aad7f7190d9bc53534192", "NETWORK": { "NAME": "Polygon", "SYMBOL": "Matic", "ID": 137 }, "NFT_NAME": "Nerdy Coder Clones", "SYMBOL": "NCC", "MAX_SUPPLY": 1000, "WEI_COST": 75000000000000000, "DISPLAY_COST": 0.075, "GAS_LIMIT": 285000, "MARKETPLACE": "OpenSea", "MARKETPLACE_LINK": "https://opensea.io/collection/nerdy-coder-clones", "SHOW_BACKGROUND": true } ``` ### Database Schema (佈署在 AWS MongoDB) 1. hashlips_nft_miniting_dapp 後端 ``` user { _id role wallet_addr email created_at } page { _id u_id n_id contract_addr scan_link network_id page_url background_url title_url both_gif_url created_at } nft { _id u_id nft_name nft_symbol max_supply tokenURI has_blind_box has_robot_prevent has_meta_hidden public_sale_count steps { type status step { normal_sale_quantity normal_sale_price whitelist_quantity reservation_quantity start_time time_step start_price end_price price_step step_number unrevealURI reveal_time } } status created_at ``` - DB Schema [說明](https://docs.google.com/spreadsheets/d/111jin2hpSuUTjjngz9r7A3RMGF3wsMDtQeKoCDxaQfk/edit#gid=0) - API ``` - Swagger - docker - nestjs - mongoose ``` ### 進度報告 1. 2022/2/26 ``` Lemon: React connect solidity Walter: API Ethan: Backend / CICD ``` Finish ``` Flow, Wireframe, DB schema, System Design, Smart Contract ``` 2. 2022/2/19 [Solidity_W1_第1組專題](https://drive.google.com/file/d/1IGWxNJmwLoltIuF4jEbv0hNTZVzuS1nk/view?usp=sharing) 3. 2022/2/12 [Solidity 開發班畢業專案進度報告](https://hackmd.io/zeWGuG-NScq0XygGZgrweg?both) ### NFT marketplace survey PS: 本次專題先不做這部份 1. miquel-nft-marketplace ``` git clone https://github.com/SWANDAO/miquel-nft-marketplace (修正版) ``` NFTCollection : [0x11c40757850a98a26e2fccbef8f70a60095032b1](https://rinkeby.etherscan.io/address/0x11c40757850a98a26e2fccbef8f70a60095032b1) NFTMarketplace : [0x8c76f9a4d3c17abc4de18d3c98abbc983564fa08](https://rinkeby.etherscan.io/address/0x8c76f9a4d3c17abc4de18d3c98abbc983564fa08) 原版 [連結](https://github.com/miquelTC/nft-marketplace) 2. netlify ``` 1. add script in package.json 2. yarn add -D netlify-cli 3. netlify login 4. npx netlify init 5. npx netlify status 6. add lines netlify.toml [build] command = "yarn build" 7. npx netlify deploy --build --prod 沒成功,再找時間處理 ``` 3. 其它的 marketplace 可參考 [github](https://github.com/topics/nft-marketplace) ### Create2 功能拆分版 1.Create2.sol ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // create2 - deterministically precompute contract address // code // demo contract Factory { event Deployed(address addr, uint256 salt); // 1. Get bytecode of contract to be deployed function getBytecode (address _owner, uint _foo) public pure returns (bytes memory) { bytes memory bytecode = type(TestContract).creationCode; // runtime code return abi.encodePacked(bytecode, abi.encode(_owner, _foo)); } // 2. Compute the address of the contract to be deployed // keccak256(0xff + sender address + salt + keccak256(creation code)) // salt:random number // take last 20 bytes function getAddress(bytes memory bytecode, uint _salt) public view returns (address) { bytes32 hash = keccak256( abi.encodePacked( bytes1(0xff), address(this), _salt, keccak256(bytecode) ) ); // cast last 20 bytes of hash to address return address(uint160(uint256(hash))); } // 3. Deploy the contract function deploy(bytes memory bytecode, uint _salt) public payable { address addr; /* How to call create2 create2(v, p, n, s) v - amount of ETH to send p - pointer to start of code in memory n - size of code s - salt */ assembly { addr := create2( callvalue(), // wei send with curent call add(bytecode, 0x20), // Actual code starts after skipping the first 32 bytes mload(bytecode), // Load the size of code contained in the first 32 bytes _salt // Salt from function arguments ) if iszero(extcodesize(addr)) { revert(0, 0) } } emit Deployed(addr, _salt); } } contract TestContract { address public owner; uint public foo; constructor(address _owner, uint _foo) payable { owner = _owner; foo = _foo; } function getBalance() public view returns (uint) { return address(this).balance; } } ``` 2.先佈署 Factory, 執行 getBytecode(owner_address, _salt),其中 owner_address = address(msg.sender),_salt為任何的隨機數 ``` 參數傳入後,會得到 bytecode 這步驟主要是生成 TestContract 的 bytecode ``` 3.計算要佈署的 bytecode 的合約地址,執行 getAddress(bytecode, _salt) bytecode 是上一步驟生成的 bytecode,_salt為上一步驟的隨機數 ``` keccak256(0xff + sender address + salt + keccak256(creation code)) 參數傳入後,會得到 佈署地址 contract address ``` 4.執行 deploy(bytecode, _salt),利用 assembly 執行 create2(v,p,n,s) ``` 參數傳入後,即可完成 TestContract 的佈署,可執行該合約的 getBalance() 函數 透過 emit Deployed(addr, _salt) 可讓外部取得合約地址及 _salt 而且 constructor 也在 Factory 合約生成 TestContract 時代入 ``` ### Create2 合併版 1.Step版的合併版本,說明同Step 版 ```solidity= // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Factory { event Deployed(address addr, uint256 salt); address private owner = address(msg.sender); address public callee_addr; constructor(uint _foo, uint _salt) { // 777, 111 bytes memory tmp_bytecode = type(TestContract).creationCode; bytes memory bytecode = abi.encodePacked(tmp_bytecode, abi.encode(owner, _foo)); bytes32 hash = keccak256( abi.encodePacked( bytes1(0xff), address(this), _salt, keccak256(bytecode) ) ); callee_addr = address(uint160(uint256(hash))); address addr; assembly { addr := create2( callvalue(), // wei send with curent call add(bytecode, 0x20), // Actual code starts after skipping the first 32 bytes mload(bytecode), // Load the size of code contained in the first 32 bytes _salt // Salt from function arguments ) if iszero(extcodesize(addr)) { revert(0, 0) } } emit Deployed(addr, _salt); } } contract TestContract { address public owner; uint public foo; constructor(address _owner, uint _foo) payable { owner = _owner; foo = _foo; } function getBalance() public view returns (uint) { return address(this).balance; } } ``` ### nunjucks 1. 連結 ``` Template: https://mozilla.github.io/nunjucks/templating.html API: https://mozilla.github.io/nunjucks/api.html ``` 2. if ``` let nunjucks = require('nunjucks') nunjucks.configure({autoescape:true}) // 使用判斷 let ret = nunjucks.renderString(` {% if score > 90 %} 優秀 {% elseif score > 80%} 良好 {% elseif score > 60%} 及格 {% else %} 不及格 {% endif %} `, {score:81} ) console.log(ret) // 及格 ``` 3. filter ``` let nunjucks = require('nunjucks') nunjucks.configure({autoescape:true}) // 使用過濾器 let ret = nunjucks.renderString( "{{username|join('-')}}", {username:['blued','city']} ) console.log(ret) // blued-city // replace 及 大寫 let ret2 = nunjucks.renderString( "{{username|replace('city','there')|capitalize}}", {username:'blued city'} ) console.log(ret2) // Blued there ``` 4. for ``` let nunjucks = require('nunjucks') nunjucks.configure({autoescape:true}) // 使用判斷 loop.index為索引 let ret = nunjucks.renderString(` <ul> {% for user in users %} <li>{{loop.index}} {{user.id}}:{{user.name}}</li> {% endfor %} </ul> `, {users:[ {id:1,name:'blued1'}, {id:2,name:'blued2'}, {id:3,name:'blued3'}, {id:4,name:'blued4'}, {id:5,name:'blued5'} ]} ) console.log(ret) ``` 5. loop ``` let nunjucks = require('nunjucks') nunjucks.configure({autoescape:true}) // 使用判斷 let ret = nunjucks.renderString(` <ul>{% for user in users %} <li> loop.index--{{loop.index}} loop.index0--{{loop.index0}} loop.revindex--{{loop.revindex}} loop.revindex0--{{loop.revindex0}} loop.first--{{loop.first}} loop.last --{{loop.last}} loop.length --{{loop.length}} {{user.id}}:{{user.name}} </li>{% endfor %} </ul> `, {users:[ {id:1,name:'Lemon'}, {id:3,name:'Walter'}, {id:4,name:'Ethan'} ]}) console.log(ret) ``` 6. layout ``` const nunjucks = require('nunjucks') const path = require('path') // 如何渲染模版文件 let data = {name:'Walter'} // 配置模版文件的所在目錄 nunjucks.configure(path.resolve(__dirname,'view'),{ autoescape:true }) let result = nunjucks.render('layout.html',data) console.log(result) view/layour.html ---------------- <body> <h1>我是頭</h1> {% block content%} 我是layout模板的內容name= {{name}} {% endblock%} <h1>我是尾</h1> </body> ``` 7. login ``` const nunjucks = require('nunjucks') const path = require('path') // 如何渲染模版文件 let data = {name:'Walter'} // 配置模版文件的所在目錄 nunjucks.configure(path.resolve(__dirname,'view'),{ autoescape:true }) let result = nunjucks.render('layout.html',data) console.log(result) ``` 8. inculde ``` const nunjucks = require('nunjucks') const path = require('path') // 模版的包含 let data = { users:[{id:1,name:'Lemon'}, {id:3,name:'Walter'}]} // 配置模版文件的所在目錄 nunjucks.configure(path.resolve(__dirname,'view'), { autoescape:true }) let result = nunjucks.render('users.html',data) console.log(result) view/users.html --------------- {% extends "layout.html"%} {% block content %} <ul style='border:1px solid red'> {% for user in users %} {% include "item.html" %} {% endfor%} </ul> {% endblock %} view/item.html -------------- <li>名次:{{loop.index}}, ID:{{user.id}}, 名字:{{user.name}}</li> ``` 9. express ``` const express = require('express'); const nunjucks = require('nunjucks'); const path = require('path'); var app = express(); // nodejs 當前工作目錄 nunjucks.configure(path.resolve(__dirname,'view'), { autoescape: true, // 控制輸出是否被轉譯 express: app, // 傳入 express instance 初始化模板設置 watch: true // 啟用模板文件,一旦改變,重新編譯 }) app.get('/', function (req, res) { console.log("GET request for Home Page"); res.send('Hello World'); }) app.get('/name/:name',function(req,res){ res.render('name.html', {name:req.params.name}) }) var server = app.listen(8081, function () { var port = server.address().port console.log("Listening at http://127.0.0.1:%s", port) }) view/name.html -------------- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div> Hello, {{ name }} <= 此處替換 <div> </body> </html> ``` ### 心得 ``` 1. 學習到一些系統架構及CICD觀念 2. 更了解NFT流程 3. 組員在不同Job fuction的互補 4. 更了解Solidity, React的code 5. 團隊分工及相互配合及體諒 6. 時程實在是很趕,但品質還是得要求 ```