Skip to content

Commit aabd5cb

Browse files
committed
demo: use Turnkey server wallets
1 parent b1cb30d commit aabd5cb

File tree

7 files changed

+126
-134
lines changed

7 files changed

+126
-134
lines changed

packages/demo/backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@eth-optimism/viem": "^0.4.13",
4242
"@hono/node-server": "^1.14.0",
4343
"@privy-io/server-auth": "^1.31.1",
44+
"@turnkey/sdk-server": "^4.9.1",
4445
"commander": "^13.1.0",
4546
"dotenv": "^16.4.5",
4647
"envalid": "^8.1.0",

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,7 @@ export const env = cleanEnv(process.env, {
5858
UNICHAIN_BUNDLER_URL: str({ devDefault: 'dummy' }),
5959
UNICHAIN_BUNDLER_SPONSORSHIP_POLICY: str({ devDefault: 'dummy' }),
6060
SESSION_SIGNER_PK: str(),
61+
TURNKEY_ORGANIZATION_ID: str(),
62+
TURNKEY_API_KEY: str(),
63+
TURNKEY_API_SECRET: str(),
6164
})

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,23 @@ import {
44
type VerbsConfig,
55
} from '@eth-optimism/verbs-sdk/node'
66
import { PrivyClient } from '@privy-io/server-auth'
7+
import { Turnkey } from '@turnkey/sdk-server'
78
import { baseSepolia, unichain } from 'viem/chains'
89

910
import { env } from './env.js'
1011
import { GauntletUSDC, MetaMorphoUSDC, USDCDemoVault } from './markets.js'
1112

12-
let verbsInstance: Verbs<'privy'>
13+
let verbsInstance: Verbs<'turnkey'>
1314

14-
export function createVerbsConfig(): VerbsConfig<'privy'> {
15+
export function createVerbsConfig(): VerbsConfig<'turnkey'> {
1516
return {
1617
wallet: {
1718
hostedWalletConfig: {
1819
provider: {
19-
type: 'privy',
20+
type: 'turnkey',
2021
config: {
21-
privyClient: getPrivyClient(),
22+
client: getTurnkeyClient().apiClient(),
23+
organizationId: env.TURNKEY_ORGANIZATION_ID,
2224
},
2325
},
2426
},
@@ -59,7 +61,7 @@ export function createVerbsConfig(): VerbsConfig<'privy'> {
5961
}
6062
}
6163

62-
export function initializeVerbs(config?: VerbsConfig<'privy'>): void {
64+
export function initializeVerbs(config?: VerbsConfig<'turnkey'>): void {
6365
const verbsConfig = config || createVerbsConfig()
6466
verbsInstance = createVerbs(verbsConfig)
6567
}
@@ -78,3 +80,12 @@ export function getPrivyClient() {
7880
}
7981
return privy
8082
}
83+
84+
export function getTurnkeyClient() {
85+
return new Turnkey({
86+
apiBaseUrl: 'https://api.turnkey.com',
87+
apiPublicKey: env.TURNKEY_API_KEY,
88+
apiPrivateKey: env.TURNKEY_API_SECRET,
89+
defaultOrganizationId: env.TURNKEY_ORGANIZATION_ID,
90+
})
91+
}

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

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,7 @@ export class WalletController {
165165
// TODO (https://github.com/ethereum-optimism/verbs/issues/124): enforce auth and clean
166166
// up this route.
167167
if (auth && auth.userId) {
168-
const wallet = await walletService.getUserWallet(auth.userId)
169-
if (!wallet) {
170-
throw new Error('Wallet not found')
171-
}
172-
const balance = await walletService.getWalletBalance(wallet)
173-
return c.json({ balance: serializeBigInt(balance) })
168+
throw new Error('Not implemented')
174169
}
175170

176171
const balance = await walletService.getBalance(userId)
@@ -203,12 +198,7 @@ export class WalletController {
203198
// TODO (https://github.com/ethereum-optimism/verbs/issues/124): enforce auth and clean
204199
// up this route.
205200
if (auth && auth.userId) {
206-
const wallet = await walletService.getUserWallet(auth.userId)
207-
if (!wallet) {
208-
throw new Error('Wallet not found')
209-
}
210-
const result = await walletService.fundWallet(wallet)
211-
return c.json(result)
201+
throw new Error('Not implemented')
212202
}
213203

214204
const wallet = await walletService.getWallet(userId)

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

Lines changed: 10 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { Address } from 'viem'
99
import { baseSepolia, unichain } from 'viem/chains'
1010

1111
import { getVerbs } from '../config/verbs.js'
12-
import { getUserWallet, getWallet } from './wallet.js'
12+
import { getWallet } from './wallet.js'
1313

1414
interface MarketBalanceResult {
1515
balance: bigint
@@ -114,31 +114,12 @@ export async function formatMarketBalanceResponse(
114114
}
115115

116116
export async function depositWithUserWallet(
117-
userId: string,
118-
amount: number,
119-
tokenAddress: Address,
120-
chainId: SupportedChainId,
117+
_userId: string,
118+
_amount: number,
119+
_tokenAddress: Address,
120+
_chainId: SupportedChainId,
121121
): Promise<LendTransaction> {
122-
const wallet = await getUserWallet(userId)
123-
124-
if (!wallet) {
125-
throw new Error(`Wallet not found for user ID: ${userId}`)
126-
}
127-
128-
const asset = SUPPORTED_TOKENS.find(
129-
(token) => token.address[chainId] === tokenAddress,
130-
)
131-
if (!asset) {
132-
throw new Error(`Asset not found for token address: ${tokenAddress}`)
133-
}
134-
135-
if ('lendExecute' in wallet && typeof wallet.lendExecute === 'function') {
136-
return await wallet.lendExecute(amount, asset, chainId)
137-
} else {
138-
throw new Error(
139-
'Lend functionality not yet implemented for this wallet type.',
140-
)
141-
}
122+
throw new Error('Not implemented')
142123
}
143124

144125
export async function deposit(
@@ -170,35 +151,11 @@ export async function deposit(
170151
}
171152

172153
export async function executeLendTransactionWithUserWallet(
173-
userId: string,
174-
lendTransaction: LendTransaction,
175-
chainId: SupportedChainId,
154+
_userId: string,
155+
_lendTransaction: LendTransaction,
156+
_chainId: SupportedChainId,
176157
): Promise<LendTransaction & { blockExplorerUrl: string }> {
177-
const wallet = await getUserWallet(userId)
178-
179-
if (!wallet) {
180-
throw new Error(`Wallet not found for user ID: ${userId}`)
181-
}
182-
183-
if (!lendTransaction.transactionData) {
184-
throw new Error('No transaction data available for execution')
185-
}
186-
187-
const depositHash = lendTransaction.transactionData.approval
188-
? await wallet.sendBatch(
189-
[
190-
lendTransaction.transactionData.approval,
191-
lendTransaction.transactionData.deposit,
192-
],
193-
chainId,
194-
)
195-
: await wallet.send(lendTransaction.transactionData.deposit, chainId)
196-
197-
return {
198-
...lendTransaction,
199-
hash: depositHash,
200-
blockExplorerUrl: await getBlockExplorerUrl(chainId),
201-
}
158+
throw new Error('Not implemented')
202159
}
203160

204161
export async function executeLendTransaction(

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

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { baseSepolia } from 'viem/chains'
1111

1212
import { mintableErc20Abi } from '@/abis/mintableErc20Abi.js'
1313
import { USDC } from '@/config/assets.js'
14-
import { getPrivyClient, getVerbs } from '@/config/verbs.js'
15-
14+
import { env } from '@/config/env.js'
15+
import { getTurnkeyClient, getVerbs } from '@/config/verbs.js'
1616
/**
1717
* Options for getting all wallets
1818
* @description Parameters for filtering and paginating wallet results
@@ -24,18 +24,50 @@ export interface GetAllWalletsOptions {
2424
cursor?: string
2525
}
2626

27+
async function turnkeyCreateWallet() {
28+
const turnkeyClient = getTurnkeyClient()
29+
const activityResponse = await turnkeyClient.apiClient().createWallet({
30+
walletName: 'ETH Wallet',
31+
accounts: [
32+
{
33+
curve: 'CURVE_SECP256K1',
34+
pathFormat: 'PATH_FORMAT_BIP32',
35+
path: "m/44'/60'/0'/0/0",
36+
addressFormat: 'ADDRESS_FORMAT_ETHEREUM',
37+
},
38+
],
39+
})
40+
console.log(activityResponse)
41+
return activityResponse
42+
}
43+
44+
async function turnkeyGetWallet(walletId: string) {
45+
const turnkeyClient = getTurnkeyClient()
46+
const activityResponse = await turnkeyClient.apiClient().getWalletAccount({
47+
organizationId: env.TURNKEY_ORGANIZATION_ID!,
48+
walletId: walletId,
49+
})
50+
console.log(activityResponse)
51+
return activityResponse
52+
}
53+
54+
async function turnkeyGetAllWallets() {
55+
const turnkeyClient = getTurnkeyClient()
56+
const activityResponse = await turnkeyClient.apiClient().getWallets({
57+
organizationId: env.TURNKEY_ORGANIZATION_ID!,
58+
})
59+
console.log(activityResponse)
60+
return activityResponse
61+
}
62+
2763
export async function createWallet(): Promise<{
2864
privyAddress: string
2965
smartWalletAddress: string
3066
}> {
3167
const verbs = getVerbs()
32-
const privyClient = getPrivyClient()
33-
const privyWallet = await privyClient.walletApi.createWallet({
34-
chainType: 'ethereum',
35-
})
68+
const turnkeyWallet = await turnkeyCreateWallet()
3669
const verbsPrivyWallet = await verbs.wallet.hostedWalletToVerbsWallet({
37-
walletId: privyWallet.id,
38-
address: privyWallet.address,
70+
signWith: turnkeyWallet.addresses[0],
3971
})
4072
const wallet = await verbs.wallet.createSmartWallet({
4173
owners: [verbsPrivyWallet.address],
@@ -50,70 +82,65 @@ export async function createWallet(): Promise<{
5082

5183
export async function getWallet(userId: string): Promise<SmartWallet | null> {
5284
const verbs = getVerbs()
53-
const privyClient = getPrivyClient()
54-
const privyWallet = await privyClient.walletApi
55-
.getWallet({
56-
id: userId,
57-
})
58-
.catch(() => null)
59-
if (!privyWallet) {
60-
return privyWallet
61-
}
62-
const verbsPrivyWallet = await verbs.wallet.hostedWalletToVerbsWallet({
63-
walletId: privyWallet.id,
64-
address: privyWallet.address,
65-
})
66-
const wallet = await verbs.wallet.getSmartWallet({
67-
signer: verbsPrivyWallet.signer,
68-
deploymentOwners: [getAddress(privyWallet.address)],
69-
})
70-
return wallet
71-
}
72-
73-
export async function getUserWallet(
74-
userId: string,
75-
): Promise<SmartWallet | null> {
76-
const verbs = getVerbs()
77-
const privyClient = getPrivyClient()
78-
const privyUser = await privyClient.getUserById(userId).catch(() => null)
79-
if (!privyUser) {
85+
const turnkeyWallet = await turnkeyGetWallet(userId).catch(() => null)
86+
if (!turnkeyWallet) {
8087
return null
8188
}
82-
const privyWallet = privyUser.wallet
83-
if (!privyWallet) {
84-
return null
85-
}
86-
const verbsPrivyWallet = await verbs.wallet.hostedWalletToVerbsWallet({
87-
walletId: privyWallet.id!,
88-
address: privyWallet.address,
89+
const verbsTurnkeyWallet = await verbs.wallet.hostedWalletToVerbsWallet({
90+
signWith: turnkeyWallet.account.address,
8991
})
9092
const wallet = await verbs.wallet.getSmartWallet({
91-
signer: verbsPrivyWallet.signer,
92-
deploymentOwners: [getAddress(privyWallet.address)],
93+
signer: verbsTurnkeyWallet.signer,
94+
deploymentOwners: [getAddress(turnkeyWallet.account.address)],
9395
})
9496
return wallet
9597
}
9698

99+
// export async function getUserWallet(
100+
// userId: string,
101+
// ): Promise<SmartWallet | null> {
102+
// const verbs = getVerbs()
103+
// const privyClient = getPrivyClient()
104+
// const privyUser = await privyClient.getUserById(userId).catch(() => null)
105+
// if (!privyUser) {
106+
// return null
107+
// }
108+
// const privyWallet = privyUser.wallet
109+
// if (!privyWallet) {
110+
// return null
111+
// }
112+
// const verbsPrivyWallet = await verbs.wallet.hostedWalletToVerbsWallet({
113+
// walletId: privyWallet.id!,
114+
// address: privyWallet.address,
115+
// })
116+
// const wallet = await verbs.wallet.getSmartWallet({
117+
// signer: verbsPrivyWallet.signer,
118+
// deploymentOwners: [getAddress(privyWallet.address)],
119+
// })
120+
// return wallet
121+
// }
122+
97123
export async function getAllWallets(
98-
options?: GetAllWalletsOptions,
124+
_options?: GetAllWalletsOptions,
99125
): Promise<Array<{ wallet: SmartWallet; id: string }>> {
100126
try {
101127
const verbs = getVerbs()
102-
const privyClient = getPrivyClient()
103-
const response = await privyClient.walletApi.getWallets(options)
128+
const turnkeyWallets = await turnkeyGetAllWallets()
104129
return Promise.all(
105-
response.data.map(async (privyWallet) => {
106-
const verbsPrivyWallet = await verbs.wallet.hostedWalletToVerbsWallet({
107-
walletId: privyWallet.id,
108-
address: privyWallet.address,
109-
})
130+
turnkeyWallets.wallets.map(async ({ walletId }) => {
131+
const turnkeyWallet = await turnkeyGetWallet(walletId)
132+
const verbsTurnkeyWallet = await verbs.wallet.hostedWalletToVerbsWallet(
133+
{
134+
signWith: turnkeyWallet.account.address,
135+
},
136+
)
110137
const wallet = await verbs.wallet.getSmartWallet({
111-
signer: verbsPrivyWallet.signer,
112-
deploymentOwners: [getAddress(privyWallet.address)],
138+
signer: verbsTurnkeyWallet.signer,
139+
deploymentOwners: [getAddress(turnkeyWallet.account.address)],
113140
})
114141
return {
115142
wallet,
116-
id: privyWallet.id,
143+
id: walletId,
117144
}
118145
}),
119146
)

0 commit comments

Comments
 (0)