---
# System prepended metadata

title: KrypoCamp 第二週實體課程

---

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