Add Modules
Extend the functionality of a FunWallet
Overview
Currently, the FunWallet
SDK offers Modules in the following categories:
General Flow
The following flow describes a Module's lifecycle. For this example flow, we choose the TokenTransfer
Module.
Create Module
Each Module constructor requires a unique set of parameters. For a complete list of Modules and their constructors' parameters, visit the Module Reference section.
import { Modules } from "@fun-wallet/sdk"const { TokenTransfer } = Modulesconst transferModule = new TokenTransfer()
Add Module to FunWallet
A Module can only perform the actions it is intended to once it is connected to a FunWallet. To add a Module to a FunWallet we simply call the addModule method.
await wallet.addModule(transferModule)
Create Required Pre-Execution Transactions
Each Module has a set of Transactions which must be deployed to a blockchain before a Module can be executed on-chain. It is possible for this set to be empty, as is the case for the Token Transfer Module, or consist of many required pre-execution Transactions.
For example, in the case of the EoaAaveWithdrawal
Module, there is one pre-execution requirement that needs taking care of, namely giving spend authorization to a FunWallet so it can close an Aave position on behalf of an EOA.
The Module pre-execution required Transactions are obtained by a call to getPreExecTxs. Each Module defines the input arguments to this method. For a complete list of modules and the required pre-execution parameters their constructors take, visit the Module Reference section.
const requiredTransactions = await transferModule.getPreExecTxs(wallet.address)
The returned requiredTransactions element is a set containing Transaction objects to eventually be deployed on a blockchain.
Deploy Required Pre-Execution Transactions
Before we can deploy a Transaction associated with a Module (in the case of TransferToken Module, there is only one possible Transaction: The transfer of a token to another address) we must first deploy all the required pre-execution Transactions obtained from step 3.
const requiredTxReceipts = await wallet.deployTxs(requiredTransactions)
Create Module Execution Transaction
Once we have added a Module to a FunWallet we can get the desired Transaction associated with that Module. In the case of the TransferToken Module, there is only one possible Transaction: The transfer of a token to another address.
const to = "0xB4C3826aFea3Bc437C49695983eAaCFF2Bf8E305" // Receiver of tokensconst amount = ethers.utils.parseEther(".01") // Amount of native gas tokens to transferconst tokenAddr = "0x9983f755bbd60d1886cbfe103c98c272aa0f03d6" // Address of ERC-20 token to tranferconst tokenTransferTx = await transferModule.createTransferTx(to, amount, { address: tokenAddr })
Deploy Module Execution Transaction
The last thing left to do once we have deployed the required Module pre-execution Transactions is to deploy the desired Module Transaction itself.
const tokenTransferReceipt = await wallet.deployTx(tokenTransferTx)
Full Example
add-module.js
// 1. Install necessary packages// npm install @fun-wallet/sdk ethers// 2. Add Importsimport { ethers } from "ethers"import { FunWallet, FunWalletConfig, Modules } from "@fun-wallet/sdk"const { TokenTransfer } = Modules// 3. Create EOA Instance// An internal Fun RPC for customer testingconst rpc = "https://avalanche-fuji.infura.io/v3/4a1a0a67f6874be6bb6947a62792dab7"const provider = new ethers.providers.JsonRpcProvider(rpc)// Note that the key here will be exposed and should never be used in productionconst eoa = new ethers.Wallet(PRIV_KEY, provider)// To create an EOA instance with an external wallet (e.g MetaMask) do this instead// const provider = new ethers.providers.Web3Provider(window.ethereum)// await provider.send('eth_requestAccounts', []) // <- this promps user to connect metamask// eoa = provider.getSigner()// 4. Create a FunWalletconst chainID = "43113" // Fuji testnetconst prefundAmt = 0.3 // amount of native gas token to prefund our FunWallet withconst API_KEY = "" // Get your API key from app.fun.xyz/api-keyconst config = new FunWalletConfig(eoa, chainID, prefundAmt)const wallet = new FunWallet(config, API_KEY)// init will perform the pre-funding of the FunWalletawait wallet.init()// 5. Create TokenTransfer Module and add it to our FunWalletconst tokenTransferModule = new TokenTransfer()await wallet.addModule(tokenTransferModule)// 6. Deploy FunWalletconst deployWalletReceipt = await wallet.deploy()// 7. Create Required Pre-Execution Transactionsconst requiredTransactions = await transferModule.getPreExecTxs(wallet.address)// 8. Deploy Required Pre-Execution Transactionsconst requiredTxReceipts = await wallet.deployTxs(requiredTransactions)// 9. Create Module Execution Transactionconst to = "0xB4C3826aFea3Bc437C49695983eAaCFF2Bf8E305" // Receiver of tokensconst amount = ethers.utils.parseEther(".01") // Amount of native gas tokens to transferconst tokenAddr = "0x9983f755bbd60d1886cbfe103c98c272aa0f03d6" // Address of ERC-20 token to tranferconst tokenTransferTx = await tokenTransferModule.createTransferTx(to, amount, { address: tokenAddr })// 10. Deploy Module Execution Transactionconst tokenTransferReceipt = await wallet.deployTx(tokenTransferTx)console.log(tokenTransferReceipt)
add-module-output.json
{userOpHash: '0xd7ea68043200a00db7bdf072842cb3669456525ab24db6032f910fe7aafd56cb',txid: '0xb98e9c5cd9451a5b7335723f223738eaa0c16b3a60f47e9d3592ff5faef54fb4',gasUsed: 173974,gasUSD: 0.09142246713}
To verify that this transaction executed successfully, view the transaction id on your testnet. The example transaction can be found here.
Mentioned Classes
FunWallet
Primary class in the FunWallet SDK that orchestrates the movement of assets & data
FunWalletConfig
FunWallet configuration class
TokenTransfer
Module that enables the transfer of tokens to known addresses