微討論 ethereum transactions
前情提要
transaction
- from (address)
- to (address)
- value (amount of ETH)
- data (optional)
➞ call 合約 function 的細節寫在這
前情提要
transaction lifecycle 簡化版
- broadcast to network
- included in a block by a miner/validator
- the chain includes the block becomes the longest chain
requested ➟ included ➟ recognized
ERC-20 的設計 有些問題


ERC20
function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
function approve(address _spender, uint256 _value) public returns (bool success)
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
例如
A 要給 B 10 個 token
A 執行 transfer(B, 10)
或
A 執行 approve(B, 10)
B 再執行 transferFrom(A, someAddress, 10)
問題會發生在想要改變 allowance 的時候
A 之前有 approve B 一些 token,假設 10 個
後來 A 發現需要 approve B 更多 token,假設需要 20 個
A 需要再 call approve(B, 20)
題外話
很多合約會直接讓你一開始就 approve 最大值
好處是之後不用再 approve 付 gas fee
壞處是如果那個合約被駭或是有 bug 你的 token 會全部被轉走
.
事情順利的話
allowance 10
allowance 20
allowance 0
A 給 B 20 個 token
A call approve(B, 20)
B call transferFrom(A, B, 20)
事情不順利的話
allowance 10
allowance 20
allowance 0
A 給 B 30 個 token
allowance 0
B call transferFrom(A, B, 10)
A call approve(B, 20)
B call transferFrom(A, B, 20)
B 怎麼知道有這個好機會
複習一下 transaction 的 lifecycle
requested ➟ included ➟ recognized
解決方案 1
增加 allowance 前要先改為 0
(弱弱的)

解決方案 2
加開 increaseAllowance 跟 decreaseAllowance 兩個 func
處理要增加或減少 allowance 的情形
approve 只用在 initial approve,或是都不要用

OpenZeppelin 的 ERC-20 實作
從另一個角度看
因為 approve 跟 transferFrom 是兩個 transaction
不是在同一個 transaction 內
所以有了趁虛而入的機會
一個 transaction
可以包含好幾個動作
某個人執行 某個 contract 的 某個 function
某個 function 改變了一些值之後 又執行另外一個 function
另外一個 function 也改變了一些值
然後又執行另外一個 contract 的某個 function
...
最後完成
一個 transaction
transaction 內的動作
會都有生效或是都沒生效
而且不必擔心做到一半被別人干擾
block 123
transaction 2345 success
transaction 2346 failed
transaction 2347 success
•
•
•
如果想要一次執行多個 function
又想要在同一個 transaction 內的話
可以透過合約來完成
contract A {
function b() {
c();
d();
e();
...
}
}
舉個例
exactInput
exactOutput
➞ 給定值的 ETH 看能換多少是多少
➞ 要換到定值的 token
假設要用 uniswap 的合約把 ETH 換成某 token
exactOutput
1 個 ETH 能換多少 token 不一定
要給多一些 ETH 才不會失敗
v2 會自動退 ETH
v3 不會 要再 call refundETH
他會把他所有剩的 ETH 給你
exactOutput
如果我先 exactOutput
再執行 refundETH
分 2 個 transaction 的話
中間可能會被別人先 call refundETH
該還我的 可能會被別人拿走
必須要透過合約寫在同一個 function 裡面
才會在同一個 transaction 執行這兩個動作
別人才沒有介入的機會
怎麼做
我們執行合約 A 的 function,把 ETH 附給他
合約 A 用他的名義去換 token 回來
然後再把 token 轉給我們
退我們 ETH

題外話 解決方案 3
要獲得授權 不一定要 send transaction
只要有簽章就可以確認
好像不行?
因為 approve 還是要我們來 approve?
前面的 approve/transferFrom
授權 跟 讓別人轉走 兩個動作
可以透過合約 在同一個 transaction 完成嗎
對方有你的簽章,再一起拿去 send transaction 就好
Flash Loan
可以沒有抵押就借超多錢
只要在同一個 transaction 可以還回去
就不會被 revert
提供 flash loan 的 function 會做三件事
1. 轉錢給 receiver
2. 執行 receiver 準備好的 callback
3. 把錢轉回來
Flash Loan 的存在
是 DeFi 的最大特色 (之一)
也是 DeFi 的最大衝擊 (嗎)
Flash Loan Attack
操控價格不用本錢
騙 DeFi 合約當韭菜
Flash Loan Attack



參考資料
ethereum transactions
By luyunghsien
ethereum transactions
- 698