# KrypoCamp 第二週實體課程 ### EOA(externally owned account) 外部擁有帳戶 - 地址 - 餘額 - nonce 十六進制 20bytes 長度(40個英文數字),透過用戶私鑰控制。 *私鑰先使用 ECDSA 計算出公鑰([橢圓曲線數位簽章算法](https://zh.wikipedia.org/zh-tw/%E6%A4%AD%E5%9C%86%E6%9B%B2%E7%BA%BF%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E7%AE%97%E6%B3%95)),接著對公鑰作出 Keccak-256 雜湊,接著取其雜湊最右邊的 40 位元組,並以 16 進制字串方式表達該 40 位元組,然後在字串前面加上 "0x" 字串後,該字串即為該私鑰所對應之乙太坊地址(Ethereum Address)。 #### 例如 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 私鑰 `ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80` 數字 `77814517325470205911140941194401928579557062014761831930645393041380819009408` *私鑰只是一個隨機選取的數字。 *圓曲線的形狀,並不是橢圓的。只是因為橢圓曲線的描述方程,類似於計算一個橢圓周長的方程故得名。 ### Contract Address 合約帳戶 - 地址 - 餘額 - 隨機數 - 程式碼 十六進制 20bytes 長度(40個英文數字),僅能透過其他合約或 EOA 發送交易驅動,無法直接被控制(無法猜測私鑰)。 *建立合約帳戶時,會檢查交易中是否有 to,當 to 不為空時,則建立合約帳戶,並記錄程式碼。 *合約地址來自建立者錢包與 nonce 等相關資料組成,可被預期。 #### JavaScript 版本 - 安裝 rlp 套件 - 安裝 keccak 套件 ```javascript= /** stack overflow https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed node version: v9.10.0 module versions: rlp@2.0.0 keccak@1.4.0 **/ const rlp = require("rlp"); const keccak = require("keccak"); var nonce = 0x00; var sender = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"; var input_arr = [sender, nonce]; var rlp_encoded = rlp.encode(input_arr); var contract_address_long = keccak('keccak256') .update(Buffer.from(rlp_encoded)) .digest('hex') var contract_address = contract_address_long.substring(24); console.log("contract_address: " + contract_address); ``` ### [ERC20(Coin)](https://docs.openzeppelin.com/contracts/4.x/api/token/erc20) 利用智能合約鑄造、分配、轉移 Token(Coin),所有行為都是在合約本身中執行。 - mint - transfer ### [ERC721(NFT)](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721) 與 ERC20 相容,擴充了非同值化代幣(NFT)必要屬性。 - tokenURI - _baseURI - mint - transfer ### [ERC1155](https://docs.openzeppelin.com/contracts/4.x/api/token/erc1155) Multi Token Standard ### 變體 ### [ERC721A](https://github.com/chiru-labs/ERC721A) 改善標準 ERC721 的 Token 紀錄演算法 #### 優點 - 改善鑄造時,`mint` 不因數量而倍增燃料費用 `same cost of minting a single NFT.` ```solidity= function _mint(address to, uint256 quantity) internal { // ... 略 unchecked { _addressData[to].balance += uint64(quantity); _addressData[to].numberMinted += uint64(quantity); _ownerships[startTokenId].addr = to; _ownerships[startTokenId].startTimestamp = uint64(block.timestamp); uint256 updatedIndex = startTokenId; uint256 end = updatedIndex + quantity; do { emit Transfer(address(0), to, updatedIndex++); } while (updatedIndex < end); _currentIndex = updatedIndex; } // ... 略 } ``` ![](https://i.imgur.com/EOGUJtm.png) #### 缺點 - 轉移時,`_ownershipOf` 會需要迴圈運算,導致費用倍增 ```solidity= function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) { // ... 略 while (true) { curr--; ownership = _ownerships[curr]; if (ownership.addr != address(0)) { return ownership; } } // ... 略 } ``` #### ERC721A 報告結果 | NUMBER MINTED | GAS USED (ENUMERABLE) | GAS USED (ERC721A) | GAS SAVED | |:--------------:|:---------------------:|:------------------:|:---------:| | Mint 1 | 154,814 | 76,690 | 78,124 | | Mint 2 | 270,339 | 78,819 | 191,520 | | Mint 3 | 384,864 | 80,948 | 303,916 | | Mint 4 | 501,389 | 83,077 | 418,312 | | Mint 5 | 616,914 | 85,206 | 531,708 | #### 改善 [ERC721Enumerable](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721Enumerable.sol#L72) ```solidity= function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal virtual override { super._beforeTokenTransfer(from, to, tokenId); if (from == address(0)) { _addTokenToAllTokensEnumeration(tokenId); } else if (from != to) { _removeTokenFromOwnerEnumeration(from, tokenId); } if (to == address(0)) { _removeTokenFromAllTokensEnumeration(tokenId); } else if (to != from) { _addTokenToOwnerEnumeration(to, tokenId); } } function _addTokenToAllTokensEnumeration(uint256 tokenId) private { _allTokensIndex[tokenId] = _allTokens.length; _allTokens.push(tokenId); } function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { uint256 length = ERC721.balanceOf(to); _ownedTokens[to][length] = tokenId; _ownedTokensIndex[tokenId] = length; } ``` ### [ERC721R](https://github.com/exo-digital-labs/ERC721R) ## Hash - 雜湊 - Hash 是一個函式,它把任意長度的資料作為輸入,對應成固定長度的輸出 - 只要有微小的改動,就會變更整個結果內容 - 無法回推原本內容(結果長度固定) #### JavaScript 版本 - md5 - 使用 nodejs 內建 crypto 模組 ```javascript= var crypto = require("crypto"); var md5 = crypto.createHash("md5"); md5.update("Hello World!"); var str = md5.digest("hex"); var hash = str console.log(hash); // ed076287532e86365e841e92bfc50d8c ``` #### JavaScript 版本 - Keccak256 - 安裝 ethers 套件 ```javascript= const ethers = require("ethers"); const hash = ethers.utils.keccak256( ethers.utils.solidityPack(["string"], ["Hello World"]) ); console.log(hash); // 0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba ``` ## Merkle Tree - 默克爾樹 - 從葉節點向上計算出根節點 ![](https://i.imgur.com/h3V168t.png) #### JavaScript 版本 - 安裝 merkletreejs 套件 - 安裝 ethers 套件 ```javascript= const { MerkleTree } = require("merkletreejs"); const { ethers } = require("ethers"); const leaves = [ { address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, { address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" }, { address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" }, ].map((x) => ethers.utils.keccak256(ethers.utils.solidityPack(["address"], [x.address])) ); const tree = new MerkleTree(leaves, ethers.utils.keccak256, { sortPairs: true, }); const root = tree.getHexRoot(); const leaf = ethers.utils.keccak256( ethers.utils.solidityPack( ["address"], ["0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"] ) ); console.log(tree.getHexProof(leaf)); console.log(tree.verify(tree.getHexProof(leaf), leaf, root)); ``` ## IPFS - 星際檔案系統 [官方網站](https://ipfs.io/) [Stanford Seminar - IPFS and the Permanent Web](https://www.youtube.com/watch?v=HUVmypx9HGI) - 不是區塊鏈 - peer-to-peer 連接傳輸 - 節點相互連接溝通,非中心伺服器運作 ##### IPFS 節點 - [Pinata](https://www.pinata.cloud/) ## 延伸讀物 [ERC721A](https://www.azuki.com/erc721a) [序列化](https://zh.m.wikipedia.org/zh-tw/%E5%BA%8F%E5%88%97%E5%8C%96)