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.

1

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 } = Modules
const transferModule = new TokenTransfer()
2

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)
3

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.

4

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)
5

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 tokens
const amount = ethers.utils.parseEther(".01") // Amount of native gas tokens to transfer
const tokenAddr = "0x9983f755bbd60d1886cbfe103c98c272aa0f03d6" // Address of ERC-20 token to tranfer
const tokenTransferTx = await transferModule.createTransferTx(to, amount, { address: tokenAddr })
5

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 Imports
import { ethers } from "ethers"
import { FunWallet, FunWalletConfig, Modules } from "@fun-wallet/sdk"
const { TokenTransfer } = Modules
// 3. Create EOA Instance
// An internal Fun RPC for customer testing
const 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 production
const 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 FunWallet
const chainID = "43113" // Fuji testnet
const prefundAmt = 0.3 // amount of native gas token to prefund our FunWallet with
const API_KEY = "" // Get your API key from app.fun.xyz/api-key
const config = new FunWalletConfig(eoa, chainID, prefundAmt)
const wallet = new FunWallet(config, API_KEY)
// init will perform the pre-funding of the FunWallet
await wallet.init()
// 5. Create TokenTransfer Module and add it to our FunWallet
const tokenTransferModule = new TokenTransfer()
await wallet.addModule(tokenTransferModule)
// 6. Deploy FunWallet
const deployWalletReceipt = await wallet.deploy()
// 7. Create Required Pre-Execution Transactions
const requiredTransactions = await transferModule.getPreExecTxs(wallet.address)
// 8. Deploy Required Pre-Execution Transactions
const requiredTxReceipts = await wallet.deployTxs(requiredTransactions)
// 9. Create Module Execution Transaction
const to = "0xB4C3826aFea3Bc437C49695983eAaCFF2Bf8E305" // Receiver of tokens
const amount = ethers.utils.parseEther(".01") // Amount of native gas tokens to transfer
const tokenAddr = "0x9983f755bbd60d1886cbfe103c98c272aa0f03d6" // Address of ERC-20 token to tranfer
const tokenTransferTx = await tokenTransferModule.createTransferTx(to, amount, { address: tokenAddr })
// 10. Deploy Module Execution Transaction
const 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