# Zerion 如何利用 Genesis Card NFT 實現跨鏈 (on Polygon) 減免手續費

By [Albert.Lin](https://paragraph.com/@albertlin) · 2022-09-21

---

TLDR
----

> Zerion 是利用鏈下判斷該帳戶是否有 Genesis NFT。若確認有擁有 Genesis NFT，Zerion的 Signer 去簽署一筆 fee = 0 的簽章並放在交易中。當交易送到合約的時候會去判斷簽章是否為 Zerion Signer 所簽。若為 Zerion Signer 所簽就不收費。

![](https://storage.googleapis.com/papyrus_images/ef46f35eaf3eca7f17b9396588e1ed93f599cee2c47bf81a5a5efb014f4b1225.png)

Trace Flow
----------

在呼叫 Zerion swap token 的時候會去呼叫 [Zerion Router](https://polygonscan.deth.net/address/0xd7F1Dd5D49206349CaE8b585fcB0Ce3D96f1696F#code) 的`execute()` external function。

![](https://storage.googleapis.com/papyrus_images/60cecb40a345fc4fc2cdb7e9ee1447dc47713251b5307811b714d79ce1723a23.png)

因為我們想要知道 protocol fee 在什麼時候被收取，所以我們直接跳到最後 `return execute(input, output, swapDescription)` 這個 internal function 去看發生什麼事。

![](https://storage.googleapis.com/papyrus_images/2e4b00b81895a596483dca31b7f4d0fd7181fabc37d73a6c79c06522fb93734e.png)

在 `execute()` internal function 中，我們往下走在 `#196` 行會發現這一行程式碼 `Base.transfer()` 。這行程式碼就是把 fee 傳送到 [Zerion MultiSig](https://polygonscan.com/address/0x4a183b7ed67b9e14b3f45abfb2cf44ed22c29e54#code) 中。

![](https://storage.googleapis.com/papyrus_images/5dd537c0d10dbd3cacb2c9355a86f7b7e86456e015f4e9ac91686ef3b8e5402c.png)

fee 的數量就是 `protocolFeeAmount` 所記錄的數值。哪這個數值是在哪邊決定的呢？往上面的程式碼找尋可以發現 `protocolFeeAmount` 是從 `getReturnedAmounts()` 所決定。

![](https://storage.googleapis.com/papyrus_images/a3ae0bbef5c4be82b0bd04c8f69bef5d424f86e5acd0d85fa56ef789b5b958b2.png)

在 `getReturnedAmounts()` 的 `#496` 是決定 `protocolFeeAmount` 的數字怎麼來。可以發現它是由 `totalFeeAmount * protocolFee.share` 所決定。

![](https://storage.googleapis.com/papyrus_images/f3d6c02eb46d985c318069eb7930c32104e8b6753ed54d02b19d722c1accb2ee.png)

我們知道若 Sender 持有 Genesis Card NFT 可以擁有 Zero Fee 的減免。`protocolFeeAmount` 變成 0 條件有兩個。 `totalFeeAmount` 等於 0 或`protocolFee.share` 等於 0 （或同時為 0 ）。 於是我找了一個擁有 Genesis Card NFT 且 fee 為 0 的 [transaction](https://dashboard.tenderly.co/tx/polygon/0x2b22b61d66703424fe1f3230b753c17e46c714429ea728636d7400e21f147687/debugger?trace=0) 來一探究竟。 使用 `Tenderly` 發現他的 `protoclFee.share` 就是帶 0。由此判斷是一開始帶進來的參數就指定了 `protocolFee.share`來決定這次 protocol fee 要收多少。

![](https://storage.googleapis.com/papyrus_images/97f0d2a623ce3c10616832814f9ee20efd6cacd98d6998009bfe1463cb677679.png)

此時出現一個疑問。若 protocol fee 是由參數帶進來，那我們不就可以任意決定 protocol fee 了嗎？這時候讓我們回頭看到一開始的 `execute()` extneral function。

![](https://storage.googleapis.com/papyrus_images/60cecb40a345fc4fc2cdb7e9ee1447dc47713251b5307811b714d79ce1723a23.png)

裡面跟 Fee 有關的檢查是 `validateProtocolFeeSignature()` 這一個 function。接下來我們就來分析裡面做了什麼事情。

![](https://storage.googleapis.com/papyrus_images/5d787146c6ec8e3cee3bdf652759d0e015b500c736a178ea2dc0cebd59a37625.png)

第一個部分是去檢查這次交易有沒有包含 `protocolFeeSignature`。若沒有（length會等於0）就單純檢查收 fee 的對象和 `protocolFee.share` 是否跟 contract 預設的一樣。

![](https://storage.googleapis.com/papyrus_images/98234dfbcd508829d70e88ea67e7704917d1996c0ee66d3e6722db4bc99d62da.png)

若有帶 `protocolFeeSignature` ，就去呼叫 `hashProtocolFeeSignatureData()` 取得 signatureData。

![](https://storage.googleapis.com/papyrus_images/456385c670f0b51b51d10d2b3f92219e3e22ee3a77bad05fc524b4d4d6c15d9a.png)

`hashProtoclFeeSignatureData` 就是把部分參數包含 `swapDescription` 和 `dealine`等取 hash 值。這個 `hash` 就是 Signature 所簽的 data。

![](https://storage.googleapis.com/papyrus_images/ba10aa2130bbdcef143412224cc36b859e1ca295f21b0e85fb42091a74ced4c9.png)

得到了 signature data 之後就去呼叫 `isValidSignatureNow()` 來比對簽章是否正確。可以注意 `isValidSignatureNow()` 其中一個參數是 `getProtoclFeeSigner()` ，這個是 Zerion Router 合約裡面所定義的 signer。

![](https://storage.googleapis.com/papyrus_images/081284dd73922a4ab9829243b0e9112b4a5e6aedc7391ec0928604436f5ca036.png)

`isValidSignatureNow()` 裡面就做兩件事情。

1.  利用剛剛得到的 `hash` 和 signature 來還原簽署的 address (就是裡面的 `recovered`)。比對 `recovered` 和 `signer` 是否相同。若相同就代表這是由 Zerion signer 所簽署，確認 `swapDescription` 裡面所帶的 `protocolFee` 相關資訊是被 Zerion Signer 所認可。
    
2.  下方的 `staticall` 是支援若 Zerion Signer 是合約而非單純的 EOA。因為合約沒有 key，所以無法簽署簽章。所以利用 ERC1271 的方式來實現合約簽章。詳細內容可以參考這篇[文章](https://medium.com/taipei-ethereum-meetup/eip-1271-%E8%AE%93%E4%BD%A0%E7%9A%84%E5%90%88%E7%B4%84%E4%B9%9F%E5%8F%AF%E4%BB%A5%E7%B0%BD%E5%90%8D-dc4e1c9c84a0)。
    

![](https://storage.googleapis.com/papyrus_images/d5fb58da0c3295a39732bb8a307817e524f2635eaf478ce2022edb96175dd053.png)

若確認簽章無誤，最後就檢查是否超過簽署的 dealine。

![](https://storage.googleapis.com/papyrus_images/5b95e2a6cc79404f8e44e69f4700bc5c3f49f19c630f30358205f86a3bbf7343.png)

通過 deadline 的檢查後，就會依照 `protocolFee` 所記錄的資訊（包含 reciver 和 fee amount）去收取 protocol fee。

Takeaway
--------

因為目前跨鏈的機制還尚未成熟，所以 Zerion 利用一個 signer 的角色來判斷該筆交易是否需要收 protocol fee。這樣的機制或許不是那麼去中心化，但卻是一個比較合理的做法。這樣的方式可以快速運用在不同的鏈上，而不需要考慮鏈與鏈之前該如何溝通。使用者也只需要在某個鏈上擁有該 NFT 就好，不需要每個鏈都有。缺點仍是中心化，若之後 Zerion 想要修改規則，變成改收 1% 的 protocol fee，一般使用者也只能乖乖接受（Zerion Signer 可以簽他們想要簽的任何交易）。在過渡時間這尚可是一個可以接受的方式，期待未來跨鏈機制的成熟，就不需要利用這個中心化的方式來做，而可以真的直接讀取鏈上的資訊來收費。

---

*Originally published on [Albert.Lin](https://paragraph.com/@albertlin/zerion-genesis-card-nft-on-polygon)*
