Transazioni gasless per eliminare le barriere ai nuovi utenti Ethereum
Una delle maggiori cause di frizione nell’adozione di app web3 da parte nuovi utenti e in particolare di utenti che non sono “nativamente” abituati all’uso di wallet Ethereum è sicuramente la complessità della procedura di onboarding.
- Atterrare sulla app
- Aprire il chrome store
- Installare l’estensione per il wallet (eg. Metamask)
- Accettare termini e condizioni
- Salvare in luogo sicuro le 12 parole del seed
- Re-inserirle a scopo di verifica
- Impostare una password sicura per cifrare il seed nel dispositivo
- Copiare il nuovo indirizzo generato
E a questo punto abbiamo un wallet ed un account Ethereum pronto all’uso, ma non abbiamo ancora ether e quindi non possiamo interagire con nessuna dapp perché non potremmo pagare il gas. Quindi dobbiamo proseguire con le seguenti azioni
- Cercare un exchange, ahime centralizzato visto che non abbiamo ancora ether
- Creare un account
- Scegliere la password sicura secondo le regole della piattaforma
- Accettare i termini e le condizioni dell’exchange
- Verificare la mail
- Inserire i propri dati anagrafici per il KYC
- Trasmettere bolletta o altro documento aggiornato per la prova di domicilio
- (dall’entrata in vigore di MiCA e travel rule) Dimostrare di controllare l’indirizzo blockchain tramite satoshi test o equivalente
- Spedire un bonifico
- Attendere accredito delle somme in euro
- Comprare ether
- Fare un prelievo di ether verso l’indirizzo ethereum del punto sopra
- Iniziare a usare la dapp
Facilitare l’adozione da parte di nuovi utenti è probabilmente l’aspetto più importante del web3. L’esperienza utente sopra descritta è totalmente frustrante.
Trilemma dell'usabilità per app web3?
Forse possiamo spingerci a fare una congettura che saremmo lieti di vedere smentita ovvero che una app Web3 può essere due di tre fra self-custodial (gestisci tu le tue chiavi), facile da usare e sicura. Si possono avere due di queste ma non tutt'e tre. Ripeto è una provocazione più che una congettura e dobbiamo lavorare per rimuoverla.
Quali sono le soluzioni per non far pagare il gas ai nuovi utenti?
Quali sono dunque le strategie possibili per mitigare questa frizione?
Nel caso di app che richiedono l’uso di token erc20 possiamo sfruttare le fondamentali funzioni approve( ) e transferFrom( ). Queste consentono di movimentare fondi “per conto” dell’utente senza che questo debba effettuare le transazioni. La app può farsi carico delle spese di gas e trovare un modo nel suo business model per assorbire queste fee.
Un flusso interessante potrebbe essere:
- Il frontend della app genera per l’utente un mnemonic seed senza trasmetterlo al backend. Ne trasmette la chiave pubblica o indirizzo al server, mentre la chiave privata assolutamente resta al sicuro nel frontend.
- La app chiede all’utente di salvare il seed e poi richiede una verifica dello stesso chiedendo all’utente di inserire le 12 parole
- In background il server della app assegna qualche frazione di ether al nuovo account eth dell’utente
- Il frontend firma una transazione di approve( ) a beneficio del contratto della app o di una chiave controllata dalla app.
Da questo punto in poi la app può movimentare i fondi per conto dell’utente. Questa strategia consente di fare l’onboarding dell’utente e permettergli di movimentare token erc20 (ma anche nft erc721) senza che questi abbia praticamente mai a che fare con un wallet e con tutte le sue complicazioni. E’ fatto salva comunque la facoltà dell’utente di installarsi un wallet come Metamask e gestire i propri fondi direttamente.
Questa strategia è funzionale ma presenta una vulnerabilità, ovvero il backend della app deve rifocillare l’account appena creato all’utente in modo che la transazione di approve( ) possa essere eseguita, quella che assegna alla app di spendere per conto dell’utente.
Per evitare che l’utente possa sfruttare questa vulnerabilità con un sybil attack che consumerebbe le riserve di ether del backend della app, il business model dovrebbe prevedere una fee in euro immediata per l’utente, una transazione con carta di credito o altro mezzo di pagamento ma si ricadrebbe in un'esperienza utente faticosa. Quasi nessuna applicazione oggi chiede un pagamento anticipato prima ancora di mostrare la sua utilità.
Quali sono dunque le best practice per mitigare questo problema?
Eip-2612 -- ERC-20 approvals via secp256k1 signatures
Una soluzione valida e molto interessante è quella che consente di creare delle transazioni a partire da messaggi firmati dall’utente che però non agisce direttamente creando una vera transazione e firmandola come avviene di solito nelle dapp. Vediamo un po’ meglio questo processo.
Supponiamo di avere un contratto erc20 in cui l’utente Alice con chiave 0xAaAa..aa ha un balance pari a 100 unità. Nel modo classico Alice potrebbe assegnare alla nostra app il potere di spesa, con una logica scritta in pseudo-codice
approve app , 100
In questo caso Alice deve creare una vera e propria transazione, firmarla, spedirla e quindi pagare il gas.
Se volessimo evitare ad Alice il pagamento del gas potremmo in realtà sviluppare nel nostro contratto ERC20 una funzione che riceve un messaggio firmato da Alice ma non una vera e propria transazione.
Potremmo invece pensare che Alice firmi un semplice messaggio con una logica del tipo
“Permit the App to spend 100 tokens” ---signed Alice
E poi questo messaggio potrebbe essere trasformato in una vera transazione, consegnata allo smart contract dalla App stessa che si farebbe carico delle fee di mining.
Ma Perché dovrebbe farlo? Semplicemente perché potrebbe essere un modo per ottenere l’onboarding di Alice nel sistema come nuovo cliente e lo sviluppatore della App potrebbe considerare questa come una spesa necessaria al pari di ogni altro costo associato al marketing.
Lo standard EIP è stato pensato proprio per rendere questo processo sicuro e riproducibile senza doverlo reinventare per ogni app. https://eips.ethereum.org/EIPS/eip-2612
Come abbiamo detto in precedenza, ERC20 contiene una logica molto potente e flessibile grazie al meccanismo di delega di spesa che permette ad un terzo di spendere per conto dell’utente tramite transferFrom( ), ma purtroppo per assegnare questa delega l’utente deve essere in grado di fare una transazione approve( ) presso il contratto ERC20 del token e quindi essere in grado di pagare il gas avendo già una scorta di ether.
Lo standard EIP2612 estende lo standard ERC-20 con una nuova funzione permit( ), che consente agli utenti di modificare la mappatura delle quote utilizzando un messaggio firmato, anziché tramite msg.sender.
La specifica qui presentata è in linea con l'implementazione in Uniswap-v2.
La funzione permit( ) è sufficiente per consentire a qualsiasi operazione che coinvolga i token ERC-20 di essere pagata utilizzando il token stesso, anziché l'ETH. Un esempio di contratto che consente di effettuare transazioni con token senza gas è disponibile qui.
EIP-2771: Secure Protocol for Native Meta Transactions
Eip-2612 descritto nella sezione precedente è una soluzione pensata appositamente per la gestione di transazioni per token erc20 e questo è il suo limite di applicazione. Al contrario EIP 2771 è una soluzione architetturale più complessa e pensata per gestire in modo generalizzato un approccio gasless a qualsiasi tipo di transazione con smart contract.
(tratto da https://eips.ethereum.org/EIPS/eip-2771)
Prevede la presenza dei seguenti attori:
- L’utente signer che firma la richiesta, in quanto non si tratta di una transazione completa ma di una sorta di surrogato che viaggia off chain.
- Il gas relay, che si occupa di validare la richiesta del signer ed è quello che sostanzialmente paga il gas
- Il forwarder, che è un’entità verificata e accettata dal contratto finale, quello che deve accettare la transazione
- Il recipient contract, che verifica che il forwarder sia nella sua whitelist e ha una speciale implementazione di msg.sender che permette al contratto di determinare chi è l’account signer che ha generato la richiesta originale, anche se in realtà quest’ultimo non sta interagendo direttamente con il contratto.
Per costruire un contratto recipient capace di eseguire queste meta-transazioni si può partire da questa base class
https://github.com/opengsn/forwarder/blob/master/contracts/BaseRelayRecipient.sol
Se stai sviluppando un prodotto web3 e vuoi facilitare l'onboarding ai tuoi clienti contattami.