@@ -6,9 +6,9 @@ import { ERC20_TOKENS, CONTRACTS, DEFAULT_CLIENT_ID } from "@/lib/constants";
66import { submitUserOpTest } from "@/lib/tee-worker-client" ;
77import {
88 buildTokenTransferUserOp ,
9+ buildNativeTransferUserOp ,
910 packUserOperation ,
1011 toSerializablePackedUserOperation ,
11- estimateUserOperationGas ,
1212 estimateUserOpGasFromWorker ,
1313} from "@/lib/aa-utils" ;
1414
@@ -34,7 +34,7 @@ export function TEETokenTransfer({
3434} : TEETokenTransferProps ) {
3535 const publicClient = usePublicClient ( ) ;
3636 const chainId = useChainId ( ) ;
37- const [ selectedToken , setSelectedToken ] = useState < "USDC" | "USDT" > ( "USDC " ) ;
37+ const [ selectedToken , setSelectedToken ] = useState < "ETH" | " USDC" | "USDT" > ( "ETH " ) ;
3838 const [ recipient , setRecipient ] = useState ( "" ) ;
3939 const [ amount , setAmount ] = useState ( "" ) ;
4040 const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
@@ -43,15 +43,42 @@ export function TEETokenTransfer({
4343 const [ tokenBalances , setTokenBalances ] = useState < TokenBalance [ ] > ( [ ] ) ;
4444 const [ nonce , setNonce ] = useState < bigint > ( BigInt ( 0 ) ) ;
4545
46- // Available tokens
47- const availableTokens = [ ERC20_TOKENS . USDC , ERC20_TOKENS . USDT ] ;
46+ // Available tokens including ETH
47+ const availableTokens = [
48+ { symbol : "ETH" , decimals : 18 , address : "0x0000000000000000000000000000000000000000" as `0x${string } `, isNative : true } ,
49+ ERC20_TOKENS . USDC ,
50+ ERC20_TOKENS . USDT
51+ ] ;
4852
4953 // Fetch token balances
5054 const fetchBalances = async ( ) => {
5155 if ( ! omniAccountAddress || ! publicClient ) return ;
5256
5357 const balances : TokenBalance [ ] = [ ] ;
54- for ( const token of availableTokens ) {
58+
59+ // Fetch ETH balance first
60+ try {
61+ const ethBalance = await publicClient . getBalance ( {
62+ address : omniAccountAddress as `0x${string } `,
63+ } ) ;
64+ balances . push ( {
65+ symbol : "ETH" ,
66+ balance : ethBalance ,
67+ decimals : 18 ,
68+ address : "0x0000000000000000000000000000000000000000" as `0x${string } `,
69+ } ) ;
70+ } catch ( error ) {
71+ console . error ( "Error fetching ETH balance:" , error ) ;
72+ balances . push ( {
73+ symbol : "ETH" ,
74+ balance : BigInt ( 0 ) ,
75+ decimals : 18 ,
76+ address : "0x0000000000000000000000000000000000000000" as `0x${string } `,
77+ } ) ;
78+ }
79+
80+ // Fetch ERC20 balances
81+ for ( const token of [ ERC20_TOKENS . USDC , ERC20_TOKENS . USDT ] ) {
5582 try {
5683 const balance = ( await publicClient . readContract ( {
5784 address : token . address ,
@@ -122,46 +149,64 @@ export function TEETokenTransfer({
122149 return ;
123150 }
124151
125- const token = selectedToken === "USDC" ? ERC20_TOKENS . USDC : ERC20_TOKENS . USDT ;
126152 const tokenBalance = tokenBalances . find ( tb => tb . symbol === selectedToken ) ;
127153
128154 if ( ! tokenBalance ) {
129155 setError ( "Token balance not loaded" ) ;
130156 return ;
131157 }
132158
133- const amountBigInt = parseUnits ( amount , token . decimals ) ;
159+ const decimals = selectedToken === "ETH" ? 18 :
160+ selectedToken === "USDC" ? ERC20_TOKENS . USDC . decimals :
161+ ERC20_TOKENS . USDT . decimals ;
162+ const amountBigInt = parseUnits ( amount , decimals ) ;
134163
135164 if ( amountBigInt > tokenBalance . balance ) {
136- setError ( "Insufficient token balance" ) ;
165+ setError ( "Insufficient balance" ) ;
137166 return ;
138167 }
139168
140169 setIsSubmitting ( true ) ;
141170
142171 try {
143- // Build the initial UserOperation for token transfer with minimal gas for estimation
144- const userOpWithoutGas = buildTokenTransferUserOp ( {
145- omniAccountAddress : omniAccountAddress as `0x${string } `,
146- tokenAddress : token . address ,
147- recipient : recipient as `0x${string } `,
148- amount : amountBigInt ,
149- nonce,
150- forGasEstimation : true , // Use dummy signature for gas estimation
151- gasParams : {
152- // Use minimal gas values for estimation to avoid prefund issues
153- callGasLimit : BigInt ( 100000 ) , // Minimal for simulation
154- verificationGasLimit : BigInt ( 150000 ) , // Enough for signature validation
155- preVerificationGas : BigInt ( 21000 ) , // Base transaction cost
156- maxFeePerGas : BigInt ( 1000000000 ) , // 1 gwei - minimal for simulation
157- maxPriorityFeePerGas : BigInt ( 1000000000 ) , // 1 gwei - minimal
158- }
159- } ) ;
172+ // Build the initial UserOperation for transfer with minimal gas for estimation
173+ const userOpForEstimation = selectedToken === "ETH" ?
174+ buildNativeTransferUserOp ( {
175+ omniAccountAddress : omniAccountAddress as `0x${string } `,
176+ recipient : recipient as `0x${string } `,
177+ amount : amountBigInt ,
178+ nonce,
179+ forGasEstimation : true , // Use dummy signature for gas estimation
180+ gasParams : {
181+ // Use minimal gas values for estimation to avoid prefund issues
182+ callGasLimit : BigInt ( 100000 ) , // Minimal for simulation
183+ verificationGasLimit : BigInt ( 150000 ) , // Enough for signature validation
184+ preVerificationGas : BigInt ( 21000 ) , // Base transaction cost
185+ maxFeePerGas : BigInt ( 1000000000 ) , // 1 gwei - minimal for simulation
186+ maxPriorityFeePerGas : BigInt ( 1000000000 ) , // 1 gwei - minimal
187+ }
188+ } ) :
189+ buildTokenTransferUserOp ( {
190+ omniAccountAddress : omniAccountAddress as `0x${string } `,
191+ tokenAddress : selectedToken === "USDC" ? ERC20_TOKENS . USDC . address : ERC20_TOKENS . USDT . address ,
192+ recipient : recipient as `0x${string } `,
193+ amount : amountBigInt ,
194+ nonce,
195+ forGasEstimation : true , // Use dummy signature for gas estimation
196+ gasParams : {
197+ // Use minimal gas values for estimation to avoid prefund issues
198+ callGasLimit : BigInt ( 100000 ) , // Minimal for simulation
199+ verificationGasLimit : BigInt ( 150000 ) , // Enough for signature validation
200+ preVerificationGas : BigInt ( 21000 ) , // Base transaction cost
201+ maxFeePerGas : BigInt ( 1000000000 ) , // 1 gwei - minimal for simulation
202+ maxPriorityFeePerGas : BigInt ( 1000000000 ) , // 1 gwei - minimal
203+ }
204+ } ) ;
160205
161206 // Estimate gas using the TEE worker
162207 console . log ( "Attempting to estimate gas using TEE worker..." ) ;
163208 const gasParams = await estimateUserOpGasFromWorker (
164- userOpWithoutGas ,
209+ userOpForEstimation ,
165210 chainId ,
166211 0 , // wallet_index
167212 omniAccountHash ,
@@ -171,14 +216,22 @@ export function TEETokenTransfer({
171216 console . log ( "Successfully estimated gas using TEE worker:" , gasParams ) ;
172217
173218 // Build the final UserOperation with gas estimates
174- const userOp = buildTokenTransferUserOp ( {
175- omniAccountAddress : omniAccountAddress as `0x${string } `,
176- tokenAddress : token . address ,
177- recipient : recipient as `0x${string } `,
178- amount : amountBigInt ,
179- nonce,
180- gasParams,
181- } ) ;
219+ const userOp = selectedToken === "ETH" ?
220+ buildNativeTransferUserOp ( {
221+ omniAccountAddress : omniAccountAddress as `0x${string } `,
222+ recipient : recipient as `0x${string } `,
223+ amount : amountBigInt ,
224+ nonce,
225+ gasParams,
226+ } ) :
227+ buildTokenTransferUserOp ( {
228+ omniAccountAddress : omniAccountAddress as `0x${string } `,
229+ tokenAddress : selectedToken === "USDC" ? ERC20_TOKENS . USDC . address : ERC20_TOKENS . USDT . address ,
230+ recipient : recipient as `0x${string } `,
231+ amount : amountBigInt ,
232+ nonce,
233+ gasParams,
234+ } ) ;
182235
183236 // Pack the UserOperation
184237 const packedOp = packUserOperation ( userOp ) ;
@@ -258,7 +311,7 @@ export function TEETokenTransfer({
258311 < Send className = "mx-auto h-12 w-12 text-blue-500 mb-4" />
259312 < h2 className = "text-2xl font-bold" > Send Token Transfer</ h2 >
260313 < p className = "text-gray-600 mt-2" >
261- Transfer USDC or USDT through the TEE worker
314+ Transfer ETH, USDC, or USDT through the TEE worker
262315 </ p >
263316 </ div >
264317
@@ -270,14 +323,18 @@ export function TEETokenTransfer({
270323 < div
271324 key = { tb . symbol }
272325 className = { `p-3 rounded-lg flex justify-between items-center cursor-pointer transition-colors ${ selectedToken === tb . symbol
273- ? tb . symbol === "USDC"
274- ? "bg-blue-100 border-2 border-blue-500"
275- : "bg-green-100 border-2 border-green-500"
276- : tb . symbol === "USDC"
277- ? "bg-blue-50 hover:bg-blue-100"
278- : "bg-green-50 hover:bg-green-100"
326+ ? tb . symbol === "ETH"
327+ ? "bg-purple-100 border-2 border-purple-500"
328+ : tb . symbol === "USDC"
329+ ? "bg-blue-100 border-2 border-blue-500"
330+ : "bg-green-100 border-2 border-green-500"
331+ : tb . symbol === "ETH"
332+ ? "bg-purple-50 hover:bg-purple-100"
333+ : tb . symbol === "USDC"
334+ ? "bg-blue-50 hover:bg-blue-100"
335+ : "bg-green-50 hover:bg-green-100"
279336 } `}
280- onClick = { ( ) => setSelectedToken ( tb . symbol as "USDC" | "USDT" ) }
337+ onClick = { ( ) => setSelectedToken ( tb . symbol as "ETH" | " USDC" | "USDT" ) }
281338 >
282339 < span className = "font-medium" > { tb . symbol } </ span >
283340 < span className = "font-mono text-sm" >
@@ -369,9 +426,9 @@ export function TEETokenTransfer({
369426 < div className = "text-sm text-blue-700" >
370427 < p className = "font-medium mb-1" > How it works:</ p >
371428 < ul className = "list-disc list-inside space-y-1" >
372- < li > This creates a UserOperation for an ERC20 transfer</ li >
429+ < li > This creates a UserOperation for { selectedToken === "ETH" ? "a native ETH" : " an ERC20" } transfer</ li >
373430 < li > The TEE worker signs and submits the operation</ li >
374- < li > Your Omni Account executes the token transfer</ li >
431+ < li > Your Omni Account executes the transfer</ li >
375432 </ ul >
376433 </ div >
377434 </ div >
0 commit comments