diff --git a/.gitignore b/.gitignore index b1a2343601..c7ca55f8fc 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ yalc.lock package-lock.json docs/ +.cursor-docs/ diff --git a/pkg/auth/.eslintrc.cjs b/pkg/auth/.eslintrc.cjs new file mode 100644 index 0000000000..73d3ddcbfa --- /dev/null +++ b/pkg/auth/.eslintrc.cjs @@ -0,0 +1,9 @@ +module.exports = { + extends: ['../../.eslintrc'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname, + }, +}; + diff --git a/pkg/auth/README.md b/pkg/auth/README.md new file mode 100644 index 0000000000..71193acdf4 --- /dev/null +++ b/pkg/auth/README.md @@ -0,0 +1,162 @@ +# @imtbl/auth + +Minimal OAuth-based authentication package for Immutable Passport. Provides a thin wrapper around `oidc-client-ts` for OAuth/OIDC authentication flows. + +## Installation + +```bash +npm install @imtbl/auth +# or +pnpm add @imtbl/auth +# or +yarn add @imtbl/auth +``` + +## Quick Start + +```typescript +import { Auth } from '@imtbl/auth'; + +const auth = new Auth({ + clientId: 'your-client-id', + redirectUri: 'https://your-app.com/callback', +}); + +// Login with popup +const user = await auth.loginPopup(); +console.log(user?.profile.email); +console.log(user?.access_token); + +// Get current user +const currentUser = await auth.getUser(); + +// Logout +await auth.logout(); +``` + +## Usage + +### Popup Flow + +```typescript +const auth = new Auth({ + clientId: 'your-client-id', + redirectUri: 'https://your-app.com/callback', +}); + +const user = await auth.loginPopup(); +if (user) { + console.log(user.access_token); + console.log(user.id_token); + console.log(user.profile.email); +} +``` + +### Redirect Flow + +```typescript +// On your login page +await auth.loginRedirect(); + +// On your callback page (e.g., /callback) +const user = await auth.handleRedirect(); +if (user) { + console.log(user.access_token); +} +``` + +### Direct Login Methods + +```typescript +// Google login +await auth.loginPopup({ directLoginMethod: 'google' }); + +// Apple login +await auth.loginPopup({ directLoginMethod: 'apple' }); + +// Email login +await auth.loginPopup({ + directLoginMethod: 'email', + email: 'user@example.com', +}); +``` + +### Token Management + +```typescript +const user = await auth.getUser(); + +if (user) { + // Check if token is expired + if (user.expired) { + await auth.refreshToken(); + } + + // Access tokens directly + const accessToken = user.access_token; + const idToken = user.id_token; + const refreshToken = user.refresh_token; +} +``` + +## API Reference + +### `Auth` + +#### Constructor + +```typescript +new Auth(config: AuthConfig) +``` + +**Config Options:** +- `clientId` (required): OAuth client ID +- `redirectUri` (required): OAuth redirect URI +- `popupRedirectUri` (optional): Custom popup redirect URI (defaults to `redirectUri`) +- `logoutRedirectUri` (optional): Custom logout redirect URI +- `scope` (optional): OAuth scope (defaults to `'openid profile email'`) + +#### Methods + +- `loginPopup(options?: LoginOptions): Promise` - Login with popup window +- `loginRedirect(options?: LoginOptions): Promise` - Login with redirect flow +- `handleRedirect(): Promise` - Handle OAuth callback after redirect +- `getUser(): Promise` - Get current authenticated user +- `logout(): Promise` - Logout with redirect +- `logoutSilent(): Promise` - Logout silently (without redirect) +- `refreshToken(): Promise` - Refresh access token if expired + +### Types + +- `User` - OIDC user object from `oidc-client-ts` (includes `id_token`, `access_token`, `refresh_token`, `profile`, `expired`, `expires_at`, `scope`, etc.) +- `AuthConfig` - Configuration options +- `LoginOptions` - Login options: + - `directLoginMethod?: string` - Direct login method (`'google'`, `'apple'`, `'email'`) + - `email?: string` - Email address (required when `directLoginMethod` is `'email'`) + - `marketingConsent?: 'opted_in' | 'unsubscribed'` - Marketing consent status + +## Integration with Wallet Package + +The auth package can be used standalone or passed to the wallet package for automatic authentication: + +```typescript +import { Auth } from '@imtbl/auth'; +import { connectWallet } from '@imtbl/wallet'; + +const auth = new Auth({ clientId: '...', redirectUri: '...' }); + +// Pass auth client - login handled automatically when needed +const provider = await connectWallet({ auth }); + +// User will be prompted to login automatically when required +const accounts = await provider.request({ method: 'eth_requestAccounts' }); +``` + +## Storage + +- **Browser**: Uses `localStorage` for token storage +- **SSR**: Uses `InMemoryWebStorage` (tokens not persisted) + +## License + +Apache-2.0 diff --git a/pkg/auth/package.json b/pkg/auth/package.json new file mode 100644 index 0000000000..c379ac31f9 --- /dev/null +++ b/pkg/auth/package.json @@ -0,0 +1,67 @@ +{ + "name": "@imtbl/auth", + "description": "Minimal authentication package for Immutable Passport", + "version": "0.0.0", + "author": "Immutable", + "bugs": "https://github.com/immutable/ts-immutable-sdk/issues", + "dependencies": { + "oidc-client-ts": "3.3.0" + }, + "devDependencies": { + "@swc/core": "^1.3.36", + "@swc/jest": "^0.2.37", + "@types/jest": "^29.4.3", + "@types/node": "^18.14.2", + "@typescript-eslint/eslint-plugin": "^5.57.1", + "@typescript-eslint/parser": "^5.57.1", + "eslint": "^8.40.0", + "jest": "^29.4.3", + "jest-environment-jsdom": "^29.4.3", + "prettier": "^2.8.7", + "ts-node": "^10.9.1", + "tsup": "8.3.0", + "typescript": "^5.6.2" + }, + "engines": { + "node": ">=20.11.0" + }, + "exports": { + "development": { + "types": "./src/index.ts", + "browser": "./dist/browser/index.js", + "require": "./dist/node/index.cjs", + "default": "./dist/node/index.js" + }, + "default": { + "types": "./dist/types/index.d.ts", + "browser": "./dist/browser/index.js", + "require": "./dist/node/index.cjs", + "default": "./dist/node/index.js" + } + }, + "files": [ + "dist" + ], + "homepage": "https://github.com/immutable/ts-immutable-sdk#readme", + "license": "Apache-2.0", + "main": "dist/node/index.cjs", + "module": "dist/node/index.js", + "browser": "dist/browser/index.js", + "publishConfig": { + "access": "public" + }, + "repository": "immutable/ts-immutable-sdk.git", + "scripts": { + "build": "pnpm transpile && pnpm typegen", + "transpile": "tsup src/index.ts --config ../../tsup.config.js", + "typegen": "tsc --customConditions default --emitDeclarationOnly --outDir dist/types", + "pack:root": "pnpm pack --pack-destination $(dirname $(pnpm root -w))", + "lint": "eslint ./src --ext .ts,.jsx,.tsx --max-warnings=0", + "test": "jest", + "test:watch": "jest --watch", + "typecheck": "tsc --customConditions default --noEmit --jsx preserve" + }, + "type": "module", + "types": "./dist/types/index.d.ts" +} + diff --git a/pkg/auth/src/index.ts b/pkg/auth/src/index.ts new file mode 100644 index 0000000000..090854974c --- /dev/null +++ b/pkg/auth/src/index.ts @@ -0,0 +1,182 @@ +import { + UserManager, + UserManagerSettings, + User, + WebStorageStateStore, + InMemoryWebStorage, +} from 'oidc-client-ts'; + +export type { User } from 'oidc-client-ts'; + +/** + * Authentication configuration + */ +export interface AuthConfig { + /** OAuth client ID (required) */ + clientId: string; + /** OAuth redirect URI */ + redirectUri: string; + /** Optional popup redirect URI (defaults to redirectUri) */ + popupRedirectUri?: string; + /** Optional logout redirect URI */ + logoutRedirectUri?: string; + /** OAuth scope (defaults to 'openid profile email') */ + scope?: string; +} + +/** + * Login options + */ +export interface LoginOptions { + /** Direct login method (e.g., 'google', 'apple', 'email') */ + directLoginMethod?: string; + /** Email address (required when directLoginMethod is 'email') */ + email?: string; + /** Marketing consent status */ + marketingConsent?: 'opted_in' | 'unsubscribed'; +} + +function buildUserManagerSettings(config: AuthConfig): UserManagerSettings { + const authDomain = 'https://auth.immutable.com'; + + // Use localStorage in browser, InMemoryWebStorage for SSR + const store: Storage = typeof window !== 'undefined' + ? window.localStorage + : new InMemoryWebStorage(); + + const userStore = new WebStorageStateStore({ store }); + + // Build logout endpoint + const logoutEndpoint = '/v2/logout'; + const endSessionEndpoint = new URL(logoutEndpoint, authDomain.replace(/^(?:https?:\/\/)?(.*)/, 'https://$1')); + endSessionEndpoint.searchParams.set('client_id', config.clientId); + if (config.logoutRedirectUri) { + endSessionEndpoint.searchParams.set('returnTo', config.logoutRedirectUri); + } + + const settings: UserManagerSettings = { + authority: authDomain, + client_id: config.clientId, + redirect_uri: config.redirectUri, + popup_redirect_uri: config.popupRedirectUri || config.redirectUri, + scope: config.scope || 'openid profile email', + userStore, + metadata: { + authorization_endpoint: `${authDomain}/authorize`, + token_endpoint: `${authDomain}/oauth/token`, + userinfo_endpoint: `${authDomain}/userinfo`, + end_session_endpoint: endSessionEndpoint.toString(), + revocation_endpoint: `${authDomain}/oauth/revoke`, + }, + mergeClaimsStrategy: { array: 'merge' }, + automaticSilentRenew: false, + revokeTokenTypes: ['refresh_token'], + extraQueryParams: { audience: 'platform_api' }, + }; + + return settings; +} + +function buildExtraQueryParams(options?: LoginOptions): Record { + const params: Record = {}; + + if (options?.directLoginMethod) { + params.direct = options.directLoginMethod; + if (options.directLoginMethod === 'email' && options.email) { + params.email = options.email; + } + } + + if (options?.marketingConsent) { + params.marketingConsent = options.marketingConsent; + } + + return params; +} + +/** + * OAuth-based authentication client for Immutable Passport + */ +export class Auth { + private userManager: UserManager; + + constructor(config: AuthConfig) { + if (!config.clientId) { + throw new Error('clientId is required'); + } + if (!config.redirectUri) { + throw new Error('redirectUri is required'); + } + + this.userManager = new UserManager(buildUserManagerSettings(config)); + } + + async loginPopup(options?: LoginOptions): Promise { + try { + await this.userManager.clearStaleState(); + + const extraQueryParams = buildExtraQueryParams(options); + const oidcUser = await this.userManager.signinPopup({ + extraQueryParams, + popupWindowFeatures: { + width: 410, + height: 450, + }, + }); + + return oidcUser; + } catch (error) { + // Return null if popup was closed by user + if (error instanceof Error && error.message.includes('Popup closed')) { + return null; + } + throw error; + } + } + + async loginRedirect(options?: LoginOptions): Promise { + await this.userManager.clearStaleState(); + + const extraQueryParams = buildExtraQueryParams(options); + await this.userManager.signinRedirect({ extraQueryParams }); + } + + async handleRedirect(): Promise { + const oidcUser = await this.userManager.signinCallback(); + return oidcUser || null; + } + + async getUser(): Promise { + try { + const oidcUser = await this.userManager.getUser(); + if (!oidcUser || oidcUser.expired) { + return null; + } + return oidcUser; + } catch (error) { + if (error instanceof Error && error.message.includes('user not found')) { + return null; + } + throw error; + } + } + + async logoutRedirect(): Promise { + await this.userManager.signoutRedirect(); + } + + async logoutSilent(): Promise { + await this.userManager.signoutSilent(); + } + + async refreshToken(): Promise { + const oidcUser = await this.userManager.getUser(); + if (!oidcUser) { + throw new Error('No user to refresh'); + } + + if (oidcUser.expired) { + await this.userManager.signinSilent(); + } + } +} diff --git a/pkg/auth/tsconfig.json b/pkg/auth/tsconfig.json new file mode 100644 index 0000000000..b97dfb1ce4 --- /dev/null +++ b/pkg/auth/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDirs": ["src"], + "customConditions": ["development"] + }, + "include": ["src"], + "exclude": [ + "node_modules", + "dist" + ] +} + diff --git a/pkg/auth/typedoc.json b/pkg/auth/typedoc.json new file mode 100644 index 0000000000..a2a44eaecf --- /dev/null +++ b/pkg/auth/typedoc.json @@ -0,0 +1,6 @@ +{ + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "name": "auth" +} + diff --git a/pkg/wallet/.eslintrc.cjs b/pkg/wallet/.eslintrc.cjs new file mode 100644 index 0000000000..73d3ddcbfa --- /dev/null +++ b/pkg/wallet/.eslintrc.cjs @@ -0,0 +1,9 @@ +module.exports = { + extends: ['../../.eslintrc'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname, + }, +}; + diff --git a/pkg/wallet/README.md b/pkg/wallet/README.md new file mode 100644 index 0000000000..cd87abc452 --- /dev/null +++ b/pkg/wallet/README.md @@ -0,0 +1,212 @@ +# @imtbl/wallet + +EIP-1193 compatible wallet provider for Immutable zkEVM. Works standalone (zero-config) or with authentication for enhanced features. + +## Installation + +```bash +npm install @imtbl/wallet +# or +pnpm add @imtbl/wallet +# or +yarn add @imtbl/wallet +``` + +## Quick Start + +### Zero Config (Standalone) + +```typescript +import { connectWallet } from '@imtbl/wallet'; + +// Works immediately - uses default Immutable chains +const provider = await connectWallet(); + +// Use with any EIP-1193 compatible library +const accounts = await provider.request({ method: 'eth_requestAccounts' }); +``` + +### With Authentication + +```typescript +import { Auth } from '@imtbl/auth'; +import { connectWallet } from '@imtbl/wallet'; + +const auth = new Auth({ clientId: '...', redirectUri: '...' }); + +// Pass auth client - login handled automatically when needed +const provider = await connectWallet({ auth }); + +// User will be prompted to login automatically +const accounts = await provider.request({ method: 'eth_requestAccounts' }); +``` + +## Usage Examples + +### With viem + +```typescript +import { connectWallet } from '@imtbl/wallet'; +import { createWalletClient, custom } from 'viem'; + +const provider = await connectWallet(); +const client = createWalletClient({ + transport: custom(provider), +}); + +const accounts = await client.requestAddresses(); +const balance = await client.getBalance({ address: accounts[0] }); +``` + +### With ethers + +```typescript +import { connectWallet } from '@imtbl/wallet'; +import { BrowserProvider } from 'ethers'; + +const provider = await connectWallet(); +const ethersProvider = new BrowserProvider(provider); + +const signer = await ethersProvider.getSigner(); +const address = await signer.getAddress(); +const balance = await ethersProvider.getBalance(address); +``` + +### Custom Chain Configuration + +```typescript +const provider = await connectWallet({ + chains: [{ + chainId: 13371, + rpcUrl: 'https://rpc.immutable.com', + relayerUrl: 'https://api.immutable.com/relayer-mr', + apiUrl: 'https://api.immutable.com', + name: 'Immutable zkEVM', + }], + initialChainId: 13371, +}); +``` + +### Sending Transactions + +```typescript +const provider = await connectWallet({ auth }); + +// Request accounts (triggers login if needed) +const accounts = await provider.request({ method: 'eth_requestAccounts' }); + +// Send transaction +const txHash = await provider.request({ + method: 'eth_sendTransaction', + params: [{ + from: accounts[0], + to: '0x...', + value: '0x0', + data: '0x...', + }], +}); +``` + +### Signing Messages + +```typescript +// Sign personal message (ERC-191) +const signature = await provider.request({ + method: 'personal_sign', + params: ['0x48656c6c6f20576f726c64', accounts[0]], +}); + +// Sign typed data (EIP-712) +const typedDataSignature = await provider.request({ + method: 'eth_signTypedData_v4', + params: [accounts[0], { + domain: { name: 'MyApp', version: '1', chainId: 13371 }, + types: { Message: [{ name: 'content', type: 'string' }] }, + message: { content: 'Hello World' }, + }], +}); +``` + +## API Reference + +### `connectWallet` + +```typescript +function connectWallet(config?: WalletConfig): Promise +``` + +Creates and returns an EIP-1193 compatible provider. + +**Config Options:** +- `chains?: ChainConfig[]` - Chain configurations (defaults to Immutable testnet + mainnet) +- `initialChainId?: number` - Initial chain ID (defaults to first chain) +- `auth?: Auth` - Auth client for automatic login +- `popupOverlayOptions?: { disableGenericPopupOverlay?: boolean; disableBlockedPopupOverlay?: boolean }` - Overlay options +- `announceProvider?: boolean` - Announce via EIP-6963 (default: `true`) + +### `Provider` (EIP-1193) + +The returned provider implements the standard EIP-1193 interface: + +```typescript +interface Provider { + request(args: RequestArguments): Promise; + on(event: string, listener: (...args: any[]) => void): void; + removeListener(event: string, listener: (...args: any[]) => void): void; +} +``` + +**Supported Methods:** +- `eth_requestAccounts` - Request wallet connection (triggers login if needed) +- `eth_accounts` - Get connected accounts +- `eth_sendTransaction` - Send transaction +- `eth_sign` - Sign message (deprecated, use `personal_sign`) +- `personal_sign` - Sign personal message (ERC-191) +- `eth_signTypedData_v4` - Sign typed data (EIP-712) +- `eth_chainId` - Get current chain ID +- `wallet_switchEthereumChain` - Switch chain +- `eth_getBalance` - Get balance +- `eth_call` - Call contract +- `eth_getTransactionReceipt` - Get transaction receipt +- `im_signEjectionTransaction` - Sign ejection transaction (wallet recovery) +- `im_addSessionActivity` - Add session activity (analytics) + +**Events:** +- `accountsChanged` - Emitted when accounts change + +## Architecture + +### Zero Config Mode + +When no auth client is provided, the wallet works in read-only mode: +- Can query chain state (`eth_call`, `eth_getBalance`, etc.) +- Cannot send transactions or sign messages +- Login is required for signing operations + +### Authenticated Mode + +When an auth client is provided: +- Login is triggered automatically when signing operations are requested +- Magic TEE signer is initialized automatically +- Wallet address is registered automatically on first use +- Full transaction and signing capabilities available + +### Meta-Transactions + +All transactions are sent as meta-transactions through Immutable's relayer: +1. Transaction is validated by Guardian API +2. Transaction is signed with Sequence signature format +3. Transaction is submitted via relayer +4. Relayer handles gas fees (paid in IMX token) + +## EIP-1193 Compatibility + +The provider is fully EIP-1193 compatible and works with: +- **viem** - `createWalletClient({ transport: custom(provider) })` +- **ethers** - `new BrowserProvider(provider)` +- **web3.js** - `new Web3(provider)` +- Any library that supports EIP-1193 + +## License + +Apache-2.0 diff --git a/pkg/wallet/jest.config.ts b/pkg/wallet/jest.config.ts new file mode 100644 index 0000000000..199283cca3 --- /dev/null +++ b/pkg/wallet/jest.config.ts @@ -0,0 +1,25 @@ +import type { Config } from 'jest'; +import { execSync } from 'child_process'; +import { name } from './package.json'; + +const rootDirs = execSync(`pnpm --filter ${name}... exec pwd`) + .toString() + .split('\n') + .filter(Boolean) + .map((dir) => `${dir}/dist`); + +const config: Config = { + clearMocks: true, + roots: ['/src', ...rootDirs], + coverageProvider: 'v8', + moduleDirectories: ['node_modules', 'src'], + moduleNameMapper: { '^@imtbl/(.*)$': '/../../node_modules/@imtbl/$1/src' }, + testEnvironment: 'jsdom', + transform: { + '^.+\\.(t|j)sx?$': '@swc/jest', + }, + transformIgnorePatterns: [], +}; + +export default config; + diff --git a/pkg/wallet/package.json b/pkg/wallet/package.json new file mode 100644 index 0000000000..faba17f3ff --- /dev/null +++ b/pkg/wallet/package.json @@ -0,0 +1,68 @@ +{ + "name": "@imtbl/wallet", + "description": "Minimal wallet package for Immutable zkEVM - EIP-1193 compatible provider", + "version": "0.0.0", + "author": "Immutable", + "bugs": "https://github.com/immutable/ts-immutable-sdk/issues", + "dependencies": { + "@imtbl/auth": "workspace:*", + "viem": "^2.39.0" + }, + "devDependencies": { + "@swc/core": "^1.3.36", + "@swc/jest": "^0.2.37", + "@types/jest": "^29.4.3", + "@types/node": "^18.14.2", + "@typescript-eslint/eslint-plugin": "^5.57.1", + "@typescript-eslint/parser": "^5.57.1", + "eslint": "^8.40.0", + "jest": "^29.4.3", + "jest-environment-jsdom": "^29.4.3", + "prettier": "^2.8.7", + "ts-node": "^10.9.1", + "tsup": "8.3.0", + "typescript": "^5.6.2" + }, + "engines": { + "node": ">=20.11.0" + }, + "exports": { + "development": { + "types": "./src/index.ts", + "browser": "./dist/browser/index.js", + "require": "./dist/node/index.cjs", + "default": "./dist/node/index.js" + }, + "default": { + "types": "./dist/types/index.d.ts", + "browser": "./dist/browser/index.js", + "require": "./dist/node/index.cjs", + "default": "./dist/node/index.js" + } + }, + "files": [ + "dist" + ], + "homepage": "https://github.com/immutable/ts-immutable-sdk#readme", + "license": "Apache-2.0", + "main": "dist/node/index.cjs", + "module": "dist/node/index.js", + "browser": "dist/browser/index.js", + "publishConfig": { + "access": "public" + }, + "repository": "immutable/ts-immutable-sdk.git", + "scripts": { + "build": "pnpm transpile && pnpm typegen", + "transpile": "tsup src/index.ts --config ../../tsup.config.js", + "typegen": "tsc --customConditions default --emitDeclarationOnly --outDir dist/types", + "pack:root": "pnpm pack --pack-destination $(dirname $(pnpm root -w))", + "lint": "eslint ./src --ext .ts,.jsx,.tsx --max-warnings=0", + "test": "jest", + "test:watch": "jest --watch", + "typecheck": "tsc --customConditions default --noEmit --jsx preserve" + }, + "type": "module", + "types": "./dist/types/index.d.ts" +} + diff --git a/pkg/wallet/src/api.ts b/pkg/wallet/src/api.ts new file mode 100644 index 0000000000..b148dfeea9 --- /dev/null +++ b/pkg/wallet/src/api.ts @@ -0,0 +1,72 @@ +/** + * Minimal API client for Passport APIs + */ + +import type { User } from '@imtbl/auth'; +import { getEip155ChainId } from './utils/chain'; +import { authenticatedFetch } from './utils/http-client'; + +export interface ApiClientConfig { + apiUrl: string; +} + +/** + * Minimal API client + */ +export class ApiClient { + private config: ApiClientConfig; + + constructor(config: ApiClientConfig) { + this.config = config; + } + + /** + * Lists available chains + */ + async listChains(): Promise> { + const data = await authenticatedFetch<{ result?: Array<{ id: string; name: string }> }>( + `${this.config.apiUrl}/v1/chains`, + ); + return data.result || []; + } + + /** + * Creates a counterfactual address (registers user) + */ + async createCounterfactualAddress( + chainName: string, + ethereumAddress: string, + ethereumSignature: string, + user: User, + ): Promise { + // User is guaranteed to be authenticated when this is called + // (ensured by ensureAuthenticated() in provider) + // Trust provider - use access_token directly + + const data = await authenticatedFetch<{ counterfactual_address: string }>( + `${this.config.apiUrl}/v2/passport/${chainName}/counterfactual-address`, + { + method: 'POST', + body: { + ethereum_address: ethereumAddress, + ethereum_signature: ethereumSignature, + }, + token: user.access_token, + }, + ); + return data.counterfactual_address; + } + + /** + * Gets chain name from chain ID + */ + async getChainName(chainId: number): Promise { + const chains = await this.listChains(); + const eipChainId = getEip155ChainId(chainId); + const chain = chains.find((c) => c.id === eipChainId); + if (!chain) { + throw new Error(`Chain ${chainId} not found`); + } + return chain.name; + } +} diff --git a/pkg/wallet/src/confirmation/confirmation.ts b/pkg/wallet/src/confirmation/confirmation.ts new file mode 100644 index 0000000000..6920f876b3 --- /dev/null +++ b/pkg/wallet/src/confirmation/confirmation.ts @@ -0,0 +1,287 @@ +/** + * Confirmation screen component + * Handles popup windows for transaction and message confirmations + */ + +import { + ConfirmationResult, + PASSPORT_CONFIRMATION_EVENT_TYPE, + ConfirmationReceiveMessage, + ConfirmationSendMessage, + MessageType, +} from './types'; +import { openPopupCenter } from './popup'; +import { ConfirmationOverlay, type PopupOverlayOptions } from './overlay'; + +const CONFIRMATION_WINDOW_TITLE = 'Confirm this transaction'; +const CONFIRMATION_WINDOW_HEIGHT = 720; +const CONFIRMATION_WINDOW_WIDTH = 480; +const CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION = 1000; + +type MessageHandler = (arg0: MessageEvent) => void; + +export interface ConfirmationScreenConfig { + /** Passport domain for confirmation URLs */ + passportDomain: string; + /** Overlay options */ + popupOverlayOptions?: PopupOverlayOptions; + /** Enable cross-SDK bridge mode - skips opening confirmation popups */ + crossSdkBridgeEnabled?: boolean; +} + +/** + * Confirmation screen component + */ +export class ConfirmationScreen { + private config: ConfirmationScreenConfig; + + private confirmationWindow: Window | undefined; + + private popupOptions: { width: number; height: number } | undefined; + + private overlay: ConfirmationOverlay | undefined; + + private overlayClosed: boolean; + + private timer: ReturnType | undefined; + + constructor(config: ConfirmationScreenConfig) { + this.config = config; + this.overlayClosed = false; + } + + private getHref(relativePath: string, queryStringParams?: { [key: string]: any }) { + let href = `${this.config.passportDomain}/transaction-confirmation/${relativePath}`; + + if (queryStringParams) { + const queryString = Object.keys(queryStringParams) + .map((key) => `${key}=${queryStringParams[key]}`) + .join('&'); + href = `${href}?${queryString}`; + } + + return href; + } + + /** + * Request confirmation for a transaction + */ + requestConfirmation( + transactionId: string, + etherAddress: string, + chainId: string, + ): Promise { + return new Promise((resolve, reject) => { + const messageHandler = ({ data, origin }: MessageEvent) => { + if ( + origin !== this.config.passportDomain + || data.eventType !== PASSPORT_CONFIRMATION_EVENT_TYPE + ) { + return; + } + + switch (data.messageType as ConfirmationReceiveMessage) { + case ConfirmationReceiveMessage.CONFIRMATION_WINDOW_READY: { + this.confirmationWindow?.postMessage({ + eventType: PASSPORT_CONFIRMATION_EVENT_TYPE, + messageType: ConfirmationSendMessage.CONFIRMATION_START, + }, this.config.passportDomain); + break; + } + case ConfirmationReceiveMessage.TRANSACTION_CONFIRMED: { + this.closeWindow(); + resolve({ confirmed: true }); + break; + } + case ConfirmationReceiveMessage.TRANSACTION_REJECTED: { + this.closeWindow(); + resolve({ confirmed: false }); + break; + } + case ConfirmationReceiveMessage.TRANSACTION_ERROR: { + this.closeWindow(); + reject(new Error('Error during transaction confirmation')); + break; + } + default: + this.closeWindow(); + reject(new Error('Unsupported message type')); + } + }; + + const href = this.getHref('zkevm/transaction', { + transactionID: transactionId, + etherAddress, + chainType: 'evm', + chainID: chainId, + }); + window.addEventListener('message', messageHandler); + this.showConfirmationScreen(href, messageHandler, resolve); + }); + } + + /** + * Request confirmation for a message + */ + requestMessageConfirmation( + messageID: string, + etherAddress: string, + messageType?: MessageType, + ): Promise { + return new Promise((resolve, reject) => { + const messageHandler = ({ data, origin }: MessageEvent) => { + if ( + origin !== this.config.passportDomain + || data.eventType !== PASSPORT_CONFIRMATION_EVENT_TYPE + ) { + return; + } + switch (data.messageType as ConfirmationReceiveMessage) { + case ConfirmationReceiveMessage.CONFIRMATION_WINDOW_READY: { + this.confirmationWindow?.postMessage({ + eventType: PASSPORT_CONFIRMATION_EVENT_TYPE, + messageType: ConfirmationSendMessage.CONFIRMATION_START, + }, this.config.passportDomain); + break; + } + case ConfirmationReceiveMessage.MESSAGE_CONFIRMED: { + this.closeWindow(); + resolve({ confirmed: true }); + break; + } + case ConfirmationReceiveMessage.MESSAGE_REJECTED: { + this.closeWindow(); + resolve({ confirmed: false }); + break; + } + case ConfirmationReceiveMessage.MESSAGE_ERROR: { + this.closeWindow(); + reject(new Error('Error during message confirmation')); + break; + } + default: + this.closeWindow(); + reject(new Error('Unsupported message type')); + } + }; + + window.addEventListener('message', messageHandler); + const href = this.getHref('zkevm/message', { + messageID, + etherAddress, + ...(messageType ? { messageType } : {}), + }); + this.showConfirmationScreen(href, messageHandler, resolve); + }); + } + + /** + * Show loading screen + */ + loading(popupOptions?: { width: number; height: number }) { + if (this.config.crossSdkBridgeEnabled) { + // Skip opening confirmation window if cross-SDK bridge is enabled + return; + } + + this.popupOptions = popupOptions; + + try { + this.confirmationWindow = openPopupCenter({ + url: this.getHref('loading'), + title: CONFIRMATION_WINDOW_TITLE, + width: popupOptions?.width || CONFIRMATION_WINDOW_WIDTH, + height: popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT, + }); + this.overlay = new ConfirmationOverlay( + this.config.popupOverlayOptions || {}, + false, + ); + } catch (error) { + // If an error is thrown here then the popup is blocked + this.overlay = new ConfirmationOverlay( + this.config.popupOverlayOptions || {}, + true, + ); + } + + this.overlay.append( + () => { + try { + this.confirmationWindow?.close(); + this.confirmationWindow = openPopupCenter({ + url: this.getHref('loading'), + title: CONFIRMATION_WINDOW_TITLE, + width: this.popupOptions?.width || CONFIRMATION_WINDOW_WIDTH, + height: this.popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT, + }); + } catch { /* Empty */ } + }, + () => { + this.overlayClosed = true; + this.closeWindow(); + }, + ); + } + + /** + * Close the confirmation window + */ + closeWindow() { + this.confirmationWindow?.close(); + this.overlay?.remove(); + this.overlay = undefined; + } + + /** + * Show confirmation screen + */ + private showConfirmationScreen(href: string, messageHandler: MessageHandler, resolve: Function) { + // If popup blocked, the confirmation window will not exist + if (this.confirmationWindow) { + this.confirmationWindow.location.href = href; + } + + // This indicates the user closed the overlay so the transaction should be rejected + if (!this.overlay) { + this.overlayClosed = false; + resolve({ confirmed: false }); + return; + } + + // Poll for window close + const timerCallback = () => { + if (this.confirmationWindow?.closed || this.overlayClosed) { + clearInterval(this.timer); + window.removeEventListener('message', messageHandler); + resolve({ confirmed: false }); + this.overlayClosed = false; + this.confirmationWindow = undefined; + } + }; + this.timer = setInterval( + timerCallback, + CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION, + ); + this.overlay.update(() => this.recreateConfirmationWindow(href, timerCallback)); + } + + /** + * Recreate confirmation window + */ + private recreateConfirmationWindow(href: string, timerCallback: () => void) { + try { + // Clears and recreates the timer to ensure when the confirmation window + // is closed and recreated the transaction is not rejected. + clearInterval(this.timer); + this.confirmationWindow?.close(); + this.confirmationWindow = openPopupCenter({ + url: href, + title: CONFIRMATION_WINDOW_TITLE, + width: this.popupOptions?.width || CONFIRMATION_WINDOW_WIDTH, + height: this.popupOptions?.height || CONFIRMATION_WINDOW_HEIGHT, + }); + this.timer = setInterval(timerCallback, CONFIRMATION_WINDOW_CLOSED_POLLING_DURATION); + } catch { /* Empty */ } + } +} diff --git a/pkg/wallet/src/confirmation/overlay.ts b/pkg/wallet/src/confirmation/overlay.ts new file mode 100644 index 0000000000..8a639a1e81 --- /dev/null +++ b/pkg/wallet/src/confirmation/overlay.ts @@ -0,0 +1,314 @@ +/** + * Confirmation overlay component + * Shows overlay when popup is blocked or needs help + */ + +export interface PopupOverlayOptions { + disableGenericPopupOverlay?: boolean; + disableBlockedPopupOverlay?: boolean; +} + +const PASSPORT_OVERLAY_ID = 'passport-overlay'; +const PASSPORT_OVERLAY_CONTENTS_ID = 'passport-overlay-contents'; +const PASSPORT_OVERLAY_CLOSE_ID = `${PASSPORT_OVERLAY_ID}-close`; +const PASSPORT_OVERLAY_TRY_AGAIN_ID = `${PASSPORT_OVERLAY_ID}-try-again`; + +/* eslint-disable max-len */ +const CLOSE_BUTTON_SVG = ` + + + +`; + +const POPUP_BLOCKED_SVG = ` + + + +`; + +const IMMUTABLE_LOGO_SVG = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; + +const getCloseButton = (): string => ` + +`; + +const getTryAgainButton = () => ` + +`; + +const getBlockedContents = () => ` + ${IMMUTABLE_LOGO_SVG} +
+ ${POPUP_BLOCKED_SVG} + Pop-up blocked +
+

+ Please try again below.
+ If the problem continues, adjust your
+ browser settings. +

+ ${getTryAgainButton()} +`; + +const getGenericContents = () => ` + ${IMMUTABLE_LOGO_SVG} +

+ Secure pop-up not showing?
We'll help you re-launch +

+ ${getTryAgainButton()} +`; + +const getOverlay = (contents: string): string => ` +
+ ${getCloseButton()} +
+ ${contents ?? ''} +
+
+`; + +export const getBlockedOverlay = () => getOverlay(getBlockedContents()); +export const getGenericOverlay = () => getOverlay(getGenericContents()); + +/** + * Confirmation overlay component + */ +export class ConfirmationOverlay { + private disableGenericPopupOverlay: boolean; + + private disableBlockedPopupOverlay: boolean; + + private overlay: HTMLDivElement | undefined; + + private isBlockedOverlay: boolean; + + private tryAgainListener: (() => void) | undefined; + + private onCloseListener: (() => void) | undefined; + + constructor(popupOverlayOptions: PopupOverlayOptions, isBlockedOverlay: boolean = false) { + this.disableBlockedPopupOverlay = popupOverlayOptions.disableBlockedPopupOverlay || false; + this.disableGenericPopupOverlay = popupOverlayOptions.disableGenericPopupOverlay || false; + this.isBlockedOverlay = isBlockedOverlay; + } + + append(tryAgainOnClick: () => void, onCloseClick: () => void) { + if (this.isBlockedOverlay && this.disableBlockedPopupOverlay) { + return; + } + if (!this.isBlockedOverlay && this.disableGenericPopupOverlay) { + return; + } + + this.tryAgainListener = tryAgainOnClick; + this.onCloseListener = onCloseClick; + + const overlayHTML = this.isBlockedOverlay ? getBlockedOverlay() : getGenericOverlay(); + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = overlayHTML; + this.overlay = tempDiv.firstChild as HTMLDivElement; + + document.body.appendChild(this.overlay); + + // Attach event listeners + const closeButton = document.getElementById(PASSPORT_OVERLAY_CLOSE_ID); + const tryAgainButton = document.getElementById(PASSPORT_OVERLAY_TRY_AGAIN_ID); + + if (closeButton) { + closeButton.addEventListener('click', () => { + this.remove(); + if (this.onCloseListener) { + this.onCloseListener(); + } + }); + } + + if (tryAgainButton) { + tryAgainButton.addEventListener('click', () => { + if (this.tryAgainListener) { + this.tryAgainListener(); + } + }); + } + } + + // eslint-disable-next-line class-methods-use-this + update(tryAgainOnClick: () => void) { + const tryAgainButton = document.getElementById(PASSPORT_OVERLAY_TRY_AGAIN_ID); + if (tryAgainButton) { + tryAgainButton.replaceWith( + Object.assign(document.createElement('button'), { + innerHTML: getTryAgainButton().match(/]*>([\s\S]*?)<\/button>/)![1], + onclick: tryAgainOnClick, + }), + ); + } + } + + remove() { + if (this.overlay) { + this.overlay.remove(); + this.overlay = undefined; + } + } +} diff --git a/pkg/wallet/src/confirmation/popup.ts b/pkg/wallet/src/confirmation/popup.ts new file mode 100644 index 0000000000..c34b36711c --- /dev/null +++ b/pkg/wallet/src/confirmation/popup.ts @@ -0,0 +1,47 @@ +/** + * Popup utility functions + */ + +export interface PopUpProps { + url: string; + title: string; + width: number; + height: number; +} + +/** + * Opens a popup window centered on screen + */ +export const openPopupCenter = ({ + url, + title, + width, + height, +}: PopUpProps): Window => { + const left = Math.max( + 0, + Math.round(window.screenX + (window.outerWidth - width) / 2), + ); + const top = Math.max( + 0, + Math.round(window.screenY + (window.outerHeight - height) / 2), + ); + + const newWindow = window.open( + url, + title, + ` + scrollbars=yes, + width=${width}, + height=${height}, + top=${top}, + left=${left} + `, + ); + if (!newWindow) { + throw new Error('Failed to open confirmation screen'); + } + + newWindow.focus(); + return newWindow; +}; diff --git a/pkg/wallet/src/confirmation/types.ts b/pkg/wallet/src/confirmation/types.ts new file mode 100644 index 0000000000..1eecbe0b1c --- /dev/null +++ b/pkg/wallet/src/confirmation/types.ts @@ -0,0 +1,25 @@ +/** + * Confirmation screen types + */ + +export enum ConfirmationSendMessage { + CONFIRMATION_START = 'confirmation_start', +} + +export enum ConfirmationReceiveMessage { + CONFIRMATION_WINDOW_READY = 'confirmation_window_ready', + TRANSACTION_CONFIRMED = 'transaction_confirmed', + TRANSACTION_ERROR = 'transaction_error', + TRANSACTION_REJECTED = 'transaction_rejected', + MESSAGE_CONFIRMED = 'message_confirmed', + MESSAGE_ERROR = 'message_error', + MESSAGE_REJECTED = 'message_rejected', +} + +export type ConfirmationResult = { + confirmed: boolean; +}; + +export const PASSPORT_CONFIRMATION_EVENT_TYPE = 'imx_passport_confirmation'; + +export type MessageType = 'erc191' | 'eip712'; diff --git a/pkg/wallet/src/eip6963.ts b/pkg/wallet/src/eip6963.ts new file mode 100644 index 0000000000..9be998a0d1 --- /dev/null +++ b/pkg/wallet/src/eip6963.ts @@ -0,0 +1,61 @@ +/** + * EIP-6963 Provider Announcement + * Allows dApps to discover Passport wallet automatically + */ + +import type { Provider } from './provider'; + +/** + * EIP-6963 Provider Info + */ +export interface EIP6963ProviderInfo { + icon: `data:image/${string}`; // RFC-2397 + name: string; + rdns: string; + uuid: string; +} + +/** + * EIP-6963 Provider Detail + */ +export interface EIP6963ProviderDetail { + info: EIP6963ProviderInfo; + provider: Provider; +} + +/** + * EIP-6963 Announce Provider Event + */ +export interface EIP6963AnnounceProviderEvent extends CustomEvent { + type: 'eip6963:announceProvider'; +} + +/** + * Passport provider info for EIP-6963 + * UUID is a constant that uniquely identifies Immutable Passport as a provider type + */ +export const passportProviderInfo: EIP6963ProviderInfo = { + // eslint-disable-next-line max-len + icon: 'data:image/svg+xml,', + name: 'Immutable Passport', + rdns: 'com.immutable.passport', + uuid: 'ca329358-25a0-4e81-b162-7a2455e97395', +}; + +/** + * Announces the provider via EIP-6963 + */ +export function announceProvider(detail: EIP6963ProviderDetail): void { + if (typeof window === 'undefined') return; + + const event: CustomEvent = new CustomEvent( + 'eip6963:announceProvider', + { detail: Object.freeze(detail) }, + ) as EIP6963AnnounceProviderEvent; + + window.dispatchEvent(event); + + // Listen for requests and re-announce + const handler = () => window.dispatchEvent(event); + window.addEventListener('eip6963:requestProvider', handler); +} diff --git a/pkg/wallet/src/ejection.ts b/pkg/wallet/src/ejection.ts new file mode 100644 index 0000000000..d8756be428 --- /dev/null +++ b/pkg/wallet/src/ejection.ts @@ -0,0 +1,74 @@ +import { JsonRpcError, RpcErrorCode } from './errors'; +import type { TransactionRequest } from './metatransaction'; +import { buildMetaTransaction } from './metatransaction'; +import { signMetaTransactions } from './sequence'; +import type { Signer } from './signer/signer'; + +/** + * Ejection transaction response + */ +export interface EjectionTransactionResponse { + to: string; + data: string; + chainId: string; +} + +/** + * Prepares and signs ejection transaction + * Ejection transactions are simpler - no fee, no Guardian validation, no relayer + * Just sign the transaction and return it for the user to submit elsewhere + */ +export async function prepareAndSignEjectionTransaction({ + transactionRequest, + ethSigner, + zkEvmAddress, + chainId, +}: { + transactionRequest: TransactionRequest; + ethSigner: Signer; + zkEvmAddress: string; + chainId: number; +}): Promise { + if (!transactionRequest.to || typeof transactionRequest.to !== 'string') { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'im_signEjectionTransaction requires a "to" field', + ); + } + + if (typeof transactionRequest.nonce === 'undefined') { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'im_signEjectionTransaction requires a "nonce" field', + ); + } + + if (!transactionRequest.chainId) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'im_signEjectionTransaction requires a "chainId" field', + ); + } + + // Convert to MetaTransaction (same as normal transactions) + const metaTransaction = buildMetaTransaction(transactionRequest); + const nonce = typeof transactionRequest.nonce === 'number' + ? BigInt(transactionRequest.nonce) + : transactionRequest.nonce!; + const chainIdBigInt = BigInt(chainId); + + // Sign the transaction + const signedTransaction = await signMetaTransactions( + [metaTransaction], + nonce, + chainIdBigInt, + zkEvmAddress, + ethSigner, + ); + + return { + to: zkEvmAddress, + data: signedTransaction, + chainId: `eip155:${chainId}`, + }; +} diff --git a/pkg/wallet/src/errors.ts b/pkg/wallet/src/errors.ts new file mode 100644 index 0000000000..2f4ac20d46 --- /dev/null +++ b/pkg/wallet/src/errors.ts @@ -0,0 +1,27 @@ +/** + * JSON-RPC error codes + * @see https://eips.ethereum.org/EIPS/eip-1193#provider-errors + * @see https://eips.ethereum.org/EIPS/eip-1474#error-codes + */ + +export enum ProviderErrorCode { + UNAUTHORIZED = 4100, + UNSUPPORTED_METHOD = 4200, +} + +export enum RpcErrorCode { + RPC_SERVER_ERROR = -32000, + INVALID_PARAMS = -32602, + INTERNAL_ERROR = -32603, + TRANSACTION_REJECTED = -32003, +} + +export class JsonRpcError extends Error { + public readonly code: ProviderErrorCode | RpcErrorCode; + + constructor(code: ProviderErrorCode | RpcErrorCode, message: string) { + super(message); + this.name = 'JsonRpcError'; + this.code = code; + } +} diff --git a/pkg/wallet/src/guardian.ts b/pkg/wallet/src/guardian.ts new file mode 100644 index 0000000000..62bd2ecba9 --- /dev/null +++ b/pkg/wallet/src/guardian.ts @@ -0,0 +1,303 @@ +/** + * Minimal guardian client for transaction evaluation + * No ethers dependency - uses native fetch + */ + +import type { User } from '@imtbl/auth'; +import { JsonRpcError, RpcErrorCode } from './errors'; +import { getEip155ChainId } from './utils/chain'; +import type { TypedDataPayload } from './types'; +import { ConfirmationScreen } from './confirmation/confirmation'; +import { authenticatedFetch } from './utils/http-client'; + +export interface GuardianClientConfig { + guardianUrl: string; + /** Confirmation screen for showing confirmation UI */ + confirmationScreen: ConfirmationScreen; + /** Enable cross-SDK bridge mode - throws errors instead of showing confirmation popups */ + crossSdkBridgeEnabled: boolean; +} + +/** + * Guardian API response types + */ +interface MessageEvaluationResponse { + confirmationRequired: boolean; + messageId?: string; +} + +interface TransactionEvaluationResponse { + confirmationRequired: boolean; + transactionId?: string; +} + +/** + * Minimal guardian client + */ +export class GuardianClient { + private config: GuardianClientConfig; + + constructor(config: GuardianClientConfig) { + this.config = config; + } + + /** + * Evaluates an ERC-191 message + */ + async evaluateERC191Message(payload: string, walletAddress: string, chainId: number, user: User): Promise { + // User is guaranteed to be authenticated when this is called + // (ensured by ensureAuthenticated() in provider) + // Trust provider - use access_token directly + + let data: MessageEvaluationResponse; + try { + data = await authenticatedFetch( + `${this.config.guardianUrl}/v1/erc191-messages/evaluate`, + { + method: 'POST', + body: { + chainID: getEip155ChainId(chainId), + payload, + }, + token: user.access_token, + }, + ); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : String(error); + throw new JsonRpcError( + RpcErrorCode.INTERNAL_ERROR, + `Message failed to validate: ${errorMessage}`, + ); + } + + // Handle confirmation if required + if (data.confirmationRequired && data.messageId) { + if (this.config.crossSdkBridgeEnabled) { + throw new JsonRpcError( + RpcErrorCode.TRANSACTION_REJECTED, + 'Transaction rejected - confirmation required but cross-SDK bridge mode is enabled', + ); + } + + const confirmed = await this.handleMessageConfirmation( + data.messageId, + walletAddress, + 'erc191', + ); + + if (!confirmed) { + throw new JsonRpcError( + RpcErrorCode.TRANSACTION_REJECTED, + 'Signature rejected by user', + ); + } + } + } + + /** + * Evaluates an EIP-712 message + */ + async evaluateEIP712Message( + payload: TypedDataPayload, + walletAddress: string, + chainId: number, + user: User, + ): Promise { + // User is guaranteed to be authenticated when this is called + // (ensured by ensureAuthenticated() in provider) + // Trust provider - use access_token directly + + let data: MessageEvaluationResponse; + try { + data = await authenticatedFetch( + `${this.config.guardianUrl}/v1/eip712-messages/evaluate`, + { + method: 'POST', + body: { + chainID: getEip155ChainId(chainId), + payload, + }, + token: user.access_token, + }, + ); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : String(error); + throw new JsonRpcError( + RpcErrorCode.INTERNAL_ERROR, + `Message failed to validate: ${errorMessage}`, + ); + } + + // Handle confirmation if required + if (data.confirmationRequired && data.messageId) { + if (this.config.crossSdkBridgeEnabled) { + throw new JsonRpcError( + RpcErrorCode.TRANSACTION_REJECTED, + 'Transaction rejected - confirmation required but cross-SDK bridge mode is enabled', + ); + } + + const confirmed = await this.handleMessageConfirmation( + data.messageId, + walletAddress, + 'eip712', + ); + + if (!confirmed) { + throw new JsonRpcError( + RpcErrorCode.TRANSACTION_REJECTED, + 'Signature rejected by user', + ); + } + } + } + + /** + * Handles message confirmation + */ + private async handleMessageConfirmation( + messageId: string, + walletAddress: string, + messageType: 'erc191' | 'eip712', + ): Promise { + const result = await this.config.confirmationScreen.requestMessageConfirmation( + messageId, + walletAddress, + messageType, + ); + return result.confirmed; + } + + /** + * Handles transaction confirmation + */ + private async handleTransactionConfirmation( + transactionId: string, + walletAddress: string, + chainId: number, + ): Promise { + const result = await this.config.confirmationScreen.requestConfirmation( + transactionId, + walletAddress, + getEip155ChainId(chainId), + ); + return result.confirmed; + } + + /** + * Maps meta-transactions to Guardian API format + * Guardian-specific transformation logic (converts bigint to string for JSON) + */ + // eslint-disable-next-line class-methods-use-this + private mapMetaTransactionsForGuardian( + metaTransactions: Array<{ + target: `0x${string}`; + value: bigint; + data: `0x${string}`; + gasLimit: bigint; + delegateCall: boolean; + revertOnError: boolean; + }>, + ): Array<{ + delegateCall: boolean; + revertOnError: boolean; + gasLimit: string; + target: string; + value: string; + data: string; + }> { + return metaTransactions.map((tx) => ({ + delegateCall: tx.delegateCall, + revertOnError: tx.revertOnError, + gasLimit: tx.gasLimit.toString(), + target: tx.target, + value: tx.value.toString(), + data: tx.data, + })); + } + + /** + * Validates EVM transaction with Guardian API + */ + async validateEVMTransaction({ + chainId, + nonce, + metaTransactions, + walletAddress, + isBackgroundTransaction, + user, + }: { + chainId: string; + nonce: string | bigint; + metaTransactions: Array<{ + target: `0x${string}`; + value: bigint; + data: `0x${string}`; + gasLimit: bigint; + delegateCall: boolean; + revertOnError: boolean; + }>; + walletAddress: string; + isBackgroundTransaction?: boolean; + user: User; + }): Promise { + // User is guaranteed to be authenticated when this is called + // (ensured by ensureAuthenticated() in provider) + // Trust provider - use access_token directly + + // Transform meta-transactions for Guardian API + const guardianTransactions = this.mapMetaTransactionsForGuardian(metaTransactions); + + let data: TransactionEvaluationResponse; + try { + data = await authenticatedFetch( + `${this.config.guardianUrl}/v1/transactions/evm/evaluate`, + { + method: 'POST', + body: { + chainType: 'evm', + chainId, + transactionData: { + nonce: nonce.toString(), + userAddress: walletAddress, + metaTransactions: guardianTransactions, + }, + }, + token: user.access_token, + }, + ); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : String(error); + throw new JsonRpcError( + RpcErrorCode.INTERNAL_ERROR, + `Transaction failed to validate: ${errorMessage}`, + ); + } + + // Handle confirmation if required + if (data.confirmationRequired && data.transactionId) { + if (this.config.crossSdkBridgeEnabled) { + throw new JsonRpcError( + RpcErrorCode.TRANSACTION_REJECTED, + 'Transaction rejected - confirmation required but cross-SDK bridge mode is enabled', + ); + } + + const confirmed = await this.handleTransactionConfirmation( + data.transactionId, + walletAddress, + parseInt(chainId.split(':')[1] || chainId, 10), + ); + + if (!confirmed) { + throw new JsonRpcError( + RpcErrorCode.TRANSACTION_REJECTED, + 'Transaction rejected by user', + ); + } + } else if (!isBackgroundTransaction) { + // Close confirmation screen if not background transaction + this.config.confirmationScreen.closeWindow(); + } + } +} diff --git a/pkg/wallet/src/index.ts b/pkg/wallet/src/index.ts new file mode 100644 index 0000000000..249b2eb048 --- /dev/null +++ b/pkg/wallet/src/index.ts @@ -0,0 +1,68 @@ +import type { Auth } from '@imtbl/auth'; +import type { Provider, ChainConfig } from './provider'; + +import { PassportEVMProvider } from './provider'; +import { announceProvider, passportProviderInfo } from './eip6963'; + +export interface WalletConfig { + /** Chain configurations - if omitted, uses default Immutable chains */ + chains?: ChainConfig[]; + /** Initial chain ID (defaults to first chain) */ + initialChainId?: number; + /** Optional auth client - login/getUser handled automatically */ + auth?: Auth; + /** Optional popup overlay options */ + popupOverlayOptions?: { + disableGenericPopupOverlay?: boolean; + disableBlockedPopupOverlay?: boolean; + }; + /** Announce provider via EIP-6963 (default: true) */ + announceProvider?: boolean; + /** Enable cross-SDK bridge mode - skips confirmation popups, throws errors instead (default: false) */ + crossSdkBridgeEnabled?: boolean; +} + +export type { Signer } from './signer/signer'; + +function getDefaultChains(): ChainConfig[] { + return [ + { + chainId: 13473, + rpcUrl: 'https://rpc.testnet.immutable.com', + relayerUrl: 'https://api.sandbox.immutable.com/relayer-mr', + apiUrl: 'https://api.sandbox.immutable.com', + name: 'Immutable zkEVM Testnet', + }, + { + chainId: 13371, + rpcUrl: 'https://rpc.immutable.com', + relayerUrl: 'https://api.immutable.com/relayer-mr', + apiUrl: 'https://api.immutable.com', + name: 'Immutable zkEVM', + }, + ]; +} + +export async function connectWallet( + config?: WalletConfig, +): Promise { + const chains = config?.chains || getDefaultChains(); + + const provider = new PassportEVMProvider({ + chains, + initialChainId: config?.initialChainId, + authenticatedUser: undefined, + auth: config?.auth, + popupOverlayOptions: config?.popupOverlayOptions, + crossSdkBridgeEnabled: config?.crossSdkBridgeEnabled || false, + }); + + if (config?.announceProvider !== false) { + announceProvider({ + info: passportProviderInfo, + provider, + }); + } + + return provider; +} diff --git a/pkg/wallet/src/metatransaction.ts b/pkg/wallet/src/metatransaction.ts new file mode 100644 index 0000000000..d0868ec11a --- /dev/null +++ b/pkg/wallet/src/metatransaction.ts @@ -0,0 +1,173 @@ +import type { User } from '@imtbl/auth'; +import { toHex, createPublicClient, http } from 'viem'; +import { JsonRpcError, RpcErrorCode } from './errors'; +import type { RelayerClient } from './relayer'; +import type { GuardianClient } from './guardian'; +import type { Signer } from './signer/signer'; +import { getFunctionSelector } from './utils/abi'; +import { signMetaTransactions } from './sequence'; +import { getEip155ChainId } from './utils/chain'; + +export interface MetaTransaction { + gasLimit: bigint; + target: `0x${string}`; + value: bigint; + data: `0x${string}`; + delegateCall: boolean; + revertOnError: boolean; +} + +export interface TransactionRequest { + to: string; + data?: string; + value?: bigint | string; + nonce?: bigint | number; + chainId?: number; +} + +export function buildMetaTransaction( + request: TransactionRequest, +): MetaTransaction { + if (!request.to) { + throw new Error('TransactionRequest.to is required'); + } + + return { + target: request.to as `0x${string}`, + value: typeof request.value === 'string' + ? BigInt(request.value) + : (request.value ?? BigInt(0)), + data: (request.data || '0x') as `0x${string}`, + gasLimit: BigInt(0), + delegateCall: false, + revertOnError: true, + }; +} + +/** + * Gets nonce from smart contract wallet via RPC + * Encodes nonce with space: space in upper 160 bits, nonce in lower 96 bits + * Returns 0 if wallet is not deployed + */ +export async function getNonce( + rpcUrl: string, + smartContractWalletAddress: string, + nonceSpace?: bigint, +): Promise { + const space = nonceSpace || BigInt(0); + const functionSelector = getFunctionSelector('readNonce(uint256)'); + const spaceHex = toHex(space, { size: 32 }); + const data = functionSelector + spaceHex.slice(2); + + try { + const client = createPublicClient({ + transport: http(rpcUrl), + }); + const result = await client.call({ + to: smartContractWalletAddress as `0x${string}`, + data: data as `0x${string}`, + }); + + if (result?.data && result.data !== '0x') { + const nonce = BigInt(result.data); + const shiftedSpace = space * (BigInt(2) ** BigInt(96)); + return nonce + shiftedSpace; + } + + return BigInt(0); + } catch (error: unknown) { + // If wallet is not deployed, RPC call will fail + // Return 0 nonce for undeployed wallets + const errorMessage = error instanceof Error ? error.message : String(error); + if ( + errorMessage.includes('BAD_DATA') + || errorMessage.includes('execution reverted') + || errorMessage.includes('revert') + || errorMessage.includes('invalid opcode') + ) { + return BigInt(0); + } + throw error; + } +} + +/** + * Builds meta-transactions array with fee transaction + * Fetches nonce and fee option in parallel, then builds final transaction array + */ +export async function buildMetaTransactions( + transactionRequest: TransactionRequest, + rpcUrl: string, + relayerClient: RelayerClient, + zkevmAddress: string, + chainId: number, + user: User, + nonceSpace?: bigint, +): Promise<{ transactions: [MetaTransaction, ...MetaTransaction[]]; nonce: bigint }> { + if (!transactionRequest.to || typeof transactionRequest.to !== 'string') { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'eth_sendTransaction requires a "to" field', + ); + } + + const txForFeeEstimation = buildMetaTransaction(transactionRequest); + + const [nonce, feeOption] = await Promise.all([ + getNonce(rpcUrl, zkevmAddress, nonceSpace), + relayerClient.getFeeOption(zkevmAddress, [txForFeeEstimation], chainId, user), + ]); + + const metaTransactions: [MetaTransaction, ...MetaTransaction[]] = [ + buildMetaTransaction(transactionRequest), + ]; + + const feeValue = BigInt(feeOption.tokenPrice); + if (feeValue !== BigInt(0)) { + metaTransactions.push({ + target: feeOption.recipientAddress as `0x${string}`, + value: feeValue, + data: '0x' as `0x${string}`, + gasLimit: BigInt(0), + delegateCall: false, + revertOnError: true, + }); + } + + return { transactions: metaTransactions, nonce }; +} + +/** + * Validates and signs meta-transactions in parallel + * Guardian validation and Sequence signing happen concurrently for performance + */ +export async function validateAndSignTransaction( + metaTransactions: MetaTransaction[], + nonce: bigint, + chainId: bigint, + walletAddress: string, + signer: Signer, + guardianClient: GuardianClient, + user: User, + isBackgroundTransaction: boolean = false, +): Promise { + const [, signedTransactionData] = await Promise.all([ + guardianClient.validateEVMTransaction({ + chainId: getEip155ChainId(Number(chainId)), + nonce, + metaTransactions, + walletAddress, + isBackgroundTransaction, + user, + }), + signMetaTransactions( + metaTransactions, + nonce, + chainId, + walletAddress, + signer, + ), + ]); + + return signedTransactionData; +} diff --git a/pkg/wallet/src/provider.ts b/pkg/wallet/src/provider.ts new file mode 100644 index 0000000000..eac8defd77 --- /dev/null +++ b/pkg/wallet/src/provider.ts @@ -0,0 +1,1113 @@ +/** + * EIP-1193 compatible provider for Immutable zkEVM + */ + +import { type User, Auth } from '@imtbl/auth'; +import { + toHex, fromHex, createPublicClient, http, isAddress, hexToString, +} from 'viem'; +import { JsonRpcError, ProviderErrorCode, RpcErrorCode } from './errors'; +import { getFunctionSelector } from './utils/abi'; +import type { Signer } from './signer/signer'; +import { MagicTEESigner } from './signer/magic'; +import { RelayerClient } from './relayer'; +import { GuardianClient } from './guardian'; +import { ApiClient } from './api'; +import { ConfirmationScreen } from './confirmation/confirmation'; +import { packSignatures } from './sequence'; +import { signERC191Message, signTypedData } from './signer/signing'; +import { buildMetaTransactions, validateAndSignTransaction } from './metatransaction'; +import { prepareAndSignEjectionTransaction } from './ejection'; +import type { TransactionRequest } from './metatransaction'; +import type { TypedDataPayload } from './types'; + +/** + * Simple event emitter for provider events + */ +class SimpleEventEmitter { + private listeners: Map void>> = new Map(); + + on(event: string, listener: (...args: any[]) => void): void { + if (!this.listeners.has(event)) { + this.listeners.set(event, new Set()); + } + this.listeners.get(event)!.add(listener); + } + + removeListener(event: string, listener: (...args: any[]) => void): void { + this.listeners.get(event)?.delete(listener); + } + + emit(event: string, ...args: any[]): void { + this.listeners.get(event)?.forEach((listener) => listener(...args)); + } +} + +/** + * Chain configuration + */ +export interface ChainConfig { + /** Chain ID (e.g., 13371 for mainnet, 13473 for testnet) */ + chainId: number; + /** RPC URL for the chain */ + rpcUrl: string; + /** Relayer URL for transaction submission */ + relayerUrl: string; + /** API URL for Passport APIs (guardian, user registration) */ + apiUrl: string; + /** Chain name (e.g., 'Immutable zkEVM') */ + name: string; +} + +/** + * Provider configuration + */ +export interface ProviderConfig { + /** Chain configurations - at least one required */ + chains: ChainConfig[]; + /** Initial chain ID (defaults to first chain in chains array) */ + initialChainId?: number; + /** Optional authenticated user */ + authenticatedUser?: User; + /** Optional auth client for automatic login/getUser */ + auth?: Auth; + /** Optional popup overlay options */ + popupOverlayOptions?: { + disableGenericPopupOverlay?: boolean; + disableBlockedPopupOverlay?: boolean; + }; + /** Enable cross-SDK bridge mode - skips confirmation popups, throws errors instead */ + crossSdkBridgeEnabled?: boolean; +} + +/** + * EIP-1193 compatible provider interface + */ +export interface Provider { + /** + * Send a JSON-RPC request + * @see https://eips.ethereum.org/EIPS/eip-1193 + */ + request(args: RequestArguments): Promise; + + /** + * Subscribe to provider events + */ + on(event: string, listener: (...args: any[]) => void): void; + + /** + * Unsubscribe from provider events + */ + removeListener(event: string, listener: (...args: any[]) => void): void; + + /** + * Indicates this is a Passport provider + */ + isPassport: boolean; +} + +/** + * JSON-RPC request arguments + */ +export interface RequestArguments { + method: string; + params?: unknown[]; +} + +/** + * Provider event types + */ +export enum ProviderEvent { + ACCOUNTS_CHANGED = 'accountsChanged', +} + +/** + * PassportEVMProvider - EIP-1193 compatible provider + */ +export class PassportEVMProvider implements Provider { + public readonly isPassport: boolean = true; + + private chains: Map; + + private currentChainId: number; + + private currentRpcUrl: string; + + private eventEmitter: SimpleEventEmitter; + + private authenticatedUser?: User; + + private auth?: Auth; + + private signer?: Signer; + + private walletAddress?: string; + + private passportDomain: string; + + private crossSdkBridgeEnabled: boolean; + + // Clients are always initialized in constructor via initializeClients() + private relayerClient!: RelayerClient; + + private guardianClient!: GuardianClient; + + private apiClient!: ApiClient; + + private confirmationScreen: ConfirmationScreen; + + // Cached HTTP transport for RPC calls (recreated when chain switches) + private rpcTransport = http(''); + + constructor(config: ProviderConfig) { + if (!config.chains || config.chains.length === 0) { + throw new Error('At least one chain configuration is required'); + } + + // Build chain map + this.chains = new Map(); + for (const chain of config.chains) { + this.chains.set(chain.chainId, chain); + } + + // Set initial chain + this.currentChainId = config.initialChainId || config.chains[0].chainId; + const chainConfig = this.chains.get(this.currentChainId); + if (!chainConfig) { + throw new Error(`Chain ${this.currentChainId} not configured`); + } + + this.currentRpcUrl = chainConfig.rpcUrl; + this.authenticatedUser = config.authenticatedUser; + this.auth = config.auth; + this.eventEmitter = new SimpleEventEmitter(); + + // Initialize RPC transport + this.rpcTransport = http(this.currentRpcUrl); + + // Determine passport domain from chain config + this.passportDomain = 'https://passport.immutable.com'; + this.crossSdkBridgeEnabled = config.crossSdkBridgeEnabled || false; + + // Initialize confirmation screen + this.confirmationScreen = new ConfirmationScreen({ + passportDomain: this.passportDomain, + popupOverlayOptions: config.popupOverlayOptions, + crossSdkBridgeEnabled: this.crossSdkBridgeEnabled, + }); + + // Initialize clients eagerly (they can exist without authenticated user) + // Clients are stateless - user is passed as parameter to methods + this.initializeClients(config.crossSdkBridgeEnabled || false); + + // If auth client provided, set it up (will try to get existing user) + if (this.auth) { + this.setAuth(this.auth); + } + + // If authenticated user provided, set it up + if (this.authenticatedUser) { + this.setAuthenticatedUser(this.authenticatedUser); + } + } + + /** + * Initializes or updates API clients + * Clients can be created without authenticated user - they'll fail on requests until user is set + */ + private initializeClients(crossSdkBridgeEnabled: boolean): void { + const chainConfig = this.chains.get(this.currentChainId)!; + + this.relayerClient = new RelayerClient({ + relayerUrl: chainConfig.relayerUrl, + }); + + this.guardianClient = new GuardianClient({ + guardianUrl: chainConfig.apiUrl, + confirmationScreen: this.confirmationScreen, + crossSdkBridgeEnabled, + }); + + this.apiClient = new ApiClient({ + apiUrl: chainConfig.apiUrl, + }); + } + + /** + * Sets the authenticated user (internal use only) + * Automatically initializes MagicTEESigner when user is authenticated + * @internal + */ + private setAuthenticatedUser(user: User): void { + this.authenticatedUser = user; + this.initializeClients(this.crossSdkBridgeEnabled); + this.initializeSigner(); + } + + /** + * Initializes Magic TEE signer when user is authenticated + */ + private initializeSigner(): void { + if (!this.authenticatedUser) { + return; + } + + // Magic TEE config - hardcoded based on passport domain + const isSandbox = this.passportDomain.includes('sandbox'); + const magicTeeBasePath = isSandbox + ? 'https://api.sandbox.immutable.com/magic-tee' + : 'https://api.immutable.com/magic-tee'; + + // Magic config values - hardcoded (same for production and sandbox) + // These match the values from the original Passport SDK + const magicPublishableApiKey = 'pk_live_10F423798A540ED7'; + const magicProviderId = 'aa80b860-8869-4f13-9000-6a6ad3d20017'; + + this.signer = new MagicTEESigner({ + magicTeeBasePath, + magicPublishableApiKey, + magicProviderId, + authenticatedUser: this.authenticatedUser, + }); + } + + /** + * Sets auth client for automatic login + * @internal + */ + private setAuth(auth: Auth): void { + this.auth = auth; + // Try to get existing user + auth.getUser().then((user: User | null) => { + if (user) { + this.setAuthenticatedUser(user); + } + }).catch(() => { + // No user yet - will be handled when needed + }); + } + + /** + * Creates a shared auth client for wallet-only mode + * Used when apps don't provide their own OAuth client + * @internal + */ + // eslint-disable-next-line class-methods-use-this + private createSharedAuthClient(): Auth { + return new Auth({ + clientId: 'immutable-passport-wallet-only', // Shared OAuth client ID for wallet-only mode + redirectUri: 'https://passport.immutable.com/wallet-callback', // This callback page is hosted by Passport and handles OAuth redirects + scope: 'openid transact', // Wallet-only scope, no profile/email access + }); + } + + /** + * Sets signer (internal use only) + * @internal + */ + private setSigner(signer: Signer): void { + this.signer = signer; + } + + /** + * Gets the current chain ID + */ + async getChainId(): Promise { + return this.currentChainId; + } + + /** + * Switches to a different chain + */ + async switchChain(chainId: number): Promise { + if (!this.chains.has(chainId)) { + throw new JsonRpcError( + ProviderErrorCode.UNSUPPORTED_METHOD, + `Chain ${chainId} not supported`, + ); + } + + this.currentChainId = chainId; + const chainConfig = this.chains.get(chainId)!; + + this.currentRpcUrl = chainConfig.rpcUrl; + + // Recreate RPC transport for new chain + this.rpcTransport = http(this.currentRpcUrl); + + // Reinitialize clients with new chain config (always, regardless of user) + // Note: crossSdkBridgeEnabled is stored in confirmationScreen config, so we need to read it back + // For simplicity, we'll store it as a private field + this.initializeClients(this.crossSdkBridgeEnabled); + + // Emit chainChanged event + this.eventEmitter.emit('chainChanged', toHex(chainId)); + } + + /** + * Adds a new chain configuration + */ + addChain(chainConfig: ChainConfig): void { + this.chains.set(chainConfig.chainId, chainConfig); + } + + /** + * Gets the cached wallet address + * Returns undefined if wallet not yet registered (call eth_requestAccounts to register) + */ + private async getWalletAddress(): Promise { + return this.walletAddress; + } + + /** + * Ensures wallet address exists, throws if not + */ + private async ensureWalletAddress(): Promise { + const address = await this.getWalletAddress(); + if (!address) { + throw new JsonRpcError( + ProviderErrorCode.UNAUTHORIZED, + 'Unauthorised - call eth_requestAccounts first', + ); + } + return address; + } + + /** + * Ensures signer is set + * Automatically initializes MagicTEESigner if user is authenticated but signer not yet created + */ + private ensureSigner(): Signer { + // If signer not set but user is authenticated, try to initialize it + if (!this.signer && this.authenticatedUser) { + this.initializeSigner(); + } + + if (!this.signer) { + throw new JsonRpcError( + ProviderErrorCode.UNAUTHORIZED, + 'Signer not available. User must be authenticated for signing operations.', + ); + } + return this.signer; + } + + /** + * Ensures user is authenticated, triggers login automatically if auth client provided + * If no auth client is provided, creates a shared auth client for wallet-only mode + */ + private async ensureAuthenticated(): Promise { + if (this.authenticatedUser) { + return; + } + + // If no auth client provided, create shared client for wallet-only mode + if (!this.auth) { + this.auth = this.createSharedAuthClient(); + } + + // Attempt to get existing user first (checks localStorage/session) + const existingUser = await this.auth.getUser(); + if (existingUser) { + this.setAuthenticatedUser(existingUser); + return; + } + + // No existing user, trigger login popup + const user = await this.auth.loginPopup(); + if (user) { + this.setAuthenticatedUser(user); + return; + } + + throw new JsonRpcError( + ProviderErrorCode.UNAUTHORIZED, + 'User not authenticated. Login popup was closed or cancelled.', + ); + } + + /** + * Handles eth_requestAccounts + * Automatically triggers login if user not authenticated + * If no auth client is provided, uses shared client ID for wallet-only mode + */ + private async handleRequestAccounts(): Promise { + // Check if we already have a wallet address + const address = await this.getWalletAddress(); + if (address) { + this.eventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, [address]); + return [address]; + } + + // Ensure authenticated (will auto-login using shared client if no auth provided) + await this.ensureAuthenticated(); + + // Ensure signer is set + const signer = this.ensureSigner(); + + const MESSAGE_TO_SIGN = 'Only sign this message from Immutable Passport'; + + // Get signer address and sign message + const [ethereumAddress, ethereumSignature] = await Promise.all([ + signer.getAddress(), + signer.signMessage(MESSAGE_TO_SIGN), + ]); + + // Get chain name from API + const chainName = await this.apiClient.getChainName(this.currentChainId); + + // Register user + const counterfactualAddress = await this.apiClient.createCounterfactualAddress( + chainName, + ethereumAddress, + ethereumSignature, + this.authenticatedUser!, + ); + + this.walletAddress = counterfactualAddress; + this.eventEmitter.emit(ProviderEvent.ACCOUNTS_CHANGED, [counterfactualAddress]); + + return [counterfactualAddress]; + } + + /** + * Handles eth_sendTransaction + */ + private async handleSendTransaction(params: any[]): Promise { + await this.ensureAuthenticated(); + const address = await this.ensureWalletAddress(); + const signer = this.ensureSigner(); + + const transactionRequest = params[0]; + if (!transactionRequest?.to || typeof transactionRequest.to !== 'string') { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'Transaction must include to field', + ); + } + + if (!isAddress(transactionRequest.to)) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + `Invalid address: ${transactionRequest.to}`, + ); + } + + // Build meta-transactions (includes fee transaction) + const { transactions: metaTransactions, nonce } = await buildMetaTransactions( + { + to: transactionRequest.to, + data: transactionRequest.data, + value: transactionRequest.value ? BigInt(transactionRequest.value) : undefined, + }, + this.currentRpcUrl, + this.relayerClient, + address, + this.currentChainId, + this.authenticatedUser!, + ); + + const chainId = BigInt(this.currentChainId); + + // Validate and sign in parallel + const signedTransactionData = await validateAndSignTransaction( + metaTransactions, + nonce, + chainId, + address, + signer, + this.guardianClient, + this.authenticatedUser!, + false, + ); + + // Send to relayer + const relayerId = await this.relayerClient.ethSendTransaction( + address, // to is the wallet address + signedTransactionData, + this.currentChainId, + this.authenticatedUser!, + ); + + // Poll for transaction hash + return this.pollTransaction(relayerId); + } + + /** + * Polls relayer for transaction hash + * authenticatedUser is guaranteed by callers (ensureAuthenticated called before this) + */ + private async pollTransaction(relayerId: string): Promise { + const maxAttempts = 30; + const delayMs = 1000; + + // Polling loop - await is intentional + // eslint-disable-next-line no-await-in-loop + for (let i = 0; i < maxAttempts; i++) { + // authenticatedUser is guaranteed by callers (ensureAuthenticated called before this) + // eslint-disable-next-line no-await-in-loop + const tx = await this.relayerClient.imGetTransactionByHash(relayerId, this.authenticatedUser!); + + if (tx.status === 'SUCCESSFUL' || tx.status === 'SUBMITTED') { + if (!tx.hash) { + throw new JsonRpcError( + RpcErrorCode.INTERNAL_ERROR, + 'Transaction hash not available', + ); + } + return tx.hash; + } + + if (tx.status === 'REVERTED' || tx.status === 'FAILED') { + throw new JsonRpcError( + RpcErrorCode.TRANSACTION_REJECTED, + tx.statusMessage || 'Transaction failed', + ); + } + + // eslint-disable-next-line no-await-in-loop + await new Promise((resolve) => { + setTimeout(() => resolve(), delayMs); + }); + } + + throw new JsonRpcError( + RpcErrorCode.RPC_SERVER_ERROR, + 'Transaction polling timeout', + ); + } + + /** + * Handles personal_sign + */ + private async handlePersonalSign(params: any[]): Promise { + await this.ensureAuthenticated(); + const address = await this.ensureWalletAddress(); + const signer = this.ensureSigner(); + + const message: string = params[0]; + const fromAddress: string = params[1]; + + if (!fromAddress || !message) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'personal_sign requires an address and a message', + ); + } + + // Validate address format + if (!isAddress(fromAddress)) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + `Invalid address: ${fromAddress}`, + ); + } + + if (fromAddress.toLowerCase() !== address.toLowerCase()) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'personal_sign requires the signer to be the from address', + ); + } + + // Convert hex to string if needed + const payload = hexToString(message as `0x${string}`); + const chainId = BigInt(this.currentChainId); + + // Evaluate with guardian (passes wallet address for confirmation) + await this.guardianClient.evaluateERC191Message(payload, address, this.currentChainId, this.authenticatedUser!); + + // Sign with EOA and get relayer signature in parallel + const [eoaSignature, relayerSignature] = await Promise.all([ + signERC191Message(chainId, payload, signer, address), + this.relayerClient.imSign(address, payload, this.currentChainId, this.authenticatedUser!), + ]); + + const eoaAddress = await signer.getAddress(); + + // Pack signatures + return packSignatures(eoaSignature, eoaAddress, relayerSignature); + } + + /** + * Deploys the smart contract wallet by sending a zero-value transaction + */ + private async deployWallet(): Promise { + await this.ensureAuthenticated(); + const address = await this.ensureWalletAddress(); + const signer = this.ensureSigner(); + + // Build meta-transactions for deployment (zero-value transaction to self) + const { transactions: metaTransactions, nonce } = await buildMetaTransactions( + { + to: address, + data: '0x', + value: BigInt(0), + }, + this.currentRpcUrl, + this.relayerClient, + address, + this.currentChainId, + this.authenticatedUser!, + ); + + const chainId = BigInt(this.currentChainId); + + // Validate and sign in parallel + const signedTransactionData = await validateAndSignTransaction( + metaTransactions, + nonce, + chainId, + address, + signer, + this.guardianClient, + this.authenticatedUser!, + false, + ); + + // Send to relayer + const relayerId = await this.relayerClient.ethSendTransaction( + address, + signedTransactionData, + this.currentChainId, + this.authenticatedUser!, + ); + + // Wait for deployment to complete + await this.pollTransaction(relayerId); + } + + /** + * Handles eth_signTypedData_v4 + */ + private async handleSignTypedDataV4(params: any[]): Promise { + await this.ensureAuthenticated(); + const address = await this.ensureWalletAddress(); + const signer = this.ensureSigner(); + + const fromAddress: string = params[0]; + const typedDataParam: string | object = params[1]; + + if (!fromAddress || !typedDataParam) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'eth_signTypedData_v4 requires an address and typed data', + ); + } + + // Validate address format + if (!isAddress(fromAddress)) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + `Invalid address: ${fromAddress}`, + ); + } + + // Parse typed data + const typedData: TypedDataPayload = typeof typedDataParam === 'string' + ? JSON.parse(typedDataParam) as TypedDataPayload + : typedDataParam as TypedDataPayload; + + // Validate typed data structure + if (!typedData.types || !typedData.domain || !typedData.primaryType || !typedData.message) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'Invalid typed data: missing required fields', + ); + } + + // Validate chainId matches current chain + if (typedData.domain.chainId) { + let providedChainId: number; + if (typeof typedData.domain.chainId === 'string') { + providedChainId = typedData.domain.chainId.startsWith('0x') + ? parseInt(typedData.domain.chainId, 16) + : parseInt(typedData.domain.chainId, 10); + } else { + providedChainId = typedData.domain.chainId; + } + + if (BigInt(providedChainId) !== BigInt(this.currentChainId)) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + `Invalid chainId, expected ${this.currentChainId}`, + ); + } + } + + const chainId = BigInt(this.currentChainId); + + // Evaluate with guardian (passes wallet address for confirmation) + await this.guardianClient.evaluateEIP712Message( + typedData, + address, + this.currentChainId, + this.authenticatedUser!, + ); + + // Get relayer signature + const relayerSignature = await this.relayerClient.imSignTypedData( + address, + typedData, + this.currentChainId, + this.authenticatedUser!, + ); + + // If signer has signTypedData method, use it (ethers/viem signers) + if (signer.signTypedData) { + const eoaSignature = await signer.signTypedData( + typedData.domain, + typedData.types, + typedData.message, + ); + const eoaAddress = await signer.getAddress(); + return packSignatures(eoaSignature, eoaAddress, relayerSignature); + } + + // Otherwise, use our implementation + return signTypedData(typedData, relayerSignature, chainId, address, signer); + } + + /** + * Handles wallet_switchEthereumChain + */ + private async handleSwitchChain(params: any[]): Promise { + const chainIdHex = params[0]?.chainId; + if (!chainIdHex) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'chainId is required', + ); + } + + const chainId = typeof chainIdHex === 'string' + ? Number(fromHex(chainIdHex as `0x${string}`, 'number')) + : chainIdHex; + + await this.switchChain(chainId); + return null; + } + + /** + * Handles wallet_addEthereumChain + */ + private async handleAddChain(params: any[]): Promise { + const chainParams = params[0]; + if (!chainParams?.chainId || !chainParams?.rpcUrls?.[0]) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'chainId and rpcUrls are required', + ); + } + + const chainId = typeof chainParams.chainId === 'string' + ? Number(fromHex(chainParams.chainId as `0x${string}`, 'number')) + : chainParams.chainId; + + // Require API URLs - don't use defaults from current chain (they may be wrong) + if (!chainParams.apiUrl || !chainParams.relayerUrl) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'apiUrl and relayerUrl are required when adding a new chain', + ); + } + + this.addChain({ + chainId, + rpcUrl: chainParams.rpcUrls[0], + relayerUrl: chainParams.relayerUrl, + apiUrl: chainParams.apiUrl, + name: chainParams.chainName || `Chain ${chainId}`, + }); + + return null; + } + + /** + * Handles im_signEjectionTransaction + */ + private async handleSignEjectionTransaction(params: any[]): Promise<{ + to: string; + data: string; + chainId: string; + }> { + await this.ensureAuthenticated(); + const address = await this.ensureWalletAddress(); + const signer = this.ensureSigner(); + + if (!params || params.length !== 1) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'im_signEjectionTransaction requires a singular param (transaction)', + ); + } + + const transactionRequest: TransactionRequest = params[0]; + + return prepareAndSignEjectionTransaction({ + transactionRequest, + ethSigner: signer, + zkEvmAddress: address, + chainId: this.currentChainId, + }); + } + + /** + * Handles im_addSessionActivity + */ + private async handleAddSessionActivity(params: any[]): Promise { + const address = await this.ensureWalletAddress(); + const [clientId] = params || []; + + if (!clientId) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + 'im_addSessionActivity requires a clientId', + ); + } + + // Session activity is handled asynchronously and doesn't block + // This is a fire-and-forget operation for analytics + this.callSessionActivity(address, clientId).catch(() => { + // Silently fail - session activity is non-critical + }); + + return null; + } + + /** + * Calls session activity API (background operation) + */ + private async callSessionActivity(walletAddress: string, clientId: string): Promise { + if (!this.authenticatedUser?.access_token) { + return; // Skip if not authenticated + } + + // Determine API URL based on chain configuration + const chainConfig = this.chains.get(this.currentChainId); + if (!chainConfig) { + return; + } + + // Session activity always uses production API + const sessionActivityUrl = 'https://api.immutable.com/v1/sdk/session-activity/check'; + const params = new URLSearchParams({ + clientId, + wallet: walletAddress, + checkCount: '0', + sendCount: '0', + }); + const url = `${sessionActivityUrl}?${params.toString()}`; + + try { + const response = await fetch( + url, + { + method: 'GET', + headers: { + Authorization: `Bearer ${this.authenticatedUser.access_token}`, + }, + }, + ); + + if (!response.ok) { + if (response.status === 404) { + return; // No session activity required + } + throw new Error(`Session activity error: ${response.status}`); + } + + const data = await response.json(); + + // If contract address and function name are provided, send a background transaction + if (data.contractAddress && data.functionName) { + // Send background transaction in nonce space 1 + await this.sendBackgroundTransaction( + walletAddress, + data.contractAddress, + data.functionName, + data.delay, + ); + } + } catch (error) { + // Silently fail - session activity is non-critical + } + } + + /** + * Sends a background transaction for session activity + */ + private async sendBackgroundTransaction( + walletAddress: string, + contractAddress: string, + functionName: string, + delay?: number, + ): Promise { + try { + await this.ensureAuthenticated(); + const signer = this.ensureSigner(); + + // Encode function call (simple function with no parameters) + const functionSelector = getFunctionSelector(`${functionName}()`); + + // Build meta-transactions with nonce space 1 (background) + const nonceSpace = BigInt(1); + const { transactions: metaTransactions, nonce } = await buildMetaTransactions( + { + to: contractAddress, + data: functionSelector, + }, + this.currentRpcUrl, + this.relayerClient, + walletAddress, + this.currentChainId, + this.authenticatedUser!, + nonceSpace, + ); + + const chainId = BigInt(this.currentChainId); + + // Validate and sign in parallel + const signedTransactionData = await validateAndSignTransaction( + metaTransactions, + nonce, + chainId, + walletAddress, + signer, + this.guardianClient, + this.authenticatedUser!, + true, // isBackgroundTransaction + ); + + // Send to relayer + await this.relayerClient.ethSendTransaction( + walletAddress, + signedTransactionData, + this.currentChainId, + this.authenticatedUser!, + ); + + // Wait for delay if specified + if (delay && delay > 0) { + await new Promise((resolve) => { + setTimeout(() => resolve(), delay * 1000); + }); + } + } catch (error) { + // Silently fail - background transaction is non-critical + } + } + + /** + * Performs the actual request handling + */ + private async performRequest(request: RequestArguments): Promise { + switch (request.method) { + case 'eth_requestAccounts': { + return this.handleRequestAccounts(); + } + case 'eth_accounts': { + const address = await this.getWalletAddress(); + return address ? [address] : []; + } + case 'eth_chainId': { + return toHex(this.currentChainId); + } + case 'eth_sendTransaction': { + return this.handleSendTransaction(request.params || []); + } + case 'personal_sign': { + return this.handlePersonalSign(request.params || []); + } + case 'eth_signTypedData': + case 'eth_signTypedData_v4': { + return this.handleSignTypedDataV4(request.params || []); + } + case 'wallet_switchEthereumChain': { + return this.handleSwitchChain(request.params || []); + } + case 'wallet_addEthereumChain': { + return this.handleAddChain(request.params || []); + } + // Pass through read-only methods to RPC + case 'eth_getBalance': + case 'eth_getCode': + case 'eth_getTransactionCount': + case 'eth_getStorageAt': + case 'eth_call': + case 'eth_estimateGas': + case 'eth_gasPrice': + case 'eth_blockNumber': + case 'eth_getBlockByHash': + case 'eth_getBlockByNumber': + case 'eth_getTransactionByHash': + case 'eth_getTransactionReceipt': { + const client = createPublicClient({ + transport: this.rpcTransport, + }); + switch (request.method) { + case 'eth_gasPrice': + return client.getGasPrice(); + case 'eth_blockNumber': + return client.getBlockNumber(); + case 'eth_getBlockByHash': + return client.getBlock({ blockHash: (request.params?.[0] as `0x${string}`) || undefined }); + case 'eth_getBlockByNumber': + return client.getBlock({ blockNumber: request.params?.[0] as any }); + case 'eth_getTransactionByHash': + return client.getTransaction({ hash: (request.params?.[0] as `0x${string}`) || undefined }); + case 'eth_getTransactionReceipt': + return client.getTransactionReceipt({ hash: (request.params?.[0] as `0x${string}`) || undefined }); + default: + throw new JsonRpcError( + ProviderErrorCode.UNSUPPORTED_METHOD, + `Method ${request.method} not supported`, + ); + } + } + case 'im_signEjectionTransaction': { + return this.handleSignEjectionTransaction(request.params || []); + } + case 'im_addSessionActivity': { + return this.handleAddSessionActivity(request.params || []); + } + default: { + throw new JsonRpcError( + ProviderErrorCode.UNSUPPORTED_METHOD, + `Method ${request.method} not supported`, + ); + } + } + } + + /** + * EIP-1193 request method + */ + public async request(request: RequestArguments): Promise { + try { + return await this.performRequest(request); + } catch (error) { + if (error instanceof JsonRpcError) { + throw error; + } + if (error instanceof Error) { + throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, error.message); + } + throw new JsonRpcError(RpcErrorCode.INTERNAL_ERROR, 'Internal error'); + } + } + + /** + * EIP-1193 event subscription + */ + public on(event: string, listener: (...args: any[]) => void): void { + this.eventEmitter.on(event, listener); + } + + /** + * EIP-1193 event unsubscription + */ + public removeListener(event: string, listener: (...args: any[]) => void): void { + this.eventEmitter.removeListener(event, listener); + } +} diff --git a/pkg/wallet/src/relayer.ts b/pkg/wallet/src/relayer.ts new file mode 100644 index 0000000000..486a9cc160 --- /dev/null +++ b/pkg/wallet/src/relayer.ts @@ -0,0 +1,160 @@ +/** + * Minimal relayer client for Immutable relayer API + */ + +import type { User } from '@imtbl/auth'; +import { encodeAbiParameters } from 'viem'; +import { getEip155ChainId } from './utils/chain'; +import { jsonRpcRequest } from './utils/http-client'; +import type { MetaTransaction } from './metatransaction'; +import type { TypedDataPayload } from './types'; + +export interface RelayerClientConfig { + relayerUrl: string; +} + +/** + * Fee option from relayer + */ +export interface FeeOption { + tokenPrice: string; + tokenSymbol: string; + tokenDecimals: number; + tokenAddress: string; + recipientAddress: string; +} + +/** + * Minimal relayer client + */ +export class RelayerClient { + private config: RelayerClientConfig; + + constructor(config: RelayerClientConfig) { + this.config = config; + } + + /** + * Makes a request to the relayer API + */ + private async request(method: string, params: unknown[], user: User): Promise { + // User is guaranteed to be authenticated when this is called + // (ensured by ensureAuthenticated() in provider) + // Keep defensive check here as final safety net + if (!user?.access_token) { + throw new Error('User not authenticated'); + } + + return jsonRpcRequest( + `${this.config.relayerUrl}/v1/transactions`, + method, + params, + user.access_token, + ); + } + + /** + * Sends a transaction via relayer + */ + async ethSendTransaction(to: string, data: string, chainId: number, user: User): Promise { + return this.request('eth_sendTransaction', [{ + to, + data, + chainId: getEip155ChainId(chainId), + }], user); + } + + /** + * Gets transaction by hash + */ + async imGetTransactionByHash(hash: string, user: User): Promise<{ + status: string; + hash?: string; + statusMessage?: string; + }> { + return this.request<{ + status: string; + hash?: string; + statusMessage?: string; + }>('im_getTransactionByHash', [hash], user); + } + + /** + * Signs a message via relayer + */ + async imSign(address: string, message: string, chainId: number, user: User): Promise { + return this.request('im_sign', [{ + chainId: getEip155ChainId(chainId), + address, + message, + }], user); + } + + /** + * Signs typed data via relayer + */ + async imSignTypedData(address: string, payload: TypedDataPayload, chainId: number, user: User): Promise { + return this.request('im_signTypedData', [{ + chainId: getEip155ChainId(chainId), + address, + eip712Payload: payload, + }], user); + } + + /** + * Gets fee options for a transaction + */ + async imGetFeeOptions( + userAddress: string, + data: string, + chainId: number, + user: User, + ): Promise { + return this.request('im_getFeeOptions', [{ + userAddress, + data, + chainId: getEip155ChainId(chainId), + }], user); + } + + /** + * Gets fee option from relayer (prefers IMX) + * Helper that selects IMX fee option from transactions + */ + async getFeeOption( + walletAddress: string, + transactions: MetaTransaction[], + chainId: number, + user: User, + ): Promise { + const META_TRANSACTIONS_TYPE = `tuple( + bool delegateCall, + bool revertOnError, + uint256 gasLimit, + address target, + uint256 value, + bytes data + )[]`; + + const encodedTransactions = encodeAbiParameters( + [{ type: META_TRANSACTIONS_TYPE }], + [transactions], + ); + + const feeOptions = await this.imGetFeeOptions(walletAddress, encodedTransactions, chainId, user); + + if (!feeOptions || !Array.isArray(feeOptions)) { + throw new Error('Invalid fee options received from relayer'); + } + + const imxFeeOption = feeOptions.find( + (feeOption) => feeOption.tokenSymbol === 'IMX', + ); + + if (!imxFeeOption) { + throw new Error('Failed to retrieve fees for IMX token'); + } + + return imxFeeOption; + } +} diff --git a/pkg/wallet/src/sequence.ts b/pkg/wallet/src/sequence.ts new file mode 100644 index 0000000000..1ededb6669 --- /dev/null +++ b/pkg/wallet/src/sequence.ts @@ -0,0 +1,307 @@ +/** + * Sequence signature encoding/decoding + * Minimal implementation for Immutable wallet signature format + */ + +import { + isAddress, keccak256, encodeAbiParameters, fromHex, +} from 'viem'; +import { cleanSignature, cleanAddress, removeHexPrefix } from './utils/hex'; +import { JsonRpcError, RpcErrorCode } from './errors'; +import type { Signer } from './signer/signer'; +import { encodeMessageSubDigest } from './utils/subdigest'; +import { getFunctionSelector } from './utils/abi'; +import type { MetaTransaction } from './metatransaction'; + +/** + * Sequence signature constants + */ +export const SEQUENCE_VERSION = 0; +export const SIGNATURE_WEIGHT = 1; +export const TRANSACTION_SIGNATURE_THRESHOLD = 1; +export const PACKED_SIGNATURE_THRESHOLD = 2; +export const ETH_SIGN_FLAG = '02'; + +/** + * Signer data structure + */ +interface SignerData { + isDynamic?: boolean; + unrecovered?: boolean; + weight: number; + signature: string; + address?: string; +} + +/** + * Signature encoding options + */ +interface EncodeSignatureOptions { + version: number; + threshold: number; + signers: SignerData[]; +} + +/** + * Decoded signature structure + */ +interface DecodedSignature { + version: number; + threshold: number; + signers: SignerData[]; +} + +/** + * Encodes a signature in Sequence format + */ +export function encodeSignature(options: EncodeSignatureOptions): string { + const { version, threshold, signers } = options; + + // Encode header: version (1 byte) + threshold (1 byte) + reserved (1 byte) + signerCount (1 byte) + let encoded = version.toString(16).padStart(2, '0'); + encoded += threshold.toString(16).padStart(2, '0'); + encoded += '00'; // Reserved byte (always 0) + encoded += signers.length.toString(16).padStart(2, '0'); + + // Encode each signer + for (const signer of signers) { + // Pack flags and weight into 1 byte: + // bit 0: isDynamic (0 or 1) + // bit 1: unrecovered (0 or 1) + // bits 2-7: weight (0-63) + const isDynamic = signer.isDynamic ? 1 : 0; + const unrecovered = signer.unrecovered !== false ? 1 : 0; // Default to true + // eslint-disable-next-line no-bitwise + const weight = signer.weight & 0x3f; // Mask to 6 bits + + // eslint-disable-next-line no-bitwise + const flagsByte = (isDynamic | (unrecovered << 1) | (weight << 2)).toString(16).padStart(2, '0'); + encoded += flagsByte; + + // Signature (65 bytes = 130 hex chars) + const sig = cleanSignature(signer.signature, 130); + encoded += sig; + + // Address (20 bytes = 40 hex chars) - only if provided + if (signer.address) { + const addr = cleanAddress(signer.address); + if (addr.length !== 40) { + throw new Error(`Invalid address length: expected 40 hex chars, got ${addr.length}`); + } + encoded += addr; + } + } + + return `0x${encoded}`; +} + +/** + * Decodes a Sequence signature + */ +export function decodeSignature(signature: string): DecodedSignature { + const hex = removeHexPrefix(signature); + + if (hex.length < 8) { + throw new Error('Invalid signature: too short'); + } + + // Read version (1 byte) + const versionHex = hex.slice(0, 2); + const version = parseInt( + versionHex, + 16, + ); + + // Read threshold (1 byte) + const threshold = parseInt(hex.slice(2, 4), 16); + + // Skip reserved byte (offset 4-6) + + // Read signers count (1 byte) + const signersCount = parseInt(hex.slice(6, 8), 16); + + const signers: SignerData[] = []; + let offset = 8; + + for (let i = 0; i < signersCount; i++) { + if (offset + 2 > hex.length) { + throw new Error('Invalid signature: incomplete signer data'); + } + + // Read flags byte + const flagsByte = parseInt(hex.slice(offset, offset + 2), 16); + offset += 2; + + // eslint-disable-next-line no-bitwise + const isDynamic = (flagsByte & 0x01) !== 0; + // eslint-disable-next-line no-bitwise + const unrecovered = (flagsByte & 0x02) !== 0; + // eslint-disable-next-line no-bitwise + const weight = (flagsByte >> 2) & 0x3f; + + // Read signature (65 bytes = 130 hex chars) + if (offset + 130 > hex.length) { + throw new Error('Invalid signature: incomplete signature data'); + } + const sig = `0x${hex.slice(offset, offset + 130)}`; + offset += 130; + + // Read address if present (20 bytes = 40 hex chars) + // For relayer signatures, address might not be present + let address: string | undefined; + if (offset + 40 <= hex.length) { + address = `0x${hex.slice(offset, offset + 40)}`; + offset += 40; + } + + signers.push({ + isDynamic, + unrecovered, + weight, + signature: sig, + address, + }); + } + + return { + version, + threshold, + signers, + }; +} + +/** + * Packs two signatures together using Sequence encoding format + * Decodes relayer signature, adds EOA signature, re-encodes with threshold 2 + */ +export function packSignatures( + eoaSignature: string, + eoaAddress: string, + relayerSignature: string, +): string { + // Validate address format + if (!isAddress(eoaAddress)) { + throw new JsonRpcError( + RpcErrorCode.INVALID_PARAMS, + `Invalid address: ${eoaAddress}`, + ); + } + + // Decode relayer signature (add 0x prefix if missing, and version/threshold prefix) + const relayerSigWithPrefix = relayerSignature.startsWith('0x') + ? relayerSignature + : `0x0000${relayerSignature}`; // Add version/threshold prefix for decoding + + const decoded = decodeSignature(relayerSigWithPrefix); + const relayerSigners = decoded.signers; + + // Add EOA signature flag + const signedDigest = `${eoaSignature}${ETH_SIGN_FLAG}`; + + // Combine signers: relayer signers + EOA signer + const combinedSigners = [ + ...relayerSigners, + { + isDynamic: false, + unrecovered: true, + weight: SIGNATURE_WEIGHT, + signature: signedDigest, + address: eoaAddress, + }, + ]; + + // Sort signers by address (lexicographic comparison) + const sortedSigners = combinedSigners.sort((a, b) => { + if (!a.address || !b.address) return 0; + return a.address.toLowerCase().localeCompare(b.address.toLowerCase()); + }); + + // Re-encode with threshold 2 + return encodeSignature({ + version: SEQUENCE_VERSION, + threshold: PACKED_SIGNATURE_THRESHOLD, + signers: sortedSigners, + }); +} + +/** + * Signs meta-transactions using Sequence signature encoding + * This is Sequence-specific logic for signing wallet transactions + */ +export async function signMetaTransactions( + metaTransactions: MetaTransaction[], + nonce: bigint, + chainId: bigint, + walletAddress: string, + signer: Signer, +): Promise { + // Get digest of transactions and nonce + const META_TRANSACTIONS_TYPE = `tuple( + bool delegateCall, + bool revertOnError, + uint256 gasLimit, + address target, + uint256 value, + bytes data + )[]`; + + const packMetaTransactionsNonceData = encodeAbiParameters( + [ + { type: 'uint256' }, + { type: META_TRANSACTIONS_TYPE }, + ], + [nonce, metaTransactions] as readonly [bigint, readonly MetaTransaction[]], + ); + + const digest = keccak256(packMetaTransactionsNonceData); + + // Create sub-digest with chain ID and wallet address + const completePayload = encodeMessageSubDigest(chainId, walletAddress, digest); + const hash = keccak256(`0x${Buffer.from(completePayload, 'utf8').toString('hex')}` as `0x${string}`); + + // Sign the hash + const hashBytes = fromHex(hash, 'bytes'); + const ethsigNoType = await signer.signMessage(hashBytes); + const signedDigest = `${ethsigNoType}${ETH_SIGN_FLAG}`; + + // Encode signature using Sequence encoder + const encodedSignature = encodeSignature({ + version: SEQUENCE_VERSION, + threshold: TRANSACTION_SIGNATURE_THRESHOLD, + signers: [ + { + isDynamic: false, + unrecovered: true, + weight: SIGNATURE_WEIGHT, + signature: signedDigest, + }, + ], + }); + + // Encode execute function call + // Function: execute(tuple[] transactions, uint256 nonce, bytes signature) + const executeSelector = getFunctionSelector('execute((bool,bool,uint256,address,uint256,bytes)[],uint256,bytes)'); + + // Encode parameters + const encodedParams = encodeAbiParameters( + [ + { + type: 'tuple[]', + components: [ + { name: 'delegateCall', type: 'bool' }, + { name: 'revertOnError', type: 'bool' }, + { name: 'gasLimit', type: 'uint256' }, + { name: 'target', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'data', type: 'bytes' }, + ], + }, + { type: 'uint256' }, + { type: 'bytes' }, + ], + [metaTransactions as any, nonce, encodedSignature as `0x${string}`], + ); + + // Prepend function selector + return executeSelector + encodedParams.slice(2); +} diff --git a/pkg/wallet/src/signer/magic.ts b/pkg/wallet/src/signer/magic.ts new file mode 100644 index 0000000000..704ccb6541 --- /dev/null +++ b/pkg/wallet/src/signer/magic.ts @@ -0,0 +1,163 @@ +/** + * Magic TEE Signer + * Alternative signer implementation using Magic's TEE (Trusted Execution Environment) + */ + +import type { User } from '@imtbl/auth'; +import type { Signer } from './signer'; + +export interface MagicTEESignerConfig { + /** Magic TEE API base URL */ + magicTeeBasePath: string; + /** Magic publishable API key */ + magicPublishableApiKey: string; + /** Magic provider ID */ + magicProviderId: string; + /** Authenticated user */ + authenticatedUser: User; +} + +const CHAIN_IDENTIFIER = 'ETH'; + +interface UserWallet { + userIdentifier: string; + walletAddress: string; +} + +/** + * Magic TEE Signer + * Uses Magic's TEE for signing without exposing private keys + */ +export class MagicTEESigner implements Signer { + private config: MagicTEESignerConfig; + + private userWallet: UserWallet | null = null; + + private createWalletPromise: Promise | null = null; + + constructor(config: MagicTEESignerConfig) { + this.config = config; + } + + /** + * Gets the user's wallet address + */ + async getAddress(): Promise { + const userWallet = await this.getUserWallet(); + return userWallet.walletAddress; + } + + /** + * Signs a message using Magic TEE + */ + async signMessage(message: string | Uint8Array): Promise { + // Ensure wallet is created + await this.getUserWallet(); + + const messageToSign = message instanceof Uint8Array + ? `0x${Array.from(message).map((b) => b.toString(16).padStart(2, '0')).join('')}` + : message; + + // Convert string to base64 + // messageToSign is always a string at this point + const messageBase64 = btoa( + new TextEncoder().encode(messageToSign).reduce((data, byte) => data + String.fromCharCode(byte), ''), + ); + + const response = await fetch( + `${this.config.magicTeeBasePath}/v1/wallet/sign/message`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Magic-API-Key': this.config.magicPublishableApiKey, + 'X-OIDC-Provider-ID': this.config.magicProviderId, + 'X-Magic-Chain': CHAIN_IDENTIFIER, + Authorization: `Bearer ${this.config.authenticatedUser.id_token}`, + }, + body: JSON.stringify({ + message_base64: messageBase64, + }), + }, + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + `MagicTEE: Failed to sign message with status ${response.status}: ${JSON.stringify(errorData)}`, + ); + } + + const data = await response.json(); + return data.signature; + } + + /** + * Gets or creates the user's wallet + */ + private async getUserWallet(): Promise { + let { userWallet } = this; + if (!userWallet) { + userWallet = await this.createWallet(); + } + + // Check if the user has changed since the last createWallet request was made + const userIdentifier = this.config.authenticatedUser.profile?.sub; + if (userIdentifier && userWallet.userIdentifier !== userIdentifier) { + userWallet = await this.createWallet(); + } + + return userWallet; + } + + /** + * Creates a wallet via Magic TEE API + * The createWallet endpoint is idempotent, so it can be called multiple times + */ + private async createWallet(): Promise { + if (this.createWalletPromise) { + return this.createWalletPromise; + } + + this.createWalletPromise = (async () => { + this.userWallet = null; + + const response = await fetch( + `${this.config.magicTeeBasePath}/v1/wallet`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Magic-API-Key': this.config.magicPublishableApiKey, + 'X-OIDC-Provider-ID': this.config.magicProviderId, + 'X-Magic-Chain': CHAIN_IDENTIFIER, + Authorization: `Bearer ${this.config.authenticatedUser.id_token}`, + }, + }, + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + `MagicTEE: Failed to initialise EOA with status ${response.status}: ${JSON.stringify(errorData)}`, + ); + } + + const data = await response.json(); + const userIdentifier = this.config.authenticatedUser.profile?.sub || ''; + + this.userWallet = { + userIdentifier, + walletAddress: data.public_address, + }; + + return this.userWallet; + })(); + + try { + return await this.createWalletPromise; + } finally { + this.createWalletPromise = null; + } + } +} diff --git a/pkg/wallet/src/signer/signer.ts b/pkg/wallet/src/signer/signer.ts new file mode 100644 index 0000000000..78595c4f5c --- /dev/null +++ b/pkg/wallet/src/signer/signer.ts @@ -0,0 +1,23 @@ +/** + * Minimal signer interface for message signing + * Compatible with ethers Signer, viem WalletClient, or custom implementations + */ +export interface Signer { + /** + * Gets the signer's address + */ + getAddress(): Promise; + + /** + * Signs a message + * @param message - Message to sign (string or Uint8Array bytes) + * @returns Promise resolving to signature hex string + */ + signMessage(message: string | Uint8Array): Promise; + + /** + * Signs typed data (for eth_signTypedData_v4) + * Optional - if not provided, will use signMessage with hashed typed data + */ + signTypedData?(domain: any, types: any, value: any): Promise; +} diff --git a/pkg/wallet/src/signer/signing.ts b/pkg/wallet/src/signer/signing.ts new file mode 100644 index 0000000000..cf84c15cde --- /dev/null +++ b/pkg/wallet/src/signer/signing.ts @@ -0,0 +1,62 @@ +import { + hashTypedData, hashMessage, keccak256, hexToBytes, +} from 'viem'; +import type { TypedDataPayload } from '../types'; +import { packSignatures } from '../sequence'; +import type { Signer } from './signer'; +import { encodeMessageSubDigest } from '../utils/subdigest'; + +/** + * Signs ERC-191 message with Immutable wallet sub-digest + * Applies custom sub-digest format: \x19\x01{chainId}{walletAddress}{digest} + */ +export async function signERC191Message( + chainId: bigint, + payload: string, + signer: Signer, + walletAddress: string, +): Promise { + const digest = hashMessage(payload); + const subDigest = encodeMessageSubDigest(chainId, walletAddress, digest); + const subDigestHash = keccak256(`0x${Buffer.from(subDigest, 'utf8').toString('hex')}` as `0x${string}`); + const hashBytes = hexToBytes(subDigestHash); + + return signer.signMessage(hashBytes); +} + +/** + * Signs EIP-712 typed data with Immutable wallet sub-digest + * Packs EOA signature with relayer signature using Sequence format + */ +export async function signTypedData( + typedData: TypedDataPayload, + relayerSignature: string, + chainId: bigint, + walletAddress: string, + signer: Signer, +): Promise { + const { EIP712Domain, ...types } = typedData.types; + + const domain = { + ...typedData.domain, + chainId: typeof typedData.domain.chainId === 'string' + ? Number(typedData.domain.chainId) + : typedData.domain.chainId, + }; + + const typedDataHash = hashTypedData({ + domain: domain as any, + types: types as any, + primaryType: typedData.primaryType, + message: typedData.message, + }); + + const subDigest = encodeMessageSubDigest(chainId, walletAddress, typedDataHash); + const subDigestHash = keccak256(`0x${Buffer.from(subDigest, 'utf8').toString('hex')}` as `0x${string}`); + const hashBytes = hexToBytes(subDigestHash); + + const eoaSignature = await signer.signMessage(hashBytes); + const eoaAddress = await signer.getAddress(); + + return packSignatures(eoaSignature, eoaAddress, relayerSignature); +} diff --git a/pkg/wallet/src/types.ts b/pkg/wallet/src/types.ts new file mode 100644 index 0000000000..c8640f536e --- /dev/null +++ b/pkg/wallet/src/types.ts @@ -0,0 +1,22 @@ +/** + * Shared types for wallet package + */ + +/** + * EIP-712 Typed Data payload + */ +export interface TypedDataPayload { + types: { + EIP712Domain: Array<{ name: string; type: string }>; + [key: string]: Array<{ name: string; type: string }>; + }; + domain: { + name?: string; + version?: string; + chainId?: number | string; + verifyingContract?: string; + salt?: string; + }; + primaryType: string; + message: Record; +} diff --git a/pkg/wallet/src/utils/abi.ts b/pkg/wallet/src/utils/abi.ts new file mode 100644 index 0000000000..c84e08359a --- /dev/null +++ b/pkg/wallet/src/utils/abi.ts @@ -0,0 +1,16 @@ +/** + * ABI utility functions + */ + +import { keccak256, toHex } from 'viem'; + +/** + * Gets function selector from function signature + * Replaces deprecated getFunctionSelector from viem + * + * @param signature Function signature (e.g., 'transfer(address,uint256)') + * @returns Function selector (first 4 bytes of keccak256 hash) + */ +export function getFunctionSelector(signature: string): `0x${string}` { + return keccak256(toHex(signature)).slice(0, 10) as `0x${string}`; +} diff --git a/pkg/wallet/src/utils/chain.ts b/pkg/wallet/src/utils/chain.ts new file mode 100644 index 0000000000..81035bfadc --- /dev/null +++ b/pkg/wallet/src/utils/chain.ts @@ -0,0 +1,10 @@ +/** + * Chain-related utility functions + */ + +/** + * Gets EIP-155 chain ID format (eip155:chainId) + */ +export function getEip155ChainId(chainId: number): string { + return `eip155:${chainId}`; +} diff --git a/pkg/wallet/src/utils/hex.ts b/pkg/wallet/src/utils/hex.ts new file mode 100644 index 0000000000..8fbe969353 --- /dev/null +++ b/pkg/wallet/src/utils/hex.ts @@ -0,0 +1,32 @@ +/** + * Hex string utilities + * Simple helpers for hex manipulation (viem handles most conversions) + */ + +/** + * Removes 0x prefix from hex string if present + * Used for manual hex string manipulation (e.g., Sequence encoding) + */ +export function removeHexPrefix(hex: string): string { + return hex.startsWith('0x') ? hex.slice(2) : hex; +} + +/** + * Cleans an address (removes prefix, lowercases) + * Used for Sequence signature encoding + */ +export function cleanAddress(addr: string): string { + return removeHexPrefix(addr).toLowerCase(); +} + +/** + * Cleans a signature (removes prefix, validates length) + * Used for Sequence signature encoding + */ +export function cleanSignature(sig: string, expectedLength?: number): string { + const cleaned = removeHexPrefix(sig); + if (expectedLength && cleaned.length !== expectedLength) { + throw new Error(`Invalid signature length: expected ${expectedLength} hex chars, got ${cleaned.length}`); + } + return cleaned; +} diff --git a/pkg/wallet/src/utils/http-client.ts b/pkg/wallet/src/utils/http-client.ts new file mode 100644 index 0000000000..f9d45fca06 --- /dev/null +++ b/pkg/wallet/src/utils/http-client.ts @@ -0,0 +1,76 @@ +/** + * Minimal HTTP client utility + * Consolidates fetch patterns across API clients + */ + +export interface AuthenticatedFetchOptions { + method?: string; + body?: unknown; + token?: string; + headers?: Record; +} + +/** + * Makes an authenticated HTTP request + */ +export async function authenticatedFetch( + url: string, + options: AuthenticatedFetchOptions = {}, +): Promise { + const headers: Record = { + 'Content-Type': 'application/json', + ...options.headers, + }; + + if (options.token) { + headers.Authorization = `Bearer ${options.token}`; + } + + const fetchOptions: RequestInit = { + method: options.method || 'GET', + headers, + }; + if (options.body) { + fetchOptions.body = JSON.stringify(options.body); + } + const response = await fetch(url, fetchOptions); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`HTTP ${response.status}: ${text}`); + } + + return response.json(); +} + +/** + * Makes a JSON-RPC request (for relayer) + */ +export async function jsonRpcRequest( + url: string, + method: string, + params: unknown[] = [], + token?: string, +): Promise { + const body = { + jsonrpc: '2.0', + id: 1, + method, + params, + }; + + const response = await authenticatedFetch<{ result?: T; error?: { message: string; code?: number } }>( + url, + { + method: 'POST', + body, + token, + }, + ); + + if (response.error) { + throw new Error(`RPC error: ${response.error.message} (code: ${response.error.code || 'unknown'})`); + } + + return response.result as T; +} diff --git a/pkg/wallet/src/utils/subdigest.ts b/pkg/wallet/src/utils/subdigest.ts new file mode 100644 index 0000000000..1dbd791553 --- /dev/null +++ b/pkg/wallet/src/utils/subdigest.ts @@ -0,0 +1,18 @@ +import { removeHexPrefix } from './hex'; + +/** + * Encodes message sub-digest for Immutable wallet contract authentication + * Format: \x19\x01{chainId}{walletAddress}{digest} + */ +export function encodeMessageSubDigest( + chainId: bigint, + walletAddress: string, + digest: string, +): string { + const prefix = '\x19\x01'; + const chainIdHex = chainId.toString(16).padStart(64, '0'); + const address = removeHexPrefix(walletAddress).toLowerCase(); + const digestHex = removeHexPrefix(digest); + + return prefix + chainIdHex + address + digestHex; +} diff --git a/pkg/wallet/tsconfig.json b/pkg/wallet/tsconfig.json new file mode 100644 index 0000000000..b97dfb1ce4 --- /dev/null +++ b/pkg/wallet/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDirs": ["src"], + "customConditions": ["development"] + }, + "include": ["src"], + "exclude": [ + "node_modules", + "dist" + ] +} + diff --git a/pkg/wallet/typedoc.json b/pkg/wallet/typedoc.json new file mode 100644 index 0000000000..8e5152304f --- /dev/null +++ b/pkg/wallet/typedoc.json @@ -0,0 +1,6 @@ +{ + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"], + "name": "wallet" +} + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f559ae5bdd..76fb75ef62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,7 +65,7 @@ importers: version: 17.1.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-react-refresh: specifier: latest - version: 0.4.19(eslint@8.57.0) + version: 0.4.24(eslint@8.57.0) events: specifier: ^3.1.0 version: 3.3.0 @@ -114,13 +114,13 @@ importers: version: 29.7.0 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.14.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2)) + version: 29.7.0(@types/node@22.7.5)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.6.2)) ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@20.14.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2)))(typescript@5.6.2) + version: 29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@22.7.5)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.6.2)))(typescript@5.6.2) ts-node: specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2) + version: 10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.6.2) typescript: specifier: ^5 version: 5.6.2 @@ -132,7 +132,7 @@ importers: version: 0.25.21(@emotion/react@11.11.3(@types/react@18.3.12)(react@18.3.1))(@rive-app/react-canvas-lite@4.9.0(react@18.3.1))(embla-carousel-react@8.1.5(react@18.3.1))(framer-motion@11.18.2(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@imtbl/sdk': specifier: latest - version: 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) + version: 2.10.5(bufferutil@4.0.8)(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) next: specifier: 14.2.25 version: 14.2.25(@babel/core@7.26.9)(@playwright/test@1.45.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -777,7 +777,7 @@ importers: version: 18.3.1(react@18.3.1) wagmi: specifier: ^2.11.3 - version: 2.12.1(@tanstack/query-core@5.51.15)(@tanstack/react-query@5.51.15(react@18.3.1))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 2.12.1(@tanstack/query-core@5.51.15)(@tanstack/react-query@5.51.15(react@18.3.1))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) devDependencies: '@playwright/test': specifier: ^1.45.2 @@ -835,7 +835,7 @@ importers: version: 18.3.1(react@18.3.1) wagmi: specifier: ^2.11.3 - version: 2.12.1(@tanstack/query-core@5.51.15)(@tanstack/react-query@5.51.15(react@18.3.1))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) + version: 2.12.1(@tanstack/query-core@5.51.15)(@tanstack/react-query@5.51.15(react@18.3.1))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) devDependencies: '@playwright/test': specifier: ^1.45.3 @@ -1196,7 +1196,7 @@ importers: version: 29.6.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) react-scripts: specifier: 5.0.1 - version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/babel__core@7.20.5)(bufferutil@4.0.8)(esbuild@0.23.1)(eslint@9.16.0(jiti@1.21.0))(node-notifier@8.0.2)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(utf-8-validate@5.0.10) + version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/babel__core@7.20.5)(bufferutil@4.0.8)(esbuild@0.23.1)(eslint@9.16.0(jiti@1.21.0))(node-notifier@8.0.2)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(utf-8-validate@5.0.10) typescript: specifier: ^5.6.2 version: 5.6.2 @@ -1362,7 +1362,7 @@ importers: version: 29.6.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) react-scripts: specifier: 5.0.1 - version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/babel__core@7.20.5)(bufferutil@4.0.8)(esbuild@0.23.1)(eslint@8.57.0)(node-notifier@8.0.2)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(utf-8-validate@5.0.10) + version: 5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/babel__core@7.20.5)(bufferutil@4.0.8)(esbuild@0.23.1)(eslint@8.57.0)(node-notifier@8.0.2)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(utf-8-validate@5.0.10) rimraf: specifier: ^6.0.1 version: 6.0.1 @@ -1374,7 +1374,7 @@ importers: version: 0.13.0(rollup@4.28.0) ts-jest: specifier: ^29.1.0 - version: 29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)))(typescript@5.6.2) + version: 29.2.5(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.23.1)(jest@29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)))(typescript@5.6.2) typescript: specifier: ^5.6.2 version: 5.6.2 @@ -2520,6 +2520,101 @@ importers: specifier: ^8.0.1 version: 8.0.1(typescript@5.6.2) + pkg/auth: + dependencies: + oidc-client-ts: + specifier: 3.3.0 + version: 3.3.0 + devDependencies: + '@swc/core': + specifier: ^1.3.36 + version: 1.9.3(@swc/helpers@0.5.13) + '@swc/jest': + specifier: ^0.2.37 + version: 0.2.37(@swc/core@1.9.3(@swc/helpers@0.5.13)) + '@types/jest': + specifier: ^29.4.3 + version: 29.5.14 + '@types/node': + specifier: ^18.14.2 + version: 18.15.13 + '@typescript-eslint/eslint-plugin': + specifier: ^5.57.1 + version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/parser': + specifier: ^5.57.1 + version: 5.62.0(eslint@8.57.0)(typescript@5.6.2) + eslint: + specifier: ^8.40.0 + version: 8.57.0 + jest: + specifier: ^29.4.3 + version: 29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)) + jest-environment-jsdom: + specifier: ^29.4.3 + version: 29.6.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + prettier: + specifier: ^2.8.7 + version: 2.8.8 + ts-node: + specifier: ^10.9.1 + version: 10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2) + tsup: + specifier: 8.3.0 + version: 8.3.0(@swc/core@1.9.3(@swc/helpers@0.5.13))(jiti@1.21.0)(postcss@8.4.49)(typescript@5.6.2)(yaml@2.5.0) + typescript: + specifier: ^5.6.2 + version: 5.6.2 + + pkg/wallet: + dependencies: + '@imtbl/auth': + specifier: workspace:* + version: link:../auth + viem: + specifier: ^2.39.0 + version: 2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) + devDependencies: + '@swc/core': + specifier: ^1.3.36 + version: 1.9.3(@swc/helpers@0.5.13) + '@swc/jest': + specifier: ^0.2.37 + version: 0.2.37(@swc/core@1.9.3(@swc/helpers@0.5.13)) + '@types/jest': + specifier: ^29.4.3 + version: 29.5.14 + '@types/node': + specifier: ^18.14.2 + version: 18.15.13 + '@typescript-eslint/eslint-plugin': + specifier: ^5.57.1 + version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/parser': + specifier: ^5.57.1 + version: 5.62.0(eslint@8.57.0)(typescript@5.6.2) + eslint: + specifier: ^8.40.0 + version: 8.57.0 + jest: + specifier: ^29.4.3 + version: 29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)) + jest-environment-jsdom: + specifier: ^29.4.3 + version: 29.6.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + prettier: + specifier: ^2.8.7 + version: 2.8.8 + ts-node: + specifier: ^10.9.1 + version: 10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2) + tsup: + specifier: 8.3.0 + version: 8.3.0(@swc/core@1.9.3(@swc/helpers@0.5.13))(jiti@1.21.0)(postcss@8.4.49)(typescript@5.6.2)(yaml@2.5.0) + typescript: + specifier: ^5.6.2 + version: 5.6.2 + sdk: dependencies: '@imtbl/blockchain-data': @@ -2724,6 +2819,9 @@ packages: '@adraffy/ens-normalize@1.10.1': resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -3634,9 +3732,11 @@ packages: '@cosmjs/crypto@0.31.3': resolution: {integrity: sha512-vRbvM9ZKR2017TO73dtJ50KxoGcFzKtKI7C8iO302BQ5p+DuB+AirUg1952UpSoLfv5ki9O416MFANNg8UN/EQ==} + deprecated: This uses elliptic for cryptographic operations, which contains several security-relevant bugs. To what degree this affects your application is something you need to carefully investigate. See https://github.com/cosmos/cosmjs/issues/1708 for further pointers. Starting with version 0.34.0 the cryptographic library has been replaced. However, private keys might still be at risk. '@cosmjs/crypto@0.32.4': resolution: {integrity: sha512-zicjGU051LF1V9v7bp8p7ovq+VyC91xlaHdsFOTo2oVry3KQikp8L/81RkXmUIT8FxMwdx1T7DmFwVQikcSDIw==} + deprecated: This uses elliptic for cryptographic operations, which contains several security-relevant bugs. To what degree this affects your application is something you need to carefully investigate. See https://github.com/cosmos/cosmjs/issues/1708 for further pointers. Starting with version 0.34.0 the cryptographic library has been replaced. However, private keys might still be at risk. '@cosmjs/encoding@0.31.3': resolution: {integrity: sha512-6IRtG0fiVYwyP7n+8e54uTx2pLYijO48V3t9TLiROERm5aUAIzIlz6Wp0NYaI5he9nh1lcEGJ1lkquVKFw3sUg==} @@ -4364,18 +4464,18 @@ packages: resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} engines: {node: '>=18.18'} - '@imtbl/blockchain-data@2.1.11': - resolution: {integrity: sha512-FZtCxgBoDwNONdbLT61MiOzTG1+rMHC/Zt3ed0K79elISf73/v65SzhyHgumngOWkcUs25TiHw+jm2uU52JyBw==} + '@imtbl/blockchain-data@2.10.5': + resolution: {integrity: sha512-3jcqrQTEabWxSl1MQG0JgtKj91FkSZ7f0L6T29O3Kg00bMajuR9uM25Ksd+IxAEiaF/vKxNKPgU8YJcvnkDqdA==} - '@imtbl/bridge-sdk@2.1.11': - resolution: {integrity: sha512-EaXeMG+Ge17CT089wHipwYoJsGz/EeCLcEztTgIYpijA6R+4wb/jOtwnoCnOAWLHJE4Ep7wi366Xf7be5jTJWQ==} + '@imtbl/bridge-sdk@2.10.5': + resolution: {integrity: sha512-Zv6TwEyrC2VYNB1LE3dQ+E98nauDw2qvFkX8d53RWGMe6q8dJrEDzyqHpSAE/X+zdTi8ku76yWHYKL3Td3eM1A==} engines: {node: '>=20.11.0'} - '@imtbl/checkout-sdk@2.1.11': - resolution: {integrity: sha512-8PvuLX7T/3fGygug6fGGnAWRXFHpXkzV3wHgBcBekOe4K16Dl1wobgzyHoal6K90N/LId/ox38Hu3gPwe/yR6Q==} + '@imtbl/checkout-sdk@2.10.5': + resolution: {integrity: sha512-84cM0IsRBbrGjldnkKI95wpKcwlSG8E4TbxWLmQczWL8DPfsVbmWU02jZcRyS880ZE51nSRc0ecgqhnp6UG4jQ==} - '@imtbl/config@2.1.11': - resolution: {integrity: sha512-6qJ579F6teAGn8Rsdi+lIHejh4KoyYoG5QPiykMwFV+vbX7rok4pT6cNkNDLHFu/ZGcNMP+w5+W3vzaOnG0pcQ==} + '@imtbl/config@2.10.5': + resolution: {integrity: sha512-6ERcaOF38+2cuy4iJA8ZH4cSJFRxkDn7Yj9GXqayxh7eb6lJtFyyV5hnwwAfRxi0ef0ejcJAno7qAveHs45mkg==} engines: {node: '>=20.11.0'} '@imtbl/contracts@2.2.17': @@ -4384,51 +4484,51 @@ packages: '@imtbl/contracts@2.2.6': resolution: {integrity: sha512-2cfE3Tojfp4GnxwVKSwoZY1CWd+/drCIbCKawyH9Nh2zASXd7VC71lo27aD5RnCweXHkZVhPzjqwQf/xrtnmIQ==} - '@imtbl/dex-sdk@2.1.11': - resolution: {integrity: sha512-Neo2/ZaeT/DW6xm9xJ4GCFAvVOuBDjawKpWu2jRcu2t15Kmjj0qHHv1yKF5DHlSRq20fktytd+uJQyqtnx/+WA==} + '@imtbl/dex-sdk@2.10.5': + resolution: {integrity: sha512-pZxJHVvIo53WwVwZYhN0lsyfKzTeSHb2EEnhN1+JTXK+0H0s/bUOxJJtffBT1FkggGI8E21jV+Wwx7yTscXY1w==} engines: {node: '>=20.11.0'} - '@imtbl/generated-clients@2.1.11': - resolution: {integrity: sha512-r0xEwQiLYE9hOYCB/q37yPIkREpvRF+JeqQ3tXELQcqMwgH7Rb30ISAN2dMuxXMgvLa9pG2P9rSEisQXLemjJQ==} + '@imtbl/generated-clients@2.10.5': + resolution: {integrity: sha512-LDlrEgYwPRLWCp8OVV9ERSAW69bEDJUkqZGjg1K7ozUJYNBSmEIMGpVRz77vE1tZLBY7uvJReXOIeZRQ393Hog==} engines: {node: '>=20.11.0'} '@imtbl/image-resizer-utils@0.0.3': resolution: {integrity: sha512-/EOJKMJF4gD/Dv0qNhpUTpp2AgWeQ7XgYK9Xjl+xP2WWgaarNv1SHe1aeqSb8aZT5W7wSXdUGzn6TIxNsuCWGw==} - '@imtbl/metrics@2.1.11': - resolution: {integrity: sha512-d+WYjjbV4ufYL1xKr5mmxnbbkgWS5LKsJbZ8dTF0O7pICrsH2WY5J74R2RGjCVgfoWk28E67WTjsTJYwP+M5CA==} + '@imtbl/metrics@2.10.5': + resolution: {integrity: sha512-rwilndT8HH9xwmmQiK4ma18B4FYnmulJuTf2pVHTJboYPFB5oXkm6C0kf6ORTWLBv1QdEd3YQqJ7NOW+h1d4NA==} engines: {node: '>=20.11.0'} - '@imtbl/minting-backend@2.1.11': - resolution: {integrity: sha512-SgfOT+4nDMAxj5dq0pIrPyaXZ5fhUVgbfOGDoYGJd6x8jJ7utADFemLWWxZHII1/gTe5hg3xSkYR7uluzxvv+Q==} + '@imtbl/minting-backend@2.10.5': + resolution: {integrity: sha512-awOma8qs1qHIms/uGnfPc0XZGc3Kl6D/f3Q3ME1VbGcaH8hTlwng5nOtW9H1A0dlXY7YQm0wP8NJZtY+w2lyMQ==} - '@imtbl/orderbook@2.1.11': - resolution: {integrity: sha512-QKt+oc0AU4kQYCzRSBc0BRwkioZ30cfsmqzthtKU4OLg8H2ngjtt7qN9f6fylflJfHCI3T8spMJPvurH9qsK+w==} + '@imtbl/orderbook@2.10.5': + resolution: {integrity: sha512-FSS7eae0GEAdqnPqh9OgtGO3k3l+v4wSRz/N5/HPsMpz00iNm1OOqa+grswVenAu2UCTgilCCniPeHrRm74wNw==} - '@imtbl/passport@2.1.11': - resolution: {integrity: sha512-62bc8Dn/RwLJBQtGC8rR+UJ9wEPNUr1z9OlOK/YOezHR2RR9EAVyXaDkhquCN4LkZuw+iqYbu2OWWJ0ST3K8Eg==} + '@imtbl/passport@2.10.5': + resolution: {integrity: sha512-UK0W5hokWB+HO7az6nGuyEDWjRayFoOQXWawbVmVPFw+KHL2dbUTz+ps8Tu2NyQH8aNR1qVjBM66y/nhMYCCGQ==} engines: {node: '>=20.11.0'} '@imtbl/react-analytics@0.3.4-alpha': resolution: {integrity: sha512-4VWvfm8RZtpLub7+x2D2wNQ507nIVBCSAPA7B5lxdb0cKrHEujM6Y/HScMImHZHvgjUFQT1jiD9b2BL/DS43Pg==} - '@imtbl/sdk@2.1.11': - resolution: {integrity: sha512-w3oYF+THX6kL3kV/gRsAa9ca18QXb66jlGUPt//rEwOqu6M2mcpWb5V4R+SzR/gKp79OuSCzkPFKYF7kNqQOJw==} + '@imtbl/sdk@2.10.5': + resolution: {integrity: sha512-VAJVXrL1VhlKvZinxzy0FIbhtbTncCULo28tJn8pV3zvIz5M3RBQ8sLaWnVxywEDCbGmeJ6ZUcHE7dVeX1K8wA==} engines: {node: '>=20.0.0'} - '@imtbl/toolkit@2.1.11': - resolution: {integrity: sha512-krQRFKF+UL7qDca2eWBwRwDLWv0p+JlNs/bCO8q9xvv5gOkUvTplbm0hA+SfTsacboDJ13MekN96n83TRvvCPg==} + '@imtbl/toolkit@2.10.5': + resolution: {integrity: sha512-j0ToERYrILjZlf5YzjyuE3ovVdZ6RM5HTN5OlrrsAyJJfPQruv7wrW7rdKaXZXJIJLPeoyUO6L/K1yvLJCZp+A==} engines: {node: '>=20.11.0'} - '@imtbl/webhook@2.1.11': - resolution: {integrity: sha512-0uoXONxwroH1VYuNwKbqxxyLE83EZRZSUz1Gvya7uZk4RG8vAmSFqsPnEfqca64B4SYePoa0qeu0Bq8+P24AJg==} + '@imtbl/webhook@2.10.5': + resolution: {integrity: sha512-NkU2bWwPBmPY7mIzBPELRTqAIqjC9ZsUgY40p761Co9OSC29lfycbFhdXjfSOTbjUHPRUPmbs1W7h0a96uE4lQ==} - '@imtbl/x-client@2.1.11': - resolution: {integrity: sha512-HLYbj6dqfFvry5xfI1d+Q2GF+w5w9UTmAD4G29vTW6rsQQGXeBtJE5R8foY+c1OIz4+kDuTZrrfXxiGnkQ4DRQ==} + '@imtbl/x-client@2.10.5': + resolution: {integrity: sha512-hp2HCQkC6X+Cx0u394/Rk2GENyRXcPtWZ/FTXEwEL1PMcTu6mFaQy3Tv8On5f8nK0jUANy35IZLosbWaRes5iA==} engines: {node: '>=20.11.0'} - '@imtbl/x-provider@2.1.11': - resolution: {integrity: sha512-MdAv353DLWJf2S4JeBJpbLsfDbBjRcEc7baLTLxZUesfVTO6Mh0KLvuZ2U0vPtyOv37rG0oeXzcmWaq8a3CGgQ==} + '@imtbl/x-provider@2.10.5': + resolution: {integrity: sha512-AmRG14vGPq118BF8dK1Wm5CHsPhtlM0PKY8AcDgk/ywc2aHDH6+7pffjffWVzQZJq8CSyKGD3M3q4WtKZLCQ0w==} engines: {node: '>=20.11.0'} '@ioredis/commands@1.2.0': @@ -4718,11 +4818,9 @@ packages: resolution: {integrity: sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==} engines: {node: '>=8'} - '@magic-ext/oidc@12.0.2': - resolution: {integrity: sha512-k7KdSprnOFQjyjO24qJX4qnrhZJjZBva2f32REpvo5sb37AbWaYcmA4F+FfhWhMXxwdHlzFwSkeWHgFvzInEgw==} - '@magic-ext/oidc@12.0.5': resolution: {integrity: sha512-EAmmRRZn/c5jmxHZ1H3IHtEqUKHYrsRtH9O+WuMFOZMv0llef/9MBa4DiRZkpnB0EPKb2hwsY7us8qk/LaFRNA==} + deprecated: 'Deprecation Notice: The OIDC extension will be deprecated soon. Please migrate to API Wallet, which offers improved performance and faster response times. Learn more: https://docs.magic.link/api-wallets/introduction' '@magic-sdk/commons@25.0.5': resolution: {integrity: sha512-/qXYCAs4Y8XISyTHzytoWf4CDejLOynW53X9XFnGJt9c6jFV7FoeuN0n/+TIngjHVUu3v+wbQoJNeFzzCE2y5g==} @@ -5024,6 +5122,10 @@ packages: '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + '@noble/curves@1.2.0': resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} @@ -5033,6 +5135,10 @@ packages: '@noble/curves@1.4.2': resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.2.0': resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} @@ -5048,6 +5154,10 @@ packages: resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@noble/secp256k1@1.7.1': resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} @@ -6100,18 +6210,27 @@ packages: '@scure/base@1.1.7': resolution: {integrity: sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==} + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + '@scure/bip32@1.1.5': resolution: {integrity: sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==} '@scure/bip32@1.4.0': resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + '@scure/bip39@1.1.1': resolution: {integrity: sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==} '@scure/bip39@1.3.0': resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + '@segment/analytics-core@1.3.0': resolution: {integrity: sha512-ujScWZH49NK1hYlp2/EMw45nOPEh+pmTydAnR6gSkRNucZD4fuinvpPL03rmFCw8ibaMuKLAdgPJfQ0gkLKZ5A==} @@ -7526,6 +7645,7 @@ packages: '@uniswap/swap-router-contracts@1.3.1': resolution: {integrity: sha512-mh/YNbwKb7Mut96VuEtL+Z5bRe0xVIbjjiryn+iMMrK2sFKhR4duk/86mEz0UO5gSx4pQIw9G5276P5heY/7Rg==} engines: {node: '>=10'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@uniswap/v2-core@1.0.1': resolution: {integrity: sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q==} @@ -7588,6 +7708,7 @@ packages: '@walletconnect/ethereum-provider@2.13.0': resolution: {integrity: sha512-dnpW8mmLpWl1AZUYGYZpaAfGw1HFkL0WSlhk5xekx3IJJKn4pLacX2QeIOo0iNkzNQxZfux1AK4Grl1DvtzZEA==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/events@1.0.1': resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} @@ -7629,6 +7750,7 @@ packages: '@walletconnect/modal@2.6.2': resolution: {integrity: sha512-eFopgKi8AjKf/0U4SemvcYw9zlLpx9njVN8sf6DAkowC2Md0gPU/UNEbH1Wwj407pEKnEds98pKWib1NN1ACoA==} + deprecated: Please follow the migration guide on https://docs.reown.com/appkit/upgrade/wcm '@walletconnect/relay-api@1.0.10': resolution: {integrity: sha512-tqrdd4zU9VBNqUaXXQASaexklv6A54yEyQQEXYOCr+Jz8Ket0dmPBDyg19LVSNUN2cipAghQc45/KVmfFJ0cYw==} @@ -7641,6 +7763,7 @@ packages: '@walletconnect/sign-client@2.13.0': resolution: {integrity: sha512-En7KSvNUlQFx20IsYGsFgkNJ2lpvDvRsSFOT5PTdGskwCkUfOpB33SQJ6nCrN19gyoKPNvWg80Cy6MJI0TjNYA==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/time@1.0.2': resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} @@ -7650,6 +7773,7 @@ packages: '@walletconnect/universal-provider@2.13.0': resolution: {integrity: sha512-B5QvO8pnk5Bqn4aIt0OukGEQn2Auk9VbHfhQb9cGwgmSCd1GlprX/Qblu4gyT5+TjHMb1Gz5UssUaZWTWbDhBg==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/utils@2.13.0': resolution: {integrity: sha512-q1eDCsRHj5iLe7fF8RroGoPZpdo2CYMZzQSrw1iqL+2+GOeqapxxuJ1vaJkmDUkwgklfB22ufqG6KQnz78sD4w==} @@ -7750,6 +7874,17 @@ packages: zod: optional: true + abitype@1.1.0: + resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -10081,8 +10216,8 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - eslint-plugin-react-refresh@0.4.19: - resolution: {integrity: sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==} + eslint-plugin-react-refresh@0.4.24: + resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==} peerDependencies: eslint: '>=8.40' @@ -11701,6 +11836,11 @@ packages: peerDependencies: ws: '*' + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -13348,10 +13488,6 @@ packages: ohash@1.1.3: resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==} - oidc-client-ts@2.4.0: - resolution: {integrity: sha512-WijhkTrlXK2VvgGoakWJiBdfIsVGz6CFzgjNNqZU1hPKV2kyeEaJgLs7RwuiSp2WhLfWBQuLvr2SxVlZnk3N1w==} - engines: {node: '>=12.13.0'} - oidc-client-ts@3.3.0: resolution: {integrity: sha512-t13S540ZwFOEZKLYHJwSfITugupW4uYLwuQSSXyKH/wHwZ+7FvgHE7gnNJh1YQIZ1Yd1hKSRjqeXGSUtS0r9JA==} engines: {node: '>=18'} @@ -13446,6 +13582,14 @@ packages: outvariant@1.4.0: resolution: {integrity: sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==} + ox@0.9.6: + resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + oxc-resolver@1.11.0: resolution: {integrity: sha512-N3qMse2AM7uST8PaiUMXZkcACyGAMN073tomyvzHTICSzaOqKHvVS0IZ3vj/OqoE140QP4CyOiWmgC1Hw5Urmg==} @@ -15407,6 +15551,7 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions sourcemap-codec@1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} @@ -16617,6 +16762,14 @@ packages: typescript: optional: true + viem@2.39.0: + resolution: {integrity: sha512-rCN+IfnMESlrg/iPyyVL+M9NS/BHzyyNy72470tFmbTuscY3iPaZGMtJDcHKKV8TC6HV9DjWk0zWX6cpu0juyA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + vite-plugin-node-polyfills@0.16.0: resolution: {integrity: sha512-uj1ymOmk7TliMxiivmXokpMY5gVMBpFPSZPLQSCv/LjkJGGKwyLjpbFL64dbYZEdFSUQ3tM7pbrxNh25yvhqOA==} peerDependencies: @@ -17010,6 +17163,18 @@ packages: utf-8-validate: optional: true + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.5.0: resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} engines: {node: '>=10.0.0'} @@ -17198,6 +17363,8 @@ snapshots: '@adraffy/ens-normalize@1.10.1': {} + '@adraffy/ens-normalize@1.11.1': {} + '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.2.1': @@ -20041,17 +20208,17 @@ snapshots: '@humanwhocodes/retry@0.4.1': {} - '@imtbl/blockchain-data@2.1.11': + '@imtbl/blockchain-data@2.10.5': dependencies: - '@imtbl/config': 2.1.11 - '@imtbl/generated-clients': 2.1.11 + '@imtbl/config': 2.10.5 + '@imtbl/generated-clients': 2.10.5 axios: 1.7.7 transitivePeerDependencies: - debug - '@imtbl/bridge-sdk@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/bridge-sdk@2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@imtbl/config': 2.1.11 + '@imtbl/config': 2.10.5 '@jest/globals': 29.7.0 axios: 1.7.7 ethers: 6.13.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -20061,16 +20228,16 @@ snapshots: - supports-color - utf-8-validate - '@imtbl/checkout-sdk@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/checkout-sdk@2.10.5(bufferutil@4.0.8)(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': dependencies: - '@imtbl/blockchain-data': 2.1.11 - '@imtbl/bridge-sdk': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/config': 2.1.11 - '@imtbl/dex-sdk': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/generated-clients': 2.1.11 - '@imtbl/metrics': 2.1.11 - '@imtbl/orderbook': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/passport': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/blockchain-data': 2.10.5 + '@imtbl/bridge-sdk': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/config': 2.10.5 + '@imtbl/dex-sdk': 2.10.5(bufferutil@4.0.8)(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + '@imtbl/generated-clients': 2.10.5 + '@imtbl/metrics': 2.10.5 + '@imtbl/orderbook': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/passport': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@metamask/detect-provider': 2.0.0 axios: 1.7.7 ethers: 6.13.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -20083,9 +20250,9 @@ snapshots: - supports-color - utf-8-validate - '@imtbl/config@2.1.11': + '@imtbl/config@2.10.5': dependencies: - '@imtbl/metrics': 2.1.11 + '@imtbl/metrics': 2.10.5 transitivePeerDependencies: - debug @@ -20132,12 +20299,12 @@ snapshots: - typescript - utf-8-validate - '@imtbl/dex-sdk@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/dex-sdk@2.10.5(bufferutil@4.0.8)(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': dependencies: - '@imtbl/config': 2.1.11 + '@imtbl/config': 2.10.5 '@uniswap/sdk-core': 3.2.3 - '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) - '@uniswap/v3-sdk': 3.10.0(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@uniswap/v3-sdk': 3.10.0(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) ethers: 6.13.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil @@ -20145,7 +20312,7 @@ snapshots: - hardhat - utf-8-validate - '@imtbl/generated-clients@2.1.11': + '@imtbl/generated-clients@2.10.5': dependencies: axios: 1.7.7 transitivePeerDependencies: @@ -20155,7 +20322,7 @@ snapshots: dependencies: buffer: 6.0.3 - '@imtbl/metrics@2.1.11': + '@imtbl/metrics@2.10.5': dependencies: axios: 1.7.7 global-const: 0.1.2 @@ -20163,13 +20330,13 @@ snapshots: transitivePeerDependencies: - debug - '@imtbl/minting-backend@2.1.11': + '@imtbl/minting-backend@2.10.5': dependencies: - '@imtbl/blockchain-data': 2.1.11 - '@imtbl/config': 2.1.11 - '@imtbl/generated-clients': 2.1.11 - '@imtbl/metrics': 2.1.11 - '@imtbl/webhook': 2.1.11 + '@imtbl/blockchain-data': 2.10.5 + '@imtbl/config': 2.10.5 + '@imtbl/generated-clients': 2.10.5 + '@imtbl/metrics': 2.10.5 + '@imtbl/webhook': 2.10.5 uuid: 8.3.2 optionalDependencies: pg: 8.11.5 @@ -20178,10 +20345,10 @@ snapshots: - debug - pg-native - '@imtbl/orderbook@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/orderbook@2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@imtbl/config': 2.1.11 - '@imtbl/metrics': 2.1.11 + '@imtbl/config': 2.10.5 + '@imtbl/metrics': 2.10.5 '@opensea/seaport-js': 4.0.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) axios: 1.7.7 ethers: 6.13.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -20192,17 +20359,17 @@ snapshots: - debug - utf-8-validate - '@imtbl/passport@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/passport@2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@0xsequence/abi': 2.2.13 '@0xsequence/core': 2.2.13(ethers@6.13.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)) - '@imtbl/config': 2.1.11 - '@imtbl/generated-clients': 2.1.11 - '@imtbl/metrics': 2.1.11 - '@imtbl/toolkit': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/x-client': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/x-provider': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@magic-ext/oidc': 12.0.2 + '@imtbl/config': 2.10.5 + '@imtbl/generated-clients': 2.10.5 + '@imtbl/metrics': 2.10.5 + '@imtbl/toolkit': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/x-client': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/x-provider': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@magic-ext/oidc': 12.0.5 '@magic-sdk/provider': 29.0.5(localforage@1.10.0) '@metamask/detect-provider': 2.0.0 axios: 1.7.7 @@ -20211,7 +20378,7 @@ snapshots: jwt-decode: 3.1.2 localforage: 1.10.0 magic-sdk: 29.0.5 - oidc-client-ts: 2.4.0 + oidc-client-ts: 3.3.0 uuid: 8.3.2 transitivePeerDependencies: - bufferutil @@ -20226,17 +20393,17 @@ snapshots: - encoding - supports-color - '@imtbl/sdk@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/sdk@2.10.5(bufferutil@4.0.8)(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': dependencies: - '@imtbl/blockchain-data': 2.1.11 - '@imtbl/checkout-sdk': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/config': 2.1.11 - '@imtbl/minting-backend': 2.1.11 - '@imtbl/orderbook': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/passport': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/webhook': 2.1.11 - '@imtbl/x-client': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/x-provider': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/blockchain-data': 2.10.5 + '@imtbl/checkout-sdk': 2.10.5(bufferutil@4.0.8)(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + '@imtbl/config': 2.10.5 + '@imtbl/minting-backend': 2.10.5 + '@imtbl/orderbook': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/passport': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/webhook': 2.10.5 + '@imtbl/x-client': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/x-provider': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - debug @@ -20245,35 +20412,35 @@ snapshots: - supports-color - utf-8-validate - '@imtbl/toolkit@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/toolkit@2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@imtbl/x-client': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@magic-ext/oidc': 12.0.2 + '@imtbl/x-client': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@magic-ext/oidc': 12.0.5 '@metamask/detect-provider': 2.0.0 axios: 1.7.7 bn.js: 5.2.1 enc-utils: 3.0.0 ethers: 6.13.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) magic-sdk: 29.0.5 - oidc-client-ts: 2.4.0 + oidc-client-ts: 3.3.0 transitivePeerDependencies: - bufferutil - debug - utf-8-validate - '@imtbl/webhook@2.1.11': + '@imtbl/webhook@2.10.5': dependencies: - '@imtbl/config': 2.1.11 - '@imtbl/generated-clients': 2.1.11 + '@imtbl/config': 2.10.5 + '@imtbl/generated-clients': 2.10.5 sns-validator: 0.3.5 transitivePeerDependencies: - debug - '@imtbl/x-client@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/x-client@2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@ethereumjs/wallet': 2.0.4 - '@imtbl/config': 2.1.11 - '@imtbl/generated-clients': 2.1.11 + '@imtbl/config': 2.10.5 + '@imtbl/generated-clients': 2.10.5 axios: 1.7.7 bn.js: 5.2.1 elliptic: 6.6.1 @@ -20285,19 +20452,19 @@ snapshots: - debug - utf-8-validate - '@imtbl/x-provider@2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + '@imtbl/x-provider@2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: - '@imtbl/config': 2.1.11 - '@imtbl/generated-clients': 2.1.11 - '@imtbl/toolkit': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@imtbl/x-client': 2.1.11(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@magic-ext/oidc': 12.0.2 + '@imtbl/config': 2.10.5 + '@imtbl/generated-clients': 2.10.5 + '@imtbl/toolkit': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@imtbl/x-client': 2.10.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@magic-ext/oidc': 12.0.5 '@metamask/detect-provider': 2.0.0 axios: 1.7.7 enc-utils: 3.0.0 ethers: 6.13.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) magic-sdk: 29.0.5 - oidc-client-ts: 2.4.0 + oidc-client-ts: 3.3.0 transitivePeerDependencies: - bufferutil - debug @@ -20978,8 +21145,6 @@ snapshots: dependencies: '@lukeed/csprng': 1.1.0 - '@magic-ext/oidc@12.0.2': {} - '@magic-ext/oidc@12.0.5': {} '@magic-sdk/commons@25.0.5(@magic-sdk/provider@29.0.5(localforage@1.10.0))(@magic-sdk/types@24.18.1)': @@ -21363,6 +21528,8 @@ snapshots: dependencies: eslint-scope: 5.1.1 + '@noble/ciphers@1.3.0': {} + '@noble/curves@1.2.0': dependencies: '@noble/hashes': 1.3.2 @@ -21375,6 +21542,10 @@ snapshots: dependencies: '@noble/hashes': 1.4.0 + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + '@noble/hashes@1.2.0': {} '@noble/hashes@1.3.2': {} @@ -21383,6 +21554,8 @@ snapshots: '@noble/hashes@1.5.0': {} + '@noble/hashes@1.8.0': {} + '@noble/secp256k1@1.7.1': {} '@nodelib/fs.scandir@2.1.5': @@ -21581,7 +21754,7 @@ snapshots: '@nrwl/tao@19.7.3(@swc-node/register@1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.13))(@swc/types@0.1.17)(typescript@5.6.2))(@swc/core@1.9.3(@swc/helpers@0.5.13))': dependencies: nx: 19.7.3(@swc-node/register@1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.13))(@swc/types@0.1.17)(typescript@5.6.2))(@swc/core@1.9.3(@swc/helpers@0.5.13)) - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - '@swc-node/register' - '@swc/core' @@ -21613,7 +21786,7 @@ snapshots: nx: 19.7.3(@swc-node/register@1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.13))(@swc/types@0.1.17)(typescript@5.6.2))(@swc/core@1.9.3(@swc/helpers@0.5.13)) semver: 7.7.1 tmp: 0.2.3 - tslib: 2.6.3 + tslib: 2.7.0 yargs-parser: 21.1.1 '@nx/js@19.7.3(@babel/traverse@7.27.0)(@swc-node/register@1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.13))(@swc/types@0.1.17)(typescript@5.6.2))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@22.7.5)(nx@19.7.3(@swc-node/register@1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.13))(@swc/types@0.1.17)(typescript@5.6.2))(@swc/core@1.9.3(@swc/helpers@0.5.13)))(typescript@5.6.2)': @@ -21696,7 +21869,7 @@ snapshots: chalk: 4.1.2 enquirer: 2.3.6 nx: 19.7.3(@swc-node/register@1.10.9(@swc/core@1.9.3(@swc/helpers@0.5.13))(@swc/types@0.1.17)(typescript@5.6.2))(@swc/core@1.9.3(@swc/helpers@0.5.13)) - tslib: 2.6.3 + tslib: 2.7.0 yargs-parser: 21.1.1 transitivePeerDependencies: - '@swc-node/register' @@ -22902,7 +23075,7 @@ snapshots: '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.22.1 - viem: 2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) + viem: 2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - typescript @@ -22913,6 +23086,8 @@ snapshots: '@scure/base@1.1.7': {} + '@scure/base@1.2.6': {} + '@scure/bip32@1.1.5': dependencies: '@noble/hashes': 1.2.0 @@ -22925,6 +23100,12 @@ snapshots: '@noble/hashes': 1.4.0 '@scure/base': 1.1.7 + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@scure/bip39@1.1.1': dependencies: '@noble/hashes': 1.2.0 @@ -22935,6 +23116,11 @@ snapshots: '@noble/hashes': 1.4.0 '@scure/base': 1.1.7 + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@segment/analytics-core@1.3.0': dependencies: '@lukeed/uuid': 2.0.1 @@ -24551,26 +24737,7 @@ snapshots: graphemer: 1.4.0 ignore: 5.3.1 natural-compare-lite: 1.4.0 - semver: 7.6.3 - tsutils: 3.21.0(typescript@5.6.2) - optionalDependencies: - typescript: 5.6.2 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) - '@typescript-eslint/utils': 5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) - debug: 4.3.7(supports-color@8.1.1) - eslint: 9.16.0(jiti@1.21.0) - graphemer: 1.4.0 - ignore: 5.3.1 - natural-compare-lite: 1.4.0 - semver: 7.6.3 + semver: 7.7.1 tsutils: 3.21.0(typescript@5.6.2) optionalDependencies: typescript: 5.6.2 @@ -24589,7 +24756,7 @@ snapshots: graphemer: 1.4.0 ignore: 5.3.1 natural-compare-lite: 1.4.0 - semver: 7.6.3 + semver: 7.7.1 tsutils: 3.21.0(typescript@5.6.2) optionalDependencies: typescript: 5.6.2 @@ -24749,6 +24916,17 @@ snapshots: transitivePeerDependencies: - hardhat + '@uniswap/swap-router-contracts@1.3.1(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': + dependencies: + '@openzeppelin/contracts': 3.4.2 + '@uniswap/v2-core': 1.0.1 + '@uniswap/v3-core': 1.0.0 + '@uniswap/v3-periphery': 1.4.4 + dotenv: 14.3.2 + hardhat-watcher: 2.5.0(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + transitivePeerDependencies: + - hardhat + '@uniswap/v2-core@1.0.1': {} '@uniswap/v3-core@1.0.0': {} @@ -24782,6 +24960,19 @@ snapshots: transitivePeerDependencies: - hardhat + '@uniswap/v3-sdk@3.10.0(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10))': + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/solidity': 5.7.0 + '@uniswap/sdk-core': 4.0.6 + '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@uniswap/v3-periphery': 1.4.3 + '@uniswap/v3-staker': 1.0.0 + tiny-invariant: 1.3.1 + tiny-warning: 1.0.3 + transitivePeerDependencies: + - hardhat + '@uniswap/v3-staker@1.0.0': dependencies: '@openzeppelin/contracts': 3.4.2 @@ -24799,17 +24990,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@wagmi/connectors@5.1.1(@types/react@18.3.12)(@wagmi/core@2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10))': + '@wagmi/connectors@5.1.1(@types/react@18.3.12)(@wagmi/core@2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10))': dependencies: '@coinbase/wallet-sdk': 4.0.4 '@metamask/sdk': 0.26.5(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.3(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) - '@wagmi/core': 2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@wagmi/core': 2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) '@walletconnect/ethereum-provider': 2.13.0(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(utf-8-validate@5.0.10) '@walletconnect/modal': 2.6.2(@types/react@18.3.12)(react@18.3.1) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) + viem: 2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: @@ -24836,11 +25027,11 @@ snapshots: - utf-8-validate - zod - '@wagmi/core@2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10))': + '@wagmi/core@2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.6.2) - viem: 2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) + viem: 2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) zustand: 4.4.1(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1) optionalDependencies: '@tanstack/query-core': 5.51.15 @@ -25248,7 +25439,7 @@ snapshots: '@yarnpkg/parsers@3.0.0-rc.46': dependencies: js-yaml: 3.14.1 - tslib: 2.6.3 + tslib: 2.7.0 '@zkochan/js-yaml@0.0.7': dependencies: @@ -25267,6 +25458,10 @@ snapshots: optionalDependencies: typescript: 5.6.2 + abitype@1.1.0(typescript@5.6.2): + optionalDependencies: + typescript: 5.6.2 + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -25782,6 +25977,20 @@ snapshots: transitivePeerDependencies: - supports-color + babel-jest@29.7.0(@babel/core@7.26.9): + dependencies: + '@babel/core': 7.26.9 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.26.9) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + optional: true + babel-loader@8.3.0(@babel/core@7.26.9)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)): dependencies: '@babel/core': 7.26.9 @@ -25982,6 +26191,13 @@ snapshots: babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.26.10) + babel-preset-jest@29.6.3(@babel/core@7.26.9): + dependencies: + '@babel/core': 7.26.9 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.26.9) + optional: true + babel-preset-react-app@10.0.1: dependencies: '@babel/core': 7.26.9 @@ -27894,7 +28110,7 @@ snapshots: dependencies: confusing-browser-globals: 1.0.11 eslint: 8.57.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) object.assign: 4.1.5 object.entries: 1.1.8 semver: 6.3.1 @@ -27905,13 +28121,13 @@ snapshots: '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) eslint: 8.57.0 eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) eslint-config-airbnb@19.0.4(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@8.57.0))(eslint-plugin-react-hooks@5.0.0(eslint@8.57.0))(eslint-plugin-react@7.35.0(eslint@8.57.0))(eslint@8.57.0): dependencies: eslint: 8.57.0 eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0(eslint@8.57.0) @@ -27926,7 +28142,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) @@ -27944,8 +28160,8 @@ snapshots: '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0) @@ -27963,8 +28179,8 @@ snapshots: '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0) @@ -27982,7 +28198,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0) @@ -28000,7 +28216,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.0) @@ -28010,45 +28226,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@8.57.0)(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2): - dependencies: - '@babel/core': 7.26.9 - '@babel/eslint-parser': 7.22.9(@babel/core@7.26.9)(eslint@8.57.0) - '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) - babel-preset-react-app: 10.0.1 - confusing-browser-globals: 1.0.11 - eslint: 8.57.0 - eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2) - eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) - eslint-plugin-react: 7.35.0(eslint@8.57.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) - eslint-plugin-testing-library: 5.11.0(eslint@8.57.0)(typescript@5.6.2) - optionalDependencies: - typescript: 5.6.2 - transitivePeerDependencies: - - '@babel/plugin-syntax-flow' - - '@babel/plugin-transform-react-jsx' - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - jest - - supports-color - eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@9.16.0(jiti@1.21.0))(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2): dependencies: '@babel/core': 7.26.9 '@babel/eslint-parser': 7.22.9(@babel/core@7.26.9)(eslint@9.16.0(jiti@1.21.0)) '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) '@typescript-eslint/parser': 5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) babel-preset-react-app: 10.0.1 confusing-browser-globals: 1.0.11 eslint: 9.16.0(jiti@1.21.0) eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@9.16.0(jiti@1.21.0)) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0)) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0)) eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2) eslint-plugin-jsx-a11y: 6.9.0(eslint@9.16.0(jiti@1.21.0)) eslint-plugin-react: 7.35.0(eslint@9.16.0(jiti@1.21.0)) @@ -28064,23 +28253,23 @@ snapshots: - jest - supports-color - eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(eslint@9.16.0(jiti@1.21.0))(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2): + eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(eslint@8.57.0)(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2): dependencies: '@babel/core': 7.26.9 - '@babel/eslint-parser': 7.22.9(@babel/core@7.26.9)(eslint@9.16.0(jiti@1.21.0)) + '@babel/eslint-parser': 7.22.9(@babel/core@7.26.9)(eslint@8.57.0) '@rushstack/eslint-patch': 1.10.4 - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) - '@typescript-eslint/parser': 5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) babel-preset-react-app: 10.0.1 confusing-browser-globals: 1.0.11 - eslint: 9.16.0(jiti@1.21.0) - eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(eslint@9.16.0(jiti@1.21.0)) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0)) - eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2) - eslint-plugin-jsx-a11y: 6.9.0(eslint@9.16.0(jiti@1.21.0)) - eslint-plugin-react: 7.35.0(eslint@9.16.0(jiti@1.21.0)) - eslint-plugin-react-hooks: 4.6.0(eslint@9.16.0(jiti@1.21.0)) - eslint-plugin-testing-library: 5.11.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) + eslint: 8.57.0 + eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) + eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2) + eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) + eslint-plugin-react: 7.35.0(eslint@8.57.0) + eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) + eslint-plugin-testing-library: 5.11.0(eslint@8.57.0)(typescript@5.6.2) optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: @@ -28099,31 +28288,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0): - dependencies: - debug: 4.3.7(supports-color@8.1.1) - enhanced-resolve: 5.15.0 - eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - get-tsconfig: 4.6.2 - globby: 13.2.2 - is-core-module: 2.15.0 - is-glob: 4.0.3 - synckit: 0.8.5 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.7(supports-color@8.1.1) enhanced-resolve: 5.15.0 eslint: 8.57.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) get-tsconfig: 4.6.2 globby: 13.2.2 is-core-module: 2.15.0 @@ -28135,17 +28306,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0) - transitivePeerDependencies: - - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0): dependencies: debug: 3.2.7 @@ -28157,16 +28317,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint@9.16.0(jiti@1.21.0)): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) - eslint: 9.16.0(jiti@1.21.0) - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint@9.16.0(jiti@1.21.0)): dependencies: debug: 3.2.7 @@ -28177,14 +28327,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@8.57.0): - dependencies: - '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.26.10) - '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.10) - eslint: 8.57.0 - lodash: 4.17.21 - string-natural-compare: 3.0.1 - eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@9.16.0(jiti@1.21.0)): dependencies: '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.26.10) @@ -28193,15 +28335,15 @@ snapshots: lodash: 4.17.21 string-natural-compare: 3.0.1 - eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(eslint@9.16.0(jiti@1.21.0)): + eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(eslint@8.57.0): dependencies: '@babel/plugin-syntax-flow': 7.24.7(@babel/core@7.26.9) '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.9) - eslint: 9.16.0(jiti@1.21.0) + eslint: 8.57.0 lodash: 4.17.21 string-natural-compare: 3.0.1 - eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -28211,34 +28353,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - hasown: 2.0.2 - is-core-module: 2.15.0 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.6.2) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0)): - dependencies: - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.16.0(jiti@1.21.0) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint@9.16.0(jiti@1.21.0)) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -28304,17 +28419,6 @@ snapshots: - supports-color - typescript - eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2): - dependencies: - '@typescript-eslint/experimental-utils': 5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) - eslint: 9.16.0(jiti@1.21.0) - optionalDependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2))(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2) - jest: 27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10) - transitivePeerDependencies: - - supports-color - - typescript - eslint-plugin-jsx-a11y@6.9.0(eslint@8.57.0): dependencies: aria-query: 5.1.3 @@ -28378,7 +28482,7 @@ snapshots: dependencies: eslint: 8.57.0 - eslint-plugin-react-refresh@0.4.19(eslint@8.57.0): + eslint-plugin-react-refresh@0.4.24(eslint@8.57.0): dependencies: eslint: 8.57.0 @@ -29728,6 +29832,11 @@ snapshots: chokidar: 3.6.0 hardhat: 2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat-watcher@2.5.0(hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10)): + dependencies: + chokidar: 3.6.0 + hardhat: 2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10) + hardhat@2.22.6(bufferutil@4.0.8)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(typescript@5.6.2)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0 @@ -30578,6 +30687,10 @@ snapshots: dependencies: ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + isows@1.0.7(ws@8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) + istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@4.0.3: @@ -33563,11 +33676,6 @@ snapshots: ohash@1.1.3: {} - oidc-client-ts@2.4.0: - dependencies: - crypto-js: 4.2.0 - jwt-decode: 3.1.2 - oidc-client-ts@3.3.0: dependencies: jwt-decode: 4.0.0 @@ -33689,6 +33797,21 @@ snapshots: outvariant@1.4.0: {} + ox@0.9.6(typescript@5.6.2): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.6.2) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - zod + oxc-resolver@1.11.0: optionalDependencies: '@oxc-resolver/binding-darwin-arm64': 1.11.0 @@ -35006,92 +35129,6 @@ snapshots: '@remix-run/router': 1.7.2 react: 18.3.1 - react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/babel__core@7.20.5)(bufferutil@4.0.8)(esbuild@0.23.1)(eslint@8.57.0)(node-notifier@8.0.2)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(utf-8-validate@5.0.10): - dependencies: - '@babel/core': 7.26.9 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.15.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)))(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - '@svgr/webpack': 5.5.0 - babel-jest: 27.5.1(@babel/core@7.26.9) - babel-loader: 8.3.0(@babel/core@7.26.9)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - babel-plugin-named-asset-import: 0.3.8(@babel/core@7.26.9) - babel-preset-react-app: 10.0.1 - bfj: 7.0.2 - browserslist: 4.23.3 - camelcase: 6.3.0 - case-sensitive-paths-webpack-plugin: 2.4.0 - css-loader: 6.8.1(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - css-minimizer-webpack-plugin: 3.4.1(esbuild@0.23.1)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - dotenv: 10.0.0 - dotenv-expand: 5.1.0 - eslint: 8.57.0 - eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(eslint@8.57.0)(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2) - eslint-webpack-plugin: 3.2.0(eslint@8.57.0)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - file-loader: 6.2.0(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - fs-extra: 10.1.0 - html-webpack-plugin: 5.5.3(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - identity-obj-proxy: 3.0.0 - jest: 27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10) - jest-resolve: 27.5.1 - jest-watch-typeahead: 1.1.0(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10)) - mini-css-extract-plugin: 2.7.6(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - postcss: 8.4.49 - postcss-flexbugs-fixes: 5.0.2(postcss@8.4.49) - postcss-loader: 6.2.1(postcss@8.4.49)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - postcss-normalize: 10.0.1(browserslist@4.23.3)(postcss@8.4.49) - postcss-preset-env: 7.8.3(postcss@8.4.49) - prompts: 2.4.2 - react: 18.3.1 - react-app-polyfill: 3.0.0 - react-dev-utils: 12.0.1(eslint@8.57.0)(typescript@5.6.2)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - react-refresh: 0.11.0 - resolve: 1.22.8 - resolve-url-loader: 4.0.0 - sass-loader: 12.6.0(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - semver: 7.6.3 - source-map-loader: 3.0.2(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - style-loader: 3.3.3(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - tailwindcss: 3.4.7(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)) - terser-webpack-plugin: 5.3.9(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - webpack: 5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1) - webpack-dev-server: 4.15.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - webpack-manifest-plugin: 4.1.1(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - workbox-webpack-plugin: 6.6.0(@types/babel__core@7.20.5)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) - optionalDependencies: - fsevents: 2.3.3 - typescript: 5.6.2 - transitivePeerDependencies: - - '@babel/plugin-syntax-flow' - - '@babel/plugin-transform-react-jsx' - - '@parcel/css' - - '@swc/core' - - '@types/babel__core' - - '@types/webpack' - - bufferutil - - canvas - - clean-css - - csso - - debug - - esbuild - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - fibers - - node-notifier - - node-sass - - rework - - rework-visit - - sass - - sass-embedded - - sockjs-client - - supports-color - - ts-node - - type-fest - - uglify-js - - utf-8-validate - - vue-template-compiler - - webpack-cli - - webpack-hot-middleware - - webpack-plugin-serve - react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.10))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/babel__core@7.20.5)(bufferutil@4.0.8)(esbuild@0.23.1)(eslint@9.16.0(jiti@1.21.0))(node-notifier@8.0.2)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(utf-8-validate@5.0.10): dependencies: '@babel/core': 7.26.9 @@ -35178,7 +35215,7 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/babel__core@7.20.5)(bufferutil@4.0.8)(esbuild@0.23.1)(eslint@9.16.0(jiti@1.21.0))(node-notifier@8.0.2)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(utf-8-validate@5.0.10): + react-scripts@5.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/babel__core@7.20.5)(bufferutil@4.0.8)(esbuild@0.23.1)(eslint@8.57.0)(node-notifier@8.0.2)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(type-fest@2.19.0)(typescript@5.6.2)(utf-8-validate@5.0.10): dependencies: '@babel/core': 7.26.9 '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(react-refresh@0.11.0)(type-fest@2.19.0)(webpack-dev-server@4.15.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)))(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) @@ -35195,9 +35232,9 @@ snapshots: css-minimizer-webpack-plugin: 3.4.1(esbuild@0.23.1)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) dotenv: 10.0.0 dotenv-expand: 5.1.0 - eslint: 9.16.0(jiti@1.21.0) - eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(eslint@9.16.0(jiti@1.21.0))(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2) - eslint-webpack-plugin: 3.2.0(eslint@9.16.0(jiti@1.21.0))(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) + eslint: 8.57.0 + eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.24.7(@babel/core@7.26.9))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.9))(eslint@8.57.0)(jest@27.5.1(bufferutil@4.0.8)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2))(utf-8-validate@5.0.10))(typescript@5.6.2) + eslint-webpack-plugin: 3.2.0(eslint@8.57.0)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) file-loader: 6.2.0(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) fs-extra: 10.1.0 html-webpack-plugin: 5.5.3(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) @@ -35214,7 +35251,7 @@ snapshots: prompts: 2.4.2 react: 18.3.1 react-app-polyfill: 3.0.0 - react-dev-utils: 12.0.1(eslint@9.16.0(jiti@1.21.0))(typescript@5.6.2)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) + react-dev-utils: 12.0.1(eslint@8.57.0)(typescript@5.6.2)(webpack@5.88.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(esbuild@0.23.1)) react-refresh: 0.11.0 resolve: 1.22.8 resolve-url-loader: 4.0.0 @@ -36607,7 +36644,7 @@ snapshots: synckit@0.8.5: dependencies: '@pkgr/utils': 2.4.2 - tslib: 2.6.3 + tslib: 2.7.0 syncpack@13.0.0(typescript@5.6.2): dependencies: @@ -37002,12 +37039,12 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)) + jest: 29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -37022,12 +37059,12 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.26.10) esbuild: 0.23.1 - ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2))(typescript@5.6.2): + ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@20.14.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2) + jest: 29.7.0(@types/node@20.14.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -37042,12 +37079,12 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.26.10) esbuild: 0.23.1 - ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@20.14.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@22.7.5)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.14.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@20.14.13)(typescript@5.6.2)) + jest: 29.7.0(@types/node@22.7.5)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.6.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -37062,12 +37099,12 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.26.10) esbuild: 0.23.1 - ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(esbuild@0.23.1)(jest@29.7.0(@types/node@22.7.5)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.6.2)))(typescript@5.6.2): + ts-jest@29.2.5(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.23.1)(jest@29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.7.5)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@22.7.5)(typescript@5.6.2)) + jest: 29.7.0(@types/node@18.15.13)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.9.3(@swc/helpers@0.5.13))(@types/node@18.15.13)(typescript@5.6.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -37076,10 +37113,10 @@ snapshots: typescript: 5.6.2 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.26.10 + '@babel/core': 7.26.9 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.10) + babel-jest: 29.7.0(@babel/core@7.26.9) esbuild: 0.23.1 ts-mockito@2.6.1: @@ -37670,6 +37707,23 @@ snapshots: - utf-8-validate - zod + viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.1.0(typescript@5.6.2) + isows: 1.0.7(ws@8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + ox: 0.9.6(typescript@5.6.2) + ws: 8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + vite-plugin-node-polyfills@0.16.0(rollup@4.28.0)(vite@5.4.7(@types/node@18.15.13)(lightningcss@1.21.5)(terser@5.34.1)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.28.0) @@ -37709,14 +37763,14 @@ snapshots: dependencies: xml-name-validator: 4.0.0 - wagmi@2.12.1(@tanstack/query-core@5.51.15)(@tanstack/react-query@5.51.15(react@18.3.1))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)): + wagmi@2.12.1(@tanstack/query-core@5.51.15)(@tanstack/react-query@5.51.15(react@18.3.1))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(immer@9.0.21)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)): dependencies: '@tanstack/react-query': 5.51.15(react@18.3.1) - '@wagmi/connectors': 5.1.1(@types/react@18.3.12)(@wagmi/core@2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) - '@wagmi/core': 2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@wagmi/connectors': 5.1.1(@types/react@18.3.12)(@wagmi/core@2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)(typescript@5.6.2)(utf-8-validate@5.0.10))(react@18.3.1)(rollup@4.28.0)(typescript@5.6.2)(utf-8-validate@5.0.10)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) + '@wagmi/core': 2.13.1(@tanstack/query-core@5.51.15)(@types/react@18.3.12)(immer@9.0.21)(react@18.3.1)(typescript@5.6.2)(viem@2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10)) react: 18.3.1 use-sync-external-store: 1.2.0(react@18.3.1) - viem: 2.18.2(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) + viem: 2.39.0(bufferutil@4.0.8)(typescript@5.6.2)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: @@ -38207,6 +38261,11 @@ snapshots: bufferutil: 4.0.8 utf-8-validate: 5.0.10 + ws@8.18.3(bufferutil@4.0.8)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + ws@8.5.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.8 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9249cc5540..ed5f607eae 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -23,6 +23,8 @@ packages: - "packages/game-bridge" - "packages/webhook/sdk" - "packages/minting-backend/sdk" + - "pkg/auth" + - "pkg/wallet" - "tests/**" - "examples/passport/**" - "examples/orderbook/**"