walkthrough คลิป Gasless MetaTransactions with OpenZeppelin Defender (Part 1)
no gas no cry
Pain point
user onboarding ของ web3 มีหลายขั้นตอนมากๆ ตามรูป
user จะต้องสมัคร wallet และสมัคร exchange แล้วไปซื้อเหรียญ native coin ใน exchange เพื่อนำมาใช้เป็นค่าแก๊สของ web3
โดย meta-transaction จะเข้ามาแก้ปัญหานั้น
Meta-transaction คืออะไร ?
คือการทำให้ user ไม่ต้องจ่ายค่า gas เอง
คร่าวๆคือ relayer จะทำการนำ request ของ user มาทำการยิง tx ให้ recipient ที่ฝั่ง blockchain ตามรูป
โดยทาง relayer จะเช็คด้วยว่า request มีการ sign มา เพื่อกันการปลอมเป็น user
ข้อดีของ meta-transaction
user สามารถ onboard ได้ง่าย โดยไม่ต้องไปสมัคร exchange เพื่อซื้อค่าแก๊ส
user ไม่ต้องเสียเวลาโอนค่าแก๊สไป sidechain (ถ้าต้องการใช้ sidechain)
สามารถทำให้ครบวงจรได้ โดยการ generate wallet ให้ user ด้วย … user ก็จะไม่ต้องสมัครทั้ง wallet และ exchange
โอเค ดู component ต่างๆกัน
Stack ที่จะใช้
ในการ sign message เราจะใช้ EIP712
relayer เราจะใช้ service Autotasks webhooks relayer ของ OpenZeppelin Defender
recipient เราจะใช้ contract MinimalForwarder EIP2771 Recipient
sign message
เราจะใช้ field ดังนี้
recipient คือ contract ปลายทางที่เราจะ interact
data คือ คำสั่งที่เราจะ execute (function และ parameter)
nonce เอาไว้กัน replay attack
domain_sep เอาไว้กัน replay attack cross-forwarders
Relayer
เราจะใช้ Openzeppelin defender กัน ซึ่งจริงๆแล้วเราจะใช้ 2 service คือ Autotask และ Relayer
Autotask จะเป็น webhook (รันแบบ schedule ก็ได้) โดยจะทำหน้าที่รับ request , verify , กำหนดคนจ่าย (relayer) แล้วส่ง tx ไปยัง relayer
Relayer จะทำการ sign transaction แล้วส่ง transaction นั้นไปยัง contract
เอ จริงๆไม่ได้ซับซ้อนเลยนะ น่าจะเขียนเองได้ แล้วเราใช้ service ทำไม
จริงๆเค้าก็ให้เหตุผลมา มาดูแต่ละข้อกัน
1 เก็บ private key อย่างปลอดภัย — อันนี้ถ้าเราไม่ต้องมา setup vps เอง ก็ประหยัดเวลาอยู่ละนะ
2 manage ค่า nonce — การยิง tx ทีละมากๆ จะมีปัญหาค่า nonce ซ้ำ ได้ เค้าเลยคิดค่า nonce ให้เราเลย
3 หาค่า gas ที่ดีที่สุด — อันนี้เหมือนเค้าบอกว่า เรายังต้อง set เองอยู่ เดี๋ยวไปดูกัน
4 Automatically resubmit txs — ถ้า tx fail เค้าจะยิงใหม่ให้อัติโนมัติ
5 High availablity through multiple provider-ถ้า provider ล่ม จะเปลี่ยน provider ใหม่ เพื่อยิง tx ต่อไปได้
Recipient
เราจะใช้ contract MinimalForwarder เพื่อ
verifySignature ว่ามาจาก relayer จริงๆ
เพิ่มค่า nonce
เพิ่ม field signer โดยจะเป็น user ที่เราจ่ายค่า gas ให้
ยิง tx ไปยัง contract ปลายทาง
จากนั้น contract ปลายทางจะทำการรับ signer มา แล้วกำหนด msgSender() เพื่อไว้ดึง user ที่เราตั้งใจจะให้เรียก contract โดยเราจะใช้ msgSender() แทน msg.sender ตามมาตรฐาน EIP2771
โอเค มาดู code กัน
Demo
โดยเราจะเปลี่ยน contract name registry ให้รองรับ meta tx
contract name registry เดิมแล้วมีการทำงานดังรูป
แต่เมื่อเรา convert contract มาให้รองรับ meta tx แล้วจะมี code ดังนี้
โดยตรง constructor จะกำหนด relayer ที่เรา trust
และ msg.sender จะเปลี่ยนเป็นใช้ _msgSender() แทน
โอเค มาลองเขียน test กัน
โดยเราสามารถเรียก register แบบธรรมดาได้
หรือจะเรียกแบบ meta tx ก็ได้
จะสังเกตุเห็นว่า signed msg จะถูกส่งให้ forwarder เพื่อ concat ตัว signer และจ่ายค่า gas ให้ (forwarder ตัว relayer จะเป็นคนเรียกใช้ ดังนั้น relayer จ่ายค่าเแก๊สให้)
โอเคการ test ก็เป็นประมาณนี้
มาดูการสร้าง relayer กัน
กำหนดชื่อ และ network
โดยเมื่อสร้างเสร็จ ตัว relayer จะทำการสร้าง wallet รวมทั้ง api key ให้ ตอนนี้เราก็สามารถใช้ relayer ในการจ่ายค่า gas แทนเราได้แล้ว
ตอนเพิ่งสร้าง relayer จะไม่มีค่า gas เลย อันนี้ให้เราโอนไปเติมใน wallet ของ relayer ก่อน
โอเค พอ relayer พร้อมใช้งานแล้ว เราก็แค่ป้อน request ที่ต้องการให้กับ relayer ตาม code ดังนี้
โดยเราจะเขียน server เอง
โดยมี api ที่รับ request และ signature ที่ user sign ไว้แล้ว มาเป็น input
จากนั้นเราจะทำการ initial ตัว relayer โดยใช้ api key ที่ได้มาตอนสร้าง relayer
และทำการ connect กับตัว forward contract (อันนี้เหมือนจะยังไม่เห็น code แหะ น่าจะใช้ตัว Minimal Forwarder ตรงเลยมั้ง ตาม link นี้)
สุดท้ายทำการ custom เช็คก่อนส่งไป relayer มาดู code การ custom check กัน
อันนี้เขียนเองได้เลย แต่ใน demo ทำการเช็คว่า
ตัว contract ปลายทาง ตรงกับที่กำหนดรึเปล่า
signature ตรงกับ request รึเปล่า
กำหนดค่า gas ให้กับ tx
ให้ relayer ทำการเรียก forwarder contract เพื่อส่ง meta tx
มาดูผลลัพธ์กัน
ตัว wallet relayer จะทำการเรียก tx
โดยใน tx นั้นจะยิงจาก wallet relayer ไปยัง forwarder contract
โดยใน internal tx ประกอบด้วย 2 step คือ
wallet relayer เรียก forwarder contract
และ forwarder เรียก contract ปลายทาง
ซึ่งจริงๆก็จบละ ทำ meta tx ได้แล้ว user ที่ sign ไม่ต้องเสีย gas
แต่ส่วนเขียน api เนี่ย เราใช้ service Autotask ของ OpenZeppelin ได้ ไม่ต้องมา host เอง แต่เอาไว้ต่อ part 2 แล้วกัน
ref