---
# System prepended metadata

title: Ethereum transaction

---

# Ethereum transaction

### 一般交易由以下幾個部分組成
```javascript=
From
To
Amount
Fee
Note
```

### Ethereum 的交易由以下幾個部份組成

```javascript=
nonce
From
To
Amount
gasPrice
gasUsed/gasLimit
Note
```

### 針對其中幾個部分進行說明：


#### nonce： prevent double spending[[1: what is nonce]](https://kb.myetherwallet.com/en/transactions/what-is-nonce/) 可以透過API得到，也可以透過自己錢包的交易數量推得。

#### gasPrice：每單位 Gas 願意付出多少 ETH，一般使用 Gwei(1e9 to wei) 作為單位。gasPrice * gasUsed 即為此次交易的手續費 Fee。

#### gasUsed：此次交易使用的 gas 數量。

#### gasLimit： 你願意花費在交易上的最大數量的 Gas 單位。


### 進行ETH交易

1. 取得 chainId， chainId是取決於Ethereum交易的網路


| ropsten | rinkeby | mordor | Eth mainnet | Eth based mainnet |
|:-------:|:-------:|:------:|:-----------:|:-----------------:|
|    3    |    4    |   63   |      1      |        61         |
 
2. 將上述 ETH 交易內容進行 rlp 編碼的格式排列

```javascript=
class Transaction{
    int nonce;
    BigInt gasPrice;
    int gasLimit;
    String to;
    BigInt amount;
    String note;
    
    Transaction(this.nonce, this.gasPrice, this.gasLimit, this.to, this.amount, this.note);
}

var tx = Transaction(0, 49 * 1e9 ,21000, '0x15b1a87b648384033fdca1b7656cb534b91ffe56', 0.002 * 1e18, 'This is a transaction on ropsten');

var v = chainId;
var r = BigInt.zero;
var s = BigInt.zero;

var rlpData = [nonce, gasPrice, gasLimit, to, amount, note, v, r, s];
```

3. 將rlpData 使用rlpEncode function進行rlp 編碼，得到 encodeRlpData，再進行rlp 編碼之前會將 rlpData裡面的各元素根據其型別轉為Buffer，可參考 toBuffer function。


```javascript=
Uint8List toBuffer(dynamic data) {
  if (data is Uint8List) return data;

  if (data is String) {
    if (isHexString(data)) {
      return Uint8List.fromList(hex.decode(padToEven(stripHexPrefix(data))));
    } else {
      return Uint8List.fromList(utf8.encode(data));
    }
  } else if (data is int) {
    return Uint8List.fromList(intToBuffer(data));
  } else if (data is BigInt) {
    return Uint8List.fromList(encodeBigInt(data));
  } else if (data is List<int>) {
    return Uint8List.fromList(data);
  }

  throw TypeError();
}
```


4. var hash = keccak256(encodeRlpData)
5. [r,s,v] = ECDSA(hash)
6. 最後將簽名後得到的r,s,v 替換上述的rlpData，再將進行rlp encode後得到的Buffer轉為HEX，加上前綴{0x}， 即為可用於Ethereum HEX。 


### RLP encode:
rlp 是 recursive length prefix 的縮寫，目的是用於對巢狀的陣列進行編碼[[eth wiki]](https://eth.wiki/fundamentals/rlp)[(中文)RLP 編碼規則](https://www.itread01.com/content/1541817991.html)。

規則一： 對於值在[0, 127]之間（因為基於ASCII標準表，所以超過128就處理不了啦）的單個字元，其編碼是其ASCII編碼。 比如，單個字元‘0’在ASCII標準表裡是48（十六進位制是0x30），所以在RLP這裡也是48。

規則二： 如果字串長度是0-55個位元組，那麼在前面加上（128+字串長度）作為字首。 比如，空字串編碼就是128，即128 = 128 + 0； “0”這個字串只有1個字元，編碼就是12948； “0123”在ASCII標準表是48495051，長度是4個位元組，字首為132，那麼RLP 轉換後就是13248495051。

規則三： 如果字串長度大於55， 那麼在前面加上（183+字串長度的二進位制編碼的長度）和（字串長度）做字首。（為什麼是183？因為在規則二里，最大就是183（128+55）。）

規則四： 如果列表中所有字串的總長度按規則1到3編碼後，小於等於55，那麼在前面加上（192+該長度）做字首。列表裡的字串編碼參照規則1到3（183和192之間差了9個位元組，9個位元組用來表示長度。

規則五： 如果列表中所有字串的總長度按規則1到3編碼後，超過55，那麼在前面加上（247+該長度的二進位制編碼的長度）和（該長度）做字首。列表裡的字串編碼參照規則1到3（為什麼是247？因為192+55=247）。


```javascript=
Uint8List encode(dynamic input) {
  if (input is List && !(input is Uint8List)) {
    final output = <Uint8List>[];
    for (var data in input) {
      output.add(encode(data));
    }

    final data = _concat(output);
    return _concat([encodeLength(data.length, 192), data]);
  } else {
    final data = toBuffer(input);
    // 對於值在[0x00，0x7f]範圍內的單個字節, 
    if (data.length == 1 && data[0] < 128) {
      return data;
    } else {
      return _concat([encodeLength(data.length, 128), data]);
    }
  }
}

Uint8List encodeLength(int length, int offset) {
  if (length < 56) {
    return Uint8List.fromList([length + offset]);
  } else {
    final String hexLen = _intToHex(length);
    final int lLength = hexLen.length ~/ 2;

    return _concat([
      Uint8List.fromList([offset + 55 + lLength]),
      Uint8List.fromList(hex.decode(hexLen))
    ]);
  }
}

Uint8List _concat(List<Uint8List> lists) {
  final list = <int>[];

  lists.forEach(list.addAll);

  return Uint8List.fromList(list);
}

```
