Skip to content

Commit

Permalink
Move to own tab
Browse files Browse the repository at this point in the history
  • Loading branch information
chunter-cb committed Oct 16, 2024
1 parent 8500366 commit 2cc4e21
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 156 deletions.
179 changes: 179 additions & 0 deletions docs/pages/guides/erc20-paymasters.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# ERC20 Paymasters

Smart Wallet enables users to pay for gas in ERC20 tokens!
Tokens can be accepted for payment by passed in app paymasters or by a set of universally supported tokens, such as USDC (this set to be expanded soon).

This guide outlines how to set up your own app paymaster to accept your token as payment.

### Choose a paymaster service provider

As a prerequisite, you'll need to obtain a paymaster service URL from a paymaster service provider. ERC20 paymasters have additional requirements that will be outlined below.

We recommend the [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) paymaster as it is fully set up to work with Smart Wallet ERC20 token gas payments out of the box. CDP is also offering up to $15k in gas credits as part of the [Base Gasless Campaign](/base-gasless-campaign).

Otherwise if using a different paymaster provider, it must conform to the specification outlined in [ERC20 Compatible Paymasters](##erc20-compatible-paymasters) to correctly work with Smart Wallet.

### App setup for custom token

Once you have a paymaster that is ERC20 compatible, you are only responsible for including the approvals to the the paymaster for your token. It is recommended to periodically top up the allowance once they hit some threshold.

```js

const tokenDecimals = 6
const minTokenThreshold = 1 * 10 ** tokenDecimals // $1
const tokenApprovalTopUp = 20 * 10 ** tokenDecimals // $20
const tokenAddress = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
const nftContractAddress = "0x66519FCAee1Ed65bc9e0aCc25cCD900668D3eD49"
const paymasterAddress = "0x2FAEB0760D4230Ef2aC21496Bb4F0b47D634FD4c"

const mintTo = {
abi: abi,
functionName: "mintTo",
to: nftContractAddress,
args: [account.address, 1],
};

calls = [mintTo]

// Checks for allowance
const allowance = await client.readContract({
abi: parseAbi(["function allowance(address owner, address spender) returns (uint256)"]),
address: tokenAddress,
functionName: "allowance",
args: [account.address, paymasterAddress],
})

if (allowance < minTokenThreshold) {
// include approval for $20 in calls so that the paymaster will be able to move the token to accept payment
calls.push({
abi: ["function approve(address,uint)"],
functionName: "approve",
to: nftContractAddress,
args: [paymasterAddress, tokenApprovalTopUp],
})
}
```

That is it! Smart Wallet will handle the rest as long as it is compatible as outlined below.

### ERC20 Compatible Paymasters
Coinbase Developer Platform is compatible out of the box and we will be working with other teams to include support soon!

The paymaster must handle the `pm_getPaymasterStubData` and `pm_getPaymasterData` JSON-RPC requests specified by ERC-7677 in addition to `pm_getAcceptedPaymentTokens`. We step through each request and response below.

#### pm_getPaymasterStubData and pm_getPaymasterData

1. The paymaster must use the specified ERC20 for payment if specified in the 7677 context field under `erc20`.
2. Upon rejection / failure the paymaster should return a `data` field in the JSONRPC response which could be used to approve the paymaster and includes:
- `acceptedTokens` array which is a struct including the token address
- `paymasterAddress` field which is the paymaster address which will perform the token transfers.
3. Upon success the paymaster must return a `tokenPayment` field in the result. This includes:
- `tokenAddress` address of the token used for payment
- `maxFee` the maximum fee to show in the UI
- `decimals` decimals to use in the UI
- `name` name of the token

Smart wallet will simulate the transaction to ensure success and accurate information.

##### Request

This is a standard V0.6 Entrypoint request example with the additional context for the specified token to be used.

```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "pm_getPaymasterData",
"params": [
{
"sender": "0xe62B4aD6A7c079F47D77a9b939D5DC67A0dcdC2B",
"nonce": "0x4e",
"initCode": "0x",
"callData": "0xb61d27f60000000000000000000000007746371e8df1d7099a84c20ed72e3335fb016b23000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit": "0x113e10",
"verificationGasLimit": "0x113e10",
"preVerificationGas": "0x113e10",
"maxFeePerGas": "0x113e10",
"maxPriorityFeePerGas": "0x113e10",
"paymasterAndData": "0x",
"signature": "0x5ee079a5dec73fe39c1ce323955fb1158fc1b9a6b2ddbec104cd5cfec740fa5531584f098b0ca95331b6e316bd76091e3ab75a7bc17c12488664d27caf19197e1c"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"0x2105",
{
"erc20": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
}
]
}
```

##### Response

Successful response:

```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"paymasterAndData": "0x2faeb0760d4230ef2ac21496bb4f0b47d634fd4c0000670fdc98000000000000494b3b6e1d074fbca920212019837860000100833589fcd6edb6e08f4c7c32d4f71b54bda029137746371e8df1d7099a84c20ed72e3335fb016b23000000000000000000000000000000000000000000000000000000009b75458400000000697841102cd520d4e0171a58dadc3e6086111a49a90826cb0ad25579f25f1652081f68c17d8652387a33bf8880dc44ecf95be4213e786566d755baa6299f477b0bb21c",
"tokenPayment": {
"name": "USDC",
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"maxFee": "0xa7c8",
"decimals": 6
}
}
}
```

Rejection response:

```json
{
"id": 1,
"jsonrpc": "2.0",
"error": {
"code": -32002,
"message": "request denied - no sponsorship and address can not pay with accepted token",
"data": {
"acceptedTokens": [
{
"name": "USDC",
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
]
}
}
}
```

#### pm_getAcceptedPaymentTokens

`pm_getAcceptedPaymentTokens` returns an array of tokens the paymaster will accept for payment.
The request contains the entrypoint and the chain id with optional context.

##### Request
```json
{
"jsonrpc": "2.0", "id": 1,
"method": "pm_getAcceptedPaymentTokens",
"params": [ "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "0x2105", {}]
}
```

##### Response
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"acceptedTokens": [
{
"name": "USDC",
"address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
}
]
}
}
```
157 changes: 1 addition & 156 deletions docs/pages/guides/paymasters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

One of the biggest UX enhancements unlocked by Smart Wallet is the ability for app developers to sponsor their users' transactions. If your app supports Smart Wallet, you can start sponsoring your users' transactions by using [standardized paymaster service communication](https://erc7677.xyz) enabled by [new wallet RPC methods](https://eip5792.xyz).

Paymasters can also accept ERC2-20 tokens for gas payment! Setting up a compatible ERC20 accepting paymaster for your token is outlined in this [section](##erc20-paymasters). Smart wallet will have some universal ERC20 tokens enabled and available for gas usage as well, such as USDC.

The code below is also in our [Wagmi Smart Wallet template](https://github.com/wilsoncusack/wagmi-scw/).

## Using Wagmi + Permissionless in a Next.js app
Expand Down Expand Up @@ -355,157 +353,4 @@ export const myNFTAddress = "0x119Ea671030FBf79AB93b436D2E20af6ea469a19";
That's it! Smart Wallet will handle the rest. If your paymaster service is able to sponsor the transaction,
in the UI Smart Wallet will indicate to your user that the transaction is sponsored.

::::

## ERC20 Paymasters

If you are using Coinbase Developer Platform's Paymaster you can sign up for ERC20 by clicking the ERC20 Paymaster tab and going through the steps to add your token. This paymaster is already set up to be fully compatible with smart wallet.
The CDP Paymaster will allow sponsorship and fallback to accepting ERC20 tokens for gas payment if no sponsorship is provided.

Otherwise if using a different paymaster provider, it must conform to the specification outlined in [ERC20 Compatible Paymasters](##erc20-compatible-paymasters) to correctly work with Smart Wallet.

### App setup

Once you have a paymaster that is ERC20 compatible, you are only responsible for including the approvals to the the paymaster for your token. It is recommended to periodically top up the allowance once they hit some threshold similar to.

```js
const usdcDecimals = 6
const minTokenThreshold = 1 * 10 ** usdcDecimals // 1 USDC
const paymasterAddress = "0x2FAEB0760D4230Ef2aC21496Bb4F0b47D634FD4c"

// Checks for allowance
const allowance = await client.readContract({
abi: parseAbi(["function allowance(address owner, address spender) returns (uint256)"]),
address: tokenAddress,
functionName: "allowance",
args: [account.address, paymasterAddress],
})

if (allowance < minTokenThreshold) {
// include approval for $20 usdc in calls so that the paymaster will be able to move the token to accept payment
}
```

That is it! Smart Wallet will handle the rest as long as it is compatible as outlined below.

### ERC20 Compatible Paymasters
Coinbase Developer Platform is compatible out of the box and we will be working with other teams to include support soon!

The paymaster must handle the `pm_getPaymasterStubData` and `pm_getPaymasterData` JSON-RPC requests specified by ERC-7677 in addition to `pm_getAcceptedPaymentTokens`. We step through each request and response below.

#### pm_getPaymasterStubData and pm_getPaymasterData

1. The paymaster must use the specified ERC20 for payment if specified in the 7677 context field under `erc20`.
2. Upon rejection / failure the paymaster should return a `data` field in the JSONRPC response which could be used to approve the paymaster and includes:
- `acceptedTokens` array which is a struct including the token address
- `paymasterAddress` field which is the paymaster address which will perform the token transfers.
3. Upon success the paymaster must return a `tokenPayment` field in the result. This includes:
- `tokenAddress` address of the token used for payment
- `maxFee` the maximum fee to show in the UI
- `decimals` decimals to use in the UI
- `name` name of the token

Smart wallet will simulate the transaction to ensure success and accurate information.

##### Request

This is a standard V0.6 Entrypoint request example with the additional context for the specified token to be used.

```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "pm_getPaymasterData",
"params": [
{
"sender": "0xe62B4aD6A7c079F47D77a9b939D5DC67A0dcdC2B",
"nonce": "0x4e",
"initCode": "0x",
"callData": "0xb61d27f60000000000000000000000007746371e8df1d7099a84c20ed72e3335fb016b23000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit": "0x113e10",
"verificationGasLimit": "0x113e10",
"preVerificationGas": "0x113e10",
"maxFeePerGas": "0x113e10",
"maxPriorityFeePerGas": "0x113e10",
"paymasterAndData": "0x",
"signature": "0x5ee079a5dec73fe39c1ce323955fb1158fc1b9a6b2ddbec104cd5cfec740fa5531584f098b0ca95331b6e316bd76091e3ab75a7bc17c12488664d27caf19197e1c"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"0x2105",
{
"erc20": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
}
]
}
```

##### Response

Successful response:

```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"paymasterAndData": "0x2faeb0760d4230ef2ac21496bb4f0b47d634fd4c0000670fdc98000000000000494b3b6e1d074fbca920212019837860000100833589fcd6edb6e08f4c7c32d4f71b54bda029137746371e8df1d7099a84c20ed72e3335fb016b23000000000000000000000000000000000000000000000000000000009b75458400000000697841102cd520d4e0171a58dadc3e6086111a49a90826cb0ad25579f25f1652081f68c17d8652387a33bf8880dc44ecf95be4213e786566d755baa6299f477b0bb21c",
"tokenPayment": {
"name": "USDC",
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"maxFee": "0xa7c8",
"decimals": 6
}
}
}
```

Rejection response:

```json
{
"id": 1,
"jsonrpc": "2.0",
"error": {
"code": -32002,
"message": "request denied - no sponsorship and address can not pay with accepted token",
"data": {
"acceptedTokens": [
{
"name": "USDC",
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
]
}
}
}
```

#### pm_getAcceptedPaymentTokens

`pm_getAcceptedPaymentTokens` returns an array of tokens the paymaster will accept for payment.
The request contains the entrypoint and the chain id with optional context.

##### Request
```json
{
"jsonrpc": "2.0", "id": 1,
"method": "pm_getAcceptedPaymentTokens",
"params": [ "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", "0x2105", {}]
}
```

##### Response
```json
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"acceptedTokens": [
{
"name": "USDC",
"address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
}
]
}
}
```
::::
4 changes: 4 additions & 0 deletions vocs.config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ export default defineConfig({
text: "Paymasters (Sponsored Transactions)",
link: "/guides/paymasters",
},
{
text: "Token Paymasters (Gas Tokens)",
link: "/guides/erc20-paymasters",
},
{
text: "Magic Spend Support",
link: "/guides/magic-spend",
Expand Down

0 comments on commit 2cc4e21

Please sign in to comment.