From d0a2be6f1fac2eab83ee005604697626a89eb213 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 12 Jun 2024 11:47:10 +0200 Subject: [PATCH 1/5] feat(eip4361): allow for reusing siwe messages --- packages/taco/src/conditions/const.ts | 6 +- .../taco/src/conditions/context/context.ts | 48 +++++++--- packages/taco/test/conditions/context.test.ts | 95 +++++++++++++++---- 3 files changed, 115 insertions(+), 34 deletions(-) diff --git a/packages/taco/src/conditions/const.ts b/packages/taco/src/conditions/const.ts index 01357b76c..626b3088b 100644 --- a/packages/taco/src/conditions/const.ts +++ b/packages/taco/src/conditions/const.ts @@ -5,6 +5,9 @@ import { USER_ADDRESS_PARAM_EIP712 } from "@nucypher/taco-auth"; +export const USER_ADDRESS_PARAM_EXTERNAL_EIP4361 = + ':userAddressExternalEIP4361'; + export const ETH_ADDRESS_REGEXP = new RegExp('^0x[a-fA-F0-9]{40}$'); // Only allow alphanumeric characters and underscores @@ -22,7 +25,8 @@ export const SUPPORTED_CHAIN_IDS = [ export const USER_ADDRESS_PARAMS = [ USER_ADDRESS_PARAM_EIP712, USER_ADDRESS_PARAM_EIP4361, - // this should always be last + USER_ADDRESS_PARAM_EXTERNAL_EIP4361, + // Ordering matters, this should always be last USER_ADDRESS_PARAM_DEFAULT, ]; diff --git a/packages/taco/src/conditions/context/context.ts b/packages/taco/src/conditions/context/context.ts index a365fb7de..d55a5d9d0 100644 --- a/packages/taco/src/conditions/context/context.ts +++ b/packages/taco/src/conditions/context/context.ts @@ -6,10 +6,10 @@ import { CoreConditions, CoreContext } from '../../types'; import { CompoundConditionType } from '../compound-condition'; import { Condition, ConditionProps } from '../condition'; import { ConditionExpression } from '../condition-expr'; -import { CONTEXT_PARAM_PREFIX, CONTEXT_PARAM_REGEXP, RESERVED_CONTEXT_PARAMS } from '../const'; +import { CONTEXT_PARAM_PREFIX, CONTEXT_PARAM_REGEXP, RESERVED_CONTEXT_PARAMS, USER_ADDRESS_PARAMS } from '../const'; -export type CustomContextParam = string | number | boolean; +export type CustomContextParam = string | number | boolean | AuthSignature; export type ContextParam = CustomContextParam | AuthSignature; const ERR_RESERVED_PARAM = (key: string) => @@ -17,11 +17,13 @@ const ERR_RESERVED_PARAM = (key: string) => const ERR_INVALID_CUSTOM_PARAM = (key: string) => `Custom parameter ${key} must start with ${CONTEXT_PARAM_PREFIX}`; const ERR_AUTH_PROVIDER_REQUIRED = (key: string) => - `Authentication provider required to satisfy ${key} context variable in condition`; + `No authentication provider required to satisfy ${key} context variable in condition`; const ERR_MISSING_CONTEXT_PARAMS = (params: string[]) => `Missing custom context parameter(s): ${params.join(', ')}`; const ERR_UNKNOWN_CONTEXT_PARAMS = (params: string[]) => `Unknown custom context parameter(s): ${params.join(', ')}`; +const ERR_NO_AUTH_PROVIDER_FOR_PARAM = (param: string) => + `No custom parameter for requested context parameter: ${param}`; export class ConditionContext { public requestedParameters: Set; @@ -86,16 +88,24 @@ export class ConditionContext { } private validateAuthProviders(requestedParameters: Set): void { - requestedParameters - .forEach(param => { - const maybeAuthMethod = AUTH_METHOD_FOR_PARAM[param]; - if (!maybeAuthMethod) { - return; - } - if (!this.authProviders[maybeAuthMethod]) { - throw new Error(ERR_AUTH_PROVIDER_REQUIRED(param)); - } - }); + for (const param of requestedParameters) { + // If it's not a user address parameter, we can skip + if (!USER_ADDRESS_PARAMS.includes(param)) { + continue; + } + + // If it's a user address parameter, we need to check if we have an auth provider + const authMethod = AUTH_METHOD_FOR_PARAM[param]; + if (!authMethod && !this.customParameters[param]) { + // If we don't have an auth method, and we don't have a custom parameter, we have a problem + throw new Error(ERR_NO_AUTH_PROVIDER_FOR_PARAM(param)); + } + + // If we have an auth method, but we don't have an auth provider, we have a problem + if (authMethod && !this.authProviders[authMethod]) { + throw new Error(ERR_AUTH_PROVIDER_REQUIRED(param)); + } + } } private async fillAuthContextParameters(requestedParameters: Set): Promise> { @@ -119,12 +129,20 @@ export class ConditionContext { // First, we want to find all the parameters we need to add const requestedParameters = new Set(); - // Search conditions for parameters // Check return value test if (condition.returnValueTest) { const rvt = condition.returnValueTest.value; - if (ConditionContext.isContextParameter(rvt)) { + // Return value test can be a single parameter or an array of parameters + if (Array.isArray(rvt)) { + rvt.forEach((value) => { + if (ConditionContext.isContextParameter(value)) { + requestedParameters.add(value); + } + }); + } else if (ConditionContext.isContextParameter(rvt)) { requestedParameters.add(rvt); + } else { + // Not a context parameter, we can skip } } diff --git a/packages/taco/test/conditions/context.test.ts b/packages/taco/test/conditions/context.test.ts index 281d45ee4..373ca924f 100644 --- a/packages/taco/test/conditions/context.test.ts +++ b/packages/taco/test/conditions/context.test.ts @@ -1,7 +1,7 @@ import { initialize } from '@nucypher/nucypher-core'; import { AuthProviders, - AuthSignature, + AuthSignature, EIP4361_AUTH_METHOD, EIP4361AuthProvider, EIP712AuthProvider, EIP712TypedData, makeAuthProviders, @@ -24,6 +24,7 @@ import { RpcCondition } from '../../src/conditions/base/rpc'; import { ConditionExpression } from '../../src/conditions/condition-expr'; import { RESERVED_CONTEXT_PARAMS, + USER_ADDRESS_PARAM_EXTERNAL_EIP4361, } from '../../src/conditions/const'; import { CustomContextParam } from '../../src/conditions/context'; import { @@ -151,7 +152,7 @@ describe('context', () => { const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.buildContext({}, authProviders)).toBeDefined(); expect(() => conditionExpr.buildContext({})).toThrow( - `Authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, + `No authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, ); }); @@ -170,7 +171,7 @@ describe('context', () => { const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.buildContext( {}, authProviders)).toBeDefined(); expect(() => conditionExpr.buildContext( {})).toThrow( - `Authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, + `No authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, ); }); @@ -195,7 +196,7 @@ describe('context', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(() => conditionExpr.buildContext( {}, undefined)).toThrow( - `Authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, + `No authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, ); }); @@ -210,7 +211,7 @@ describe('context', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(() => conditionExpr.buildContext( {}, undefined)).toThrow( - `Authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, + `No authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, ); }); @@ -286,11 +287,29 @@ describe('context', () => { }); }); -describe('authentication provider', () => { +// TODO: Move to a separate file +describe('No authentication provider', () => { let provider: ethers.providers.Provider; let signer: ethers.Signer; let authProviders: AuthProviders; + async function testEIP4361AuthSignature(authSignature: AuthSignature) { + expect(authSignature).toBeDefined(); + expect(authSignature.signature).toBeDefined(); + expect(authSignature.scheme).toEqual('EIP4361'); + + const signerAddress = await signer.getAddress(); + expect(authSignature.address).toEqual(signerAddress); + + expect(authSignature.typedData).toContain( + `localhost wants you to sign in with your Ethereum account:\n${signerAddress}`, + ); + expect(authSignature.typedData).toContain('URI: http://localhost:3000'); + + const chainId = (await provider.getNetwork()).chainId; + expect(authSignature.typedData).toContain(`Chain ID: ${chainId}`); + } + beforeAll(async () => { await initialize(); provider = fakeProvider(); @@ -310,7 +329,7 @@ describe('authentication provider', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(() => conditionExpr.buildContext( {}, {})).toThrow( - `Authentication provider required to satisfy ${userAddressParam} context variable in condition`, + `No authentication provider required to satisfy ${userAddressParam} context variable in condition`, ); }); }); @@ -397,23 +416,63 @@ describe('authentication provider', () => { EIP4361AuthProvider.prototype, 'getOrCreateAuthSignature', ); + const authSignature = await makeAuthSignature(USER_ADDRESS_PARAM_EIP4361); - expect(authSignature).toBeDefined(); - expect(authSignature.signature).toBeDefined(); - expect(authSignature.scheme).toEqual('EIP4361'); + await testEIP4361AuthSignature(authSignature); - const signerAddress = await signer.getAddress(); - expect(authSignature.address).toEqual(signerAddress); + expect(eip4361Spy).toHaveBeenCalledOnce(); + }); - expect(authSignature.typedData).toContain( - `localhost wants you to sign in with your Ethereum account:\n${signerAddress}`, + it('supports reusing external eip4361', async () => { + // Because we are reusing an existing SIWE auth message, we have to pass it as a custom parameter + const authMessage = await makeAuthSignature(USER_ADDRESS_PARAM_EIP4361); + const customParams: Record = { + [USER_ADDRESS_PARAM_EXTERNAL_EIP4361]: authMessage as CustomContextParam, + }; + + // Spying on the EIP4361 provider to make sure it's not called + const eip4361Spy = vi.spyOn( + EIP4361AuthProvider.prototype, + 'getOrCreateAuthSignature', ); - expect(authSignature.typedData).toContain('URI: http://localhost:3000'); - const chainId = (await provider.getNetwork()).chainId; - expect(authSignature.typedData).toContain(`Chain ID: ${chainId}`); + // Now, creating the condition context to run the actual test + const conditionObj = { + ...testContractConditionObj, + returnValueTest: { + ...testReturnValueTest, + value: USER_ADDRESS_PARAM_EXTERNAL_EIP4361, + }, + }; + const condition = new ContractCondition(conditionObj); + const conditionExpr = new ConditionExpression(condition); - expect(eip4361Spy).toHaveBeenCalledOnce(); + // Make sure we remove the EIP4361 auth method from the auth providers first + delete authProviders[EIP4361_AUTH_METHOD]; + // Should throw an error if we don't pass the custom parameter + expect( + () => conditionExpr.buildContext( {}, authProviders) + ).toThrow( + `No custom parameter for requested context parameter: ${USER_ADDRESS_PARAM_EXTERNAL_EIP4361}`, + ); + + // Remembering to pass in customParams here: + const builtContext = conditionExpr.buildContext( + customParams, + authProviders, + ); + const contextVars = await builtContext.toContextParameters(); + expect(eip4361Spy).not.toHaveBeenCalledOnce(); + + // Now, we expect that the auth signature will be available in the context variables + const authSignature = contextVars[ + USER_ADDRESS_PARAM_EXTERNAL_EIP4361 + ] as AuthSignature; + expect(authSignature).toBeDefined(); + await testEIP4361AuthSignature(authSignature); + + // TODO: Should we enforce that? It does not affect the server-side verification of the signature + // expect(contextVars[USER_ADDRESS_PARAM_EXTERNAL_EIP4361]).not.toBeDefined(); }); }); From 680fe191513fc3890ebc2b226529a181138677be Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 24 Jun 2024 08:34:56 +0200 Subject: [PATCH 2/5] refactor: don't reuse eip4361 alias --- packages/taco/test/conditions/context.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/taco/test/conditions/context.test.ts b/packages/taco/test/conditions/context.test.ts index 373ca924f..19662417a 100644 --- a/packages/taco/test/conditions/context.test.ts +++ b/packages/taco/test/conditions/context.test.ts @@ -470,9 +470,6 @@ describe('No authentication provider', () => { ] as AuthSignature; expect(authSignature).toBeDefined(); await testEIP4361AuthSignature(authSignature); - - // TODO: Should we enforce that? It does not affect the server-side verification of the signature - // expect(contextVars[USER_ADDRESS_PARAM_EXTERNAL_EIP4361]).not.toBeDefined(); }); }); From 1bf3960076a5de0a3c6ad66cc5f66956ffbe4189 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Mon, 24 Jun 2024 14:21:13 +0200 Subject: [PATCH 3/5] chore: document external eip4361 behavior --- packages/taco/src/conditions/const.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/taco/src/conditions/const.ts b/packages/taco/src/conditions/const.ts index 626b3088b..601888c31 100644 --- a/packages/taco/src/conditions/const.ts +++ b/packages/taco/src/conditions/const.ts @@ -34,4 +34,6 @@ export const RESERVED_CONTEXT_PARAMS = [ USER_ADDRESS_PARAM_DEFAULT, USER_ADDRESS_PARAM_EIP712, USER_ADDRESS_PARAM_EIP4361, + // USER_ADDRESS_PARAM_EXTERNAL_EIP4361 is not reserved and can be used as a custom context parameter + // USER_ADDRESS_PARAM_EXTERNAL_EIP4361 ]; From 275f00e0ada23696452272e04b617253205af835 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 25 Jun 2024 10:38:43 +0200 Subject: [PATCH 4/5] apply pr suggestions --- packages/taco/src/conditions/context/context.ts | 2 +- packages/taco/test/conditions/context.test.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/taco/src/conditions/context/context.ts b/packages/taco/src/conditions/context/context.ts index d55a5d9d0..b68181ed3 100644 --- a/packages/taco/src/conditions/context/context.ts +++ b/packages/taco/src/conditions/context/context.ts @@ -17,7 +17,7 @@ const ERR_RESERVED_PARAM = (key: string) => const ERR_INVALID_CUSTOM_PARAM = (key: string) => `Custom parameter ${key} must start with ${CONTEXT_PARAM_PREFIX}`; const ERR_AUTH_PROVIDER_REQUIRED = (key: string) => - `No authentication provider required to satisfy ${key} context variable in condition`; + `No matching authentication provider to satisfy ${key} context variable in condition`; const ERR_MISSING_CONTEXT_PARAMS = (params: string[]) => `Missing custom context parameter(s): ${params.join(', ')}`; const ERR_UNKNOWN_CONTEXT_PARAMS = (params: string[]) => diff --git a/packages/taco/test/conditions/context.test.ts b/packages/taco/test/conditions/context.test.ts index 19662417a..7b17d16f2 100644 --- a/packages/taco/test/conditions/context.test.ts +++ b/packages/taco/test/conditions/context.test.ts @@ -152,7 +152,7 @@ describe('context', () => { const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.buildContext({}, authProviders)).toBeDefined(); expect(() => conditionExpr.buildContext({})).toThrow( - `No authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, + `No matching authentication provider to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, ); }); @@ -171,7 +171,7 @@ describe('context', () => { const conditionExpr = new ConditionExpression(condition); expect(conditionExpr.buildContext( {}, authProviders)).toBeDefined(); expect(() => conditionExpr.buildContext( {})).toThrow( - `No authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, + `No matching authentication provider to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, ); }); @@ -196,7 +196,7 @@ describe('context', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(() => conditionExpr.buildContext( {}, undefined)).toThrow( - `No authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, + `No matching authentication provider to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, ); }); @@ -211,7 +211,7 @@ describe('context', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(() => conditionExpr.buildContext( {}, undefined)).toThrow( - `No authentication provider required to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, + `No matching authentication provider to satisfy ${USER_ADDRESS_PARAM_DEFAULT} context variable in condition`, ); }); @@ -329,7 +329,7 @@ describe('No authentication provider', () => { const condition = new ContractCondition(conditionObj); const conditionExpr = new ConditionExpression(condition); expect(() => conditionExpr.buildContext( {}, {})).toThrow( - `No authentication provider required to satisfy ${userAddressParam} context variable in condition`, + `No matching authentication provider to satisfy ${userAddressParam} context variable in condition`, ); }); }); From f77f973269b39f1e2110e874a368ac759af9a0fc Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Tue, 25 Jun 2024 10:55:49 +0200 Subject: [PATCH 5/5] feat(auth): set defaults for eip4361 providers --- packages/taco-auth/src/helper.ts | 10 +++++--- packages/taco-auth/src/providers/eip4361.ts | 25 ++++++++++++++----- packages/taco-auth/test/taco-auth.test.ts | 4 +-- packages/taco/test/conditions/context.test.ts | 4 +-- packages/test-utils/src/variables.ts | 5 ++++ 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/taco-auth/src/helper.ts b/packages/taco-auth/src/helper.ts index 864dd4952..22d94a0ad 100644 --- a/packages/taco-auth/src/helper.ts +++ b/packages/taco-auth/src/helper.ts @@ -1,12 +1,16 @@ import {ethers} from "ethers"; -import { EIP4361AuthProvider } from './providers/eip4361'; +import { EIP4361AuthProvider, EIP4361AuthProviderParams } from './providers/eip4361'; import { EIP712AuthProvider } from './providers/eip712'; import { AuthProviders, EIP4361_AUTH_METHOD, EIP712_AUTH_METHOD } from './types'; -export const makeAuthProviders = (provider: ethers.providers.Provider, signer?: ethers.Signer): AuthProviders => { +export const makeAuthProviders = ( + provider: ethers.providers.Provider, + signer?: ethers.Signer, + siweDefaultParams?: EIP4361AuthProviderParams +): AuthProviders => { return { [EIP712_AUTH_METHOD]: signer ? new EIP712AuthProvider(provider, signer) : undefined, - [EIP4361_AUTH_METHOD]: signer ? new EIP4361AuthProvider(provider, signer) : undefined + [EIP4361_AUTH_METHOD]: signer ? new EIP4361AuthProvider(provider, signer, siweDefaultParams) : undefined } as AuthProviders; }; diff --git a/packages/taco-auth/src/providers/eip4361.ts b/packages/taco-auth/src/providers/eip4361.ts index db35aee7f..48c3fc1a0 100644 --- a/packages/taco-auth/src/providers/eip4361.ts +++ b/packages/taco-auth/src/providers/eip4361.ts @@ -6,6 +6,13 @@ import { AuthSignature, EIP4361_AUTH_METHOD } from '../types'; export type EIP4361TypedData = string; +export type EIP4361AuthProviderParams = { + domain: string; + uri: string; +} + +const ERR_MISSING_SIWE_PARAMETERS = 'Missing default SIWE parameters'; + export class EIP4361AuthProvider { private readonly storage: LocalStorage; @@ -13,6 +20,7 @@ export class EIP4361AuthProvider { // TODO: We only need the provider to fetch the chainId, consider removing it private readonly provider: ethers.providers.Provider, private readonly signer: ethers.Signer, + private readonly providerParams?: EIP4361AuthProviderParams, ) { this.storage = new LocalStorage(); } @@ -55,7 +63,10 @@ export class EIP4361AuthProvider { } // TODO: Create a facility to set these parameters or expose them to the user - private getParametersOrDefault() { + private getParametersOrDefault(): { + domain: string; + uri: string; + } { // If we are in a browser environment, we can get the domain and uri from the window object if (typeof window !== 'undefined') { const maybeOrigin = window?.location?.origin; @@ -64,10 +75,12 @@ export class EIP4361AuthProvider { uri: maybeOrigin, }; } - // TODO: Add a facility to manage this case - return { - domain: 'localhost', - uri: 'http://localhost:3000', - }; + if (this.providerParams) { + return { + domain: this.providerParams.domain, + uri: this.providerParams.uri, + } + } + throw new Error(ERR_MISSING_SIWE_PARAMETERS); } } diff --git a/packages/taco-auth/test/taco-auth.test.ts b/packages/taco-auth/test/taco-auth.test.ts index 80e4da215..7cf7d0175 100644 --- a/packages/taco-auth/test/taco-auth.test.ts +++ b/packages/taco-auth/test/taco-auth.test.ts @@ -1,7 +1,7 @@ import { bobSecretKeyBytes, fakeProvider, - fakeSigner, + fakeSigner, TEST_SIWE_PARAMS, } from '@nucypher/test-utils'; import { SiweMessage } from 'siwe'; import { describe, expect, it } from 'vitest'; @@ -43,7 +43,7 @@ describe('taco authorization', () => { const provider = fakeProvider(bobSecretKeyBytes); const signer = fakeSigner(bobSecretKeyBytes); - const eip4361Provider = new EIP4361AuthProvider(provider, signer); + const eip4361Provider = new EIP4361AuthProvider(provider, signer, TEST_SIWE_PARAMS); const typedSignature = await eip4361Provider.getOrCreateAuthSignature(); expect(typedSignature.signature).toBeDefined(); expect(typedSignature.address).toEqual(await signer.getAddress()); diff --git a/packages/taco/test/conditions/context.test.ts b/packages/taco/test/conditions/context.test.ts index 7b17d16f2..a48aa7e45 100644 --- a/packages/taco/test/conditions/context.test.ts +++ b/packages/taco/test/conditions/context.test.ts @@ -11,7 +11,7 @@ import { USER_ADDRESS_PARAM_EIP4361, USER_ADDRESS_PARAM_EIP712 } from "@nucypher/taco-auth"; -import {fakeAuthProviders, fakeProvider, fakeSigner} from '@nucypher/test-utils'; +import { fakeAuthProviders, fakeProvider, fakeSigner, TEST_SIWE_PARAMS } from '@nucypher/test-utils'; import { ethers } from 'ethers'; import { beforeAll, describe, expect, it, vi } from 'vitest'; @@ -314,7 +314,7 @@ describe('No authentication provider', () => { await initialize(); provider = fakeProvider(); signer = fakeSigner(); - authProviders = makeAuthProviders(provider, signer); + authProviders = makeAuthProviders(provider, signer, TEST_SIWE_PARAMS); }); it('throws an error if there is no auth provider', () => { diff --git a/packages/test-utils/src/variables.ts b/packages/test-utils/src/variables.ts index 85da75d92..462f56609 100644 --- a/packages/test-utils/src/variables.ts +++ b/packages/test-utils/src/variables.ts @@ -14,3 +14,8 @@ export const TEST_CONTRACT_ADDR = '0x0000000000000000000000000000000000000001'; export const TEST_CONTRACT_ADDR_2 = '0x0000000000000000000000000000000000000002'; export const TEST_CHAIN_ID = ChainId.SEPOLIA; + +export const TEST_SIWE_PARAMS = { + domain: 'localhost', + uri: 'http://localhost:3000', +};