Skip to main content

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

  1. Clone the repository: git clone https://github.com/shibaone/account-abstraction-sdk.git
  2. Navigate to the project directory: cd account-abstraction-sdk
  3. Install the dependencies: npm install
  4. Build the project: npm run build
  5. Install yalc to publish it locally: npm install -g yalc
  6. Publish the package locally: yalc publish
  7. Go to you project and install this package there: yalc add [email protected]
  8. Link the package: yalc link [email protected]
  9. 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:

PropertyDescription
paymasterUrlThe 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.
isSponsoredA boolean value to determine if the transactions are sponsored.
sponsorshipPolicyIdThe sponsorship policy ID for the paymaster.
paymasterAddressThe address of the paymaster contract.
paymasterTokenAddressThe address of the token used by the ERC20 paymaster.
amountToApproveThe 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:

KeyValue
toThe destination address.
valueThe amount of Ether to send (in wei).
dataThe transaction data (e.g., encoded function call).
operationThe 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:

PropertyRequiredDescription
typeYESThe Paymaster type (see below).
paymasterAndDataFor CustomPaymaster, a string specifying custom Paymaster data.
amountToApproveAmount of tokens to approve for the ERC-20 Paymaster.
validUntilExpiry time for the Paymaster (timestamp).
validAfterStart time for the Paymaster (timestamp).
feeEstimatorA 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.

TypeNumberDescription
NoPaymaster (Default)0No Paymaster is used; standard transaction fees apply.
PaymasterContract1Uses an on-chain Paymaster contract. Requires paymasterAddress.
VerifyingPaymaster2Uses a sponsored off-chain Paymaster service. Requires paymasterUrl.
ERC20Paymaster3Uses an on-chain ERC-20 Paymaster. Requires paymasterAddress and paymasterTokenAddress.
OffchainPaymaster4Uses a non-sponsored off-chain Paymaster service. Requires paymasterUrl and paymasterTokenAddress.
CustomPaymaster5Allows 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}`);