PWA with Privy and Account Abstraction
Objectives
The goal of this tutorial is to:
Implement Privy's template to create a PWA that runs a Next.js application using their embedded wallets.
Adjust the application to sign and submit transactions for an Account Abstraction Wallet.
Prerequisites
Before starting this tutorial, you should have a good understanding of the following:
React: You need to know how to code with React as this tutorial involves creating a PWA using Next.js, which is based on React.
Hardhat: You should be familiar with deploying smart contracts using Hardhat.
Fee Delegation: Understanding fee delegation, especially in the context of VeChain, is crucial for implementing Account Abstraction Wallets.
Privy
Introduction
Privy is a service that provides authentication services, which most users use daily, to access web3 wallets.
Privy handles wallet generation, security aspects, and interfaces that manage the signing of transactions or messages.
Setup
Privy offers a setup guide in a template project on GitHub: https://github.com/privy-io/create-privy-pwa
The steps to be done are:
Clone Template
Install Dependencies
Run npm install
to install the project required dependencies:
Configure Privy
Visit https://privy.io to create a new App.
Create the file .env
and add your created App Id:
Start App
You can start the app with npm run dev
, and you will be able to sign into the app.
For testing development, its recommend to use
ngrok
(guide), since privy makes use of crypto.subtle which requires a secure context (https
).
Login Methods
To adjust the desired login methods, toggle them on or off in the Privy dashboard for the app at: https://dashboard.privy.io
Account Abstraction
Introduction
Account Abstraction is a standard that allows smart contracts to initiate transactions, providing an on-chain wallet that can replace an externally owned account (EOA).
Example projects using this standard are available on GitHub at:
Setup
For the purpose of this tutorial, the account abstraction contracts from the SimpleAccount
example by eth-infinitism
are used.
The contracts to be deployed are:
To simplify the process, a modified project is available on GitHub at: https://github.com/vechain-energy/example-pwa-privy-account-abstraction/tree/main/account-abstraction-contracts
Clone Contracts
Install Dependencies
Run npm install --legacy-peer-deps
to install the project required dependencies:
Configure Network
Generate a new private key and create the file .env
, to configure it as deployer:
A simple way to generate a private key can be openssl
:
Optionally adjust more network related options in the hardhat.config.ts
file.
Deploy Contracts
The deploy scripts are in the deploy/
folder. Inspect for further understanding.
Use the hardhat deploy plugin to deploy the example contracts:
The output of the command shows the addresses of the deployed contracts:
The deployment addresses and ABIs are archived in the deployments/vechain_testnet
folder.
Please document the address of the deployed SimpleAccountFactory
for future use. In the above example output, it is 0x41Ea0cDa1471d70961bdc81bB9203a09cbf9B65e
.
Background: Accept Signed Executions
The SimpleAccount
restricts access to its owner or the endpoint contract.
Because Privy does not support the VeChain network, the interaction will be based on signed messages. A new function executeWithAuthorization
verifies a signature for an execution to be from the owner.
You can add yourself a signature verification to SimpleAccount.sol
with these changes:
Call the EIP721 with the constructor by adjusting its definition:
And add a new function that verifies signatures and does the same as execute
:
Implement Account Abstraction into Privy PWA
Configure Account Abstraction and VeChain
Extend the .env
file with the following lines:
NEXT_PUBLIC_AA_FACTORY
is the address of the previously deployedSimpleAccountFactory
NEXT_PUBLIC_NODE_URL
is the link to the VeChain network to use.NEXT_PUBLIC_DELEGATOR_URL
is a fee delegation service, that will pay the VTHO gas fees. Visit vechain.energy to create one or build one yourself.
Install VeChain Dependencies
Install the VeChain SDK to simplify interaction with the VeChain network:
useVeChainAccount.tsx
useVeChainAccount.tsx
A prepared hook will provide connectivity to the Account Abtractions deployment.
Copy useVeChainAccount.tsx
into the components/lib
folder.
Background: How the Hook Works
The hook internally performs the following steps:
Generates a random wallet for blockchain interaction, providing an entry point into the network from which transactions can be accepted.
Requests the user to sign a typed message with the Privy embedded wallet.
If the SimpleAccount does not exist yet, it injects a clause to deploy a new SimpleAccount first.
Builds a transaction to interact with the Account Abstraction Wallet.
Requests the Gas Fee Payment from a Fee Delegator to avoid funding the random wallet with VTHO.
Sends the transaction to the VeChain Network.
Returns the transaction ID.
The visualized process is:
Add VeChainAccountProvider
VeChainAccountProvider
Add the VeChainAccountProvider
into the pages/_app.tsx
to wrap the application components.
VeChainAccountProvider
is a React Context provider that does the following:
Creates a new ThorClient to connect to the VeChain Network.
Identifies the address of the embedded Privy wallet.
Loads the
SimpleAccount
address from theSimpleAccountFactory
identified by the user's embedded Privy address.Exports the address, a function to send transactions, and some generic network/connectivity information.
The file after modification looks like this:
Use useVeChainAccount
useVeChainAccount
Import the useVeChainAccount
hook into pages/embedded-wallet.tsx
and call it within the component:
Display Address
The address exported from the useVeChainAccount
hook is the address from which future transactions will originate.
Add the following snippet to display the account address in the embedded-wallet.tsx
:
Configure Test Call
To test an interaction, replace the existing test transfer with a call to vechain.sendTransaction
.
Replace the onTransfer
function in embedded-wallet.tsx
with the following code:
Test the PWA
Open two terminals to run the application and create a public https
endpoint:
Run
npm run dev
to start the PWA application.Run
ngrok http 3004
to start an ngrok proxy and get a public secure address.
Switch to your browser:
Open the forwarding address shown by
ngrok
.Log in with your social account.
Switch to the
Embedded Wallet
menu.
Fund VeChain Wallet
Visit faucet.vecha.in to claim VET on the TestNet and send 1 VET to the VeChain address shown in your PWA.
Test Transactions
In your PWA, enter a valid address for the transfer and test the Transfer function. A dialog will open and request to sign a transaction. Once signed, the SimpleAccount
will transfer 0.004 VET
to the entered address.
Interact with Smart Contracts
To interact with other smart contracts, sendTransaction
accepts to
, data
, and value
parameters like other similar functions.
Either pass in the data for the smart contract call encoded or pass the data as an object, which will then be used to call encodeFunctionData
within the useVeChainAccount
hook.
For example:
Hide Transaction Signing
Signing messages or transactions can be complex and a hurdle for regular users. To reduce friction, you can disable the signing dialog in the Privy Dashboard under Embedded Wallets > Add confirmation modals
.
Disabling this option allows the application to instantly interact with the blockchain, improving the user experience. Pending transactions can be managed with loading animations.
Conclusion
In this article, we've created a way of adding VeChain support within the Privy demo project by creating an Account Abstraction Factory that serves as the interaction source.
By using Fee Delegation, the gas fees are covered for the user, eliminating the need to ask the user to fund it with VTHO first.
An example application for further exploration of Account Abstractions and a Privy PWA is available on GitHub at docs-pwa-privy-account-abstraction-my-pwa-project.
You can use the information to either build on top of the example project and hook or derive your own process.
Last updated