Skip to content

Commit 6dabf28

Browse files
committed
feat: add kernel example
1 parent ffc1d2e commit 6dabf28

12 files changed

Lines changed: 271 additions & 7 deletions

File tree

File renamed without changes.
File renamed without changes.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "bundler-gelato-sponsored-payment",
3+
"private": true,
4+
"version": "0.0.1",
5+
"scripts": {
6+
"dev": "tsx src/index.ts"
7+
},
8+
"dependencies": {
9+
"@gelatocloud/gasless": "workspace:*",
10+
"viem": "catalog:"
11+
},
12+
"devDependencies": {
13+
"@types/node": "latest",
14+
"dotenv": "^17.2.3",
15+
"tsx": "^4.21.0",
16+
"typescript": "catalog:"
17+
}
18+
}
File renamed without changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../../../../tsconfig.json"
3+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
GELATO_API_KEY="Get your api key at https://app.gelato.cloud/"
2+
PRIVATE_KEY="0x<PRIVATE_KEY>" # Optional
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Bundler Kernel Sponsored Example
2+
3+
This example demonstrates how to use Gelato's **ERC-4337 Bundler** with third-party smart accounts for gasless transactions.
4+
5+
## Overview
6+
7+
The Bundler provides full ERC-4337 compliance, allowing you to use any compatible smart account (like Kernel, Safe, etc.) with Gelato's infrastructure.
8+
9+
## Prerequisites
10+
11+
- Node.js >= 23
12+
- Gelato API Key from [app.gelato.cloud](https://app.gelato.cloud/)
13+
14+
## Setup
15+
16+
1. Install dependencies:
17+
```bash
18+
pnpm install
19+
```
20+
21+
2. Configure environment variables (see [Environment Variables](#environment-variables) section below)
22+
23+
## Environment Variables
24+
25+
This example loads environment variables from two sources:
26+
27+
1. **Root `.env`** (at project root) - Shared defaults for all examples
28+
2. **Local `.env`** (in this directory) - Optional overrides
29+
30+
Local values take precedence over root values. To set up:
31+
32+
1. Copy the root `.env.example` to `.env` at the project root:
33+
```bash
34+
cp ../../../.env.example ../../../.env
35+
```
36+
37+
2. Add your credentials to the root `.env`:
38+
```
39+
GELATO_API_KEY="your-api-key"
40+
PRIVATE_KEY="0x..." # Optional - generates random key if not set
41+
```
42+
43+
3. (Optional) Create a local `.env` in this directory to override specific values for this example
44+
45+
## Run
46+
47+
```bash
48+
pnpm dev
49+
```
50+
51+
## Code Walkthrough
52+
53+
### Step 1: Create Owner Account
54+
55+
```typescript
56+
const owner = privateKeyToAccount((PRIVATE_KEY ?? generatePrivateKey()) as Hex);
57+
```
58+
59+
Creates an EOA that will own the smart account.
60+
61+
### Step 2: Create Kernel Smart Account
62+
63+
```typescript
64+
const account = await toKernelSmartAccount({
65+
client,
66+
owners: [owner],
67+
useMetaFactory: false,
68+
version: '0.3.3'
69+
});
70+
```
71+
72+
Creates a Kernel v0.3.3 smart account using the `permissionless` library. You can substitute any ERC-4337 compatible account.
73+
74+
### Step 3: Create Bundler Client
75+
76+
```typescript
77+
const bundler = await createGelatoBundlerClient({
78+
account,
79+
apiKey: GELATO_API_KEY,
80+
client,
81+
payment: sponsored(),
82+
pollingInterval: 100
83+
});
84+
```
85+
86+
Creates a bundler client that handles user operation submission and gas sponsorship.
87+
88+
### Step 4: Send User Operation
89+
90+
```typescript
91+
const hash = await bundler.sendUserOperation({
92+
calls: [
93+
{
94+
data: '0xd09de08a',
95+
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De'
96+
}
97+
]
98+
});
99+
```
100+
101+
Submits an ERC-4337 user operation. Returns the user operation hash.
102+
103+
### Step 5: Wait for Receipt
104+
105+
```typescript
106+
const { receipt } = await bundler.waitForUserOperationReceipt({ hash });
107+
console.log(`Transaction hash: ${receipt.transactionHash}`);
108+
```
109+
110+
Waits for the user operation to be bundled and included on-chain.
111+
112+
## Key Concepts
113+
114+
| Concept | Description |
115+
|---------|-------------|
116+
| ERC-4337 | Account Abstraction standard for user operations |
117+
| User Operation | Transaction format for smart accounts |
118+
| `toKernelSmartAccount` | Creates a Kernel smart account (from `permissionless`) |
119+
| `sendUserOperation` | Submits user operation to the bundler |
120+
| `waitForUserOperationReceipt` | Waits for on-chain inclusion |
121+
122+
## When to Use Bundler vs Smart Account
123+
124+
| Use Case | Recommended |
125+
|----------|-------------|
126+
| Need ERC-4337 ecosystem compatibility | Bundler |
127+
| Using third-party smart accounts (Kernel, Safe, etc.) | Bundler |
128+
| Want Gelato's native smart account | Smart Account |
129+
| Simplest possible integration | Relayer |

examples/bundler/sponsored/package.json renamed to examples/bundler/kernel/sponsored/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "bundler-sponsored-payment",
2+
"name": "bundler-kernel-sponsored-payment",
33
"private": true,
44
"version": "0.0.1",
55
"scripts": {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { resolve } from 'node:path';
2+
import { config } from 'dotenv';
3+
4+
// Load root .env first (defaults)
5+
config({ path: resolve(__dirname, '../../../../.env') });
6+
7+
// Load local .env to override (optional)
8+
config({ override: true });
9+
10+
import { createGelatoBundlerClient, sponsored } from '@gelatocloud/gasless';
11+
import { to7702KernelSmartAccount } from 'permissionless/accounts';
12+
import { createPublicClient, type Hex, http, type SignedAuthorization } from 'viem';
13+
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
14+
import { inkSepolia } from 'viem/chains';
15+
16+
const GELATO_API_KEY = process.env['GELATO_API_KEY'];
17+
const PRIVATE_KEY = process.env['PRIVATE_KEY'];
18+
19+
if (!GELATO_API_KEY) {
20+
throw new Error('GELATO_API_KEY is not set');
21+
}
22+
23+
const chain = inkSepolia;
24+
25+
const main = async () => {
26+
const owner = privateKeyToAccount((PRIVATE_KEY ?? generatePrivateKey()) as Hex);
27+
28+
const client = createPublicClient({
29+
chain,
30+
transport: http()
31+
});
32+
33+
const account = await to7702KernelSmartAccount({
34+
client,
35+
owner,
36+
version: '0.3.3'
37+
});
38+
39+
const bundler = await createGelatoBundlerClient({
40+
account,
41+
apiKey: GELATO_API_KEY,
42+
client,
43+
payment: sponsored(),
44+
pollingInterval: 100
45+
});
46+
47+
const deployed = await account.isDeployed();
48+
49+
let authorization: SignedAuthorization | undefined;
50+
51+
if (!deployed) {
52+
const nonce = await client.getTransactionCount({ address: owner.address });
53+
54+
authorization = await owner.signAuthorization({
55+
address: account.authorization.address,
56+
chainId: chain.id,
57+
nonce
58+
});
59+
}
60+
61+
const hash = await bundler.sendUserOperation({
62+
authorization,
63+
calls: [
64+
{
65+
data: '0xd09de08a',
66+
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De'
67+
}
68+
]
69+
});
70+
71+
console.log(`User operation hash: ${hash}`);
72+
73+
const { receipt } = await bundler.waitForUserOperationReceipt({ hash });
74+
75+
console.log(`Transaction hash: ${receipt.transactionHash}`);
76+
77+
process.exit(0);
78+
};
79+
80+
main().catch((error) => {
81+
console.error(error);
82+
process.exit(1);
83+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../../../../tsconfig.json"
3+
}

0 commit comments

Comments
 (0)