Integrate Paymaster into your application
The recommended way to implement the paymaster is through our Account Abstraction SDK, as it provides a simple interface to interact with the Account Abstraction service. The Account Abstraction SDK (Shib4337) allows developers to create 4337-enabled safe wallets & interact with them on-chain using account abstraction. By integrating the paymaster service, you can sponsor gas fees for users or allow them to pay gas fees using ERC-20 tokens.
Installation
Pre-requisites
Here is what you need before installing the SDK:
- Node.js
- npm
Steps
- Clone the repository:
git clone https://github.com/shibaone/account-abstraction-sdk.git
- Navigate to the project directory:
cd account-abstraction-sdk
- Install the dependencies:
npm install
- Build the project:
npm run build
- Install yalc to publish it locally:
npm install -g yalc
- Publish the package locally:
yalc publish
- Go to you project and install this package there:
yalc add [email protected]
- Link the package:
yalc link [email protected]
- Now you can use this package in your project.
npm install shib-account-abstraction-sdk
Usage
To use the SDK, you can create an instance of the the Shib4337
class and call its methods.
const shibAAservice = Shib4337.init({
signer,
options,
paymasterOptions
});
signer
: An optional signer object for transaction signing.options
: Configuration options for the wallet.owners
: An array of owner addresses for the smart contract wallet.threshold
: The number of required signatures for executing transactions.saltNonce
: A nonce value to ensure unique wallet addresses.
paymasterOptions
: Configuration options for the Paymaster service, as seen below.
Paymaster Options
To use Paymaster-as-a-service, you need to provide the configuration for the paymaster in the options object. The options in the paymasterOptions object should have the following properties:
Property | Description |
---|---|
paymasterUrl | The URL of the paymaster service. This will probably have this format https://paymaster.shib.io/shibarium/<api-key> , where the API key is acquired from the developer portal. |
isSponsored | A boolean value to determine if the transactions are sponsored. |
sponsorshipPolicyId | The sponsorship policy ID for the paymaster. |
paymasterAddress | The address of the paymaster contract. |
paymasterTokenAddress | The address of the token used by the ERC20 paymaster. |
amountToApprove | The amount to approve for the ERC20 paymaster. |
Creating transactions
Use the createTransaction
method to prepare transactions for execution. You can add multiple transactions to a queue and then create a single user operation (userOp
).
Here is how you can add transactions to the queue, where transactionObj
is an object representing the transaction details:
await shibAAservice.addTransaction(transactionObj);
The transactionObj
has the following keys and values:
Key | Value |
---|---|
to | The destination address. |
value | The amount of Ether to send (in wei). |
data | The transaction data (e.g., encoded function call). |
operation | The operation type (0 for call, 1 for delegate call). |
Creating the User Operation
To create a user operation, you need to provide the settings:
const userOp = await shibAAservice.createTransaction({
transactions: [],
options: paymasterOptions
});
where:
transactions
is an array of transactions or leave empty to use the queued transactions.- and
options
represents the paymaster options which are specific to this transaction. It will change as per the type of Paymaster you want to integrate
The options
object should have the following properties:
Property | Required | Description |
---|---|---|
type | YES | The Paymaster type (see below). |
paymasterAndData | For CustomPaymaster , a string specifying custom Paymaster data. | |
amountToApprove | Amount of tokens to approve for the ERC-20 Paymaster. | |
validUntil | Expiry time for the Paymaster (timestamp). | |
validAfter | Start time for the Paymaster (timestamp). | |
feeEstimator | A function or value to estimate fees for the Paymaster. |
The first property is used to specify the type of Paymaster to use. The SDK supports several Paymaster types, each with different requirements. In most of the times, our requirement is fulfilled by VerifyingPaymaster(type 2) and ERC20Paymaster(type 3) based on our Paymaster-as-a-service portal. The other types are also present to let users choose how they want to integrate the Paymaster.
Type | Number | Description |
---|---|---|
NoPaymaster (Default) | 0 | No Paymaster is used; standard transaction fees apply. |
PaymasterContract | 1 | Uses an on-chain Paymaster contract. Requires paymasterAddress. |
VerifyingPaymaster | 2 | Uses a sponsored off-chain Paymaster service. Requires paymasterUrl . |
ERC20Paymaster | 3 | Uses an on-chain ERC-20 Paymaster. Requires paymasterAddress and paymasterTokenAddress . |
OffchainPaymaster | 4 | Uses a non-sponsored off-chain Paymaster service. Requires paymasterUrl and paymasterTokenAddress . |
CustomPaymaster | 5 | Allows custom Paymaster configurations. Requires paymasterAndData . |
Examples
Here is an example for sponsored paymaster:
///
/// For sponsored paymaster
///
import { Shib4337 } from 'shib-account-abstraction-sdk';
import { ethers } from 'ethers';
// creating `signer` for Shib4337 service, you can use ethers.js or any other library to create ethers-signers
// providing the signer to Shib4337 init is optional but it is required for the methods that require executing transactions
// Replace PRIVATE_KEY with your private key
const provider = new ethers.JsonRpcProvider('https://puppynet.shibrpc.com');
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
const paymasterUrl = 'https://offchainpaymaster.shibinternal.com/';
// initializing the Shib4337 service to interact with the Account Abstraction service
const shibAAservice = Shib4337.init({
signer,
options: {
owners: [signer.address],
threshold: 1,
saltNonce: '0'
},
paymasterOptions: {
paymasterUrl: paymasterUrl,
isSponsored: true,
sponsorshipPolicyId: ''
}
})
// calling the methods on the Shib4337 service
// determining the safe smart contract address of user
const address = await shibAAservice.getWalletAddress()
// the trxn should be in this format, it can be for anything like transfer, approve, createIdentity, sns etc.
const transactionObj = {
to: destinationAddress,
value: etherValue,
data: transactionData,
operation: operationType // 0 for call, 1 for delegate call
}
// adding trxn to the queue, it can be called multiple times to add multiple trxns to the queue separately
const res = await shibAAservice.addTransaction(transaction1)
// getting the trxn queue
const res = await shibAAservice.getTransactions()
// create options for sponsored paymaster
const sponsoredPaymasterOptions = {
type: 2
}
// creating the trxn operation, it is repsonsible for batching added trxns together and creating userOp
const userOp = await shibAAservice.createTransaction({
transactions:[],
options: sponsoredPaymasterOptions
});
// executing the trxn
const executeRespopnse = await shibAAservice.executeTransaction({
userOp,
signer
});
const userOpHash = executeRespopnse.userOpHash;
const receipt = await executeRespopnse.wait();
console.log({receipt});
// getting the trxn status
const res = await shibAAservice.getUserOpsByHash(userOpHash);
console.log(`Find the transaction on explorer at: https://puppynet.shib.io/tx/${res.transactionHash}`);
Here is an example for non-sponsored paymaster:
///
/// For non-sponsored paymaster
///
import { Shib4337 } from 'shib-account-abstraction-sdk';
import { ethers } from 'ethers';
// creating `signer` for Shib4337 service, you can use ethers.js or any other library to create ethers-signers
// providing the signer to Shib4337 init is optional but it is required for the methods that require executing transactions
// Replace PRIVATE_KEY with your private key
const provider = new ethers.JsonRpcProvider('https://puppynet.shibrpc.com');
const signer = new ethers.Wallet(PRIVATE_KEY, provider);
const paymasterTokenAddress = "0x2258900dE1261D70D6D1643B190f3111aC056bD1"
const paymasterAddress = "0x72717b4f7d7af24739992C9B1D22BAf2203E618c"
// initializing the Shib4337 service to interact with the Account Abstraction service
const shibAAservice = Shib4337.init({
signer,
options: {
owners: [signer.address],
threshold: 1,
saltNonce: '0'
},
paymasterOptions: {
paymasterAddress: paymasterAddress,
paymasterTokenAddress: paymasterTokenAddress,
isSponsored: false
}
})
// calling the methods on the Shib4337 service
// determining the safe smart contract address of user
const address = await shibAAservice.getWalletAddress()
// the trxn should be in this format, it can be for anything like transfer, approve, createIdentity, sns etc.
const transactionObj = {
to: destinationAddress,
value: etherValue,
data: transactionData,
operation: operationType // 0 for call, 1 for delegate call
}
// adding trxn to the queue, it can be called multiple times to add multiple trxns to the queue separately
const res = await shibAAservice.addTransaction(transaction1)
// getting the trxn queue
const res = await shibAAservice.getTransactions()
// create options for non-sponsored paymaster
const erc20paymasterOptions = {
type: 3,
amountToApprove: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn
}
// creating the trxn operation, it is repsonsible for batching added trxns together and creating userOp
const userOp = await shibAAservice.createTransaction({
transactions:[],
options: erc20paymasterOptions
});
// executing the trxn
const executeRespopnse = await shibAAservice.executeTransaction({
userOp,
signer
});
const userOpHash = executeRespopnse.userOpHash;
const receipt = await executeRespopnse.wait();
console.log({receipt});
// getting the trxn status
const res = await shibAAservice.getUserOpsByHash(userOpHash);
console.log(`Find the transaction on explorer at: https://puppynet.shib.io/tx/${res.transactionHash}`);