# 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;
}
// ... 略
}
```

#### 缺點
- 轉移時,`_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 - 默克爾樹
- 從葉節點向上計算出根節點

#### 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)