4545 Send 0.1 ETH with Paymaster
4646 </button >
4747
48+ <div
49+ v-if =" address"
50+ class =" mt-8 border-t pt-4"
51+ >
52+ <h2 class =" text-xl font-bold mb-4" >
53+ Typed Data Signature Verification
54+ </h2 >
55+ <div class =" mb-4" >
56+ <pre class =" bg-gray-100 p-3 rounded text-xs overflow-x-auto max-w-2xl max-h-60" >{{ JSON.stringify(typedData, null, 2) }}</pre >
57+ </div >
58+ <div
59+ v-if =" ERC1271CallerContract.deployedTo"
60+ class =" mb-4 text-xs text-gray-600"
61+ >
62+ <p >ERC1271 Caller address: {{ ERC1271CallerContract.deployedTo }}</p >
63+ </div >
64+ <button
65+ class =" bg-purple-500 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded disabled:bg-slate-300"
66+ :disabled =" isSigningTypedData"
67+ @click =" signTypedDataHandler"
68+ >
69+ {{ isSigningTypedData ? 'Signing...' : 'Sign Typed Data' }}
70+ </button >
71+ <div
72+ v-if =" typedDataSignature"
73+ class =" mt-4"
74+ >
75+ <p class =" break-all" >
76+ <strong >Signature:</strong > <span class =" text-xs line-clamp-2" >{{ typedDataSignature }}</span >
77+ </p >
78+ </div >
79+ <div
80+ v-if =" isVerifyingTypedDataSignature"
81+ class =" mt-4"
82+ >
83+ <p class =" text-gray-600" >
84+ Verifying typed data signature...
85+ </p >
86+ </div >
87+ <div
88+ v-else-if =" isValidTypedDataSignature !== null"
89+ class =" mt-4"
90+ >
91+ <p :class =" isValidTypedDataSignature ? 'text-green-600' : 'text-red-600'" >
92+ <strong >Typed Data Verification Result:</strong > {{ isValidTypedDataSignature ? 'Valid ✓' : 'Invalid ✗' }}
93+ </p >
94+ </div >
95+ </div >
96+
4897 <div
4998 v-if =" errorMessage"
5099 class =" p-4 mt-4 mb-4 max-w-96 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400"
55104</template >
56105
57106<script lang="ts" setup>
58- import { disconnect , getBalance , watchAccount , sendTransaction , createConfig , connect , reconnect , waitForTransactionReceipt , type GetBalanceReturnType } from " @wagmi/core" ;
59- import { zksyncSsoConnector , eraTestNode } from " zksync-sso-wagmi-connector " ;
60- import { createWalletClient , http , parseEther , type Address } from " viem " ;
107+ import { disconnect , getBalance , watchAccount , sendTransaction , createConfig , connect , reconnect , waitForTransactionReceipt , type GetBalanceReturnType , signTypedData , readContract } from " @wagmi/core" ;
108+ import { createWalletClient , createPublicClient , http , parseEther , type Address , type Hash } from " viem " ;
109+ import { zksyncSsoConnector } from " zksync-sso-wagmi-connector " ;
61110import { privateKeyToAccount } from " viem/accounts" ;
62- import { getGeneralPaymasterInput } from " viem/zksync" ;
63- import PaymasterContract from " ../forge-output.json" ;
111+ import { getGeneralPaymasterInput , zksyncInMemoryNode } from " viem/zksync" ;
112+ import PaymasterContract from " ../forge-output-paymaster.json" ;
113+ import ERC1271CallerContract from " ../forge-output-erc1271.json" ;
64114
65- const chain = eraTestNode ; // Now using the exported eraTestNode instead of zksyncInMemoryNode
115+ const chain = zksyncInMemoryNode ;
66116
67117const testTransferTarget = " 0x55bE1B079b53962746B2e86d12f158a41DF294A6" ;
118+
119+ const publicClient = createPublicClient ({
120+ chain: chain ,
121+ transport: http (),
122+ });
68123const zksyncConnectorWithSession = zksyncSsoConnector ({
69124 authServerUrl: " http://localhost:3002/confirm" ,
70125 session: {
@@ -92,7 +147,6 @@ reconnect(wagmiConfig);
92147const address = ref <Address | null >(null );
93148const balance = ref <GetBalanceReturnType | null >(null );
94149const errorMessage = ref <string | null >(null );
95- const isSendingEth = ref <boolean >(false );
96150
97151const fundAccount = async () => {
98152 if (! address .value ) throw new Error (" Not connected" );
@@ -166,6 +220,9 @@ const disconnectWallet = async () => {
166220 await disconnect (wagmiConfig );
167221};
168222
223+ /* Send ETH */
224+ const isSendingEth = ref <boolean >(false );
225+
169226const sendTokens = async (usePaymaster : boolean ) => {
170227 if (! address .value ) return ;
171228
@@ -178,7 +235,7 @@ const sendTokens = async (usePaymaster: boolean) => {
178235 transactionHash = await sendTransaction (wagmiConfig , {
179236 to: testTransferTarget ,
180237 value: parseEther (" 0.1" ),
181- paymaster: PaymasterContract .deployedTo as ` 0x${ string } ` ,
238+ paymaster: PaymasterContract .deployedTo as Address ,
182239 paymasterInput: getGeneralPaymasterInput ({ innerInput: " 0x" }),
183240 });
184241 } else {
@@ -225,4 +282,108 @@ const sendTokens = async (usePaymaster: boolean) => {
225282 isSendingEth .value = false ;
226283 }
227284};
285+
286+ /* Typed data */
287+ const typedDataSignature = ref <Hash | null >(null );
288+ const isValidTypedDataSignature = ref <boolean | null >(null );
289+ const isSigningTypedData = ref <boolean >(false );
290+ const isVerifyingTypedDataSignature = ref <boolean >(false );
291+
292+ const typedData = {
293+ types: {
294+ TestStruct: [
295+ { name: " message" , type: " string" },
296+ { name: " value" , type: " uint256" },
297+ ],
298+ },
299+ primaryType: " TestStruct" ,
300+ message: {
301+ message: " test" ,
302+ value: 42n ,
303+ },
304+ } as const ;
305+
306+ const signTypedDataHandler = async () => {
307+ if (! address .value ) return ;
308+
309+ errorMessage .value = " " ;
310+ isSigningTypedData .value = true ;
311+ isValidTypedDataSignature .value = null ;
312+ try {
313+ const erc1271CallerAddress = ERC1271CallerContract .deployedTo as Address ;
314+ const { domain : callerDomain } = await publicClient .getEip712Domain ({
315+ address: erc1271CallerAddress ,
316+ });
317+
318+ const signature = await signTypedData (wagmiConfig , {
319+ domain: {
320+ ... callerDomain ,
321+ salt: undefined , // Otherwise the signature verification fails (todo: figure out why)
322+ },
323+ ... typedData ,
324+ });
325+ typedDataSignature .value = signature ;
326+ } catch (error ) {
327+ // eslint-disable-next-line no-console
328+ console .error (" Typed data signing failed:" , error );
329+ errorMessage .value = " Typed data signing failed, see console for more info." ;
330+ } finally {
331+ isSigningTypedData .value = false ;
332+ }
333+ };
334+
335+ const verifyTypedDataSignatureAutomatically = async () => {
336+ if (! address .value || ! typedDataSignature .value ) {
337+ isValidTypedDataSignature .value = null ;
338+ return ;
339+ }
340+
341+ isVerifyingTypedDataSignature .value = true ;
342+ try {
343+ const contractAddress = ERC1271CallerContract .deployedTo as Address ;
344+
345+ const isValid = await readContract (wagmiConfig , {
346+ address: contractAddress ,
347+ abi: [{
348+ type: " function" ,
349+ name: " validateStruct" ,
350+ stateMutability: " view" ,
351+ inputs: [
352+ {
353+ name: " testStruct" , type: " tuple" , internalType: " struct ERC1271Caller.TestStruct" ,
354+ components: [
355+ { name: " message" , type: " string" , internalType: " string" },
356+ { name: " value" , type: " uint256" , internalType: " uint256" },
357+ ],
358+ },
359+ { name: " signer" , type: " address" , internalType: " address" },
360+ { name: " encodedSignature" , type: " bytes" , internalType: " bytes" },
361+ ],
362+ outputs: [{ name: " " , type: " bool" , internalType: " bool" }],
363+ }] as const ,
364+ functionName: " validateStruct" ,
365+ args: [
366+ typedData .message ,
367+ address .value ,
368+ typedDataSignature .value ,
369+ ],
370+ });
371+
372+ isValidTypedDataSignature .value = isValid ;
373+ } catch (error ) {
374+ // eslint-disable-next-line no-console
375+ console .error (" Typed data signature verification failed:" , error );
376+ isValidTypedDataSignature .value = false ;
377+ } finally {
378+ isVerifyingTypedDataSignature .value = false ;
379+ }
380+ };
381+
382+ watch (address , () => typedDataSignature .value = null );
383+ watch (typedDataSignature , verifyTypedDataSignatureAutomatically );
384+
385+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
386+ (window .BigInt as any ).prototype .toJSON = function () {
387+ return this .toString ();
388+ };
228389 </script >
0 commit comments