diff --git a/packages/demo/backend/src/config/env.ts b/packages/demo/backend/src/config/env.ts index 34fb2b8e..df2fb3b0 100644 --- a/packages/demo/backend/src/config/env.ts +++ b/packages/demo/backend/src/config/env.ts @@ -57,4 +57,7 @@ export const env = cleanEnv(process.env, { FAUCET_ADDRESS: str({ default: getFaucetAddressDefault(), }), + SMART_WALLET_IMPL_ADDRESS: str({ + default: '0x000100abaad02f1cfc8bbe32bd5a564817339e72', + }), }) diff --git a/packages/demo/backend/src/controllers/wallet.ts b/packages/demo/backend/src/controllers/wallet.ts index 1d1e3454..25869b5b 100644 --- a/packages/demo/backend/src/controllers/wallet.ts +++ b/packages/demo/backend/src/controllers/wallet.ts @@ -58,12 +58,10 @@ export class WalletController { const { params: { userId }, } = validation.data - const { privyAddress, smartWalletAddress } = - await walletService.createWallet() + const { walletAddress } = await walletService.createWallet() return c.json({ - privyAddress, - smartWalletAddress, + walletAddress, userId, } satisfies CreateWalletResponse) } catch (error) { diff --git a/packages/demo/backend/src/services/wallet.ts b/packages/demo/backend/src/services/wallet.ts index 5ed924e1..5572058d 100644 --- a/packages/demo/backend/src/services/wallet.ts +++ b/packages/demo/backend/src/services/wallet.ts @@ -23,14 +23,14 @@ import { env } from '@/config/env.js' import { getVerbs } from '../config/verbs.js' -export async function createWallet(): Promise<{ - privyAddress: string - smartWalletAddress: string -}> { +export async function createWallet() { const verbs = getVerbs() const privyWallet = await verbs.wallet.privy!.createWallet() - const smartWallet = await verbs.wallet.smartWallet!.createWallet([getAddress(privyWallet.address)]) - return { privyAddress: privyWallet.address, smartWalletAddress: smartWallet.address } + // fund the privy wallet with ETH + await fundWallet(privyWallet.walletId, 'ETH') + console.log('delegating privy wallet to smart wallet') + await privyWallet.authorize7702(unichain.id, env.SMART_WALLET_IMPL_ADDRESS as Address); + return { walletAddress: privyWallet.address } } export async function getWallet(userId: string): Promise<{ @@ -49,7 +49,7 @@ export async function getWallet(userId: string): Promise<{ throw new Error('Wallet not found') } const wallet = await verbs.wallet.smartWallet.getWallet( - [getAddress(privyWallet.address)], + getAddress(privyWallet.address), ) return { privyWallet, wallet } } @@ -70,7 +70,7 @@ export async function getAllWallets( if (!verbs.wallet.smartWallet) { throw new Error('Smart wallet not configured') } - return { privyWallet: wallet, wallet: await verbs.wallet.smartWallet.getWallet([getAddress(wallet.address)]) } + return { privyWallet: wallet, wallet: await verbs.wallet.smartWallet.getWallet(getAddress(wallet.address)) } }), ) } catch { diff --git a/packages/demo/frontend/src/components/Terminal.tsx b/packages/demo/frontend/src/components/Terminal.tsx index 4838c395..2ddcedce 100644 --- a/packages/demo/frontend/src/components/Terminal.tsx +++ b/packages/demo/frontend/src/components/Terminal.tsx @@ -546,8 +546,7 @@ Active Wallets: 0`, id: `success-${Date.now()}`, type: 'success', content: `Wallet created successfully! -Privy Address: ${result.privyAddress} -Smart Wallet Address: ${result.smartWalletAddress} +Wallet Address: ${result.walletAddress} User ID: ${result.userId}`, timestamp: new Date(), } diff --git a/packages/sdk/src/types/service.ts b/packages/sdk/src/types/service.ts index ad8ad907..b61dacdd 100644 --- a/packages/sdk/src/types/service.ts +++ b/packages/sdk/src/types/service.ts @@ -30,9 +30,7 @@ export interface GetAllWalletsResponse { */ export interface CreateWalletResponse { /** Wallet address */ - privyAddress: string - /** Smart wallet address */ - smartWalletAddress: string + walletAddress: string /** User ID */ userId: string } diff --git a/packages/sdk/src/wallet/PrivyWallet.ts b/packages/sdk/src/wallet/PrivyWallet.ts index 4265b125..1c2b2bb6 100644 --- a/packages/sdk/src/wallet/PrivyWallet.ts +++ b/packages/sdk/src/wallet/PrivyWallet.ts @@ -1,8 +1,12 @@ -import type { Address, Hex, Quantity } from 'viem' +import { type Address, type Hash,type PublicClient, type Hex, type Quantity, encodeFunctionData, encodeAbiParameters, http, createWalletClient } from 'viem' import type { ChainManager } from '@/services/ChainManager.js' import type { PrivyWalletProvider } from './providers/privy.js' +import { SupportedChainId } from '@/constants/supportedChains.js' +import { smartWalletAbi } from '@/abis/smartWallet.js' +import { unichain } from 'viem/chains' +import { privateKeyToAccount } from 'viem/accounts' /** * Privy wallet implementation @@ -31,6 +35,31 @@ export class PrivyWallet { this.address = address } + /** + * Send a signed transaction + * @description Sends a pre-signed transaction to the network + * @param signedTransaction - Signed transaction to send + * @param publicClient - Viem public client to send the transaction + * @returns Promise resolving to transaction hash + */ + async send( + signedTransaction: string, + publicClient: PublicClient, + ): Promise { + try { + const hash = await publicClient.sendRawTransaction({ + serializedTransaction: signedTransaction as `0x${string}`, + }) + return hash + } catch (error) { + throw new Error( + `Failed to send transaction: ${ + error instanceof Error ? error.message : 'Unknown error' + }`, + ) + } + } + /** * Sign a transaction without sending it * @description Signs a transaction using Privy's wallet API but doesn't send it @@ -83,4 +112,54 @@ export class PrivyWallet { ) } } + + async authorize7702(chainId: SupportedChainId, contract: Address) { + const authorization = await this.privyProvider.privy.walletApi.ethereum.sign7702Authorization({ + walletId: this.walletId, + contract, + chainId, + }); + console.log('Authorization signed: %s', authorization) + + const initializeData = encodeFunctionData({ + abi: smartWalletAbi, + functionName: 'initialize', + args: [[encodeAbiParameters([{ type: 'address' }], [this.address])]], + }) + const publicClient = this.chainManager.getPublicClient(chainId); + + const nonce = await publicClient.getTransactionCount({ + address: this.address, + blockTag: 'pending', // Use pending to get the next nonce including any pending txs + }) + const walletClient = createWalletClient({ + chain: unichain, + transport: http('http://127.0.0.1:9545'), + account: privateKeyToAccount('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'), + }) + const hash = await walletClient.sendTransaction({ + // account: this.address, + to: this.address, + data: initializeData, + authorizationList: [{...authorization, address: authorization.contract, chainId: Number(authorization.chainId), nonce: Number(authorization.nonce)}], + value: 0n, + }) + // const tx = { + // from: this.address, + // to: this.address, + // data: initializeData, + // authorizationList: [authorization], + // chainId, + // nonce, + // }; + // const response = await this.privyProvider.privy.walletApi.ethereum.signTransaction({ + // walletId: this.walletId, + // transaction: tx, + // }); + console.log('delegated smart wallet hash', hash); + // const hash = await this.send(response.signedTransaction, publicClient); + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + console.log('delegated smart wallet receipt', receipt); + return hash; + } } diff --git a/packages/sdk/src/wallet/providers/smartWallet.ts b/packages/sdk/src/wallet/providers/smartWallet.ts index 2390089a..84074204 100644 --- a/packages/sdk/src/wallet/providers/smartWallet.ts +++ b/packages/sdk/src/wallet/providers/smartWallet.ts @@ -78,27 +78,11 @@ export class SmartWalletProvider { } async getWallet( - initialOwnerAddresses: Address[], - nonce?: bigint, - currentOwnerAddresses?: Address[], + eoaAddress: Address ): Promise { - // Factory is the same accross all chains, so we can use the first chain to get the wallet address - const publicClient = this.chainManager.getPublicClient( - this.chainManager.getSupportedChains()[0], - ) - const encodedOwners = initialOwnerAddresses.map((ownerAddress) => - encodeAbiParameters([{ type: 'address' }], [ownerAddress]), - ) - const smartWalletAddress = await publicClient.readContract({ - abi: smartWalletFactoryAbi, - address: smartWalletFactoryAddress, - functionName: 'getAddress', - args: [encodedOwners, nonce || 0n], - }) - const owners = currentOwnerAddresses || initialOwnerAddresses return new SmartWallet( - smartWalletAddress, - owners, + eoaAddress, + [eoaAddress], this.chainManager, this.lendProvider, )