Vissza a bloghoz

ProCat Solutions

Web3 kitérő: amit a javíthatatlan smart contractok tanítottak

Token presale Solidityben és OpenZeppelinnel, Merkle-fás whitelist, referral, gas Polygonon és Arbitrumon, Ethers.js backend, audit, és ami ebből megmaradt.

ProCat Solutions web3solidityopenzeppelinetherspolygonarbitrum
Megosztás
Web3 kitérő: amit a javíthatatlan smart contractok tanítottak

Egy telepített smart contract nem javítható. Ha hiba van benne, a hiba ott marad, és a felhasználók pénzével fizetnek érte. Az elmúlt két évben több Web3-projektben dolgoztunk: token presale rendszereken, whitelist- és referral-logikán, kriptofizetés-integráción EVM-kompatibilis hálózatokon. Egy olyan csapatnak, amely egyébként backend-rendszereket épít, ez a mondat minden döntést átírt. Ez a cikk arról szól, mit jelentett a gyakorlatban smart contractot fejleszteni és üzemeltetni, és mit hoztunk vissza belőle a megszokott munkánkba.

Hardveres kriptotárca és jegyzetfüzet egy asztalon

Ne írj sajátot

A Solidity-fejlesztés első szabálya, amit megtanultunk: a szabványos komponenseket magunk nem írjuk meg. Az ERC-20, az ERC-721, a hozzáférés-vezérlés és az újrahívás elleni védelem auditált, széles körben használt formában megvan az OpenZeppelin könyvtárban. Minden saját implementáció csak kockázatot ad hozzá.

Saját kód ott kezdődik, ahol az üzleti logika: a presale-szerződésben, amely eldönti, ki, mikor, mennyiért és mennyit vásárolhat. Ebben négy elvet követünk:

  • a szerződés állapota minimális; ami nem kell a láncon belüli döntéshez, az láncon kívül marad,
  • minden módosító függvény explicit jogosultsághoz kötött (Ownable vagy AccessControl),
  • a szerződés szüneteltethető (Pausable), mert hiba esetén ez az egyetlen vészfék,
  • a pénzmozgás a “checks-effects-interactions” mintát követi, és ReentrancyGuard védi.

A javíthatatlanság miatt a hibakezelés, a tesztelés és a jogosultságok itt szigorúbbak, mint bármelyik backendnél, amit korábban írtunk.

Merkle-fa a címlista helyett

Egy tipikus presale-rendszer három részből áll. A szerződés kezeli a vásárlást, a backend tartja a whitelistet és a referral-kódokat, a frontenden pedig a felhasználó tárcát csatlakoztat.

A whitelist naiv változata, a címek listája a szerződésben, drága: minden új cím egy tranzakció. Mi Merkle-fát használunk. A backend kiszámolja a jogosult címek fájának gyökerét, csak ez kerül a szerződésbe, a felhasználó pedig vásárláskor bizonyítékot (Merkle proof) küld, amelyet a szerződés ellenőriz. A whitelist frissítése így egyetlen tranzakció.

A referral logikát szándékosan megosztjuk a lánc és a backend között. A szerződés eseményként csak annyit rögzít, melyik cím vásárolt és milyen referral-azonosítóval. A jutalékot, a jogosultság ellenőrzését és a kifizetések ütemezését a backend intézi. A szabályok így a szerződés újratelepítése nélkül módosíthatók, és a láncra nem kerül olyan adat, amelynek nem kellene nyilvánosnak lennie.

Fejlesztő smart contract kódon dolgozik

Polygon, Arbitrum, gas

Az Ethereum főhálózat tranzakciós díjai a legtöbb presale-nek vállalhatatlanok, ezért a projektek Polygonon vagy Arbitrumon futnak. Mindkettő EVM-kompatibilis, a szerződéskód változtatás nélkül telepíthető. Az üzemeltetés viszont eltér:

  • más a gas ára és a blokkidő, ezért a tranzakció-megerősítésre váró logikát hálózatonként állítjuk be,
  • az Arbitrum L2-es díjába az L1-es adatköltség is beleszámít, ami a tranzakció méretétől függ; a nagy calldata, például egy hosszú Merkle proof, itt drágább, mint elsőre gondolnánk,
  • a Polygonon időnként torlódás és átszervezett blokkok (reorg) fordulnak elő, ezért az eseményfeldolgozás csak több megerősítés után tekint véglegesnek egy tranzakciót.

A szerződéskódban a gas-optimalizálás valódi pénzt jelent: tárolási változók összecsomagolása, felesleges írások elhagyása, események használata tárolás helyett. A Hardhat gas-riportja minden tesztfutásnál jelzi, ha valami drágult.

A websocket csendben szakad meg

A backendünk Node.js-ben, Ethers.js-szel beszél a lánccal. Két dolga van: tranzakciókat küld (whitelist-gyökér frissítése, kifizetések) és eseményeket figyel (vásárlások).

Az üzemeltetési gondok többségét az eseményfigyelés okozta. A websocket-alapú figyelés megbízhatatlan, a kapcsolat szó nélkül megszakad. Áttértünk lekérdezésre (polling): a backend nyilvántartja az utoljára feldolgozott blokkot, és rendszeresen lekéri az azóta keletkezett eseményeket. A reorgok miatt mindig néhány blokkal a láncfej mögött marad. A feldolgozás idempotens, a kulcs a tranzakció-hash és a log-index.

A küldő oldalon a nonce-kezelés és a beragadt tranzakciók pótlása külön logikát kíván. Egy alacsony gas-árral küldött tranzakció a hálózat drágulásakor órákig függhet, és amíg ott van, a következő nonce sem megy át.

Az audit a leíratlan feltevéseket találja meg

A smart contractokat sokkal alaposabban teszteljük, mint a backend-kódot. Hardhat alatt minden ágra van egységteszt, fork-tesztek futnak valós hálózati állapoton, a számítási függvényeket fuzz-tesztekkel ellenőrizzük. Telepítés előtt független auditot kérünk. Ez kötelező, mert a hiba ára a felhasználók pénze.

Az audit többnyire nem kritikus hibát talál. Azokat a feltételezéseket hozza felszínre, amelyeket a fejlesztő soha nem írt le: mi van, ha elvész a tulajdonosi kulcs, mi lesz a presale lejárta után a szerződésben maradt összeggel, ki hívhatja meg a vészleállítót.

Ami megmaradt

A Web3 nálunk kitérő maradt, fő irány nem lett belőle. Néhány dolog viszont velünk maradt:

  • az “ez nem javítható” gondolkodás: a migrációkat, a fizetési folyamatokat és a külső integrációkat úgy tervezzük, mintha nem lehetne visszavonni őket,
  • az idempotens, újrajátszható eseményfeldolgozás lett az alapminta minden webhooknál és üzenetsornál,
  • a jogosultsági modellt minden módosító műveletre explicit leírjuk és teszteljük,
  • a kritikus kódrészeket független szem is átnézi, akkor is, ha nincs formális audit.

Ezek a minták a következő projektjeinkben, a hangalapú rendszereknél is beváltak. Ott a hívásesemények feldolgozása pontosan ugyanezeket a problémákat veti fel.

Megosztás
Széchenyi Terv Plusz kedvezményezetti infoblokk
QR Code