Skip to content

Commit 1d5cc31

Browse files
committed
feat: add Dynamic support to verbs
1 parent 0b5366c commit 1d5cc31

26 files changed

+879
-638
lines changed

packages/demo/backend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
},
3838
"dependencies": {
3939
"@clerk/backend": "^2.12.0",
40+
"@dynamic-labs-wallet/node": "^0.0.158",
41+
"@dynamic-labs-wallet/node-evm": "^0.0.158",
4042
"@eth-optimism/utils-app": "^0.0.6",
4143
"@eth-optimism/verbs-sdk": "workspace:*",
4244
"@eth-optimism/viem": "^0.4.13",

packages/demo/backend/src/config/env.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,6 @@ export const env = cleanEnv(process.env, {
5959
BASE_SEPOLIA_BUNDER_URL: str({ devDefault: 'dummy' }),
6060
UNICHAIN_BUNDLER_URL: str({ devDefault: 'dummy' }),
6161
UNICHAIN_BUNDLER_SPONSORSHIP_POLICY: str({ devDefault: 'dummy' }),
62+
DYNAMIC_AUTH_TOKEN: str({ devDefault: 'dummy' }),
63+
DYNAMIC_ENVIRONMENT_ID: str({ devDefault: 'dummy' }),
6264
})

packages/demo/backend/src/config/verbs.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1+
import { DynamicEvmWalletClient } from '@dynamic-labs-wallet/node-evm'
12
import { Verbs, type VerbsConfig } from '@eth-optimism/verbs-sdk'
23
import { PrivyClient } from '@privy-io/server-auth'
34
import { baseSepolia, unichain } from 'viem/chains'
45

56
import { env } from './env.js'
67

7-
let verbsInstance: Verbs<'privy'>
8+
let verbsInstance: Verbs<'dynamic'>
89

9-
export function createVerbsConfig(): VerbsConfig<'privy'> {
10+
export function createVerbsConfig(): VerbsConfig<'dynamic'> {
1011
return {
1112
wallet: {
1213
hostedWalletConfig: {
1314
provider: {
14-
type: 'privy',
15+
type: 'dynamic',
1516
config: {
16-
privyClient: new PrivyClient(
17-
env.PRIVY_APP_ID,
18-
env.PRIVY_APP_SECRET,
19-
),
17+
dynamicClient: getDynamicClient(),
2018
},
2119
},
2220
},
@@ -55,7 +53,7 @@ export function createVerbsConfig(): VerbsConfig<'privy'> {
5553
}
5654
}
5755

58-
export function initializeVerbs(config?: VerbsConfig<'privy'>): void {
56+
export function initializeVerbs(config?: VerbsConfig<'dynamic'>): void {
5957
const verbsConfig = config || createVerbsConfig()
6058
verbsInstance = new Verbs(verbsConfig)
6159
}
@@ -70,3 +68,10 @@ export function getVerbs() {
7068
export function getPrivyClient() {
7169
return new PrivyClient(env.PRIVY_APP_ID, env.PRIVY_APP_SECRET)
7270
}
71+
72+
export function getDynamicClient() {
73+
return new DynamicEvmWalletClient({
74+
authToken: env.DYNAMIC_AUTH_TOKEN,
75+
environmentId: env.DYNAMIC_ENVIRONMENT_ID,
76+
})
77+
}

packages/demo/backend/src/controllers/lend.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import { serializeBigInt } from '../utils/serializers.js'
99

1010
const DepositRequestSchema = z.object({
1111
body: z.object({
12-
walletId: z.string().min(1, 'walletId is required'),
12+
walletAddress: z
13+
.string()
14+
.regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid wallet address format')
15+
.trim(),
1316
amount: z.number().positive('amount must be positive'),
1417
tokenAddress: z
1518
.string()
@@ -23,7 +26,10 @@ const MarketBalanceParamsSchema = z.object({
2326
vaultAddress: z
2427
.string()
2528
.regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid vault address format'),
26-
walletId: z.string().min(1, 'walletId is required'),
29+
walletAddress: z
30+
.string()
31+
.regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid wallet address format')
32+
.trim(),
2733
}),
2834
})
2935

@@ -93,11 +99,11 @@ export class LendController {
9399
if (!validation.success) return validation.response
94100

95101
const {
96-
params: { vaultAddress, walletId },
102+
params: { vaultAddress, walletAddress },
97103
} = validation.data
98104
const balance = await lendService.getMarketBalance(
99105
vaultAddress as Address,
100-
walletId,
106+
walletAddress as Address,
101107
)
102108
const formattedBalance =
103109
await lendService.formatMarketBalanceResponse(balance)
@@ -122,16 +128,16 @@ export class LendController {
122128
if (!validation.success) return validation.response
123129

124130
const {
125-
body: { walletId, amount, tokenAddress, chainId },
131+
body: { walletAddress, amount, tokenAddress, chainId },
126132
} = validation.data
127133
const lendTransaction = await lendService.deposit(
128-
walletId,
134+
walletAddress as Address,
129135
amount,
130136
tokenAddress as Address,
131137
chainId as SupportedChainId,
132138
)
133139
const result = await lendService.executeLendTransaction(
134-
walletId,
140+
walletAddress as Address,
135141
lendTransaction,
136142
chainId as SupportedChainId,
137143
)

packages/demo/backend/src/controllers/wallet.ts

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Context } from 'hono'
22
import type { Address } from 'viem'
3+
import { getAddress } from 'viem'
34
import { z } from 'zod'
45

56
import type {
@@ -12,21 +13,30 @@ import { validateRequest } from '../helpers/validation.js'
1213
import * as walletService from '../services/wallet.js'
1314
import { serializeBigInt } from '../utils/serializers.js'
1415

15-
const UserIdParamSchema = z.object({
16+
const WalletAddressParamSchema = z.object({
1617
params: z.object({
17-
userId: z.string().min(1, 'User ID is required').trim(),
18+
walletAddress: z
19+
.string()
20+
.regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid wallet address format')
21+
.trim(),
1822
}),
1923
})
2024

2125
const FundWalletRequestSchema = z.object({
2226
params: z.object({
23-
userId: z.string().min(1, 'User ID is required').trim(),
27+
walletAddress: z
28+
.string()
29+
.regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid wallet address format')
30+
.trim(),
2431
}),
2532
})
2633

2734
const SendTokensRequestSchema = z.object({
2835
body: z.object({
29-
walletId: z.string().min(1, 'walletId is required'),
36+
walletAddress: z
37+
.string()
38+
.regex(/^0x[a-fA-F0-9]{40}$/, 'Invalid wallet address format')
39+
.trim(),
3040
amount: z.number().positive('amount must be positive'),
3141
recipientAddress: z
3242
.string()
@@ -50,19 +60,12 @@ export class WalletController {
5060
*/
5161
async createWallet(c: Context) {
5262
try {
53-
const validation = await validateRequest(c, UserIdParamSchema)
54-
if (!validation.success) return validation.response
55-
56-
const {
57-
params: { userId },
58-
} = validation.data
59-
const { privyAddress, smartWalletAddress } =
63+
const { signerAddress, smartWalletAddress } =
6064
await walletService.createWallet()
6165

6266
return c.json({
63-
privyAddress,
67+
signerAddress,
6468
smartWalletAddress,
65-
userId,
6669
} satisfies CreateWalletResponse)
6770
} catch (error) {
6871
console.error(error)
@@ -81,27 +84,27 @@ export class WalletController {
8184
*/
8285
async getWallet(c: Context) {
8386
try {
84-
const validation = await validateRequest(c, UserIdParamSchema)
87+
const validation = await validateRequest(c, WalletAddressParamSchema)
8588
if (!validation.success) return validation.response
8689

8790
const {
88-
params: { userId },
91+
params: { walletAddress },
8992
} = validation.data
90-
const wallet = await walletService.getWallet(userId)
93+
const wallet = await walletService.getWallet(getAddress(walletAddress))
9194

9295
if (!wallet) {
9396
return c.json(
9497
{
9598
error: 'Wallet not found',
96-
message: `No wallet found for user ${userId}`,
99+
message: `No wallet found for user ${walletAddress}`,
97100
},
98101
404,
99102
)
100103
}
101104

102105
return c.json({
103106
address: wallet.address,
104-
userId,
107+
userId: walletAddress,
105108
} satisfies GetWalletResponse)
106109
} catch (error) {
107110
console.error(error)
@@ -153,13 +156,13 @@ export class WalletController {
153156
*/
154157
async getBalance(c: Context) {
155158
try {
156-
const validation = await validateRequest(c, UserIdParamSchema)
159+
const validation = await validateRequest(c, WalletAddressParamSchema)
157160
if (!validation.success) return validation.response
158161

159162
const {
160-
params: { userId },
163+
params: { walletAddress },
161164
} = validation.data
162-
const balance = await walletService.getBalance(userId)
165+
const balance = await walletService.getBalance(getAddress(walletAddress))
163166

164167
return c.json({ balance: serializeBigInt(balance) })
165168
} catch (error) {
@@ -183,10 +186,10 @@ export class WalletController {
183186
if (!validation.success) return validation.response
184187

185188
const {
186-
params: { userId },
189+
params: { walletAddress },
187190
} = validation.data
188191

189-
const result = await walletService.fundWallet(userId)
192+
const result = await walletService.fundWallet(getAddress(walletAddress))
190193

191194
return c.json(result)
192195
} catch (error) {
@@ -209,11 +212,11 @@ export class WalletController {
209212
if (!validation.success) return validation.response
210213

211214
const {
212-
body: { walletId, amount, recipientAddress },
215+
body: { walletAddress, amount, recipientAddress },
213216
} = validation.data
214217

215218
const transactionData = await walletService.sendTokens(
216-
walletId,
219+
getAddress(walletAddress),
217220
amount,
218221
recipientAddress as Address,
219222
)

packages/demo/backend/src/router.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,17 @@ router.get('/version', (c) => {
4242
// router.post('/wallet/:userId', authMiddleware, walletController.createWallet)
4343

4444
router.get('/wallets', walletController.getAllWallets)
45-
router.post('/wallet/:userId', walletController.createWallet)
45+
router.post('/wallet', walletController.createWallet)
4646
router.get('/wallet/:userId', walletController.getWallet)
47-
router.get('/wallet/:userId/balance', walletController.getBalance)
48-
router.post('/wallet/:userId/fund', walletController.fundWallet)
47+
router.get('/wallet/:walletAddress/balance', walletController.getBalance)
48+
router.post('/wallet/:walletAddress/fund', walletController.fundWallet)
4949
router.post('/wallet/send', walletController.sendTokens)
5050

5151
// Lend endpoints
5252
router.get('/lend/markets', lendController.getMarkets)
5353
router.get('/lend/market/:chainId/:marketId', lendController.getMarket)
5454
router.get(
55-
'/lend/market/:vaultAddress/balance/:walletId',
55+
'/lend/market/:vaultAddress/balance/:walletAddress',
5656
lendController.getMarketBalance,
5757
)
5858
router.post('/lend/deposit', lendController.deposit)

packages/demo/backend/src/services/lend.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ export async function getMarket(
6161

6262
export async function getMarketBalance(
6363
vaultAddress: Address,
64-
walletId: string,
64+
walletAddress: Address,
6565
): Promise<MarketBalanceResult> {
6666
const verbs = getVerbs()
67-
const wallet = await getWallet(walletId)
67+
const wallet = await getWallet(walletAddress)
6868

6969
if (!wallet) {
70-
throw new Error(`Wallet not found for user ID: ${walletId}`)
70+
throw new Error(`Wallet not found for user ID: ${walletAddress}`)
7171
}
7272

7373
return verbs.lend.getMarketBalance(vaultAddress, wallet.address)
@@ -109,15 +109,15 @@ export async function formatMarketBalanceResponse(
109109
}
110110

111111
export async function deposit(
112-
walletId: string,
112+
walletAddress: Address,
113113
amount: number,
114114
tokenAddress: Address,
115115
chainId: SupportedChainId,
116116
): Promise<LendTransaction> {
117-
const wallet = await getWallet(walletId)
117+
const wallet = await getWallet(walletAddress)
118118

119119
if (!wallet) {
120-
throw new Error(`Wallet not found for user ID: ${walletId}`)
120+
throw new Error(`Wallet not found for user ID: ${walletAddress}`)
121121
}
122122

123123
if ('lendExecute' in wallet && typeof wallet.lendExecute === 'function') {
@@ -130,14 +130,14 @@ export async function deposit(
130130
}
131131

132132
export async function executeLendTransaction(
133-
walletId: string,
133+
walletAddress: Address,
134134
lendTransaction: LendTransaction,
135135
chainId: SupportedChainId,
136136
): Promise<LendTransaction & { blockExplorerUrl: string }> {
137-
const wallet = await getWallet(walletId)
137+
const wallet = await getWallet(walletAddress)
138138

139139
if (!wallet) {
140-
throw new Error(`Wallet not found for user ID: ${walletId}`)
140+
throw new Error(`Wallet not found for user ID: ${walletAddress}`)
141141
}
142142

143143
if (!lendTransaction.transactionData) {

0 commit comments

Comments
 (0)