From 6d8d0fcbb911f453090a4e46ab1da5820d15d600 Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Sun, 12 Apr 2026 23:26:04 +0100 Subject: [PATCH 01/12] feat: fix stellar routing issues --- apps/backend-relayer/.env.example | 4 +- apps/backend-relayer/Dockerfile | 56 ++ apps/backend-relayer/README.md | 3 +- apps/backend-relayer/docker-compose.e2e.yaml | 46 ++ apps/backend-relayer/package.json | 1 - apps/backend-relayer/src/libs/configs.ts | 2 +- .../src/modules/ads/ad.service.ts | 74 +- .../src/modules/ads/dto/ad.dto.ts | 19 +- .../auth/stellar/stellar-auth.service.spec.ts | 10 +- .../src/modules/trades/trade.service.ts | 110 +-- .../src/providers/stellar/stellar.service.ts | 2 + .../src/providers/stellar/utils/eip712.ts | 6 +- .../src/providers/viem/ethers/typedData.ts | 37 + apps/backend-relayer/test/integrations/api.ts | 152 ----- .../eth-stellar.e2e-integration.ts | 404 ----------- .../test/integrations/jest-e2e.json | 17 - .../backend-relayer/test/setups/create-app.ts | 27 +- .../test/setups/evm-actions.ts | 639 ------------------ .../test/setups/evm-deployed-contracts.json | 11 - apps/backend-relayer/test/setups/evm-setup.ts | 118 ---- .../test/setups/jest-e2e.setup.ts | 8 + .../test/setups/jest-integrations.setup.ts | 64 -- .../test/setups/mock-chain-adapter.ts | 4 +- apps/backend-relayer/test/setups/seed.ts | 67 +- .../test/setups/stellar-actions.ts | 260 ------- .../test/setups/stellar-setup.ts | 300 -------- apps/backend-relayer/test/setups/utils.ts | 90 +-- 27 files changed, 330 insertions(+), 2201 deletions(-) create mode 100644 apps/backend-relayer/Dockerfile create mode 100644 apps/backend-relayer/docker-compose.e2e.yaml delete mode 100644 apps/backend-relayer/test/integrations/api.ts delete mode 100644 apps/backend-relayer/test/integrations/eth-stellar.e2e-integration.ts delete mode 100644 apps/backend-relayer/test/integrations/jest-e2e.json delete mode 100644 apps/backend-relayer/test/setups/evm-actions.ts delete mode 100644 apps/backend-relayer/test/setups/evm-deployed-contracts.json delete mode 100644 apps/backend-relayer/test/setups/evm-setup.ts delete mode 100644 apps/backend-relayer/test/setups/jest-integrations.setup.ts delete mode 100644 apps/backend-relayer/test/setups/stellar-actions.ts delete mode 100644 apps/backend-relayer/test/setups/stellar-setup.ts diff --git a/apps/backend-relayer/.env.example b/apps/backend-relayer/.env.example index df6278a..df85990 100644 --- a/apps/backend-relayer/.env.example +++ b/apps/backend-relayer/.env.example @@ -1,8 +1,8 @@ -ADMIN_SECRET="" +EVM_ADMIN_PRIVATE_KEY="" DATABASE_URL="" EVM_RPC_API_KEY="" JWT_EXPIRY="" -JWT_REFRESH_EXPIRTY="" +JWT_REFRESH_EXPIRY="" JWT_SECRET="" NODE_ENV="" PORT="" diff --git a/apps/backend-relayer/Dockerfile b/apps/backend-relayer/Dockerfile new file mode 100644 index 0000000..85608a0 --- /dev/null +++ b/apps/backend-relayer/Dockerfile @@ -0,0 +1,56 @@ +# syntax=docker/dockerfile:1.7 + +# ----------------------------------------------------------------------------- +# Stage 1 — builder +# ----------------------------------------------------------------------------- +FROM node:20-alpine AS builder + +RUN apk add --no-cache python3 make g++ openssl libc6-compat + +RUN corepack enable && corepack prepare pnpm@10.10.0 --activate + +WORKDIR /repo + +# Copy workspace manifests first for better cache hits. +COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./ +COPY apps/backend-relayer/package.json apps/backend-relayer/ +COPY packages/proofbridge_mmr/package.json packages/proofbridge_mmr/ + +# Prefetch all workspace deps once. +RUN pnpm fetch --ignore-scripts + +# Now pull in the sources we need to build. +COPY apps/backend-relayer apps/backend-relayer +COPY packages/proofbridge_mmr packages/proofbridge_mmr + +# Install & build the relayer (and only the relayer — avoid compiling every +# workspace package). +RUN pnpm install --frozen-lockfile --offline --ignore-scripts \ + && pnpm --filter backend-relayer exec prisma generate \ + && pnpm --filter backend-relayer build + +# Produce a self-contained runtime dir with only the relayer's prod deps. +RUN pnpm --filter backend-relayer deploy --prod --legacy /out + +# ----------------------------------------------------------------------------- +# Stage 2 — runtime +# ----------------------------------------------------------------------------- +FROM node:20-alpine AS runtime + +RUN apk add --no-cache openssl libc6-compat tini + +WORKDIR /app + +COPY --from=builder /out/package.json ./package.json +COPY --from=builder /out/node_modules ./node_modules +COPY --from=builder /out/dist ./dist +COPY --from=builder /out/prisma ./prisma + +ENV NODE_ENV=production +ENV PORT=2005 +EXPOSE 2005 + +# `tini` keeps signal forwarding sane. Migrations run on every boot — idempotent +# for an already-migrated DB. +ENTRYPOINT ["/sbin/tini", "--"] +CMD ["sh", "-c", "npx prisma migrate deploy && node dist/main.js"] diff --git a/apps/backend-relayer/README.md b/apps/backend-relayer/README.md index 1da1f30..8e85f53 100644 --- a/apps/backend-relayer/README.md +++ b/apps/backend-relayer/README.md @@ -282,7 +282,8 @@ pnpm run start:prod **Core Configuration**: ```bash -ADMIN_SECRET="" +EVM_ADMIN_PRIVATE_KEY="" +STELLAR_ADMIN_SECRET="" DATABASE_URL="" EVM_RPC_API_KEY="" JWT_EXPIRY="" diff --git a/apps/backend-relayer/docker-compose.e2e.yaml b/apps/backend-relayer/docker-compose.e2e.yaml new file mode 100644 index 0000000..0e981bc --- /dev/null +++ b/apps/backend-relayer/docker-compose.e2e.yaml @@ -0,0 +1,46 @@ +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_USER: relayer + POSTGRES_PASSWORD: relayer + POSTGRES_DB: relayer + ports: + - "5433:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U relayer -d relayer"] + interval: 2s + timeout: 3s + retries: 30 + + backend-relayer: + build: + context: ../.. + dockerfile: apps/backend-relayer/Dockerfile + depends_on: + postgres: + condition: service_healthy + environment: + NODE_ENV: production + PORT: 2005 + DATABASE_URL: postgresql://relayer:relayer@postgres:5432/relayer + JWT_ACCESS_SECRET: test-access-secret + JWT_REFRESH_SECRET: test-refresh-secret + SIGN_DOMAIN: proofbridge.xyz + SIGN_URI: https://proofbridge.xyz + STELLAR_AUTH_SECRET: ${STELLAR_AUTH_SECRET:-SA3C2KPR5TCHYJ5TNQXAY2776Z3H4CB723GDCAMEX5I2NLWP25QUYB3X} + SECRET_KEY: ${SECRET_KEY:-0xfdba5a242ddce02cd1d585297aa4afe5aa2831391198746c680a3e16a41676dc} + # Chain RPCs live on the host — rewrite localhost → host.docker.internal. + ETHEREUM_RPC_URL: http://host.docker.internal:9545 + STELLAR_RPC_URL: http://host.docker.internal:8000/soroban/rpc + STELLAR_NETWORK_PASSPHRASE: ${STELLAR_NETWORK_PASSPHRASE:-Standalone Network ; February 2017} + STELLAR_ADMIN_SECRET: ${STELLAR_ADMIN_SECRET} + extra_hosts: + - "host.docker.internal:host-gateway" + ports: + - "2005:2005" + healthcheck: + test: ["CMD-SHELL", "wget -q -O- http://localhost:2005/health || exit 1"] + interval: 3s + timeout: 5s + retries: 40 diff --git a/apps/backend-relayer/package.json b/apps/backend-relayer/package.json index 9082ad2..57714ad 100644 --- a/apps/backend-relayer/package.json +++ b/apps/backend-relayer/package.json @@ -18,7 +18,6 @@ "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/e2e/jest-e2e.json", - "test:integrations": "jest --config ./test/integrations/jest-e2e.json", "prisma:migrate": "prisma migrate deploy" }, "dependencies": { diff --git a/apps/backend-relayer/src/libs/configs.ts b/apps/backend-relayer/src/libs/configs.ts index a6ed405..c8093f5 100644 --- a/apps/backend-relayer/src/libs/configs.ts +++ b/apps/backend-relayer/src/libs/configs.ts @@ -18,7 +18,7 @@ export const env = { port: process.env.PORT || 9090, appDomain: process.env.SIGN_DOMAIN || 'proofbridge.xyz', appUri: process.env.SIGN_URI || 'https://proofbridge.xyz', - admin: process.env.ADMIN_SECRET || '', + admin: process.env.EVM_ADMIN_PRIVATE_KEY || '', secretKey: process.env.SECRET_KEY || '32_byte_secret_key_for_aes!', evmRpcApiKey: process.env.EVM_RPC_API_KEY || '', rpcUrlHedera: process.env.RPC_URL_HEDERA || '', diff --git a/apps/backend-relayer/src/modules/ads/ad.service.ts b/apps/backend-relayer/src/modules/ads/ad.service.ts index dca9b0f..499616c 100644 --- a/apps/backend-relayer/src/modules/ads/ad.service.ts +++ b/apps/backend-relayer/src/modules/ads/ad.service.ts @@ -16,11 +16,13 @@ import { ConfirmAdActionDto, CloseAdDto, } from './dto/ad.dto'; -import { getAddress } from 'viem'; import { AdStatus, Prisma } from '@prisma/client'; import { Request } from 'express'; import { ChainAdapterService } from '../../chain-adapters/chain-adapter.service'; -import { toBytes32 } from '../../providers/viem/ethers/typedData'; +import { + normalizeChainAddress, + toBytes32, +} from '../../providers/viem/ethers/typedData'; import { randomUUID } from 'crypto'; type AdQueryInput = { @@ -343,7 +345,7 @@ export class AdsService { select: { id: true, symbol: true, - chain: { select: { chainId: true } }, + chain: { select: { chainId: true, kind: true } }, }, }, }, @@ -387,6 +389,18 @@ export class AdsService { ); } + // creatorDstAddress lives on the order chain (where the ad creator + // receives proceeds on unlock), so validate against that chain's kind. + let normalizedCreatorDst: string; + try { + normalizedCreatorDst = normalizeChainAddress( + dto.creatorDstAddress, + route.orderToken.chain.kind, + ); + } catch { + throw new BadRequestException('Invalid creatorDstAddress'); + } + const adId = randomUUID(); const reqContractDetails = await this.chainAdapters @@ -399,15 +413,15 @@ export class AdsService { orderChainId: route.orderToken.chain.chainId, adToken: route.adToken.address as `0x${string}`, initialAmount: fundAmount.toFixed(0), - adRecipient: toBytes32(dto.creatorDstAddress), + adRecipient: toBytes32(normalizedCreatorDst), }); const requestDetails = await this.prisma.$transaction(async (prisma) => { const ad = await prisma.ad.create({ data: { id: adId, - creatorAddress: getAddress(user.walletAddress), - creatorDstAddress: getAddress(dto.creatorDstAddress), + creatorAddress: normalizeChainAddress(user.walletAddress), + creatorDstAddress: normalizedCreatorDst, routeId: route.id, adTokenId: route.adToken.id, orderTokenId: route.orderToken.id, @@ -482,7 +496,10 @@ export class AdsService { if (!user) throw new ForbiddenException('Unauthorized'); const ad = await this.prisma.ad.findUnique({ - where: { id, creatorAddress: getAddress(user.walletAddress) }, + where: { + id, + creatorAddress: normalizeChainAddress(user.walletAddress), + }, select: { id: true, creatorAddress: true, @@ -601,7 +618,10 @@ export class AdsService { if (!user) throw new ForbiddenException('Unauthorized'); const ad = await this.prisma.ad.findUnique({ - where: { id, creatorAddress: getAddress(user.walletAddress) }, + where: { + id, + creatorAddress: normalizeChainAddress(user.walletAddress), + }, select: { id: true, creatorAddress: true, @@ -648,8 +668,6 @@ export class AdsService { const locked = lockSum._sum.amount ?? new Prisma.Decimal(0); const available = ad.poolAmount.sub(locked); - console.log(available, withdrawAmt); - if (withdrawAmt.gt(available)) throw new BadRequestException('Insufficient available balance'); @@ -657,6 +675,16 @@ export class AdsService { ? 'EXHAUSTED' : ad.status; + let normalizedTo: string; + try { + normalizedTo = normalizeChainAddress( + dto.to, + ad.route.adToken.chain.kind, + ); + } catch { + throw new BadRequestException('Invalid withdraw destination'); + } + const reqContractDetails = await this.chainAdapters .forChain(ad.route.adToken.chain.kind) .getWithdrawFromAdRequestContractDetails({ @@ -665,7 +693,7 @@ export class AdsService { adChainId: ad.route.adToken.chain.chainId, adId: ad.id, amount: withdrawAmt.toFixed(0), - to: dto.to as `0x${string}`, + to: normalizedTo as `0x${string}`, }); const finalValue = ad.poolAmount.sub(withdrawAmt); @@ -812,7 +840,10 @@ export class AdsService { if (!user) throw new ForbiddenException('Unauthorized'); const ad = await this.prisma.ad.findFirst({ - where: { id, creatorAddress: getAddress(user.walletAddress) }, + where: { + id, + creatorAddress: normalizeChainAddress(user.walletAddress), + }, select: { id: true, creatorAddress: true, @@ -861,6 +892,16 @@ export class AdsService { throw new BadRequestException('Ad is already closed'); } + let normalizedTo: string; + try { + normalizedTo = normalizeChainAddress( + dto.to, + ad.route.adToken.chain.kind, + ); + } catch { + throw new BadRequestException('Invalid close destination'); + } + const reqContractDetails = await this.chainAdapters .forChain(ad.route.adToken.chain.kind) .getCloseAdRequestContractDetails({ @@ -868,7 +909,7 @@ export class AdsService { .adManagerAddress as `0x${string}`, adChainId: ad.route.adToken.chain.chainId, adId: ad.id, - to: dto.to as `0x${string}`, + to: normalizedTo as `0x${string}`, }); await this.prisma.$transaction(async (prisma) => { @@ -947,8 +988,8 @@ export class AdsService { if (!adLogUpdate) throw new NotFoundException('Ad update log not found'); if ( - getAddress(adLogUpdate.ad.creatorAddress) !== - getAddress(user.walletAddress) + normalizeChainAddress(adLogUpdate.ad.creatorAddress) !== + normalizeChainAddress(user.walletAddress) ) { throw new ForbiddenException('Unauthorized'); } @@ -1015,11 +1056,10 @@ export class AdsService { this.prisma.adUpdateLog.delete({ where: { id: adLogUpdate.id } }), ]); - console.log(dto); - return { adId: adId, success: true, + dto: dto, }; } catch (e) { if (e instanceof Error) { diff --git a/apps/backend-relayer/src/modules/ads/dto/ad.dto.ts b/apps/backend-relayer/src/modules/ads/dto/ad.dto.ts index 9bbb089..ed04d09 100644 --- a/apps/backend-relayer/src/modules/ads/dto/ad.dto.ts +++ b/apps/backend-relayer/src/modules/ads/dto/ad.dto.ts @@ -1,4 +1,11 @@ -import { IsIn, IsInt, IsOptional, IsString, IsUUID, Matches } from 'class-validator'; +import { + IsIn, + IsInt, + IsOptional, + IsString, + IsUUID, + Matches, +} from 'class-validator'; import { Transform } from 'class-transformer'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { JsonObject, JsonArray } from '@prisma/client/runtime/library'; @@ -205,7 +212,7 @@ export class CloseAdDto { example: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', }) @IsString() - to: string; + to!: string; } export class ConfirmAdActionDto { @@ -326,7 +333,7 @@ export class ListAdResponseDto { data!: AdResponseDto[]; @ApiProperty({ type: String, nullable: true }) - nextCursor: string | null; + nextCursor!: string | null; } export class CreateAdResponseDto { @@ -349,6 +356,12 @@ export class CreateAdResponseDto { }) signature!: `0x${string}`; + @ApiPropertyOptional({ + description: + 'Signer public key (Stellar chains only — 0x-prefixed 32-byte hex of the relayer signer)', + }) + signerPublicKey?: string; + @ApiProperty({ description: 'Request auth token', example: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', diff --git a/apps/backend-relayer/src/modules/auth/stellar/stellar-auth.service.spec.ts b/apps/backend-relayer/src/modules/auth/stellar/stellar-auth.service.spec.ts index bd1d225..3449c1b 100644 --- a/apps/backend-relayer/src/modules/auth/stellar/stellar-auth.service.spec.ts +++ b/apps/backend-relayer/src/modules/auth/stellar/stellar-auth.service.spec.ts @@ -51,11 +51,11 @@ describe('StellarAuthService (SEP-10)', () => { describe('verifyLogin', () => { const sign = (xdr: string, kp: Keypair) => { - const tx = TransactionBuilder.fromXDR( - xdr, - (process.env.STELLAR_NETWORK_PASSPHRASE as Networks) ?? - Networks.TESTNET, - ); + // Match the service's fallback: configs.ts uses `||` so an empty env + // var falls back to TESTNET. `??` would keep `""` and mismatch. + const passphrase = + (process.env.STELLAR_NETWORK_PASSPHRASE as Networks) || Networks.TESTNET; + const tx = TransactionBuilder.fromXDR(xdr, passphrase); tx.sign(kp); return tx.toEnvelope().toXDR('base64'); }; diff --git a/apps/backend-relayer/src/modules/trades/trade.service.ts b/apps/backend-relayer/src/modules/trades/trade.service.ts index 969d64f..cd0a3f8 100644 --- a/apps/backend-relayer/src/modules/trades/trade.service.ts +++ b/apps/backend-relayer/src/modules/trades/trade.service.ts @@ -14,7 +14,6 @@ import { QueryTradesDto, UnlockTradeDto, } from './dto/trade.dto'; -import { getAddress, isAddress } from 'viem'; import { Request } from 'express'; import { ChainAdapterService } from '../../chain-adapters/chain-adapter.service'; import { MMRService } from '../mmr/mmr.service'; @@ -22,7 +21,11 @@ import { ProofService } from '../../providers/noir/proof.service'; import { randomUUID } from 'crypto'; import { Prisma, TradeStatus } from '@prisma/client'; import { EncryptionService } from '@libs/encryption.service'; -import { toBytes32, uuidToBigInt } from '../../providers/viem/ethers/typedData'; +import { + normalizeChainAddress, + toBytes32, + uuidToBigInt, +} from '../../providers/viem/ethers/typedData'; @Injectable() export class TradesService { @@ -139,8 +142,9 @@ export class TradesService { if (q.routeId) where.routeId = q.routeId; if (q.adId) where.adId = q.adId; if (q.adCreatorAddress) - where.adCreatorAddress = getAddress(q.adCreatorAddress); - if (q.bridgerAddress) where.bridgerAddress = getAddress(q.bridgerAddress); + where.adCreatorAddress = normalizeChainAddress(q.adCreatorAddress); + if (q.bridgerAddress) + where.bridgerAddress = normalizeChainAddress(q.bridgerAddress); if (q.adTokenId || q.orderTokenId) { where.route = { @@ -274,10 +278,6 @@ export class TradesService { if (!user) throw new UnauthorizedException('Unauthorized'); - if (!isAddress(dto.bridgerDstAddress)) { - throw new BadRequestException('Invalid address'); - } - const ad = await this.prisma.ad .findUnique({ where: { id: dto.adId }, @@ -305,6 +305,7 @@ export class TradesService { select: { adManagerAddress: true, chainId: true, + kind: true, }, }, }, @@ -346,6 +347,18 @@ export class TradesService { ); } + // bridgerDstAddress lives on the ad chain (where the bridger receives + // the locked tokens), so validate against that chain's kind. + let normalizedBridgerDst: string; + try { + normalizedBridgerDst = normalizeChainAddress( + dto.bridgerDstAddress, + ad.route.adToken.chain.kind, + ); + } catch { + throw new BadRequestException('Invalid bridgerDstAddress'); + } + const amount = new Prisma.Decimal(dto.amount); if (ad.minAmount && amount.lt(ad.minAmount)) { throw new BadRequestException('Amount below minAmount'); @@ -382,7 +395,7 @@ export class TradesService { orderPortal: toBytes32( ad.route.orderToken.chain.orderPortalAddress, ), - orderRecipient: toBytes32(dto.bridgerDstAddress), + orderRecipient: toBytes32(normalizedBridgerDst), adChainId: ad.route.adToken.chain.chainId.toString(), adManager: toBytes32(ad.route.adToken.chain.adManagerAddress), adId: ad.id, @@ -400,10 +413,10 @@ export class TradesService { adId: ad.id, routeId: ad.route.id, amount: amount.toFixed(0), - adCreatorAddress: getAddress(ad.creatorAddress), - adCreatorDstAddress: getAddress(ad.creatorDstAddress), - bridgerAddress: getAddress(user.walletAddress), - bridgerDstAddress: getAddress(dto.bridgerDstAddress), + adCreatorAddress: normalizeChainAddress(ad.creatorAddress), + adCreatorDstAddress: normalizeChainAddress(ad.creatorDstAddress), + bridgerAddress: normalizeChainAddress(user.walletAddress), + bridgerDstAddress: normalizedBridgerDst, orderHash: reqContractDetails.orderHash, }, select: { id: true, status: true }, @@ -525,27 +538,30 @@ export class TradesService { if (!trade) throw new NotFoundException('Trade not found'); if ( - getAddress(trade.bridgerAddress) !== getAddress(user.walletAddress) && - getAddress(trade.adCreatorAddress) !== getAddress(user.walletAddress) + normalizeChainAddress(trade.bridgerAddress) !== + normalizeChainAddress(user.walletAddress) && + normalizeChainAddress(trade.adCreatorAddress) !== + normalizeChainAddress(user.walletAddress) ) { throw new ForbiddenException('Unauthorized'); } + // All address-like fields are declared bytes32 in the cross-chain + // Order typed-data (EVM addresses left-padded; Stellar accounts already + // 32 bytes), so return the padded wire form here. return { - orderChainToken: getAddress(trade.route.orderToken.address), - adChainToken: getAddress(trade.route.adToken.address), + orderChainToken: toBytes32(trade.route.orderToken.address), + adChainToken: toBytes32(trade.route.adToken.address), amount: trade.amount.toFixed(0), - bridger: getAddress(trade.bridgerAddress), + bridger: toBytes32(trade.bridgerAddress), orderChainId: trade.route.orderToken.chain.chainId.toString(), - orderPortal: getAddress( - trade.route.orderToken.chain.orderPortalAddress, - ), - orderRecipient: getAddress(trade.bridgerDstAddress), + orderPortal: toBytes32(trade.route.orderToken.chain.orderPortalAddress), + orderRecipient: toBytes32(trade.bridgerDstAddress), adChainId: trade.route.adToken.chain.chainId.toString(), - adManager: getAddress(trade.route.adToken.chain.adManagerAddress), + adManager: toBytes32(trade.route.adToken.chain.adManagerAddress), adId: trade.adId, - adCreator: getAddress(trade.adCreatorAddress), - adRecipient: getAddress(trade.adCreatorDstAddress), + adCreator: toBytes32(trade.adCreatorAddress), + adRecipient: toBytes32(trade.adCreatorDstAddress), salt: uuidToBigInt(trade.id).toString(), }; } catch (e) { @@ -581,7 +597,7 @@ export class TradesService { const trade = await this.prisma.trade.findFirst({ where: { id: tradeId, - adCreatorAddress: getAddress(user.walletAddress), + adCreatorAddress: normalizeChainAddress(user.walletAddress), }, select: { id: true, @@ -776,11 +792,11 @@ export class TradesService { if (!trade) throw new NotFoundException('Trade not found'); - const caller = getAddress(user.walletAddress); + const caller = normalizeChainAddress(user.walletAddress); const isParty = - caller === getAddress(trade.bridgerAddress) || - caller === getAddress(trade.adCreatorAddress); + caller === normalizeChainAddress(trade.bridgerAddress) || + caller === normalizeChainAddress(trade.adCreatorAddress); if (!isParty) throw new UnauthorizedException('Not a participant'); @@ -791,16 +807,25 @@ export class TradesService { ); } - const isAdCreator = caller === getAddress(trade.adCreatorAddress); + const isAdCreator = + caller === normalizeChainAddress(trade.adCreatorAddress); const unlockChain = isAdCreator ? trade.route.orderToken.chain : trade.route.adToken.chain; + // The on-chain contract recovers the unlocker's *destination* address on + // the unlock chain from the signature. Caller's walletAddress is their + // origin-chain wallet, which is the wrong format when unlocking cross-chain. + const unlockSigner = normalizeChainAddress( + isAdCreator ? trade.adCreatorDstAddress : trade.bridgerDstAddress, + unlockChain.kind, + ); + const isAuthorized = this.chainAdapters .forChain(unlockChain.kind) .verifyOrderSignature( - caller, + unlockSigner as `0x${string}`, trade.orderHash as `0x${string}`, dto.signature as `0x${string}`, ); @@ -963,10 +988,10 @@ export class TradesService { throw new NotFoundException('Trade update log not found'); if ( - getAddress(tradeLogUpdate.trade.bridgerAddress) !== - getAddress(user.walletAddress) && - getAddress(tradeLogUpdate.trade.adCreatorAddress) !== - getAddress(user.walletAddress) + normalizeChainAddress(tradeLogUpdate.trade.bridgerAddress) !== + normalizeChainAddress(user.walletAddress) && + normalizeChainAddress(tradeLogUpdate.trade.adCreatorAddress) !== + normalizeChainAddress(user.walletAddress) ) { throw new ForbiddenException('Unauthorized'); } @@ -1119,7 +1144,7 @@ export class TradesService { const authorizationLog = await this.prisma.authorizationLog.findFirst({ where: { tradeId: tradeId, - userAddress: getAddress(user.walletAddress), + userAddress: normalizeChainAddress(user.walletAddress), }, orderBy: { createdAt: 'desc' }, include: { trade: true }, @@ -1129,10 +1154,10 @@ export class TradesService { throw new NotFoundException('Authorization log not found'); if ( - getAddress(authorizationLog.trade.bridgerAddress) !== - getAddress(user.walletAddress) && - getAddress(authorizationLog.trade.adCreatorAddress) !== - getAddress(user.walletAddress) + normalizeChainAddress(authorizationLog.trade.bridgerAddress) !== + normalizeChainAddress(user.walletAddress) && + normalizeChainAddress(authorizationLog.trade.adCreatorAddress) !== + normalizeChainAddress(user.walletAddress) ) { throw new ForbiddenException('Unauthorized'); } @@ -1202,10 +1227,11 @@ export class TradesService { } } - const caller = getAddress(user.walletAddress); + const caller = normalizeChainAddress(user.walletAddress); const isAdCreator = - caller === getAddress(authorizationLog.trade.adCreatorAddress); + caller === + normalizeChainAddress(authorizationLog.trade.adCreatorAddress); const updatedTrade = await this.prisma.trade.update({ where: { id: authorizationLog.tradeId }, diff --git a/apps/backend-relayer/src/providers/stellar/stellar.service.ts b/apps/backend-relayer/src/providers/stellar/stellar.service.ts index 18d830b..64f2f47 100644 --- a/apps/backend-relayer/src/providers/stellar/stellar.service.ts +++ b/apps/backend-relayer/src/providers/stellar/stellar.service.ts @@ -117,6 +117,7 @@ export class StellarService { } { const signer = this.getSigner(); const sig = signEd25519(message, signer.seed); + return { signature: `0x${sig.toString('hex')}`, signerPublicKey: `0x${signer.publicKey.toString('hex')}`, @@ -166,6 +167,7 @@ export class StellarService { contractAddress: hex32ToBuffer(adContractAddress), }); const { signature, signerPublicKey } = this.sign(message); + return Promise.resolve({ chainId: adChainId.toString(), contractAddress: adContractAddress, diff --git a/apps/backend-relayer/src/providers/stellar/utils/eip712.ts b/apps/backend-relayer/src/providers/stellar/utils/eip712.ts index 9afd147..0795c9e 100644 --- a/apps/backend-relayer/src/providers/stellar/utils/eip712.ts +++ b/apps/backend-relayer/src/providers/stellar/utils/eip712.ts @@ -6,6 +6,7 @@ import { keccak256 } from 'viem'; import { T_OrderParams } from '../../../chain-adapters/types'; +import { uuidToBigInt } from '../../viem/ethers/typedData'; function keccak(data: Buffer): Buffer { const hex = keccak256(`0x${data.toString('hex')}`); @@ -55,7 +56,10 @@ function structHashOrder(p: T_OrderParams): Buffer { keccak(Buffer.from(p.adId)), hexToBytes32(p.adCreator), hexToBytes32(p.adRecipient), - u256BE(BigInt(p.salt)), + // salt is carried as a UUID string across the API; both EVM typedData + // and the Stellar contracts encode it as a uint256 derived from the + // raw 128-bit UUID. Use uuidToBigInt to match. + u256BE(uuidToBigInt(p.salt)), ]), ); } diff --git a/apps/backend-relayer/src/providers/viem/ethers/typedData.ts b/apps/backend-relayer/src/providers/viem/ethers/typedData.ts index b5f971e..66b742c 100644 --- a/apps/backend-relayer/src/providers/viem/ethers/typedData.ts +++ b/apps/backend-relayer/src/providers/viem/ethers/typedData.ts @@ -1,10 +1,14 @@ import { TypedDataEncoder, Wallet, recoverAddress } from 'ethers'; +import { ChainKind } from '@prisma/client'; +import { StrKey } from '@stellar/stellar-sdk'; +import { getAddress, isAddress } from 'viem'; import { Bytes32Hex, T_AdManagerOrderParams, T_OrderParams, T_OrderPortalParams, } from '../../../chain-adapters/types'; +import { accountIdToHex32 } from '../../stellar/utils/address'; // Left-pad a 20-byte EVM address to 32 bytes (the cross-chain wire format). // Accepts an already-32-byte hex string and returns it unchanged. Throws on @@ -16,6 +20,39 @@ export function toBytes32(value: string): Bytes32Hex { throw new Error(`toBytes32: expected 20- or 32-byte hex, got ${value}`); } +// Chain-aware canonicalization for addresses. Returns the storage-canonical +// form: +// EVM — EIP-55 20-byte hex +// STELLAR — lowercased 0x-prefixed 32-byte hex of the account public key +// +// When `chainKind` is provided, the native form is accepted (EVM 20-byte hex +// or Stellar G-strkey) alongside the 32-byte hex wire form. +// When `chainKind` is omitted, the chain is inferred from the canonical +// stored form: 40 hex chars → EVM, 64 hex chars → Stellar. Use this variant +// for values already read from the DB where the chain is implicit. +// Throws if the value is not a valid address for the given / inferred chain. +export function normalizeChainAddress( + value: string, + chainKind?: ChainKind, +): string { + if (chainKind === ChainKind.EVM) { + if (isAddress(value)) return getAddress(value); + throw new Error(`normalizeChainAddress: invalid EVM address ${value}`); + } + if (chainKind === ChainKind.STELLAR) { + if (StrKey.isValidEd25519PublicKey(value)) return accountIdToHex32(value); + const hex = value.replace(/^0x/i, ''); + if (/^[a-fA-F0-9]{64}$/.test(hex)) return `0x${hex.toLowerCase()}`; + throw new Error(`normalizeChainAddress: invalid Stellar address ${value}`); + } + const hex = value.replace(/^0x/i, ''); + if (hex.length === 40) return getAddress(`0x${hex}`); + if (hex.length === 64) return `0x${hex.toLowerCase()}`; + throw new Error( + `normalizeChainAddress: cannot infer chain from ${value}; pass chainKind`, + ); +} + // ---------------------------- // OrderPortal typed data // ---------------------------- diff --git a/apps/backend-relayer/test/integrations/api.ts b/apps/backend-relayer/test/integrations/api.ts deleted file mode 100644 index c7ae2a2..0000000 --- a/apps/backend-relayer/test/integrations/api.ts +++ /dev/null @@ -1,152 +0,0 @@ -import request from 'supertest'; -import { INestApplication } from '@nestjs/common'; -import { parseEther } from 'viem'; - -export const getRoutes = ( - app: INestApplication, - fromId: string, - toId: string, -) => - request(app.getHttpServer()) - .get('/v1/routes') - .query({ adChainId: fromId, orderChainId: toId }); - -export const apiCreateAd = ( - app: INestApplication, - access: string, - routeId: string, - dst: `0x${string}`, - fundAmount: string, -) => - request(app.getHttpServer()) - .post('/v1/ads/create') - .set('Authorization', `Bearer ${access}`) - .send({ routeId, creatorDstAddress: dst, fundAmount }); - -export const apiConfirm = ( - app: INestApplication, - adId: string, - access: string, - txHash: `0x${string}`, -) => - request(app.getHttpServer()) - .post(`/v1/ads/${adId}/confirm`) - .set('Authorization', `Bearer ${access}`) - .send({ txHash }); - -export const apiFundAd = ( - app: INestApplication, - adId: string, - access: string, - amtEth: string, -) => - request(app.getHttpServer()) - .post(`/v1/ads/${adId}/fund`) - .set('Authorization', `Bearer ${access}`) - .send({ poolAmountTopUp: parseEther(amtEth).toString() }); - -export const apiWithdraw = ( - app: INestApplication, - adId: string, - access: string, - amtEth: string, - to: `0x${string}`, -) => - request(app.getHttpServer()) - .post(`/v1/ads/${adId}/withdraw`) - .set('Authorization', `Bearer ${access}`) - .send({ poolAmountWithdraw: parseEther(amtEth).toString(), to }); - -export const apiUpdateAd = ( - app: INestApplication, - adId: string, - access: string, - body: any, -) => - request(app.getHttpServer()) - .patch(`/v1/ads/${adId}/update`) - .set('Authorization', `Bearer ${access}`) - .send(body); - -export const apiGetAd = (app: INestApplication, adId: string) => - request(app.getHttpServer()).get(`/v1/ads/${adId}`); - -export const apiCloseAd = ( - app: INestApplication, - adId: string, - access: string, - body: any, -) => - request(app.getHttpServer()) - .post(`/v1/ads/${adId}/close`) - .set('Authorization', `Bearer ${access}`) - .send(body); - -export const apiCreateOrder = ( - app: INestApplication, - access: string, - body: any, -) => - request(app.getHttpServer()) - .post('/v1/trades/create') - .set('Authorization', `Bearer ${access}`) - .send(body); - -export const apiGetTrade = (app: INestApplication, tradeId: string) => - request(app.getHttpServer()).get(`/v1/trades/${tradeId}`); - -export const apiTradeConfirm = ( - app: INestApplication, - tradeId: string, - access: string, - txHash: `0x${string}`, -) => - request(app.getHttpServer()) - .post(`/v1/trades/${tradeId}/confirm`) - .set('Authorization', `Bearer ${access}`) - .send({ txHash }); - -export const apiLockOrder = ( - app: INestApplication, - access: string, - tradeId: string, -) => - request(app.getHttpServer()) - .post(`/v1/trades/${tradeId}/lock`) - .set('Authorization', `Bearer ${access}`) - .send(); - -export const apiTradeParams = ( - app: INestApplication, - access: string, - tradeId: string, -) => - request(app.getHttpServer()) - .get(`/v1/trades/${tradeId}/params`) - .set('Authorization', `Bearer ${access}`); - -export const apiUnlockOrder = ( - app: INestApplication, - access: string, - tradeId: string, - signature: string, -) => - request(app.getHttpServer()) - .post(`/v1/trades/${tradeId}/unlock`) - .set('Authorization', `Bearer ${access}`) - .send({ - signature, - }); - -export const apiTradeUnlockConfirm = ( - app: INestApplication, - access: string, - tradeId: string, - txHash: string, -) => - request(app.getHttpServer()) - .post(`/v1/trades/${tradeId}/unlock/confirm`) - .set('Authorization', `Bearer ${access}`) - .send({ - txHash, - }); diff --git a/apps/backend-relayer/test/integrations/eth-stellar.e2e-integration.ts b/apps/backend-relayer/test/integrations/eth-stellar.e2e-integration.ts deleted file mode 100644 index 1f90239..0000000 --- a/apps/backend-relayer/test/integrations/eth-stellar.e2e-integration.ts +++ /dev/null @@ -1,404 +0,0 @@ -import { INestApplication } from '@nestjs/common'; -import { Keypair, StrKey } from '@stellar/stellar-sdk'; -import { createTestingApp } from '../setups/create-app'; -import { - fundEthAddress, - loginStellarUser, - loginUser, - makeEthClient, -} from '../setups/utils'; -import * as ethContracts from '../setups/evm-deployed-contracts.json'; -import { - getRoutes, - apiCreateAd, - apiConfirm, - apiFundAd, - apiWithdraw, - apiGetAd, - apiCloseAd, - apiCreateOrder, - apiGetTrade, - apiTradeConfirm, - apiLockOrder, - apiTradeParams, - apiUnlockOrder, - apiTradeUnlockConfirm, -} from './api'; -import { - createOrder, - unlockOrderChain, - mintToken, - approveToken, -} from '../setups/evm-actions'; -import { - createAdSoroban, - fundAdSoroban, - withdrawFromAdSoroban, - closeAdSoroban, - lockForOrderSoroban, - unlockSoroban, - StellarOrderParams, -} from '../setups/stellar-actions'; -import type { StellarChainData } from '../setups/stellar-setup'; -import { getAddress, parseEther } from 'viem'; -import { expectObject } from '../setups/utils'; -import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; -import { - AdResponseDto, - CreateAdResponseDto, -} from '../../src/modules/ads/dto/ad.dto'; -import { - CreateOrderRequestContractDetailsDto, - LockForOrderResponseDto, - UnlockOrderResponseDto, -} from '../../src/modules/trades/dto/trade.dto'; -import { - domain, - orderTypes, - signTypedOrder, - verifyTypedData, -} from '../../src/providers/viem/ethers/typedData'; -import { - T_OrderParams, - T_OrderPortalParams, -} from '../../src/chain-adapters/types'; -import { TypedDataEncoder } from 'ethers'; - -// Gate the entire suite on the external orchestrator having provisioned -// a Stellar localnet + keypairs. When running `jest` standalone without the -// bash runner, the global will be unset and we skip cleanly. -const stellarContracts = (global as any).__STELLAR_CONTRACTS__ as - | StellarChainData - | undefined; -const describeIfStellar = stellarContracts ? describe : describe.skip; - -describeIfStellar('Integrations E2E — (Stellar → ETH)', () => { - let app: INestApplication; - - // EVM-side bridger (creates the order on the EVM order chain). - const bridgerKey = generatePrivateKey(); - const bridger = privateKeyToAccount(bridgerKey); - - // Ad creator is a Stellar account. The orchestrator passes the secret via - // STELLAR_AD_CREATOR_SECRET; fall back to the deploy admin as a last resort. - const adCreatorSecret = - process.env.STELLAR_AD_CREATOR_SECRET || - (stellarContracts?.adminSecret as string); - const adCreator = adCreatorSecret - ? Keypair.fromSecret(adCreatorSecret) - : Keypair.random(); - // EVM destination for the ad creator's proceeds on unlock. - const adCreatorEvmKey = generatePrivateKey(); - const adCreatorEvm = privateKeyToAccount(adCreatorEvmKey); - - const ethChain = { - ...ethContracts, - adManagerAddress: ethContracts.adManagerAddress as `0x${string}`, - orderPortalAddress: ethContracts.orderPortalAddress as `0x${string}`, - tokenAddress: ethContracts.tokenAddress as `0x${string}`, - }; - - const ethClient = makeEthClient(); - - let route: AdResponseDto; - - beforeAll(async () => { - app = await createTestingApp(); - await fundEthAddress(ethClient, bridger.address); - await fundEthAddress(ethClient, adCreatorEvm.address); - - // Route: Stellar ad token → EVM order token. - const routes = await getRoutes( - app, - stellarContracts!.chainId, - ethChain.chainId.toString(), - ).expect(200); - expect(routes.body.data.length).toBeGreaterThan(0); - route = routes.body.data[0] as AdResponseDto; - }, 120_000); - - afterAll(async () => { - await app.close(); - }); - - it('Ad lifecycle', async () => { - const access = await loginStellarUser(app, adCreator); - - // Create ad — Stellar uses 7-decimal XLM; use a modest amount in stroops. - const INITIAL = '500000000'; // 50 XLM - const create = await apiCreateAd( - app, - access, - route.id, - adCreatorEvm.address, - INITIAL, - ).expect(201); - - const req = create.body as CreateAdResponseDto; - const adId = req.adId; - - const txCreate = await createAdSoroban( - adCreator, - req.signature, - (req as any).signer, - req.authToken, - req.timeToExpire, - adCreator.publicKey(), - req.adId, - req.adToken, - req.initialAmount, - req.orderChainId, - req.adRecipient, - req.contractAddress, - ); - await apiConfirm(app, adId, access, txCreate as `0x${string}`).expect(200); - - const adAfterCreate = await apiGetAd(app, adId).expect(200); - expectObject(adAfterCreate.body, { - id: adId, - status: 'ACTIVE', - poolAmount: INITIAL, - }); - - // Fund. - const topup = await apiFundAd(app, adId, access, '5').expect(200); - const txFund = await fundAdSoroban( - adCreator, - topup.body.signature, - topup.body.signer, - topup.body.authToken, - topup.body.timeToExpire, - topup.body.adId, - topup.body.amount, - topup.body.contractAddress, - ); - await apiConfirm(app, adId, access, txFund as `0x${string}`).expect(200); - - // Withdraw — destination is the ad creator's Stellar account. - const withdraw = await apiWithdraw( - app, - adId, - access, - '1', - adCreator.publicKey() as `0x${string}`, - ).expect(200); - const txW = await withdrawFromAdSoroban( - adCreator, - withdraw.body.signature, - withdraw.body.signer, - withdraw.body.authToken, - withdraw.body.timeToExpire, - withdraw.body.adId, - withdraw.body.amount, - StrKey.isValidEd25519PublicKey(withdraw.body.to) - ? withdraw.body.to - : adCreator.publicKey(), - withdraw.body.contractAddress, - ); - await apiConfirm(app, adId, access, txW as `0x${string}`).expect(200); - - // Close. - const close = await apiCloseAd(app, adId, access, { - to: adCreator.publicKey(), - }).expect(200); - const txClose = await closeAdSoroban( - adCreator, - close.body.signature, - close.body.signer, - close.body.authToken, - close.body.timeToExpire, - close.body.adId, - StrKey.isValidEd25519PublicKey(close.body.to) - ? close.body.to - : adCreator.publicKey(), - close.body.contractAddress, - ); - await apiConfirm(app, adId, access, txClose as `0x${string}`).expect(200); - - const finalAd = await apiGetAd(app, adId); - expectObject(finalAd.body, { status: 'CLOSED', poolAmount: '0' }); - }, 600_000); - - it('Trade lifecycle', async () => { - const adAccess = await loginStellarUser(app, adCreator); - - // Seed the ad. - const INITIAL = '500000000'; // 50 XLM - const create = await apiCreateAd( - app, - adAccess, - route.id, - adCreatorEvm.address, - INITIAL, - ).expect(201); - const req = create.body as CreateAdResponseDto; - const adId = req.adId; - - const txCreate = await createAdSoroban( - adCreator, - req.signature, - (req as any).signer, - req.authToken, - req.timeToExpire, - adCreator.publicKey(), - req.adId, - req.adToken, - req.initialAmount, - req.orderChainId, - req.adRecipient, - req.contractAddress, - ); - await apiConfirm(app, adId, adAccess, txCreate as `0x${string}`).expect(200); - - // Bridger creates the order on the EVM side. - const bridgerAccess = await loginUser(app, bridgerKey); - - // Bridger's destination on the ad chain is a Stellar account (the ad creator here). - const order = await apiCreateOrder(app, bridgerAccess, { - adId, - routeId: route.id, - amount: '100000000', // 10 XLM worth - bridgerDstAddress: adCreator.publicKey(), - }).expect(201); - - const orderReq = order.body - .reqContractDetails as CreateOrderRequestContractDetailsDto; - const tradeId = order.body.tradeId as string; - - expect(getAddress(ethChain.orderPortalAddress)).toEqual( - getAddress(orderReq.contractAddress), - ); - - await mintToken( - ethClient, - bridger, - ethChain.tokenAddress, - bridger.address, - parseEther('1000'), - ); - await approveToken( - ethClient, - bridger, - ethChain.tokenAddress, - ethChain.orderPortalAddress, - parseEther('100'), - ); - - const orderCreateTx = await createOrder( - ethClient, - bridger, - orderReq.signature, - orderReq.authToken as `0x${string}`, - orderReq.timeToExpire, - orderReq.orderParams as T_OrderPortalParams, - ethChain.orderPortalAddress, - ); - await apiTradeConfirm(app, tradeId, bridgerAccess, orderCreateTx).expect( - 200, - ); - - // Lock on the Stellar ad chain — signed by the ad creator (maker). - const lockOrder = await apiLockOrder(app, adAccess, tradeId).expect(200); - const lockReq = lockOrder.body as LockForOrderResponseDto; - - const lockTxn = await lockForOrderSoroban( - adCreator, - lockReq.signature as `0x${string}`, - (lockReq as any).signer, - lockReq.authToken as `0x${string}`, - lockReq.timeToExpire, - lockReq.orderParams as unknown as StellarOrderParams, - lockReq.contractAddress, - ); - await apiTradeConfirm(app, tradeId, adAccess, lockTxn as `0x${string}`).expect( - 200, - ); - - const afterLock = await apiGetTrade(app, tradeId); - expectObject(afterLock.body, { status: 'LOCKED' }); - - // Ad-creator unlocks on the EVM order chain. - const adCreatorParams = await apiTradeParams(app, adAccess, tradeId).expect( - 200, - ); - const adCreatorOrderParams = adCreatorParams.body as T_OrderParams; - // Ad-creator signs with their EVM destination key so the order chain - // recognises the signature. - const adCreatorSig = await signTypedOrder( - adCreatorEvmKey, - adCreatorOrderParams, - ); - const adCreatorHash = TypedDataEncoder.hash( - domain, - orderTypes, - adCreatorOrderParams, - ); - expect( - verifyTypedData( - adCreatorHash as `0x${string}`, - adCreatorSig as `0x${string}`, - adCreatorEvm.address, - ), - ).toBe(true); - - const unlockOnOrder = await apiUnlockOrder( - app, - adAccess, - tradeId, - adCreatorSig, - ).expect(200); - const unlockOrderReq = unlockOnOrder.body as UnlockOrderResponseDto; - - const unlockOrderTx = await unlockOrderChain( - ethClient, - adCreatorEvm, - unlockOrderReq.signature, - unlockOrderReq.authToken as `0x${string}`, - unlockOrderReq.timeToExpire, - unlockOrderReq.orderParams as T_OrderPortalParams, - unlockOrderReq.nullifierHash as `0x${string}`, - unlockOrderReq.targetRoot as `0x${string}`, - unlockOrderReq.proof as `0x${string}`, - unlockOrderReq.contractAddress, - ); - await apiTradeUnlockConfirm(app, adAccess, tradeId, unlockOrderTx).expect( - 200, - ); - - // Bridger unlocks on the Stellar ad chain. - const bridgerParams = await apiTradeParams( - app, - bridgerAccess, - tradeId, - ).expect(200); - const bridgerOrderParams = bridgerParams.body as T_OrderParams; - const bridgerSig = await signTypedOrder(bridgerKey, bridgerOrderParams); - - const unlockOnAd = await apiUnlockOrder( - app, - bridgerAccess, - tradeId, - bridgerSig, - ).expect(200); - const unlockAdReq = unlockOnAd.body as UnlockOrderResponseDto; - - const unlockAdTx = await unlockSoroban( - adCreator, - unlockAdReq.signature as `0x${string}`, - (unlockAdReq as any).signer, - unlockAdReq.authToken as `0x${string}`, - unlockAdReq.timeToExpire, - unlockAdReq.orderParams as unknown as StellarOrderParams, - unlockAdReq.nullifierHash as `0x${string}`, - unlockAdReq.targetRoot as `0x${string}`, - Buffer.from((unlockAdReq.proof as string).replace(/^0x/, ''), 'hex'), - unlockAdReq.contractAddress, - ); - await apiTradeUnlockConfirm( - app, - bridgerAccess, - tradeId, - unlockAdTx as `0x${string}`, - ).expect(200); - }, 600_000); -}); diff --git a/apps/backend-relayer/test/integrations/jest-e2e.json b/apps/backend-relayer/test/integrations/jest-e2e.json deleted file mode 100644 index cfdaaf8..0000000 --- a/apps/backend-relayer/test/integrations/jest-e2e.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": "../", - "testEnvironment": "node", - "testRegex": ".e2e-integration.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "transformIgnorePatterns": ["node_modules/(?!(?:\\.pnpm/)?@noble)"], - "setupFilesAfterEnv": [], - "globalSetup": "/setups/jest-integrations.setup.ts", - "globalTeardown": "/setups/jest.teardown.ts", - "moduleNameMapper": { - "^@prisma/(?!client)(.*)$": "/../prisma/$1", - "^@libs/(.*)$": "/../src/libs/$1" - } -} diff --git a/apps/backend-relayer/test/setups/create-app.ts b/apps/backend-relayer/test/setups/create-app.ts index 2506adc..3f6c4c1 100644 --- a/apps/backend-relayer/test/setups/create-app.ts +++ b/apps/backend-relayer/test/setups/create-app.ts @@ -4,15 +4,26 @@ import { AppModule } from '../../src/app.module'; import { ChainAdapterService } from '../../src/chain-adapters/chain-adapter.service'; import { MockChainAdapter } from './mock-chain-adapter'; -export async function createTestingApp(): Promise { - const mockAdapter = new MockChainAdapter(); +export interface CreateTestingAppOptions { + // When true, bypass the MockChainAdapter override so the real chain-adapter + // service is used. Required for `test:integrations` which drives real + // on-chain EVM + Stellar contracts. + useRealChainAdapters?: boolean; +} + +export async function createTestingApp( + opts: CreateTestingAppOptions = {}, +): Promise { + const builder = Test.createTestingModule({ imports: [AppModule] }); + + if (!opts.useRealChainAdapters) { + const mockAdapter = new MockChainAdapter(); + builder + .overrideProvider(ChainAdapterService) + .useValue({ forChain: () => mockAdapter }); + } - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }) - .overrideProvider(ChainAdapterService) - .useValue({ forChain: () => mockAdapter }) - .compile(); + const moduleFixture: TestingModule = await builder.compile(); const app = moduleFixture.createNestApplication(); app.useGlobalPipes( diff --git a/apps/backend-relayer/test/setups/evm-actions.ts b/apps/backend-relayer/test/setups/evm-actions.ts deleted file mode 100644 index cd98cb1..0000000 --- a/apps/backend-relayer/test/setups/evm-actions.ts +++ /dev/null @@ -1,639 +0,0 @@ -import { AD_MANAGER_ABI } from '../../src/providers/viem/abis/adManager.abi'; -import { ORDER_PORTAL_ABI } from '../../src/providers/viem/abis/orderPortal.abi'; -import { MERKLE_MANAGER_ABI } from '../../src/providers/viem/abis/merkleManager.abi'; - -import Erc20MockArtifact from '../../../../contracts/evm/out/ERC20Mock.sol/ERC20Mock.json'; - -import { ethers } from 'ethers'; -import { ChainData, AddressLike } from './utils'; -import { - T_AdManagerOrderParams, - T_OrderPortalParams, -} from '../../src/chain-adapters/types'; -import { - createWalletClient, - getAddress, - http, - PrivateKeyAccount, - PublicClient, -} from 'viem'; - -const DEFAULT_ADMIN_ROLE = ethers.ZeroHash as `0x${string}`; - -export async function grantManagerRole( - publicClient: PublicClient, - account: PrivateKeyAccount, - chain: ChainData, -) { - const mgrAddr = account.address; - - const isAdmin = await publicClient.readContract({ - address: chain.merkleManagerAddress, - abi: MERKLE_MANAGER_ABI, - functionName: 'hasRole', - args: [DEFAULT_ADMIN_ROLE, mgrAddr], - }); - - if (!isAdmin) throw new Error(`Signer ${mgrAddr} is NOT DEFAULT_ADMIN_ROLE`); - - const MANAGER_ROLE = ethers.id('MANAGER_ROLE'); - - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - let hash = await wallet.writeContract({ - chain: publicClient.chain, - address: chain.merkleManagerAddress, - abi: MERKLE_MANAGER_ABI, - functionName: 'grantRole', - args: [MANAGER_ROLE, chain.orderPortalAddress], - }); - let receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error(`grantRole(OP) tx failed: ${receipt.transactionHash}`); - } - console.log('Orderportal granted'); - - hash = await wallet.writeContract({ - chain: publicClient.chain, - address: chain.merkleManagerAddress, - abi: MERKLE_MANAGER_ABI, - functionName: 'grantRole', - args: [MANAGER_ROLE, chain.adManagerAddress], - }); - receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error(`grantRole(AD) tx failed: ${receipt.transactionHash}`); - } - - console.log('AdManager granted'); -} - -export async function setupAdManager( - publicClient: PublicClient, - account: PrivateKeyAccount, - adChain: ChainData, - orderChain: ChainData, -) { - const mgrAddr = account.address; - - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const isAdmin = await publicClient.readContract({ - address: adChain.adManagerAddress, - abi: AD_MANAGER_ABI, - functionName: 'hasRole', - args: [DEFAULT_ADMIN_ROLE, mgrAddr], - }); - - if (!isAdmin) throw new Error(`Signer ${mgrAddr} is NOT DEFAULT_ADMIN_ROLE`); - - let hash = await wallet.writeContract({ - chain: publicClient.chain, - address: adChain.adManagerAddress, - abi: AD_MANAGER_ABI, - functionName: 'setChain', - args: [BigInt(orderChain.chainId), orderChain.orderPortalAddress, true], - }); - - let receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `AdManager.setChain tx failed revert: ${receipt.transactionHash}`, - ); - } - - console.log('AdManager set'); - - hash = await wallet.writeContract({ - chain: publicClient.chain, - address: adChain.adManagerAddress, - abi: AD_MANAGER_ABI, - functionName: 'setTokenRoute', - args: [ - adChain.tokenAddress, - orderChain.tokenAddress, - BigInt(orderChain.chainId), - ], - }); - - receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `AdManager.setChain tx failed revert: ${receipt.transactionHash}`, - ); - } - - console.log('AdManager route set'); -} - -export async function setupOrderPortal( - publicClient: PublicClient, - account: PrivateKeyAccount, - adChain: ChainData, - orderChain: ChainData, -) { - const mgrAddr = account.address; - - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const isAdmin = await publicClient.readContract({ - address: orderChain.orderPortalAddress, - abi: AD_MANAGER_ABI, - functionName: 'hasRole', - args: [DEFAULT_ADMIN_ROLE, mgrAddr], - }); - - if (!isAdmin) throw new Error(`Signer ${mgrAddr} is NOT DEFAULT_ADMIN_ROLE`); - - let hash = await wallet.writeContract({ - chain: publicClient.chain, - address: orderChain.orderPortalAddress, - abi: ORDER_PORTAL_ABI, - functionName: 'setChain', - args: [BigInt(adChain.chainId), adChain.adManagerAddress, true], - }); - - let receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `OrderPortal.setChain tx failed revert: ${receipt.transactionHash}`, - ); - } - - console.log('OrderPortal set'); - - hash = await wallet.writeContract({ - chain: publicClient.chain, - address: orderChain.orderPortalAddress, - abi: ORDER_PORTAL_ABI, - functionName: 'setTokenRoute', - args: [ - orderChain.tokenAddress, - BigInt(adChain.chainId), - adChain.tokenAddress, - ], - }); - - receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `OrderPortal.setTokenRoute tx failed revert: ${receipt.transactionHash}`, - ); - } - - console.log('OrderPortal route set'); -} - -export async function adminSetup( - publicClient: PublicClient, - account: PrivateKeyAccount, - chain1: ChainData, - chain2: ChainData, -) { - // On the same provider chain (chain 1) - // Setup roles and contracts - await grantManagerRole(publicClient, account, chain1); - // chain 1 is the ad chain, chain 2 is the order chain for setupAdManager - await setupAdManager(publicClient, account, chain1, chain2); - // chain 2 is the ad chain, chain 1 is the order chain for setupOrderPortal - await setupOrderPortal(publicClient, account, chain2, chain1); -} - -export async function createAd( - publicClient: PublicClient, - account: PrivateKeyAccount, - signature: `0x${string}`, - authToken: `0x${string}`, - timeToExpire: number, - adId: string, - adToken: `0x${string}`, - fundAmount: string, - orderChainId: string, - adRecipient: `0x${string}`, - adManagerAddress: string, -) { - console.log(adManagerAddress); - - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: adManagerAddress as AddressLike, - abi: AD_MANAGER_ABI, - functionName: 'createAd', - args: [ - signature, - authToken, - BigInt(timeToExpire), - adId, - adToken, - BigInt(fundAmount), - BigInt(orderChainId), - adRecipient, - ], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `AdManager.setChain tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function fundAd( - publicClient: PublicClient, - account: PrivateKeyAccount, - signature: `0x${string}`, - authToken: `0x${string}`, - timeToExpire: number, - adId: string, - amount: bigint, - adManagerAddress: string, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: adManagerAddress as AddressLike, - abi: AD_MANAGER_ABI, - functionName: 'fundAd', - args: [signature, authToken, BigInt(timeToExpire), adId, amount], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `AdManager.fundAd tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function withdrawAdFunds( - publicClient: PublicClient, - account: PrivateKeyAccount, - signature: `0x${string}`, - authToken: `0x${string}`, - timeToExpire: number, - adId: string, - amount: bigint, - to: `0x${string}`, - adManagerAddress: string, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: adManagerAddress as AddressLike, - abi: AD_MANAGER_ABI, - functionName: 'withdrawFromAd', - args: [signature, authToken, BigInt(timeToExpire), adId, amount, to], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `AdManager.withdrawFromAd tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function closeAd( - publicClient: PublicClient, - account: PrivateKeyAccount, - signature: `0x${string}`, - authToken: `0x${string}`, - timeToExpire: number, - adId: string, - to: `0x${string}`, - adManagerAddress: string, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: adManagerAddress as AddressLike, - abi: AD_MANAGER_ABI, - functionName: 'closeAd', - args: [signature, authToken, BigInt(timeToExpire), adId, to], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `AdManager.closeAd tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function createOrder( - publicClient: PublicClient, - account: PrivateKeyAccount, - signature: `0x${string}`, - authToken: `0x${string}`, - timeToExpire: number, - orderParams: T_OrderPortalParams, - orderPortalAddress: string, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: orderPortalAddress as AddressLike, - abi: ORDER_PORTAL_ABI, - functionName: 'createOrder', - args: [ - signature, - authToken, - BigInt(timeToExpire), - { - ...orderParams, - amount: BigInt(orderParams.amount), - adChainId: BigInt(orderParams.adChainId), - salt: BigInt(orderParams.salt), - }, - ], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `OrderPortal.createOrder tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function lockForOrder( - publicClient: PublicClient, - account: PrivateKeyAccount, - signature: `0x${string}`, - authToken: `0x${string}`, - timeToExpire: number, - orderParams: T_AdManagerOrderParams, - adManagerAddress: AddressLike, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - if (!adManagerAddress) { - throw new Error('adManagerAddress is undefined'); - } - - const formattedAddress = getAddress(adManagerAddress); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: formattedAddress, - abi: AD_MANAGER_ABI, - functionName: 'lockForOrder', - args: [ - signature, - authToken, - BigInt(timeToExpire), - { - orderChainToken: orderParams.orderChainToken, - adChainToken: orderParams.adChainToken, - amount: BigInt(orderParams.amount), - bridger: orderParams.bridger, - orderChainId: BigInt(orderParams.orderChainId), - srcOrderPortal: orderParams.srcOrderPortal, - orderRecipient: orderParams.orderRecipient, - adId: orderParams.adId, - adCreator: orderParams.adCreator, - adRecipient: orderParams.adRecipient, - salt: BigInt(orderParams.salt), - }, - ], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `AdManager.lockForOrder tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function unlockAdChain( - publicClient: PublicClient, - account: PrivateKeyAccount, - signature: `0x${string}`, - authToken: `0x${string}`, - timeToExpire: number, - orderParams: T_AdManagerOrderParams, - nullifierHash: `0x${string}`, - targetRoot: `0x${string}`, - proof: `0x${string}`, - adManagerAddress: string, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: adManagerAddress as AddressLike, - abi: AD_MANAGER_ABI, - functionName: 'unlock', - args: [ - signature, - authToken, - BigInt(timeToExpire), - { - ...orderParams, - amount: BigInt(orderParams.amount), - orderChainId: BigInt(orderParams.orderChainId), - salt: BigInt(orderParams.salt), - }, - nullifierHash, - targetRoot, - proof, - ], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `AdManager.unlockOrder tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function unlockOrderChain( - publicClient: PublicClient, - account: PrivateKeyAccount, - signature: `0x${string}`, - authToken: `0x${string}`, - timeToExpire: number, - orderParams: T_OrderPortalParams, - nullifierHash: `0x${string}`, - targetRoot: `0x${string}`, - proof: `0x${string}`, - orderPortalAddress: string, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: orderPortalAddress as AddressLike, - abi: ORDER_PORTAL_ABI, - functionName: 'unlock', - - args: [ - signature, - authToken, - BigInt(timeToExpire), - { - ...orderParams, - amount: BigInt(orderParams.amount), - adChainId: BigInt(orderParams.adChainId), - salt: BigInt(orderParams.salt), - }, - nullifierHash, - targetRoot, - proof, - ], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `OrderPortal.unlockOrder tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function mintToken( - publicClient: PublicClient, - account: PrivateKeyAccount, - tokenAddress: AddressLike, - to: AddressLike, - amount: bigint, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: tokenAddress, - abi: Erc20MockArtifact.abi, - functionName: 'mint', - args: [to, amount], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `ERC20Mock.mint tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} - -export async function approveToken( - publicClient: PublicClient, - account: PrivateKeyAccount, - tokenAddress: AddressLike, - spender: AddressLike, - amount: bigint, -) { - const wallet = createWalletClient({ - chain: publicClient.chain, - transport: http(), - account, - }); - - const hash = await wallet.writeContract({ - chain: publicClient.chain, - address: tokenAddress, - abi: Erc20MockArtifact.abi, - functionName: 'approve', - args: [spender, amount], - }); - - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - - if (receipt.status !== 'success') { - throw new Error( - `ERC20Mock.approve tx failed revert: ${receipt.transactionHash}`, - ); - } - - return hash; -} diff --git a/apps/backend-relayer/test/setups/evm-deployed-contracts.json b/apps/backend-relayer/test/setups/evm-deployed-contracts.json deleted file mode 100644 index a62e161..0000000 --- a/apps/backend-relayer/test/setups/evm-deployed-contracts.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "adManagerAddress": "0x366D90CB2A8606A82164C717cF1889c3ed5aE1f4", - "orderPortalAddress": "0xF1C313faAD40ccAeDb4Fd3e7C838993569E2572C", - "chainId": "11155111", - "name": "ETH SEPOLIA", - "tokenName": "ProofBridge", - "tokenSymbol": "PBT", - "tokenAddress": "0x1B62aDdB315CC98ab4625ffA170c1BC5C75F9da7", - "merkleManagerAddress": "0x397E7356aF447B2754D8Ea0838d285FB78F2482d", - "verifierAddress": "0xDc930A3b5CC073092750aE7f4FF45409B2428592" -} diff --git a/apps/backend-relayer/test/setups/evm-setup.ts b/apps/backend-relayer/test/setups/evm-setup.ts deleted file mode 100644 index d75d927..0000000 --- a/apps/backend-relayer/test/setups/evm-setup.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { writeFileSync } from 'fs'; -import path from 'path'; -import dotenv from 'dotenv'; - -import MerkleManagerArtifact from '../../../../contracts/evm/out/MerkleManager.sol/MerkleManager.json'; -import VerifierArtifact from '../../../../contracts/evm/out/Verifier.sol/HonkVerifier.json'; -import AdManagerArtifact from '../../../../contracts/evm/out/AdManager.sol/AdManager.json'; -import OrderPortalArtifact from '../../../../contracts/evm/out/OrderPortal.sol/OrderPortal.json'; -import Erc20MockArtifact from '../../../../contracts/evm/out/ERC20Mock.sol/ERC20Mock.json'; - -import { createPublicClient, createWalletClient, http } from 'viem'; -import { ethLocalnet } from '../../src/providers/viem/ethers/localnet'; -import { AddressLike, ChainData, fundEthAddress } from './utils'; -import { privateKeyToAccount } from 'viem/accounts'; - -dotenv.config({ path: path.resolve(__dirname, '../../.env.test') }); - -export async function deployEvmContracts(): Promise { - console.log(`Deploying ETH contracts...`); - - const managerKey = process.env.MANAGER_KEY; - if (!managerKey) { - throw new Error('MANAGER_KEY not set in environment'); - } - const chain = ethLocalnet; - - const publicClient = createPublicClient({ - chain, - transport: http(), - }); - - const wallet = createWalletClient({ - chain, - transport: http(), - account: privateKeyToAccount(managerKey as AddressLike), - }); - - const managerAddress = wallet.account.address; - - await fundEthAddress(publicClient, managerAddress, '1'); - console.log('Using manager address:', wallet.account.address); - - // Deploy mock ERC20 token - const hash = await wallet.deployContract({ - abi: Erc20MockArtifact.abi, - bytecode: Erc20MockArtifact.bytecode.object as `0x${string}`, - args: [], - }); - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - const erc20Address = receipt.contractAddress!; - console.log('ERC20Mock deployed to:', erc20Address); - - // Deploy Verifier contract - const txHash = await wallet.deployContract({ - abi: VerifierArtifact.abi, - bytecode: VerifierArtifact.bytecode.object as `0x${string}`, - args: [], - }); - const txReceipt = await publicClient.waitForTransactionReceipt({ - hash: txHash, - }); - const verifierAddress = txReceipt.contractAddress!; - console.log('Verifier deployed to:', verifierAddress); - - // Deploy MerkleManager contract - const mmHash = await wallet.deployContract({ - abi: MerkleManagerArtifact.abi, - bytecode: MerkleManagerArtifact.bytecode.object as `0x${string}`, - args: [managerAddress], - }); - const mmReceipt = await publicClient.waitForTransactionReceipt({ - hash: mmHash, - }); - const merkleManagerAddress = mmReceipt.contractAddress!; - console.log('MerkleManager deployed to:', merkleManagerAddress); - - // Deploy AdManager contract - const adHash = await wallet.deployContract({ - abi: AdManagerArtifact.abi, - bytecode: AdManagerArtifact.bytecode.object as `0x${string}`, - args: [managerAddress, verifierAddress, merkleManagerAddress], - }); - const adReceipt = await publicClient.waitForTransactionReceipt({ - hash: adHash, - }); - const adManagerAddress = adReceipt.contractAddress!; - console.log('AdManager deployed to:', adManagerAddress); - - // Deploy OrderPortal contract - const orderHash = await wallet.deployContract({ - abi: OrderPortalArtifact.abi, - bytecode: OrderPortalArtifact.bytecode.object as `0x${string}`, - args: [managerAddress, verifierAddress, merkleManagerAddress], - }); - const orderReceipt = await publicClient.waitForTransactionReceipt({ - hash: orderHash, - }); - const orderPortalAddress = orderReceipt.contractAddress!; - console.log('OrderPortal deployed to:', orderPortalAddress); - - const contracts: ChainData = { - adManagerAddress, - orderPortalAddress, - chainId: chain.id.toString(), - name: 'ETH LOCALNET', - tokenName: 'ERC20Mock', - tokenSymbol: 'E20M', - tokenAddress: erc20Address, - merkleManagerAddress, - verifierAddress, - }; - - const filePath = path.join(__dirname, 'evm-deployed-contracts.json'); - writeFileSync(filePath, JSON.stringify(contracts, null, 2)); - console.log('Contract addresses saved to:', filePath); - - return contracts; -} diff --git a/apps/backend-relayer/test/setups/jest-e2e.setup.ts b/apps/backend-relayer/test/setups/jest-e2e.setup.ts index fc80733..f6a86d5 100644 --- a/apps/backend-relayer/test/setups/jest-e2e.setup.ts +++ b/apps/backend-relayer/test/setups/jest-e2e.setup.ts @@ -39,10 +39,18 @@ export default async () => { const databaseUrl = container.getConnectionUri(); process.env.DATABASE_URL = databaseUrl; process.env.NODE_ENV = 'test'; + process.env.JWT_EXPIRY = '7d'; + process.env.JWT_REFRESH_EXPIRY = '30d'; process.env.JWT_ACCESS_SECRET = 'test-access-secret'; process.env.JWT_REFRESH_SECRET = 'test-refresh-secret'; process.env.SIGN_DOMAIN = process.env.SIGN_DOMAIN || 'proofbridge.xyz'; process.env.SIGN_URI = process.env.SIGN_URI || 'https://proofbridge.xyz'; + process.env.STELLAR_AUTH_SECRET = + process.env.STELLAR_AUTH_SECRET || + 'SA3C2KPR5TCHYJ5TNQXAY2776Z3H4CB723GDCAMEX5I2NLWP25QUYB3X'; + process.env.SECRET_KEY = + process.env.SECRET_KEY || + '0xfdba5a242ddce02cd1d585297aa4afe5aa2831391198746c680a3e16a41676dc'; await migrate(databaseUrl); diff --git a/apps/backend-relayer/test/setups/jest-integrations.setup.ts b/apps/backend-relayer/test/setups/jest-integrations.setup.ts deleted file mode 100644 index 404db5e..0000000 --- a/apps/backend-relayer/test/setups/jest-integrations.setup.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { - StartedPostgreSqlContainer, - PostgreSqlContainer, -} from '@testcontainers/postgresql'; -import * as dotenv from 'dotenv'; -import { execa } from 'execa'; -import path from 'path'; -import { deployEvmContracts } from './evm-setup'; -import { - deployStellarContracts, - linkStellarAdManagerToOrderChain, - StellarChainData, -} from './stellar-setup'; -import { seedDB } from './seed'; - -// Load .env (optional) -dotenv.config({ path: path.resolve(__dirname, '../../.env.test') }); - -let container: StartedPostgreSqlContainer; - -async function migrate(databaseUrl: string) { - // prisma migrate deploy - await execa('npx', ['prisma', 'migrate', 'deploy'], { - stdio: 'inherit', - env: { ...process.env, DATABASE_URL: databaseUrl }, - }); -} - -export default async () => { - container = await new PostgreSqlContainer('postgres:16-alpine') - .withDatabase('testdb') - .withUsername('test') - .withPassword('test') - .start(); - - const databaseUrl = container.getConnectionUri(); - process.env.DATABASE_URL = databaseUrl; - process.env.NODE_ENV = 'test'; - process.env.JWT_ACCESS_SECRET = 'test-access-secret'; - process.env.JWT_REFRESH_SECRET = 'test-refresh-secret'; - process.env.SIGN_DOMAIN = process.env.SIGN_DOMAIN || 'proofbridge.xyz'; - process.env.SIGN_URI = process.env.SIGN_URI || 'https://proofbridge.xyz'; - - await migrate(databaseUrl); - - const ethContracts = await deployEvmContracts(); - - // Stellar side is optional — only engages when the external bash - // orchestrator (scripts/run_cross_chain_e2e.sh) has exported the RPC + - // admin secret. Tests that depend on Stellar should skip when absent. - let stellarContracts: StellarChainData | undefined; - if (process.env.STELLAR_RPC_URL && process.env.STELLAR_ADMIN_SECRET) { - stellarContracts = await deployStellarContracts(); - await linkStellarAdManagerToOrderChain(stellarContracts, ethContracts); - } - - await seedDB(ethContracts, stellarContracts); - - (global as any).__ETH_CONTRACTS__ = ethContracts; - if (stellarContracts) { - (global as any).__STELLAR_CONTRACTS__ = stellarContracts; - } - (global as any).__PG_CONTAINER__ = container; -}; diff --git a/apps/backend-relayer/test/setups/mock-chain-adapter.ts b/apps/backend-relayer/test/setups/mock-chain-adapter.ts index 493f6a9..26f5458 100644 --- a/apps/backend-relayer/test/setups/mock-chain-adapter.ts +++ b/apps/backend-relayer/test/setups/mock-chain-adapter.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { randomBytes } from 'crypto'; import { ChainAdapter } from '../../src/chain-adapters/adapters/chain-adapter.abstract'; import { @@ -24,8 +25,7 @@ import { const ZERO_32 = '0x0000000000000000000000000000000000000000000000000000000000000000' as const; -const FAKE_SIG = - ('0x' + 'ab'.repeat(65)) as `0x${string}`; +const FAKE_SIG = ('0x' + 'ab'.repeat(65)) as `0x${string}`; const ONE_HOUR_S = 3600; function uniqueHash(): `0x${string}` { diff --git a/apps/backend-relayer/test/setups/seed.ts b/apps/backend-relayer/test/setups/seed.ts index 6781966..3059b5a 100644 --- a/apps/backend-relayer/test/setups/seed.ts +++ b/apps/backend-relayer/test/setups/seed.ts @@ -1,68 +1,5 @@ -import { ChainKind, PrismaClient } from '@prisma/client'; -import { ChainData, seedAdmin, seedChain, seedRoute, seedToken } from './utils'; -import { StellarChainData } from './stellar-setup'; - -export const seedDB = async ( - ethContracts: ChainData, - stellarContracts?: StellarChainData, -) => { - const prisma = new PrismaClient(); - - try { - await prisma.$connect(); - - await seedAdmin(prisma, 'admin@x.com', 'ChangeMe123!'); - - const ethChain = await seedChain(prisma, { - name: ethContracts.name, - chainId: BigInt(ethContracts.chainId), - ad: ethContracts.adManagerAddress, - op: ethContracts.orderPortalAddress, - kind: ChainKind.EVM, - }); - - const ethToken = await seedToken( - prisma, - ethChain.id, - ethContracts.tokenName, - ethContracts.tokenSymbol, - ethContracts.tokenAddress, - ); - - if (stellarContracts) { - const stellarChain = await seedChain(prisma, { - name: stellarContracts.name, - chainId: BigInt(stellarContracts.chainId), - ad: stellarContracts.adManagerAddress, - // Stellar side has no OrderPortal in this direction — reuse the - // AdManager address as a non-null placeholder for the schema. - op: stellarContracts.adManagerAddress, - kind: ChainKind.STELLAR, - }); - - const stellarToken = await seedToken( - prisma, - stellarChain.id, - stellarContracts.tokenName, - stellarContracts.tokenSymbol, - stellarContracts.tokenAddress, - 'NATIVE', - 7, - ); - - // Stellar ad token → EVM order token. - await seedRoute(prisma, stellarToken.id, ethToken.id); - } - - await prisma.$disconnect(); - - console.log('Seeding completed.'); - } catch (error) { - console.error('Error seeding db:', error); - } finally { - await prisma.$disconnect(); - } -}; +import { PrismaClient } from '@prisma/client'; +import { seedAdmin } from './utils'; export const seedDBe2e = async () => { const prisma = new PrismaClient(); diff --git a/apps/backend-relayer/test/setups/stellar-actions.ts b/apps/backend-relayer/test/setups/stellar-actions.ts deleted file mode 100644 index 6c1f460..0000000 --- a/apps/backend-relayer/test/setups/stellar-actions.ts +++ /dev/null @@ -1,260 +0,0 @@ -// Stellar contract-action helpers — Soroban analogue of contract-actions.ts. -// -// Each wrapper takes the relayer's signed-request payload (signature + signer -// public key + authToken + timeToExpire) plus the raw contract args, builds a -// Soroban invocation, prepares + signs + submits, and polls for success. - -import { - Address, - Contract, - Keypair, - Networks, - TransactionBuilder, - nativeToScVal, - rpc, - xdr, -} from '@stellar/stellar-sdk'; -import { hex32ToBuffer, hex32ToContractId } from '../../src/providers/stellar/utils/address'; - -const BASE_FEE = '1000'; - -function getServer(): rpc.Server { - const url = process.env.STELLAR_RPC_URL; - if (!url) throw new Error('STELLAR_RPC_URL not set'); - return new rpc.Server(url, { allowHttp: url.startsWith('http://') }); -} - -function passphrase(): string { - return process.env.STELLAR_NETWORK_PASSPHRASE || Networks.TESTNET; -} - -async function invoke( - signer: Keypair, - contractHex: string, - method: string, - args: xdr.ScVal[], -): Promise { - const server = getServer(); - const contract = new Contract(hex32ToContractId(contractHex)); - const source = await server.getAccount(signer.publicKey()); - const tx = new TransactionBuilder(source, { - fee: BASE_FEE, - networkPassphrase: passphrase(), - }) - .addOperation(contract.call(method, ...args)) - .setTimeout(60) - .build(); - const prepared = await server.prepareTransaction(tx); - prepared.sign(signer); - const sent = await server.sendTransaction(prepared); - if (sent.status === 'ERROR') { - throw new Error( - `Stellar send failed [${method}]: ${JSON.stringify(sent.errorResult)}`, - ); - } - for (let i = 0; i < 20; i++) { - const got = await server.getTransaction(sent.hash); - if (got.status === rpc.Api.GetTransactionStatus.SUCCESS) return sent.hash; - if (got.status === rpc.Api.GetTransactionStatus.FAILED) { - throw new Error(`Stellar tx [${method}] FAILED hash=${sent.hash}`); - } - await new Promise((r) => setTimeout(r, 1000)); - } - throw new Error(`Stellar tx [${method}] timed out hash=${sent.hash}`); -} - -// ── scval helpers ─────────────────────────────────────────────────── - -function bytesN(hex: string): xdr.ScVal { - return nativeToScVal(hex32ToBuffer(hex), { type: 'bytes' }); -} - -function bytes(buf: Buffer): xdr.ScVal { - return nativeToScVal(buf, { type: 'bytes' }); -} - -function u64(n: number | bigint): xdr.ScVal { - return nativeToScVal(BigInt(n), { type: 'u64' }); -} - -function u128(n: string | bigint): xdr.ScVal { - return nativeToScVal(BigInt(n), { type: 'u128' }); -} - -function strVal(s: string): xdr.ScVal { - return nativeToScVal(s, { type: 'string' }); -} - -// Shared auth quadruple (signature, public_key, auth_token, time_to_expire). -function authArgs( - signatureHex: string, - publicKeyHex: string, - authTokenHex: string, - timeToExpire: number, -): xdr.ScVal[] { - return [ - bytes(Buffer.from(signatureHex.replace(/^0x/, ''), 'hex')), - bytesN(publicKeyHex), - bytesN(authTokenHex), - u64(timeToExpire), - ]; -} - -// ── ad-manager wrappers ───────────────────────────────────────────── - -export async function createAdSoroban( - signer: Keypair, - signatureHex: string, - signerPublicKeyHex: string, - authTokenHex: string, - timeToExpire: number, - creatorPublicKey: string, // G-strkey of the ad creator (signer) - adId: string, - adTokenHex: string, - initialAmount: string, - orderChainId: string, - adRecipientHex: string, - adManagerHex: string, -): Promise { - const args = [ - ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), - new Address(creatorPublicKey).toScVal(), - strVal(adId), - bytesN(adTokenHex), - u128(initialAmount), - u128(orderChainId), - bytesN(adRecipientHex), - ]; - return invoke(signer, adManagerHex, 'create_ad', args); -} - -export async function fundAdSoroban( - signer: Keypair, - signatureHex: string, - signerPublicKeyHex: string, - authTokenHex: string, - timeToExpire: number, - adId: string, - amount: string, - adManagerHex: string, -): Promise { - const args = [ - ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), - strVal(adId), - u128(amount), - ]; - return invoke(signer, adManagerHex, 'fund_ad', args); -} - -export async function withdrawFromAdSoroban( - signer: Keypair, - signatureHex: string, - signerPublicKeyHex: string, - authTokenHex: string, - timeToExpire: number, - adId: string, - amount: string, - toPublicKey: string, // G-strkey - adManagerHex: string, -): Promise { - const args = [ - ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), - strVal(adId), - u128(amount), - new Address(toPublicKey).toScVal(), - ]; - return invoke(signer, adManagerHex, 'withdraw_from_ad', args); -} - -export async function closeAdSoroban( - signer: Keypair, - signatureHex: string, - signerPublicKeyHex: string, - authTokenHex: string, - timeToExpire: number, - adId: string, - toPublicKey: string, // G-strkey - adManagerHex: string, -): Promise { - const args = [ - ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), - strVal(adId), - new Address(toPublicKey).toScVal(), - ]; - return invoke(signer, adManagerHex, 'close_ad', args); -} - -// Matches contracts/stellar/contracts/ad-manager/src/types.rs::OrderParams. -export interface StellarOrderParams { - orderChainToken: string; // 0x + 64 hex - adChainToken: string; - amount: string; - bridger: string; - orderChainId: string; - srcOrderPortal: string; - orderRecipient: string; - adId: string; - adCreator: string; - adRecipient: string; - salt: string; -} - -function orderParamsScVal(p: StellarOrderParams): xdr.ScVal { - // Soroban struct is encoded as an ScMap with entries sorted by key. - const entries: Array<[string, xdr.ScVal]> = [ - ['ad_chain_token', bytesN(p.adChainToken)], - ['ad_creator', bytesN(p.adCreator)], - ['ad_id', strVal(p.adId)], - ['ad_recipient', bytesN(p.adRecipient)], - ['amount', u128(p.amount)], - ['bridger', bytesN(p.bridger)], - ['order_chain_id', u128(p.orderChainId)], - ['order_chain_token', bytesN(p.orderChainToken)], - ['order_recipient', bytesN(p.orderRecipient)], - ['salt', u128(p.salt)], - ['src_order_portal', bytesN(p.srcOrderPortal)], - ]; - return xdr.ScVal.scvMap( - entries.map(([k, v]) => - new xdr.ScMapEntry({ key: xdr.ScVal.scvSymbol(k), val: v }), - ), - ); -} - -export async function lockForOrderSoroban( - signer: Keypair, - signatureHex: string, - signerPublicKeyHex: string, - authTokenHex: string, - timeToExpire: number, - params: StellarOrderParams, - adManagerHex: string, -): Promise { - const args = [ - ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), - orderParamsScVal(params), - ]; - return invoke(signer, adManagerHex, 'lock_for_order', args); -} - -export async function unlockSoroban( - signer: Keypair, - signatureHex: string, - signerPublicKeyHex: string, - authTokenHex: string, - timeToExpire: number, - params: StellarOrderParams, - nullifierHashHex: string, - targetRootHex: string, - proof: Buffer, - adManagerHex: string, -): Promise { - const args = [ - ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), - orderParamsScVal(params), - bytesN(nullifierHashHex), - bytesN(targetRootHex), - bytes(proof), - ]; - return invoke(signer, adManagerHex, 'unlock', args); -} diff --git a/apps/backend-relayer/test/setups/stellar-setup.ts b/apps/backend-relayer/test/setups/stellar-setup.ts deleted file mode 100644 index 911aa0b..0000000 --- a/apps/backend-relayer/test/setups/stellar-setup.ts +++ /dev/null @@ -1,300 +0,0 @@ -// Stellar deploy helper — symmetric with `deployEvmContracts()`. -// Uploads each WASM, instantiates the contracts, initialises them, and -// deploys the native XLM SAC so the test has a token route. -// -// Assumes a Stellar network is already running at STELLAR_RPC_URL with the -// admin account (STELLAR_ADMIN_SECRET) friendbot-funded. The external -// `run_cross_chain_e2e.sh` script sets both up. - -import fs from 'node:fs'; -import path from 'node:path'; -import { - Asset, - Keypair, - Networks, - Operation, - StrKey, - TransactionBuilder, - hash, - nativeToScVal, - rpc, - xdr, - Address, -} from '@stellar/stellar-sdk'; -import { - accountIdToHex32, - contractIdToHex32, - hex32ToBuffer, - hex32ToContractId, -} from '../../src/providers/stellar/utils/address'; -import { ChainData } from './utils'; - -const BASE_FEE = '1000'; - -export interface StellarChainData { - adManagerAddress: `0x${string}`; - merkleManagerAddress: `0x${string}`; - verifierAddress: `0x${string}`; - // The native XLM SAC doubles as the ad token for this test. - tokenAddress: `0x${string}`; - chainId: string; - name: string; - tokenName: string; - tokenSymbol: string; - adminPublicKeyHex: `0x${string}`; - adminSecret: string; // S… strkey, handed back so the test can sign with it -} - -export const STELLAR_CHAIN_ID = '1000001'; -const STELLAR_CHAIN_NAME = 'STELLAR LOCALNET'; - -function getServer(): rpc.Server { - const url = process.env.STELLAR_RPC_URL; - if (!url) throw new Error('STELLAR_RPC_URL not set'); - return new rpc.Server(url, { allowHttp: url.startsWith('http://') }); -} - -function networkPassphrase(): string { - return process.env.STELLAR_NETWORK_PASSPHRASE || Networks.TESTNET; -} - -function loadAdminKeypair(): Keypair { - const raw = (process.env.STELLAR_ADMIN_SECRET ?? '').trim(); - if (!raw) throw new Error('STELLAR_ADMIN_SECRET not set'); - if (StrKey.isValidEd25519SecretSeed(raw)) return Keypair.fromSecret(raw); - if (/^0x[a-fA-F0-9]{64}$/.test(raw)) { - return Keypair.fromRawEd25519Seed(Buffer.from(raw.slice(2), 'hex')); - } - throw new Error( - 'Invalid STELLAR_ADMIN_SECRET (expected S… strkey or 0x + 64 hex)', - ); -} - -async function submit( - server: rpc.Server, - signer: Keypair, - buildOp: () => xdr.Operation, -): Promise { - const source = await server.getAccount(signer.publicKey()); - const tx = new TransactionBuilder(source, { - fee: BASE_FEE, - networkPassphrase: networkPassphrase(), - }) - .addOperation(buildOp()) - .setTimeout(60) - .build(); - const prepared = await server.prepareTransaction(tx); - prepared.sign(signer); - const sent = await server.sendTransaction(prepared); - if (sent.status === 'ERROR') { - throw new Error( - `Stellar send failed: ${JSON.stringify(sent.errorResult)}`, - ); - } - for (let i = 0; i < 20; i++) { - const got = await server.getTransaction(sent.hash); - if (got.status === rpc.Api.GetTransactionStatus.SUCCESS) return got; - if (got.status === rpc.Api.GetTransactionStatus.FAILED) { - throw new Error(`Stellar tx FAILED hash=${sent.hash}`); - } - await new Promise((r) => setTimeout(r, 1000)); - } - throw new Error(`Stellar tx timed out hash=${sent.hash}`); -} - -async function uploadWasm( - server: rpc.Server, - signer: Keypair, - wasm: Buffer, -): Promise { - await submit(server, signer, () => Operation.uploadContractWasm({ wasm })); - return hash(wasm); -} - -async function createContract( - server: rpc.Server, - signer: Keypair, - wasmHash: Buffer, - constructorArgs: xdr.ScVal[] = [], -): Promise { - const salt = new Uint8Array(32); - globalThis.crypto.getRandomValues(salt); - const res = await submit(server, signer, () => - Operation.createCustomContract({ - address: Address.fromString(signer.publicKey()), - wasmHash, - salt: Buffer.from(salt), - constructorArgs, - }), - ); - const retval = res.returnValue; - if (!retval) throw new Error('createCustomContract: no return value'); - const addr = Address.fromScAddress(retval.address()).toString(); - if (!addr.startsWith('C')) throw new Error(`unexpected contract addr: ${addr}`); - return addr; -} - -async function invoke( - server: rpc.Server, - signer: Keypair, - contractIdStrkey: string, - method: string, - args: xdr.ScVal[], -): Promise { - await submit(server, signer, () => - Operation.invokeContractFunction({ - contract: contractIdStrkey, - function: method, - args, - }), - ); -} - -async function deployNativeSac( - server: rpc.Server, - signer: Keypair, -): Promise { - // If the native SAC is already deployed on this network, createStellarAssetContract - // returns an error — fall back to the deterministic contractId. - const asset = Asset.native(); - try { - const res = await submit(server, signer, () => - Operation.createStellarAssetContract({ asset }), - ); - const retval = res.returnValue; - if (!retval) throw new Error('createStellarAssetContract: no return value'); - return Address.fromScAddress(retval.address()).toString(); - } catch { - return asset.contractId(networkPassphrase()); - } -} - -function wasmPath(name: string): string { - return path.join( - __dirname, - '../../src/providers/stellar/wasm', - `${name}.wasm`, - ); -} - -function vkBytes(): Buffer { - const vkPath = path.resolve( - __dirname, - '../../../../proof_circuits/deposits/target/vk', - ); - if (!fs.existsSync(vkPath)) { - throw new Error( - `Verifier VK not found at ${vkPath}. Run scripts/build_circuits.sh first.`, - ); - } - return fs.readFileSync(vkPath); -} - -export async function deployStellarContracts(): Promise { - const admin = loadAdminKeypair(); - const server = getServer(); - console.log( - `Deploying STELLAR contracts (admin=${admin.publicKey()}, rpc=${process.env.STELLAR_RPC_URL})...`, - ); - - // Upload WASMs. - const verifierWasm = fs.readFileSync(wasmPath('verifier')); - const merkleWasm = fs.readFileSync(wasmPath('merkle_manager')); - const adWasm = fs.readFileSync(wasmPath('ad_manager')); - const verifierHash = await uploadWasm(server, admin, verifierWasm); - const merkleHash = await uploadWasm(server, admin, merkleWasm); - const adHash = await uploadWasm(server, admin, adWasm); - - // Native XLM SAC — used as the Stellar-side ad token. - const xlmSacStrkey = await deployNativeSac(server, admin); - console.log(` Native XLM SAC: ${xlmSacStrkey}`); - - // Verifier — constructor takes the VK bytes. - const verifierStrkey = await createContract(server, admin, verifierHash, [ - nativeToScVal(vkBytes(), { type: 'bytes' }), - ]); - console.log(` Verifier: ${verifierStrkey}`); - - // MerkleManager — initialize(admin). - const merkleStrkey = await createContract(server, admin, merkleHash); - await invoke(server, admin, merkleStrkey, 'initialize', [ - new Address(admin.publicKey()).toScVal(), - ]); - console.log(` MerkleManager: ${merkleStrkey}`); - - // AdManager — initialize(admin, verifier, merkle, w_native, chain_id). - const adStrkey = await createContract(server, admin, adHash); - await invoke(server, admin, adStrkey, 'initialize', [ - new Address(admin.publicKey()).toScVal(), - new Address(verifierStrkey).toScVal(), - new Address(merkleStrkey).toScVal(), - new Address(xlmSacStrkey).toScVal(), - nativeToScVal(BigInt(STELLAR_CHAIN_ID), { type: 'u128' }), - ]); - console.log(` AdManager: ${adStrkey}`); - - // Grant AdManager merkle_manager role so it can write roots on create/lock. - await invoke(server, admin, merkleStrkey, 'set_manager', [ - new Address(adStrkey).toScVal(), - xdr.ScVal.scvBool(true), - ]); - - const contracts: StellarChainData = { - adManagerAddress: contractIdToHex32(adStrkey), - merkleManagerAddress: contractIdToHex32(merkleStrkey), - verifierAddress: contractIdToHex32(verifierStrkey), - tokenAddress: contractIdToHex32(xlmSacStrkey), - chainId: STELLAR_CHAIN_ID, - name: STELLAR_CHAIN_NAME, - tokenName: 'Native XLM', - tokenSymbol: 'XLM', - adminPublicKeyHex: accountIdToHex32(admin.publicKey()), - adminSecret: admin.secret(), - }; - - const filePath = path.join(__dirname, 'stellar-deployed-contracts.json'); - fs.writeFileSync(filePath, JSON.stringify(contracts, null, 2)); - console.log('Stellar contract addresses saved to:', filePath); - - return contracts; -} - -// Hex-form bytes32 helpers so callers on the EVM side can pass raw addresses. -function bytes32FromEvmAddress(evmAddress: string): `0x${string}` { - const clean = evmAddress.replace(/^0x/i, '').toLowerCase().padStart(40, '0'); - return `0x${'00'.repeat(12)}${clean}` as `0x${string}`; -} - -function bytesN(hex: string) { - return nativeToScVal(hex32ToBuffer(hex), { type: 'bytes' }); -} - -// Register the order chain + token route on the Stellar AdManager so orders -// from the EVM chain are accepted. Analogous to setupAdManager() on EVM. -export async function linkStellarAdManagerToOrderChain( - stellar: StellarChainData, - orderChain: ChainData, -): Promise { - const admin = loadAdminKeypair(); - const server = getServer(); - const adStrkey = hex32ToContractId(stellar.adManagerAddress); - - // Stellar AdManager expects bytes32 for addresses from the order chain — - // left-pad 20-byte EVM addresses to 32 bytes. - const orderPortalBytes32 = bytes32FromEvmAddress(orderChain.orderPortalAddress); - const orderTokenBytes32 = bytes32FromEvmAddress(orderChain.tokenAddress); - - await invoke(server, admin, adStrkey, 'set_chain', [ - nativeToScVal(BigInt(orderChain.chainId), { type: 'u128' }), - bytesN(orderPortalBytes32), - xdr.ScVal.scvBool(true), - ]); - console.log(' Stellar AdManager.set_chain → order chain registered'); - - await invoke(server, admin, adStrkey, 'set_token_route', [ - bytesN(stellar.tokenAddress), - bytesN(orderTokenBytes32), - nativeToScVal(BigInt(orderChain.chainId), { type: 'u128' }), - ]); - console.log(' Stellar AdManager.set_token_route → route set'); -} diff --git a/apps/backend-relayer/test/setups/utils.ts b/apps/backend-relayer/test/setups/utils.ts index 9a10afd..0733e43 100644 --- a/apps/backend-relayer/test/setups/utils.ts +++ b/apps/backend-relayer/test/setups/utils.ts @@ -5,38 +5,11 @@ import { SiweMessage } from 'siwe'; import { PrismaClient, ChainKind } from '@prisma/client'; import { hash } from '@node-rs/argon2'; import { privateKeyToAddress, signMessage } from 'viem/accounts'; -import { - createPublicClient, - createWalletClient, - http, - PublicClient, -} from 'viem'; -import { parseEther } from 'viem'; -import { privateKeyToAccount } from 'viem/accounts'; -import { - Keypair, - Networks, - TransactionBuilder, -} from '@stellar/stellar-sdk'; -import { ethLocalnet } from '../../src/providers/viem/ethers/localnet'; - -export type AddressLike = `0x${string}`; - -export interface ChainData { - adManagerAddress: AddressLike; - orderPortalAddress: AddressLike; - merkleManagerAddress: AddressLike; - verifierAddress: AddressLike; - chainId: string; - name: string; - tokenName: string; - tokenSymbol: string; - tokenAddress: AddressLike; -} +import { Keypair, Networks, TransactionBuilder } from '@stellar/stellar-sdk'; export interface ChallengeResponse { nonce: string; - address: AddressLike; + address: `0x${string}`; expiresAt: string; domain: string; uri: string; @@ -250,65 +223,6 @@ export function randomAddress() { return wallet.address; } -export const makeEthClient = () => - createPublicClient({ chain: ethLocalnet, transport: http() }); - -async function tryTopUpViaRpc(addr: AddressLike, hexWei: string) { - const ethRpc = process.env.ETHEREUM_RPC_URL ?? 'http://localhost:9545'; - - const provider = new ethers.JsonRpcProvider(ethRpc); - try { - await provider.send('anvil_setBalance', [addr, hexWei]); - return true; - } catch { - // ignore - } - - try { - await provider.send('hardhat_setBalance', [addr, hexWei]); - return true; - } catch { - // ignore - } - - return false; -} - -export async function fundEthAddress( - client: PublicClient, - to: AddressLike, - minBalanceEther = '1.0', -): Promise { - const needed = parseEther(minBalanceEther); - const current = await client.getBalance({ address: to }); - if (current >= needed) return; - - const funderKey = process.env.FUNDER_KEY as `0x${string}` | undefined; - - if (funderKey) { - const wallet = createWalletClient({ - chain: client.chain ?? ethLocalnet, - transport: http(), - account: privateKeyToAccount(funderKey), - }); - - const hash = await wallet.sendTransaction({ - to, - value: parseEther('10'), // send 10 ETH - }); - await client.waitForTransactionReceipt({ hash }); - return; - } - - // No FUNDER_KEY; attempt node-specific balance set - const ok = await tryTopUpViaRpc(to, '0x8AC7230489E80000'); // 10 ETH - if (!ok) { - throw new Error( - 'Unable to fund address. Set FUNDER_KEY in env, or run against Anvil/Hardhat and allow *_setBalance.', - ); - } -} - export const expectObject = ( obj: any, fields: Partial>, From 849a41ed7f38ad9ff7fb78e0b42742eecf532764 Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Sun, 12 Apr 2026 23:28:43 +0100 Subject: [PATCH 02/12] feat: add backend relayer e2e ci --- .github/workflows/backend-relayer-e2e.yml | 93 +++ .github/workflows/backend-relayer-tests.yml | 72 ++ .github/workflows/cross-chain-e2e.yml | 9 +- pnpm-lock.yaml | 256 ++++--- pnpm-workspace.yaml | 1 + scripts/cross-chain-e2e/lib/deploy.ts | 288 ++++++++ scripts/cross-chain-e2e/run.ts | 158 ++--- scripts/relayer-e2e/cli.ts | 88 +++ scripts/relayer-e2e/e2e.sh | 85 +++ scripts/relayer-e2e/flows/ad-lifecycle.ts | 169 +++++ scripts/relayer-e2e/flows/trade-lifecycle.ts | 251 +++++++ scripts/relayer-e2e/lib/amount.ts | 18 + scripts/relayer-e2e/lib/api.ts | 138 ++++ scripts/relayer-e2e/lib/assert.ts | 34 + scripts/relayer-e2e/lib/auth.ts | 71 ++ scripts/relayer-e2e/lib/deploy.ts | 15 + scripts/relayer-e2e/lib/eth.ts | 82 +++ scripts/relayer-e2e/lib/evm-actions.ts | 659 ++++++++++++++++++ scripts/relayer-e2e/lib/seed.ts | 154 ++++ scripts/relayer-e2e/lib/stellar-actions.ts | 263 +++++++ scripts/relayer-e2e/package.json | 23 + scripts/relayer-e2e/tsconfig.json | 18 + ...run_cross_chain_e2e.sh => start_chains.sh} | 120 ++-- scripts/stop_chains.sh | 25 + 24 files changed, 2824 insertions(+), 266 deletions(-) create mode 100644 .github/workflows/backend-relayer-e2e.yml create mode 100644 .github/workflows/backend-relayer-tests.yml create mode 100644 scripts/cross-chain-e2e/lib/deploy.ts create mode 100644 scripts/relayer-e2e/cli.ts create mode 100755 scripts/relayer-e2e/e2e.sh create mode 100644 scripts/relayer-e2e/flows/ad-lifecycle.ts create mode 100644 scripts/relayer-e2e/flows/trade-lifecycle.ts create mode 100644 scripts/relayer-e2e/lib/amount.ts create mode 100644 scripts/relayer-e2e/lib/api.ts create mode 100644 scripts/relayer-e2e/lib/assert.ts create mode 100644 scripts/relayer-e2e/lib/auth.ts create mode 100644 scripts/relayer-e2e/lib/deploy.ts create mode 100644 scripts/relayer-e2e/lib/eth.ts create mode 100644 scripts/relayer-e2e/lib/evm-actions.ts create mode 100644 scripts/relayer-e2e/lib/seed.ts create mode 100644 scripts/relayer-e2e/lib/stellar-actions.ts create mode 100644 scripts/relayer-e2e/package.json create mode 100644 scripts/relayer-e2e/tsconfig.json rename scripts/{run_cross_chain_e2e.sh => start_chains.sh} (57%) create mode 100755 scripts/stop_chains.sh diff --git a/.github/workflows/backend-relayer-e2e.yml b/.github/workflows/backend-relayer-e2e.yml new file mode 100644 index 0000000..b1697e2 --- /dev/null +++ b/.github/workflows/backend-relayer-e2e.yml @@ -0,0 +1,93 @@ +name: Backend Relayer E2E + +on: + push: + branches: [main] + paths: + - "apps/backend-relayer/**" + - "contracts/**" + - "proof_circuits/**" + - "scripts/**" + - "packages/**" + - "pnpm-workspace.yaml" + - "pnpm-lock.yaml" + - ".github/workflows/backend-relayer-e2e.yml" + pull_request: + paths: + - "apps/backend-relayer/**" + - "contracts/**" + - "proof_circuits/**" + - "scripts/**" + - "packages/**" + - "pnpm-workspace.yaml" + - "pnpm-lock.yaml" + - ".github/workflows/backend-relayer-e2e.yml" + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + backend-relayer-e2e: + name: Backend Relayer E2E + runs-on: ubuntu-latest + timeout-minutes: 45 + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: dtolnay/rust-toolchain@stable + + - name: Install wasm32v1-none target + run: rustup target add wasm32v1-none + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Install Stellar CLI + run: | + curl -Ls https://github.com/stellar/stellar-cli/releases/download/v23.3.0/stellar-cli-23.3.0-x86_64-unknown-linux-gnu.tar.gz -o /tmp/stellar-cli.tar.gz + mkdir -p "$HOME/.local/bin" + tar -xzf /tmp/stellar-cli.tar.gz -C "$HOME/.local/bin" stellar + chmod +x "$HOME/.local/bin/stellar" + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install Node dependencies + run: pnpm install --frozen-lockfile + + - name: Cache cargo directories + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + contracts/stellar/target + key: ${{ runner.os }}-cargo-relayer-e2e-${{ hashFiles('contracts/stellar/Cargo.lock') }} + + - name: Generate Prisma client + run: pnpm --filter backend-relayer exec prisma generate + + - name: Run backend-relayer e2e + run: bash scripts/relayer-e2e/e2e.sh + + - name: Upload relayer logs + if: failure() + uses: actions/upload-artifact@v4 + with: + name: relayer-logs + path: | + .relayer.log + .chains.anvil.log + scripts/relayer-e2e/deployed.json + if-no-files-found: ignore diff --git a/.github/workflows/backend-relayer-tests.yml b/.github/workflows/backend-relayer-tests.yml new file mode 100644 index 0000000..eb78850 --- /dev/null +++ b/.github/workflows/backend-relayer-tests.yml @@ -0,0 +1,72 @@ +name: Backend Relayer Tests + +on: + push: + branches: [main] + paths: + - "apps/backend-relayer/**" + - "packages/**" + - "pnpm-workspace.yaml" + - "pnpm-lock.yaml" + - ".github/workflows/backend-relayer-tests.yml" + pull_request: + paths: + - "apps/backend-relayer/**" + - "packages/**" + - "pnpm-workspace.yaml" + - "pnpm-lock.yaml" + - ".github/workflows/backend-relayer-tests.yml" + workflow_dispatch: + +jobs: + unit: + name: Unit tests + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install Node dependencies + run: pnpm install --frozen-lockfile + + - name: Generate Prisma client + run: pnpm --filter backend-relayer exec prisma generate + + - name: Run unit tests + run: pnpm --filter backend-relayer test + + e2e: + name: Jest e2e tests + runs-on: ubuntu-latest + timeout-minutes: 25 + + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "pnpm" + + - name: Install Node dependencies + run: pnpm install --frozen-lockfile + + - name: Generate Prisma client + run: pnpm --filter backend-relayer exec prisma generate + + - name: Run Jest e2e tests + run: pnpm --filter backend-relayer test:e2e diff --git a/.github/workflows/cross-chain-e2e.yml b/.github/workflows/cross-chain-e2e.yml index 967fa5b..64aa640 100644 --- a/.github/workflows/cross-chain-e2e.yml +++ b/.github/workflows/cross-chain-e2e.yml @@ -67,5 +67,12 @@ jobs: contracts/stellar/target key: ${{ runner.os }}-cargo-e2e-${{ hashFiles('contracts/stellar/Cargo.lock') }} + - name: Start chains (Stellar + Anvil) and build contracts + run: bash scripts/start_chains.sh + - name: Run cross-chain E2E test - run: bash scripts/run_cross_chain_e2e.sh + run: npx tsx scripts/cross-chain-e2e/run.ts + + - name: Stop chains + if: always() + run: bash scripts/stop_chains.sh diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4d138b..82ac488 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -235,10 +235,10 @@ importers: version: 2.1.2(gsap@3.14.2)(react@19.1.0) '@rainbow-me/rainbowkit': specifier: ^2.2.8 - version: 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + version: 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) '@rainbow-me/rainbowkit-siwe-next-auth': specifier: ^0.5.0 - version: 0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0) '@tanstack/react-query': specifier: ^5.89.0 version: 5.98.0(react@19.1.0) @@ -304,10 +304,10 @@ importers: version: 4.0.0(tailwindcss@4.2.2) viem: specifier: ^2.37.7 - version: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) wagmi: specifier: ^2.17.1 - version: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + version: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) devDependencies: '@eslint/eslintrc': specifier: ^3 @@ -485,6 +485,40 @@ importers: specifier: ^5.3.3 version: 5.9.3 + scripts/relayer-e2e: + dependencies: + '@node-rs/argon2': + specifier: ^2.0.2 + version: 2.0.2 + '@prisma/client': + specifier: 6.16.1 + version: 6.16.1(prisma@6.16.1(typescript@5.9.3))(typescript@5.9.3) + '@stellar/stellar-sdk': + specifier: ^15.0.1 + version: 15.0.1 + cross-chain-e2e: + specifier: workspace:* + version: link:../cross-chain-e2e + ethers: + specifier: ^6.15.0 + version: 6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + siwe: + specifier: ^3.0.0 + version: 3.0.0(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + viem: + specifier: ^2.37.7 + version: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + devDependencies: + '@types/node': + specifier: ^22.10.7 + version: 22.19.17 + tsx: + specifier: ^4.21.0 + version: 4.21.0 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + packages: '@adraffy/ens-normalize@1.10.1': @@ -8313,16 +8347,16 @@ snapshots: '@balena/dockerignore@1.0.2': {} - '@base-org/account@2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@base-org/account@2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@coinbase/cdp-sdk': 1.47.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + ox: 0.6.9(typescript@5.9.3)(zod@4.3.6) preact: 10.24.2 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.3(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) transitivePeerDependencies: - '@types/react' @@ -8385,15 +8419,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@coinbase/wallet-sdk@4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@coinbase/wallet-sdk@4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + ox: 0.6.9(typescript@5.9.3)(zod@4.3.6) preact: 10.24.2 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.3(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) transitivePeerDependencies: - '@types/react' @@ -8602,11 +8636,11 @@ snapshots: ethereum-cryptography: 2.2.1 micro-ftch: 0.3.1 - '@gemini-wallet/core@0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@gemini-wallet/core@0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))': dependencies: '@metamask/rpc-errors': 7.0.2 eventemitter3: 5.0.1 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - supports-color @@ -9729,13 +9763,13 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@rainbow-me/rainbowkit-siwe-next-auth@0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0)': + '@rainbow-me/rainbowkit-siwe-next-auth@0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0)': dependencies: - '@rainbow-me/rainbowkit': 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + '@rainbow-me/rainbowkit': 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) next-auth: 4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0) react: 19.1.0 - '@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))': + '@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))': dependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) '@vanilla-extract/css': 1.17.3 @@ -9747,8 +9781,8 @@ snapshots: react-dom: 19.2.5(react@19.1.0) react-remove-scroll: 2.6.2(@types/react@19.2.14)(react@19.1.0) ua-parser-js: 1.0.41 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) transitivePeerDependencies: - '@types/react' - babel-plugin-macros @@ -9833,24 +9867,24 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9879,12 +9913,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-pay@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) lit: 3.3.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) transitivePeerDependencies: @@ -9919,12 +9953,12 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) lit: 3.3.0 transitivePeerDependencies: @@ -9956,10 +9990,10 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) lit: 3.3.0 qrcode: 1.5.3 @@ -9991,16 +10025,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76)': + '@reown/appkit-utils@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -10040,21 +10074,21 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-pay': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/types': 2.21.0 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -10085,9 +10119,9 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -10095,10 +10129,10 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - bufferutil - typescript @@ -11129,19 +11163,19 @@ snapshots: dependencies: '@vanilla-extract/css': 1.17.3 - '@wagmi/connectors@6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6)': dependencies: - '@base-org/account': 2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76) - '@gemini-wallet/core': 0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@base-org/account': 2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6) + '@gemini-wallet/core': 0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + porto: 0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -11182,11 +11216,11 @@ snapshots: - wagmi - zod - '@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.9.3) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.0(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/query-core': 5.98.0 @@ -11197,7 +11231,7 @@ snapshots: - react - use-sync-external-store - '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -11211,7 +11245,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -11241,7 +11275,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -11255,7 +11289,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -11289,18 +11323,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/ethereum-provider@2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/types': 2.21.1 - '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11423,16 +11457,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11459,16 +11493,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11557,7 +11591,7 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -11566,9 +11600,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -11597,7 +11631,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -11606,9 +11640,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -11637,7 +11671,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -11655,7 +11689,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11681,7 +11715,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -11699,7 +11733,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11821,10 +11855,10 @@ snapshots: typescript: 5.9.3 zod: 3.25.76 - abitype@1.0.8(typescript@5.9.3)(zod@3.25.76): + abitype@1.0.8(typescript@5.9.3)(zod@4.3.6): optionalDependencies: typescript: 5.9.3 - zod: 3.25.76 + zod: 4.3.6 abitype@1.2.3(typescript@5.9.3)(zod@3.22.4): optionalDependencies: @@ -13051,7 +13085,7 @@ snapshots: eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1)) @@ -13088,7 +13122,7 @@ snapshots: tinyglobby: 0.2.16 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -13102,7 +13136,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -15144,28 +15178,28 @@ snapshots: transitivePeerDependencies: - zod - ox@0.6.7(typescript@5.9.3)(zod@3.25.76): + ox@0.6.7(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + abitype: 1.0.8(typescript@5.9.3)(zod@4.3.6) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.6.9(typescript@5.9.3)(zod@3.25.76): + ox@0.6.9(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 @@ -15299,21 +15333,21 @@ snapshots: pony-cause@2.1.11: {} - porto@0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): + porto@0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) hono: 4.12.12 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.9.3) ox: 0.9.17(typescript@5.9.3)(zod@4.3.6) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) zod: 4.3.6 zustand: 5.0.12(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) react: 19.1.0 typescript: 5.9.3 - wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) transitivePeerDependencies: - '@types/react' - immer @@ -16923,15 +16957,15 @@ snapshots: vary@1.1.2: {} - viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + abitype: 1.0.8(typescript@5.9.3)(zod@4.3.6) isows: 1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.9.3)(zod@3.25.76) + ox: 0.6.7(typescript@5.9.3)(zod@4.3.6) ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.3 @@ -16991,14 +17025,14 @@ snapshots: - utf-8-validate - zod - wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): + wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6): dependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) - '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) react: 19.1.0 use-sync-external-store: 1.4.0(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c54acd8..21271b0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,3 +4,4 @@ packages: - "packages/*" - "contracts/stellar/tests/fixtures" - "scripts/cross-chain-e2e" + - "scripts/relayer-e2e" diff --git a/scripts/cross-chain-e2e/lib/deploy.ts b/scripts/cross-chain-e2e/lib/deploy.ts new file mode 100644 index 0000000..de61e57 --- /dev/null +++ b/scripts/cross-chain-e2e/lib/deploy.ts @@ -0,0 +1,288 @@ +/** + * Shared chain deployment + cross-chain linking. + * + * Extracted from run.ts phases 1-3 so both the monolithic cross-chain-e2e + * runner and the relayer-e2e script can share the same deploy path. + * + * The function signatures intentionally stay close to the inline originals so + * run.ts could keep behaving identically before and after the refactor. + */ + +import * as path from "path"; +import * as fs from "fs"; +import { ethers } from "ethers"; +import { + deployContract, + deploySAC, + invokeContract, + strkeyToHex, + evmAddressToBytes32, +} from "./stellar.js"; +import { + deployEvmContracts, + NonceTracker, + type EvmContracts, +} from "./evm.js"; + +// ── env / paths ─────────────────────────────────────────────────────── + +function requireEnv(name: string): string { + const v = process.env[name]; + if (!v) throw new Error(`env ${name} not set`); + return v; +} + +export const DEFAULT_STELLAR_CHAIN_ID = 1000001n; +export const DEFAULT_EVM_CHAIN_ID = 31337n; // Anvil default + +// ── types ───────────────────────────────────────────────────────────── + +export interface StellarDeployResult { + verifier: string; // contract id (strkey) + merkleManager: string; + adToken: string; // native XLM SAC (strkey) + adTokenHex: string; // 0x + 64 hex + adManager: string; // strkey + adManagerHex: string; + adminStrkey: string; + chainId: bigint; +} + +export interface EvmDeployResult { + chainId: bigint; + signer: ethers.Wallet; + nonces: NonceTracker; + contracts: EvmContracts; + addresses: EvmContracts["addresses"]; +} + +export interface DeployAllOpts { + /** Absolute repo root. Defaults to process.env.ROOT_DIR. */ + rootDir?: string; + /** Absolute path to compiled .wasm outputs. Defaults to {rootDir}/contracts/stellar/target/wasm32v1-none/release. */ + wasmDir?: string; + /** Absolute path to the verifier vk bytes. Defaults to {rootDir}/proof_circuits/deposits/target/vk. */ + vkPath?: string; + /** EVM JSON-RPC. Defaults to process.env.EVM_RPC_URL. */ + evmRpcUrl?: string; + /** EVM admin private key (deployer + manager). Defaults to process.env.EVM_ADMIN_PRIVATE_KEY. */ + evmAdminPrivateKey?: string; + /** Stellar chain id. */ + stellarChainId?: bigint; + /** EVM chain id. */ + evmChainId?: bigint; +} + +export interface DeployAllResult { + stellar: StellarDeployResult; + evm: EvmDeployResult; +} + +// ── stellar ─────────────────────────────────────────────────────────── + +export interface DeployStellarOpts { + wasmDir: string; + vkPath: string; + adminStrkey: string; + chainId: bigint; +} + +export function deployStellarChain(opts: DeployStellarOpts): StellarDeployResult { + const { wasmDir, vkPath, adminStrkey, chainId } = opts; + + console.log("Deploying Stellar Verifier..."); + const verifier = deployContract(path.join(wasmDir, "verifier.wasm"), [ + `--vk_bytes-file-path`, + vkPath, + ]); + console.log(` Verifier: ${verifier}`); + + console.log("Deploying Stellar MerkleManager..."); + const merkleManager = deployContract(path.join(wasmDir, "merkle_manager.wasm")); + console.log(` MerkleManager: ${merkleManager}`); + invokeContract(merkleManager, "initialize", [`--admin`, adminStrkey]); + + console.log("Deploying native XLM SAC..."); + const adToken = deploySAC("native"); + const adTokenHex = strkeyToHex(adToken); + console.log(` NativeXLM SAC: ${adToken}`); + + console.log("Deploying Stellar AdManager..."); + const adManager = deployContract(path.join(wasmDir, "ad_manager.wasm")); + console.log(` AdManager: ${adManager}`); + invokeContract(adManager, "initialize", [ + `--admin`, + adminStrkey, + `--verifier`, + verifier, + `--merkle_manager`, + merkleManager, + `--w_native_token`, + adToken, + `--chain_id`, + chainId.toString(), + ]); + + invokeContract(merkleManager, "set_manager", [ + `--manager`, + adManager, + `--status`, + "true", + ]); + + return { + verifier, + merkleManager, + adToken, + adTokenHex, + adManager, + adManagerHex: strkeyToHex(adManager), + adminStrkey, + chainId, + }; +} + +// ── evm ─────────────────────────────────────────────────────────────── + +export interface DeployEvmOpts { + rpcUrl: string; + adminPrivateKey: string; + chainId: bigint; +} + +export async function deployEvmChain(opts: DeployEvmOpts): Promise { + const contracts = await deployEvmContracts(opts.rpcUrl, opts.adminPrivateKey); + return { + chainId: opts.chainId, + signer: contracts.signer, + nonces: contracts.nonces, + contracts, + addresses: contracts.addresses, + }; +} + +// ── cross-link ──────────────────────────────────────────────────────── + +export async function linkChains( + stellar: StellarDeployResult, + evm: EvmDeployResult, +): Promise { + const { contracts, nonces, chainId: evmChainId } = evm; + const stellarChainIdStr = stellar.chainId.toString(); + const evmChainIdStr = evmChainId.toString(); + + const evmOrderPortalBytes32 = evmAddressToBytes32(contracts.addresses.orderPortal); + const evmTokenBytes32 = evmAddressToBytes32(contracts.addresses.testToken); + + console.log("Linking Stellar AdManager → EVM OrderPortal..."); + invokeContract(stellar.adManager, "set_chain", [ + `--order_chain_id`, + evmChainIdStr, + `--order_portal`, + evmOrderPortalBytes32.replace(/^0x/, ""), + `--supported`, + "true", + ]); + + console.log("Setting Stellar token route..."); + invokeContract(stellar.adManager, "set_token_route", [ + `--ad_token`, + stellar.adTokenHex.replace(/^0x/, ""), + `--order_token`, + evmTokenBytes32.replace(/^0x/, ""), + `--order_chain_id`, + evmChainIdStr, + ]); + + console.log("Linking EVM OrderPortal → Stellar AdManager..."); + { + const tx = await contracts.orderPortal.getFunction("setChain")( + stellar.chainId, + stellar.adManagerHex, + true, + { nonce: nonces.next() }, + ); + await tx.wait(); + } + + console.log("Setting EVM token route..."); + { + const tx = await contracts.orderPortal.getFunction("setTokenRoute")( + contracts.addresses.testToken, + stellar.chainId, + stellar.adTokenHex, + { nonce: nonces.next() }, + ); + await tx.wait(); + } + + console.log("Cross-chain linking complete."); +} + +// ── top-level ───────────────────────────────────────────────────────── + +export async function deployAll(opts: DeployAllOpts = {}): Promise { + const rootDir = opts.rootDir ?? requireEnv("ROOT_DIR"); + const wasmDir = + opts.wasmDir ?? path.join(rootDir, "contracts/stellar/target/wasm32v1-none/release"); + const vkPath = opts.vkPath ?? path.join(rootDir, "proof_circuits/deposits/target/vk"); + const evmRpcUrl = opts.evmRpcUrl ?? requireEnv("EVM_RPC_URL"); + const evmAdminPrivateKey = + opts.evmAdminPrivateKey ?? requireEnv("EVM_ADMIN_PRIVATE_KEY"); + const stellarChainId = opts.stellarChainId ?? DEFAULT_STELLAR_CHAIN_ID; + const evmChainId = opts.evmChainId ?? DEFAULT_EVM_CHAIN_ID; + + const { getAddress } = await import("./stellar.js"); + const adminStrkey = getAddress(); + + const stellar = deployStellarChain({ + wasmDir, + vkPath, + adminStrkey, + chainId: stellarChainId, + }); + + const evm = await deployEvmChain({ + rpcUrl: evmRpcUrl, + adminPrivateKey: evmAdminPrivateKey, + chainId: evmChainId, + }); + + await linkChains(stellar, evm); + + return { stellar, evm }; +} + +/** Write a JSON snapshot of deployed addresses. Used by relayer-e2e to seed the DB. */ +export function writeDeployedSnapshot( + outPath: string, + { stellar, evm }: DeployAllResult, +): void { + const snapshot = { + eth: { + name: "AnvilLocal", + chainId: evm.chainId.toString(), + adManagerAddress: "0x" + "0".repeat(40), // EVM AdManager not deployed in this flow + orderPortalAddress: evm.addresses.orderPortal, + merkleManagerAddress: evm.addresses.merkleManager, + verifierAddress: evm.addresses.verifier, + tokenName: "TestToken", + tokenSymbol: "TT", + tokenAddress: evm.addresses.testToken, + }, + stellar: { + name: "StellarLocal", + chainId: stellar.chainId.toString(), + adManagerAddress: stellar.adManagerHex, + orderPortalAddress: "0x" + "0".repeat(64), // Stellar OrderPortal not deployed here + merkleManagerAddress: strkeyToHex(stellar.merkleManager), + verifierAddress: strkeyToHex(stellar.verifier), + tokenName: "XLM", + tokenSymbol: "XLM", + tokenAddress: stellar.adTokenHex, + adminSecret: process.env.STELLAR_ADMIN_SECRET ?? "", + }, + }; + fs.writeFileSync(outPath, JSON.stringify(snapshot, null, 2)); + console.log(`[deploy] wrote snapshot → ${outPath}`); +} diff --git a/scripts/cross-chain-e2e/run.ts b/scripts/cross-chain-e2e/run.ts index 9428c60..ee7a1d0 100644 --- a/scripts/cross-chain-e2e/run.ts +++ b/scripts/cross-chain-e2e/run.ts @@ -4,7 +4,7 @@ * Deploys ProofBridge on a local Stellar network (ad chain) and Anvil (EVM * order chain), then exercises the full cross-chain bridge flow. * - * Four actors drive the flow (all provisioned by run_cross_chain_e2e.sh): + * Four actors drive the flow (all provisioned by scripts/start_chains.sh): * 1. stellarAdmin — configures AdManager on Stellar; ed25519 key signs * manager pre-auth for create_ad / lock_for_order / unlock. * 2. evmAdmin — configures OrderPortal on EVM; ECDSA key signs manager @@ -20,8 +20,6 @@ import * as path from "path"; import { ethers } from "ethers"; import { - deployContract, - deploySAC, invokeContract, getAddress, getSecret, @@ -29,11 +27,12 @@ import { strkeyToHex, evmAddressToBytes32, } from "./lib/stellar.js"; +import { NonceTracker, getContract } from "./lib/evm.js"; import { - deployEvmContracts, - NonceTracker, - getContract, -} from "./lib/evm.js"; + deployStellarChain, + deployEvmChain, + linkChains, +} from "./lib/deploy.js"; import { AuthTokenCounter, signEd25519, @@ -165,60 +164,26 @@ async function main() { // ════════════════════════════════════════════════════════════════ phase(1, "Deploy Stellar Contracts (ad chain)"); - console.log("Deploying Verifier..."); - const stellarVerifier = deployContract(path.join(WASM_DIR, "verifier.wasm"), [ - `--vk_bytes-file-path`, - VK_PATH, - ]); - console.log(` Verifier: ${stellarVerifier}`); - - console.log("Deploying MerkleManager..."); - const stellarMerkle = deployContract( - path.join(WASM_DIR, "merkle_manager.wasm"), - ); - console.log(` MerkleManager: ${stellarMerkle}`); - - invokeContract(stellarMerkle, "initialize", [`--admin`, stellarAdmin]); - - // Native XLM SAC — admin has friendbot-funded XLM the AdManager can pull - // on create_ad; the same SAC credits the order creator on Stellar unlock. - console.log("Deploying native XLM SAC..."); - const stellarAdToken = deploySAC("native"); - console.log(` NativeXLM SAC: ${stellarAdToken}`); - const stellarAdTokenHex = strkeyToHex(stellarAdToken); - - console.log("Deploying AdManager..."); - const stellarAdManager = deployContract( - path.join(WASM_DIR, "ad_manager.wasm"), - ); - console.log(` AdManager: ${stellarAdManager}`); - - invokeContract(stellarAdManager, "initialize", [ - `--admin`, - stellarAdmin, - `--verifier`, - stellarVerifier, - `--merkle_manager`, - stellarMerkle, - `--w_native_token`, - stellarAdToken, - `--chain_id`, - STELLAR_CHAIN_ID.toString(), - ]); - - invokeContract(stellarMerkle, "set_manager", [ - `--manager`, - stellarAdManager, - `--status`, - "true", - ]); - + const stellarDeploy = deployStellarChain({ + wasmDir: WASM_DIR, + vkPath: VK_PATH, + adminStrkey: stellarAdmin, + chainId: STELLAR_CHAIN_ID, + }); + const stellarVerifier = stellarDeploy.verifier; + const stellarMerkle = stellarDeploy.merkleManager; + const stellarAdToken = stellarDeploy.adToken; + const stellarAdTokenHex = stellarDeploy.adTokenHex; + const stellarAdManager = stellarDeploy.adManager; console.log("Stellar contracts deployed and initialized."); // Snapshot XLM balances before any contract movement so we can assert // deltas at the end: ad creator should lose AMOUNT (locked in the ad), // order creator should gain AMOUNT (received on Stellar unlock). - const adCreatorXlmBefore = stellarTokenBalance(stellarAdToken, adCreatorStellar); + const adCreatorXlmBefore = stellarTokenBalance( + stellarAdToken, + adCreatorStellar, + ); const orderCreatorXlmBefore = stellarTokenBalance( stellarAdToken, orderCreatorStellar, @@ -231,7 +196,12 @@ async function main() { // ════════════════════════════════════════════════════════════════ phase(2, "Deploy EVM Contracts (order chain)"); - const evm = await deployEvmContracts(EVM_RPC_URL, EVM_ADMIN_PRIVATE_KEY); + const evmDeploy = await deployEvmChain({ + rpcUrl: EVM_RPC_URL, + adminPrivateKey: EVM_ADMIN_PRIVATE_KEY, + chainId: EVM_CHAIN_ID, + }); + const evm = evmDeploy.contracts; const evmSigner = evm.signer; const nonces = evm.nonces; const evmAdmin = await evmSigner.getAddress(); @@ -270,55 +240,13 @@ async function main() { // ════════════════════════════════════════════════════════════════ phase(3, "Cross-Chain Linking"); - const stellarAdManagerHex = strkeyToHex(stellarAdManager); + await linkChains(stellarDeploy, evmDeploy); + + const stellarAdManagerHex = stellarDeploy.adManagerHex; const stellarAdManagerBuf = hexToBuffer(stellarAdManagerHex); const evmOrderPortalBytes32 = evmAddressToBytes32(evm.addresses.orderPortal); const evmTokenBytes32 = evmAddressToBytes32(evm.addresses.testToken); - console.log("Linking Stellar AdManager → EVM OrderPortal..."); - invokeContract(stellarAdManager, "set_chain", [ - `--order_chain_id`, - EVM_CHAIN_ID.toString(), - `--order_portal`, - evmOrderPortalBytes32.replace(/^0x/, ""), - `--supported`, - "true", - ]); - - console.log("Setting Stellar token route..."); - invokeContract(stellarAdManager, "set_token_route", [ - `--ad_token`, - stellarAdTokenHex.replace(/^0x/, ""), - `--order_token`, - evmTokenBytes32.replace(/^0x/, ""), - `--order_chain_id`, - EVM_CHAIN_ID.toString(), - ]); - - console.log("Linking EVM OrderPortal → Stellar AdManager..."); - { - const tx = await evm.orderPortal.getFunction("setChain")( - STELLAR_CHAIN_ID, - stellarAdManagerHex, - true, - { nonce: nonces.next() }, - ); - await tx.wait(); - } - - console.log("Setting EVM token route..."); - { - const tx = await evm.orderPortal.getFunction("setTokenRoute")( - evm.addresses.testToken, - STELLAR_CHAIN_ID, - stellarAdTokenHex, - { nonce: nonces.next() }, - ); - await tx.wait(); - } - - console.log("Cross-chain linking complete."); - // ════════════════════════════════════════════════════════════════ // Phase 4: Create Ad on Stellar // ════════════════════════════════════════════════════════════════ @@ -530,8 +458,12 @@ async function main() { console.log(` Target root: ${proofResult.targetRoot}`); console.log(` Bridger nullifier: ${proofResult.bridgerNullifier}`); console.log(` Ad-creator nullifier: ${proofResult.adCreatorNullifier}`); - console.log(` Bridger proof: ${proofResult.bridgerProof.length} bytes`); - console.log(` Ad-creator proof: ${proofResult.adCreatorProof.length} bytes`); + console.log( + ` Bridger proof: ${proofResult.bridgerProof.length} bytes`, + ); + console.log( + ` Ad-creator proof: ${proofResult.adCreatorProof.length} bytes`, + ); // ════════════════════════════════════════════════════════════════ // Phase 8: Order Creator Unlocks on Stellar (ad chain) @@ -643,15 +575,18 @@ async function main() { // EVM token accounting: // order creator: minted 10*AMOUNT, spent AMOUNT on createOrder → 9*AMOUNT // ad creator (EVM recipient): 0 → AMOUNT after unlock - const orderCreatorBalance = await evm.testToken.getFunction("balanceOf")( - orderCreatorEvm, - ); + const orderCreatorBalance = + await evm.testToken.getFunction("balanceOf")(orderCreatorEvm); const adRecipientBalance = await evm.testToken.getFunction("balanceOf")( AD_CREATOR_EVM_RECIPIENT, ); - console.log(` Order creator EVM token balance: ${orderCreatorBalance} (expected ${AMOUNT * 9n})`); - console.log(` Ad creator EVM token balance: ${adRecipientBalance} (expected ${AMOUNT})`); + console.log( + ` Order creator EVM token balance: ${orderCreatorBalance} (expected ${AMOUNT * 9n})`, + ); + console.log( + ` Ad creator EVM token balance: ${adRecipientBalance} (expected ${AMOUNT})`, + ); assert( orderCreatorBalance === AMOUNT * 9n, @@ -665,7 +600,10 @@ async function main() { // Stellar XLM accounting — allow small slop for network fees (both // accounts were the source of at least one tx). - const adCreatorXlmAfter = stellarTokenBalance(stellarAdToken, adCreatorStellar); + const adCreatorXlmAfter = stellarTokenBalance( + stellarAdToken, + adCreatorStellar, + ); const orderCreatorXlmAfter = stellarTokenBalance( stellarAdToken, orderCreatorStellar, diff --git a/scripts/relayer-e2e/cli.ts b/scripts/relayer-e2e/cli.ts new file mode 100644 index 0000000..1887df5 --- /dev/null +++ b/scripts/relayer-e2e/cli.ts @@ -0,0 +1,88 @@ +// CLI entrypoint for the relayer e2e lifecycle. +// +// Usage: +// tsx cli.ts deploy [--out ] +// tsx cli.ts seed [--in ] +// tsx cli.ts flows +// tsx cli.ts all [--out ] +// +// `deploy` brings up the on-chain state and writes a JSON snapshot. +// `seed` feeds that snapshot into Postgres via Prisma. +// `flows` drives the relayer over HTTP through the ad + trade lifecycles. +// `all` runs every step in-process; intended for local dev. In CI, the shell +// orchestrator in `e2e.sh` calls the subcommands individually so Docker can +// be started in between. + +import * as fs from "fs"; +import * as path from "path"; +import { deploy } from "./lib/deploy.js"; +import { seedDb, type DeployedContracts } from "./lib/seed.js"; +import { runAdLifecycle } from "./flows/ad-lifecycle.js"; +import { runTradeLifecycle } from "./flows/trade-lifecycle.js"; + +function parseFlag(argv: string[], name: string): string | undefined { + const i = argv.indexOf(name); + if (i === -1) return undefined; + return argv[i + 1]; +} + +function defaultSnapshotPath(): string { + return path.resolve(process.cwd(), "deployed.json"); +} + +function readSnapshot(p: string): DeployedContracts { + const raw = fs.readFileSync(p, "utf8"); + return JSON.parse(raw) as DeployedContracts; +} + +async function cmdDeploy(argv: string[]): Promise { + const out = parseFlag(argv, "--out") ?? defaultSnapshotPath(); + await deploy(out); +} + +async function cmdSeed(argv: string[]): Promise { + const inPath = parseFlag(argv, "--in") ?? defaultSnapshotPath(); + const snapshot = readSnapshot(inPath); + await seedDb(snapshot); +} + +async function cmdFlows(): Promise { + await runAdLifecycle(); + await runTradeLifecycle(); +} + +async function cmdAll(argv: string[]): Promise { + const out = parseFlag(argv, "--out") ?? defaultSnapshotPath(); + await deploy(out); + const snapshot = readSnapshot(out); + await seedDb(snapshot); + await cmdFlows(); +} + +async function main(): Promise { + const [, , cmd, ...rest] = process.argv; + switch (cmd) { + case "deploy": + await cmdDeploy(rest); + return; + case "seed": + await cmdSeed(rest); + return; + case "flows": + await cmdFlows(); + return; + case "all": + await cmdAll(rest); + return; + default: + console.error( + `Unknown command '${cmd ?? ""}'. Usage: tsx cli.ts {deploy|seed|flows|all} [--out/--in ]`, + ); + process.exit(2); + } +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/relayer-e2e/e2e.sh b/scripts/relayer-e2e/e2e.sh new file mode 100755 index 0000000..9b378b8 --- /dev/null +++ b/scripts/relayer-e2e/e2e.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +set -euo pipefail + +# End-to-end driver for the backend-relayer lifecycle: +# 1. start_chains.sh — anvil + stellar localnet on the host +# 2. cli.ts deploy — deploy contracts + write deployed.json +# 3. docker compose up — postgres + backend-relayer (relayer runs +# prisma migrate deploy on boot) +# 4. cli.ts seed — seed Postgres from deployed.json +# 5. cli.ts flows — exercise ad + trade lifecycles over HTTP +# 6. teardown — compose down + stop_chains.sh +# +# Controlled via env: +# SKIP_START_CHAINS=1 — assume chains are already running and +# `.chains.env` is sourced. +# SKIP_TEARDOWN=1 — leave docker + chains running on exit. +# SNAPSHOT_PATH — override deployed.json location. + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +RELAYER_DIR="$ROOT_DIR/apps/backend-relayer" +E2E_DIR="$ROOT_DIR/scripts/relayer-e2e" +SNAPSHOT_PATH="${SNAPSHOT_PATH:-$E2E_DIR/deployed.json}" + +COMPOSE_FILE="$RELAYER_DIR/docker-compose.e2e.yaml" +COMPOSE=(docker compose -f "$COMPOSE_FILE" -p relayer-e2e) + +# DB URL used from the host (postgres exposes 5433:5432 in the compose file). +HOST_DATABASE_URL="${HOST_DATABASE_URL:-postgresql://relayer:relayer@localhost:5433/relayer}" + +cleanup() { + local ec=$? + if [[ "${SKIP_TEARDOWN:-0}" == "1" ]]; then + echo "[e2e.sh] SKIP_TEARDOWN=1 — leaving services up." + exit "$ec" + fi + echo "[e2e.sh] tearing down…" + "${COMPOSE[@]}" logs backend-relayer > "$ROOT_DIR/.relayer.log" 2>&1 || true + "${COMPOSE[@]}" down --remove-orphans --volumes || true + if [[ "${SKIP_START_CHAINS:-0}" != "1" ]]; then + bash "$ROOT_DIR/scripts/stop_chains.sh" || true + fi + exit "$ec" +} +trap cleanup EXIT INT TERM + +# ── 1. chains ───────────────────────────────────────────────────────── +if [[ "${SKIP_START_CHAINS:-0}" != "1" ]]; then + echo "[e2e.sh] starting chains…" + bash "$ROOT_DIR/scripts/start_chains.sh" +fi + +if [[ -f "$ROOT_DIR/.chains.env" ]]; then + # shellcheck disable=SC1091 + source "$ROOT_DIR/.chains.env" +fi + +# ── 2. deploy ───────────────────────────────────────────────────────── +echo "[e2e.sh] deploying contracts…" +cd "$E2E_DIR" +pnpm --filter relayer-e2e exec tsx cli.ts deploy --out "$SNAPSHOT_PATH" +cd "$ROOT_DIR" + +# ── 3. compose up postgres + relayer ────────────────────────────────── +echo "[e2e.sh] starting postgres + backend-relayer containers…" +export STELLAR_ADMIN_SECRET="${STELLAR_ADMIN_SECRET:-}" +export STELLAR_NETWORK_PASSPHRASE="${STELLAR_NETWORK_PASSPHRASE:-Standalone Network ; February 2017}" +"${COMPOSE[@]}" up -d --build --wait + +# ── 4. seed ─────────────────────────────────────────────────────────── +echo "[e2e.sh] seeding database…" +# Generate prisma client against the relayer schema so seed.ts can import +# @prisma/client. Idempotent. +pnpm --filter backend-relayer exec prisma generate >/dev/null + +DATABASE_URL="$HOST_DATABASE_URL" \ + pnpm --filter relayer-e2e exec tsx cli.ts seed --in "$SNAPSHOT_PATH" + +# ── 5. flows ────────────────────────────────────────────────────────── +echo "[e2e.sh] running flows…" +RELAYER_URL="${RELAYER_URL:-http://localhost:2005}" \ +STELLAR_CHAIN_ID="${STELLAR_CHAIN_ID:-1000001}" \ +EVM_CHAIN_ID="${EVM_CHAIN_ID:-31337}" \ + pnpm --filter relayer-e2e exec tsx cli.ts flows + +echo "[e2e.sh] all phases passed ✓" diff --git a/scripts/relayer-e2e/flows/ad-lifecycle.ts b/scripts/relayer-e2e/flows/ad-lifecycle.ts new file mode 100644 index 0000000..873f25e --- /dev/null +++ b/scripts/relayer-e2e/flows/ad-lifecycle.ts @@ -0,0 +1,169 @@ +// Ad lifecycle flow — port of the `Ad lifecycle` jest test in +// apps/backend-relayer/test/integrations/eth-stellar.e2e-integration.ts. +// +// Ad creator is a Stellar identity; its EVM destination is the anvil +// prefunded #2 address. The flow: +// create → confirm → getAd → fund → confirm → withdraw → confirm → close → confirm + +import { Keypair, StrKey } from "@stellar/stellar-sdk"; +import { privateKeyToAccount } from "viem/accounts"; +import { + apiCreateAd, + apiConfirm, + apiFundAd, + apiWithdraw, + apiGetAd, + apiCloseAd, + getRoutes, + expectStatus, +} from "../lib/api.js"; +import { loginStellar } from "../lib/auth.js"; +import { toBaseUnits } from "../lib/amount.js"; +import { assert, assertObject, phase } from "../lib/assert.js"; +import { + createAdSoroban, + fundAdSoroban, + withdrawFromAdSoroban, + closeAdSoroban, +} from "../lib/stellar-actions.js"; + +export async function runAdLifecycle(): Promise { + phase("A", "Ad lifecycle"); + + const adCreatorSecret = process.env.STELLAR_AD_CREATOR_SECRET!; + const adCreator = Keypair.fromSecret(adCreatorSecret); + + const adCreatorEvmKey = process.env.EVM_AD_CREATOR_PRIVATE_KEY as `0x${string}`; + const adCreatorEvm = privateKeyToAccount(adCreatorEvmKey); + + const stellarChainId = process.env.STELLAR_CHAIN_ID!; + const evmChainId = process.env.EVM_CHAIN_ID!; + + const routes = expectStatus( + await getRoutes(stellarChainId, evmChainId), + 200, + "getRoutes", + ); + assert(routes.body.data.length > 0, "no routes seeded"); + const route = routes.body.data[0]; + + const access = await loginStellar(adCreator); + + // Create ad — 50 XLM. + const INITIAL = toBaseUnits("50", "STELLAR"); + const create = expectStatus( + await apiCreateAd(access, route.id, adCreatorEvm.address, INITIAL), + 201, + "apiCreateAd", + ); + const req = create.body as any; + const adId = req.adId; + + const txCreate = await createAdSoroban( + adCreator, + req.signature, + req.signerPublicKey, + req.authToken, + req.timeToExpire, + adCreator.publicKey(), + req.adId, + req.adToken, + req.initialAmount, + req.orderChainId, + req.adRecipient, + req.contractAddress, + ); + expectStatus( + await apiConfirm(adId, access, txCreate as `0x${string}`), + 200, + "apiConfirm(create)", + ); + + const adAfterCreate = expectStatus(await apiGetAd(adId), 200, "apiGetAd(create)"); + assertObject(adAfterCreate.body, { + id: adId, + status: "ACTIVE", + poolAmount: INITIAL, + }); + + // Fund — 5 XLM. + const topup = expectStatus( + await apiFundAd(adId, access, toBaseUnits("5", "STELLAR")), + 200, + "apiFundAd", + ); + const txFund = await fundAdSoroban( + adCreator, + topup.body.signature, + topup.body.signerPublicKey, + topup.body.authToken, + topup.body.timeToExpire, + topup.body.adId, + topup.body.amount, + topup.body.contractAddress, + ); + expectStatus( + await apiConfirm(adId, access, txFund as `0x${string}`), + 200, + "apiConfirm(fund)", + ); + + // Withdraw — 1 XLM to the ad creator. + const withdraw = expectStatus( + await apiWithdraw( + adId, + access, + toBaseUnits("1", "STELLAR"), + adCreator.publicKey(), + ), + 200, + "apiWithdraw", + ); + const txW = await withdrawFromAdSoroban( + adCreator, + withdraw.body.signature, + withdraw.body.signerPublicKey, + withdraw.body.authToken, + withdraw.body.timeToExpire, + withdraw.body.adId, + withdraw.body.amount, + StrKey.isValidEd25519PublicKey(withdraw.body.to) + ? withdraw.body.to + : adCreator.publicKey(), + withdraw.body.contractAddress, + ); + expectStatus( + await apiConfirm(adId, access, txW as `0x${string}`), + 200, + "apiConfirm(withdraw)", + ); + + // Close. + const close = expectStatus( + await apiCloseAd(adId, access, { to: adCreator.publicKey() }), + 200, + "apiCloseAd", + ); + const txClose = await closeAdSoroban( + adCreator, + close.body.signature, + close.body.signerPublicKey, + close.body.authToken, + close.body.timeToExpire, + close.body.adId, + StrKey.isValidEd25519PublicKey(close.body.to) + ? close.body.to + : adCreator.publicKey(), + close.body.contractAddress, + ); + expectStatus( + await apiConfirm(adId, access, txClose as `0x${string}`), + 200, + "apiConfirm(close)", + ); + + const finalAd = expectStatus(await apiGetAd(adId), 200, "apiGetAd(final)"); + assertObject(finalAd.body, { status: "CLOSED", poolAmount: "0" }); + + console.log("[ad-lifecycle] passed"); +} diff --git a/scripts/relayer-e2e/flows/trade-lifecycle.ts b/scripts/relayer-e2e/flows/trade-lifecycle.ts new file mode 100644 index 0000000..67b9c1c --- /dev/null +++ b/scripts/relayer-e2e/flows/trade-lifecycle.ts @@ -0,0 +1,251 @@ +// Trade lifecycle flow — port of the `Trade lifecycle` jest test in +// apps/backend-relayer/test/integrations/eth-stellar.e2e-integration.ts. +// +// Ad on Stellar, order on EVM. Ad creator unlocks on EVM (ECDSA). Bridger +// unlocks on Stellar (ed25519 over the order hash). + +import { Keypair } from "@stellar/stellar-sdk"; +import { getAddress, parseEther } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { TypedDataEncoder } from "ethers"; +import { + apiCreateAd, + apiConfirm, + apiCreateOrder, + apiTradeConfirm, + apiLockOrder, + apiTradeParams, + apiUnlockOrder, + apiTradeUnlockConfirm, + apiGetTrade, + getRoutes, + expectStatus, +} from "../lib/api.js"; +import { loginStellar, loginEvm } from "../lib/auth.js"; +import { toBaseUnits } from "../lib/amount.js"; +import { assert, assertObject, phase } from "../lib/assert.js"; +import { + createAdSoroban, + lockForOrderSoroban, + unlockSoroban, +} from "../lib/stellar-actions.js"; +import { + createOrder, + unlockOrderChain, + mintToken, + approveToken, +} from "../lib/evm-actions.js"; +import { makeEthClient, fundEthAddress } from "../lib/eth.js"; +import { + domain, + orderTypes, + signTypedOrder, +} from "../../../apps/backend-relayer/src/providers/viem/ethers/typedData.js"; + +export async function runTradeLifecycle(): Promise { + phase("B", "Trade lifecycle"); + + const bridgerKey = process.env.EVM_ORDER_CREATOR_PRIVATE_KEY as `0x${string}`; + const bridger = privateKeyToAccount(bridgerKey); + + const bridgerStellarSecret = process.env.STELLAR_ORDER_CREATOR_SECRET!; + const bridgerStellar = Keypair.fromSecret(bridgerStellarSecret); + + const adCreatorSecret = process.env.STELLAR_AD_CREATOR_SECRET!; + const adCreator = Keypair.fromSecret(adCreatorSecret); + + const adCreatorEvmKey = process.env.EVM_AD_CREATOR_PRIVATE_KEY as `0x${string}`; + const adCreatorEvm = privateKeyToAccount(adCreatorEvmKey); + + const stellarChainId = process.env.STELLAR_CHAIN_ID!; + const evmChainId = process.env.EVM_CHAIN_ID!; + + const ethClient = makeEthClient(); + await fundEthAddress(ethClient, bridger.address); + await fundEthAddress(ethClient, adCreatorEvm.address); + + const routes = expectStatus( + await getRoutes(stellarChainId, evmChainId), + 200, + "getRoutes", + ); + assert(routes.body.data.length > 0, "no routes seeded"); + const route = routes.body.data[0]; + + const adAccess = await loginStellar(adCreator); + + // Seed the ad. + const INITIAL = toBaseUnits("50", "STELLAR"); + const create = expectStatus( + await apiCreateAd(adAccess, route.id, adCreatorEvm.address, INITIAL), + 201, + "apiCreateAd", + ); + const req = create.body as any; + const adId = req.adId; + const txCreate = await createAdSoroban( + adCreator, + req.signature, + req.signerPublicKey, + req.authToken, + req.timeToExpire, + adCreator.publicKey(), + req.adId, + req.adToken, + req.initialAmount, + req.orderChainId, + req.adRecipient, + req.contractAddress, + ); + expectStatus( + await apiConfirm(adId, adAccess, txCreate as `0x${string}`), + 200, + "apiConfirm(create)", + ); + + // Bridger creates the order on EVM. + const bridgerAccess = await loginEvm(bridgerKey); + const order = expectStatus( + await apiCreateOrder(bridgerAccess, { + adId, + routeId: route.id, + amount: toBaseUnits("10", "STELLAR"), + bridgerDstAddress: bridgerStellar.publicKey(), + }), + 201, + "apiCreateOrder", + ); + const orderReq = order.body.reqContractDetails; + const tradeId = order.body.tradeId as string; + + // Mint + approve test tokens to the order portal. + const evmTokenAddress = orderReq.orderParams.orderChainToken; // bytes32 + // addresses in orderParams are bytes32 — the portal address is the contractAddress field. + const orderPortalAddress = orderReq.contractAddress as `0x${string}`; + + // Need the real (20-byte) token address — recover from the orderParams + // bytes32 by slicing the last 20 bytes. + const tokenAddr20 = getAddress(`0x${evmTokenAddress.slice(-40)}`); + + await mintToken(ethClient, bridger, tokenAddr20, bridger.address, parseEther("1000")); + await approveToken(ethClient, bridger, tokenAddr20, orderPortalAddress, parseEther("100")); + + const orderCreateTx = await createOrder( + ethClient, + bridger, + orderReq.signature, + orderReq.authToken as `0x${string}`, + orderReq.timeToExpire, + orderReq.orderParams, + orderPortalAddress, + ); + expectStatus( + await apiTradeConfirm(tradeId, bridgerAccess, orderCreateTx), + 200, + "apiTradeConfirm(order)", + ); + + // Lock on the Stellar ad chain. + const lockOrder = expectStatus( + await apiLockOrder(adAccess, tradeId), + 200, + "apiLockOrder", + ); + const lockReq = lockOrder.body; + const lockTxn = await lockForOrderSoroban( + adCreator, + lockReq.signature as `0x${string}`, + lockReq.signerPublicKey, + lockReq.authToken as `0x${string}`, + lockReq.timeToExpire, + lockReq.orderParams, + lockReq.contractAddress, + ); + expectStatus( + await apiTradeConfirm(tradeId, adAccess, lockTxn as `0x${string}`), + 200, + "apiTradeConfirm(lock)", + ); + + const afterLock = expectStatus( + await apiGetTrade(tradeId), + 200, + "apiGetTrade(after lock)", + ); + assertObject(afterLock.body, { status: "LOCKED" }); + + // Ad-creator unlocks on EVM: sign with the EVM destination key (ECDSA). + const adCreatorParams = expectStatus( + await apiTradeParams(adAccess, tradeId), + 200, + "apiTradeParams(adCreator)", + ); + const adCreatorOrderParams = adCreatorParams.body; + const adCreatorSig = await signTypedOrder(adCreatorEvmKey, adCreatorOrderParams); + + const unlockOnOrder = expectStatus( + await apiUnlockOrder(adAccess, tradeId, adCreatorSig), + 200, + "apiUnlockOrder(adCreator)", + ); + const unlockOrderReq = unlockOnOrder.body; + const unlockOrderTx = await unlockOrderChain( + ethClient, + adCreatorEvm, + unlockOrderReq.signature, + unlockOrderReq.authToken as `0x${string}`, + unlockOrderReq.timeToExpire, + unlockOrderReq.orderParams, + unlockOrderReq.nullifierHash as `0x${string}`, + unlockOrderReq.targetRoot as `0x${string}`, + unlockOrderReq.proof as `0x${string}`, + unlockOrderReq.contractAddress, + ); + expectStatus( + await apiTradeUnlockConfirm(adAccess, tradeId, unlockOrderTx), + 200, + "apiTradeUnlockConfirm(adCreator)", + ); + + // Bridger unlocks on Stellar: ed25519 over the TypedDataEncoder hash. + const bridgerParams = expectStatus( + await apiTradeParams(bridgerAccess, tradeId), + 200, + "apiTradeParams(bridger)", + ); + const bridgerOrderParams = bridgerParams.body; + const bridgerOrderHash = TypedDataEncoder.hash(domain, orderTypes, { + ...bridgerOrderParams, + salt: BigInt(bridgerOrderParams.salt), + }); + const bridgerSigBytes = bridgerStellar.sign( + Buffer.from(bridgerOrderHash.replace(/^0x/, ""), "hex"), + ); + const bridgerSig = `0x${bridgerSigBytes.toString("hex")}`; + + const unlockOnAd = expectStatus( + await apiUnlockOrder(bridgerAccess, tradeId, bridgerSig), + 200, + "apiUnlockOrder(bridger)", + ); + const unlockAdReq = unlockOnAd.body; + const unlockAdTx = await unlockSoroban( + bridgerStellar, + unlockAdReq.signature, + unlockAdReq.signerPublicKey, + unlockAdReq.authToken, + unlockAdReq.timeToExpire, + unlockAdReq.orderParams, + unlockAdReq.nullifierHash, + unlockAdReq.targetRoot, + Buffer.from(unlockAdReq.proof.replace(/^0x/, ""), "hex"), + unlockAdReq.contractAddress, + ); + expectStatus( + await apiTradeUnlockConfirm(bridgerAccess, tradeId, unlockAdTx as `0x${string}`), + 200, + "apiTradeUnlockConfirm(bridger)", + ); + + console.log("[trade-lifecycle] passed"); +} diff --git a/scripts/relayer-e2e/lib/amount.ts b/scripts/relayer-e2e/lib/amount.ts new file mode 100644 index 0000000..4faf4c7 --- /dev/null +++ b/scripts/relayer-e2e/lib/amount.ts @@ -0,0 +1,18 @@ +// Chain-aware base-unit formatter — mirrors apps/backend-relayer/test/setups/amount.ts. + +import { parseUnits } from "viem"; + +export type ChainKind = "EVM" | "STELLAR"; + +export const DEFAULT_DECIMALS: Record = { + EVM: 18, + STELLAR: 7, +}; + +export function toBaseUnits( + amount: string, + chainKind: ChainKind, + decimals: number = DEFAULT_DECIMALS[chainKind] +): string { + return parseUnits(amount, decimals).toString(); +} diff --git a/scripts/relayer-e2e/lib/api.ts b/scripts/relayer-e2e/lib/api.ts new file mode 100644 index 0000000..e882c39 --- /dev/null +++ b/scripts/relayer-e2e/lib/api.ts @@ -0,0 +1,138 @@ +// HTTP client against the running backend-relayer. Mirrors the surface of +// apps/backend-relayer/test/integrations/api.ts but uses fetch instead of +// supertest so we can drive a containerized relayer over the network. + +const RELAYER_URL = process.env.RELAYER_URL ?? "http://localhost:2005"; + +export interface ApiResponse { + status: number; + ok: boolean; + body: T; +} + +async function request( + method: "GET" | "POST" | "PATCH" | "DELETE", + path: string, + opts: { body?: unknown; token?: string } = {} +): Promise> { + const headers: Record = { + "content-type": "application/json", + }; + if (opts.token) headers.authorization = `Bearer ${opts.token}`; + + const res = await fetch(`${RELAYER_URL}${path}`, { + method, + headers, + body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined, + }); + + let body: any = null; + const text = await res.text(); + if (text) { + try { + body = JSON.parse(text); + } catch { + body = text; + } + } + + return { status: res.status, ok: res.ok, body }; +} + +function expectStatus(res: ApiResponse, expected: number, label: string): ApiResponse { + if (res.status !== expected) { + throw new Error( + `[${label}] expected status ${expected}, got ${res.status}: ${JSON.stringify(res.body)}` + ); + } + return res; +} + +// ── auth ────────────────────────────────────────────────────────────── + +export const apiAuthChallenge = (body: { address: string; chainKind: "EVM" | "STELLAR" }) => + request("POST", "/v1/auth/challenge", { body }); + +export const apiAuthLogin = (body: Record) => + request("POST", "/v1/auth/login", { body }); + +// ── routes ──────────────────────────────────────────────────────────── + +export const getRoutes = (fromChainId: string, toChainId: string) => + request("GET", `/v1/routes?fromChainId=${fromChainId}&toChainId=${toChainId}`); + +// ── ads ─────────────────────────────────────────────────────────────── + +export const apiCreateAd = ( + token: string, + routeId: string, + creatorDstAddress: string, + fundAmount: string +) => + request("POST", "/v1/ads", { + token, + body: { routeId, creatorDstAddress, fundAmount }, + }); + +export const apiConfirm = (adId: string, token: string, txHash: `0x${string}`) => + request("POST", `/v1/ads/${adId}/confirm`, { token, body: { txHash } }); + +export const apiFundAd = (adId: string, token: string, amount: string) => + request("POST", `/v1/ads/${adId}/fund`, { token, body: { amount } }); + +export const apiWithdraw = ( + adId: string, + token: string, + amount: string, + to: string +) => + request("POST", `/v1/ads/${adId}/withdraw`, { + token, + body: { amount, to }, + }); + +export const apiGetAd = (adId: string) => request("GET", `/v1/ads/${adId}`); + +export const apiCloseAd = (adId: string, token: string, body: { to: string }) => + request("POST", `/v1/ads/${adId}/close`, { token, body }); + +// ── trades ──────────────────────────────────────────────────────────── + +export const apiCreateOrder = ( + token: string, + body: { + adId: string; + routeId: string; + amount: string; + bridgerDstAddress: string; + } +) => request("POST", "/v1/trades", { token, body }); + +export const apiGetTrade = (tradeId: string) => request("GET", `/v1/trades/${tradeId}`); + +export const apiTradeConfirm = (tradeId: string, token: string, txHash: `0x${string}`) => + request("POST", `/v1/trades/${tradeId}/confirm`, { token, body: { txHash } }); + +export const apiLockOrder = (token: string, tradeId: string) => + request("POST", `/v1/trades/${tradeId}/lock`, { token }); + +export const apiTradeParams = (token: string, tradeId: string) => + request("GET", `/v1/trades/${tradeId}/params`, { token }); + +export const apiUnlockOrder = (token: string, tradeId: string, signature: string) => + request("POST", `/v1/trades/${tradeId}/unlock`, { + token, + body: { signature }, + }); + +export const apiTradeUnlockConfirm = ( + token: string, + tradeId: string, + txHash: `0x${string}` +) => + request("POST", `/v1/trades/${tradeId}/unlock/confirm`, { + token, + body: { txHash }, + }); + +export { expectStatus, RELAYER_URL }; diff --git a/scripts/relayer-e2e/lib/assert.ts b/scripts/relayer-e2e/lib/assert.ts new file mode 100644 index 0000000..783f07a --- /dev/null +++ b/scripts/relayer-e2e/lib/assert.ts @@ -0,0 +1,34 @@ +export function assert(condition: unknown, message: string): asserts condition { + if (!condition) throw new Error(`ASSERT: ${message}`); +} + +export function assertEq(actual: T, expected: T, label = "value"): void { + if (actual !== expected) { + throw new Error( + `ASSERT: ${label} mismatch — expected ${JSON.stringify( + expected, + )}, got ${JSON.stringify(actual)}`, + ); + } +} + +export function assertObject( + obj: Record, + expected: Record, +): void { + for (const [k, v] of Object.entries(expected)) { + const a = obj[k]; + if (a !== v && JSON.stringify(a) !== JSON.stringify(v)) { + throw new Error( + `ASSERT: field '${k}' mismatch — expected ${JSON.stringify( + v, + )}, got ${JSON.stringify(a)}`, + ); + } + } +} + +export function phase(n: number | string, title: string): void { + const bar = "=".repeat(60); + console.log(`\n${bar}\nPhase ${n}: ${title}\n${bar}`); +} diff --git a/scripts/relayer-e2e/lib/auth.ts b/scripts/relayer-e2e/lib/auth.ts new file mode 100644 index 0000000..a428eb5 --- /dev/null +++ b/scripts/relayer-e2e/lib/auth.ts @@ -0,0 +1,71 @@ +// HTTP login flows — EVM (SIWE) and Stellar (signed XDR). +// Port of apps/backend-relayer/test/setups/utils.ts:loginUser/loginStellarUser +// but driven by fetch against the running relayer. + +import { SiweMessage } from "siwe"; +import { privateKeyToAddress, signMessage } from "viem/accounts"; +import { + Keypair, + Networks, + TransactionBuilder, +} from "@stellar/stellar-sdk"; +import { apiAuthChallenge, apiAuthLogin, expectStatus } from "./api.js"; + +export async function loginEvm(privateKey: `0x${string}`): Promise { + const address = privateKeyToAddress(privateKey); + + const challenge = expectStatus( + await apiAuthChallenge({ address, chainKind: "EVM" }), + 200, + "auth.challenge(EVM)" + ); + + const now = new Date().toISOString(); + const exp = new Date(Date.now() + 5 * 60_000).toISOString(); + + const msg = new SiweMessage({ + domain: challenge.body.domain, + address, + statement: "Sign in to ProofBridge", + uri: challenge.body.uri, + version: "1", + chainId: 1, + nonce: challenge.body.nonce, + issuedAt: now, + expirationTime: exp, + }); + const message = msg.prepareMessage(); + const signature = await signMessage({ message, privateKey }); + + const login = expectStatus( + await apiAuthLogin({ message, signature, chainKind: "EVM" }), + 201, + "auth.login(EVM)" + ); + return login.body.tokens.access as string; +} + +export async function loginStellar(keypair: Keypair): Promise { + const challenge = expectStatus( + await apiAuthChallenge({ address: keypair.publicKey(), chainKind: "STELLAR" }), + 200, + "auth.challenge(STELLAR)" + ); + + const xdrString = challenge.body.transaction as string; + const passphrase = + (challenge.body.networkPassphrase as string) || + process.env.STELLAR_NETWORK_PASSPHRASE || + Networks.TESTNET; + + const tx = TransactionBuilder.fromXDR(xdrString, passphrase as Networks); + tx.sign(keypair); + const signedXdr = tx.toEnvelope().toXDR("base64"); + + const login = expectStatus( + await apiAuthLogin({ transaction: signedXdr, chainKind: "STELLAR" }), + 201, + "auth.login(STELLAR)" + ); + return login.body.tokens.access as string; +} diff --git a/scripts/relayer-e2e/lib/deploy.ts b/scripts/relayer-e2e/lib/deploy.ts new file mode 100644 index 0000000..c3e00f2 --- /dev/null +++ b/scripts/relayer-e2e/lib/deploy.ts @@ -0,0 +1,15 @@ +import * as path from "path"; +import { + deployAll, + writeDeployedSnapshot, + type DeployAllResult, +} from "cross-chain-e2e/lib/deploy.js"; + +export async function deploy(outPath?: string): Promise { + const result = await deployAll(); + const snapshotPath = outPath ?? path.resolve(process.cwd(), "deployed.json"); + writeDeployedSnapshot(snapshotPath, result); + return result; +} + +export { deployAll, writeDeployedSnapshot }; diff --git a/scripts/relayer-e2e/lib/eth.ts b/scripts/relayer-e2e/lib/eth.ts new file mode 100644 index 0000000..0a20e09 --- /dev/null +++ b/scripts/relayer-e2e/lib/eth.ts @@ -0,0 +1,82 @@ +import { ethers } from "ethers"; +import { + createPublicClient, + createWalletClient, + http, + parseEther, + type PublicClient, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { ethLocalnet } from "../../../apps/backend-relayer/src/providers/viem/ethers/localnet.js"; + +export type AddressLike = `0x${string}`; + +export interface EthChainData { + adManagerAddress: AddressLike; + orderPortalAddress: AddressLike; + merkleManagerAddress: AddressLike; + verifierAddress: AddressLike; + chainId: string; + name: string; + tokenName: string; + tokenSymbol: string; + tokenAddress: AddressLike; +} + +export const makeEthClient = () => + createPublicClient({ chain: ethLocalnet, transport: http() }); + +async function tryTopUpViaRpc(addr: AddressLike, hexWei: string) { + const ethRpc = process.env.ETHEREUM_RPC_URL ?? "http://localhost:9545"; + const provider = new ethers.JsonRpcProvider(ethRpc); + + try { + await provider.send("anvil_setBalance", [addr, hexWei]); + return true; + } catch { + // ignore + } + + try { + await provider.send("hardhat_setBalance", [addr, hexWei]); + return true; + } catch { + // ignore + } + + return false; +} + +export async function fundEthAddress( + client: PublicClient, + to: AddressLike, + minBalanceEther = "1.0", +): Promise { + const needed = parseEther(minBalanceEther); + const current = await client.getBalance({ address: to }); + if (current >= needed) return; + + const funderKey = process.env.FUNDER_KEY as `0x${string}` | undefined; + + if (funderKey) { + const wallet = createWalletClient({ + chain: client.chain ?? ethLocalnet, + transport: http(), + account: privateKeyToAccount(funderKey), + }); + + const hash = await wallet.sendTransaction({ + to, + value: parseEther("10"), + }); + await client.waitForTransactionReceipt({ hash }); + return; + } + + const ok = await tryTopUpViaRpc(to, "0x8AC7230489E80000"); // 10 ETH + if (!ok) { + throw new Error( + "Unable to fund address. Set FUNDER_KEY in env, or run against Anvil/Hardhat and allow *_setBalance.", + ); + } +} diff --git a/scripts/relayer-e2e/lib/evm-actions.ts b/scripts/relayer-e2e/lib/evm-actions.ts new file mode 100644 index 0000000..eda4825 --- /dev/null +++ b/scripts/relayer-e2e/lib/evm-actions.ts @@ -0,0 +1,659 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { ethers } from 'ethers'; +import { EthChainData, AddressLike } from './eth.js'; + +// Same pattern as scripts/cross-chain-e2e/lib/evm.ts — read Foundry output at +// runtime from $ROOT_DIR/contracts/evm/out. +const ROOT_DIR = process.env.ROOT_DIR!; +const EVM_OUT = path.join(ROOT_DIR ?? '', 'contracts/evm/out'); + +function loadArtifact(contractFileName: string, contractName: string) { + const artifactPath = path.join( + EVM_OUT, + `${contractFileName}.sol`, + `${contractName}.json`, + ); + if (!fs.existsSync(artifactPath)) { + throw new Error(`Artifact not found: ${artifactPath}`); + } + const artifact = JSON.parse(fs.readFileSync(artifactPath, 'utf8')); + return { abi: artifact.abi, bytecode: artifact.bytecode.object }; +} + +const AD_MANAGER_ABI = loadArtifact('AdManager', 'AdManager').abi; +const ORDER_PORTAL_ABI = loadArtifact('OrderPortal', 'OrderPortal').abi; +const MERKLE_MANAGER_ABI = loadArtifact('MerkleManager', 'MerkleManager').abi; +const ERC20_MOCK_ABI = loadArtifact('ERC20Mock', 'ERC20Mock').abi; +import { + T_AdManagerOrderParams, + T_OrderPortalParams, +} from '../../../apps/backend-relayer/src/chain-adapters/types.js'; +import { + createWalletClient, + getAddress, + http, + PrivateKeyAccount, + PublicClient, +} from 'viem'; + +const DEFAULT_ADMIN_ROLE = ethers.ZeroHash as `0x${string}`; + +export async function grantManagerRole( + publicClient: PublicClient, + account: PrivateKeyAccount, + chain: EthChainData, +) { + const mgrAddr = account.address; + + const isAdmin = await publicClient.readContract({ + address: chain.merkleManagerAddress, + abi: MERKLE_MANAGER_ABI, + functionName: 'hasRole', + args: [DEFAULT_ADMIN_ROLE, mgrAddr], + }); + + if (!isAdmin) throw new Error(`Signer ${mgrAddr} is NOT DEFAULT_ADMIN_ROLE`); + + const MANAGER_ROLE = ethers.id('MANAGER_ROLE'); + + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + let hash = await wallet.writeContract({ + chain: publicClient.chain, + address: chain.merkleManagerAddress, + abi: MERKLE_MANAGER_ABI, + functionName: 'grantRole', + args: [MANAGER_ROLE, chain.orderPortalAddress], + }); + let receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error(`grantRole(OP) tx failed: ${receipt.transactionHash}`); + } + console.log('Orderportal granted'); + + hash = await wallet.writeContract({ + chain: publicClient.chain, + address: chain.merkleManagerAddress, + abi: MERKLE_MANAGER_ABI, + functionName: 'grantRole', + args: [MANAGER_ROLE, chain.adManagerAddress], + }); + receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error(`grantRole(AD) tx failed: ${receipt.transactionHash}`); + } + + console.log('AdManager granted'); +} + +export async function setupAdManager( + publicClient: PublicClient, + account: PrivateKeyAccount, + adChain: EthChainData, + orderChain: EthChainData, +) { + const mgrAddr = account.address; + + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const isAdmin = await publicClient.readContract({ + address: adChain.adManagerAddress, + abi: AD_MANAGER_ABI, + functionName: 'hasRole', + args: [DEFAULT_ADMIN_ROLE, mgrAddr], + }); + + if (!isAdmin) throw new Error(`Signer ${mgrAddr} is NOT DEFAULT_ADMIN_ROLE`); + + let hash = await wallet.writeContract({ + chain: publicClient.chain, + address: adChain.adManagerAddress, + abi: AD_MANAGER_ABI, + functionName: 'setChain', + args: [BigInt(orderChain.chainId), orderChain.orderPortalAddress, true], + }); + + let receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `AdManager.setChain tx failed revert: ${receipt.transactionHash}`, + ); + } + + console.log('AdManager set'); + + hash = await wallet.writeContract({ + chain: publicClient.chain, + address: adChain.adManagerAddress, + abi: AD_MANAGER_ABI, + functionName: 'setTokenRoute', + args: [ + adChain.tokenAddress, + orderChain.tokenAddress, + BigInt(orderChain.chainId), + ], + }); + + receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `AdManager.setChain tx failed revert: ${receipt.transactionHash}`, + ); + } + + console.log('AdManager route set'); +} + +export async function setupOrderPortal( + publicClient: PublicClient, + account: PrivateKeyAccount, + adChain: EthChainData, + orderChain: EthChainData, +) { + const mgrAddr = account.address; + + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const isAdmin = await publicClient.readContract({ + address: orderChain.orderPortalAddress, + abi: AD_MANAGER_ABI, + functionName: 'hasRole', + args: [DEFAULT_ADMIN_ROLE, mgrAddr], + }); + + if (!isAdmin) throw new Error(`Signer ${mgrAddr} is NOT DEFAULT_ADMIN_ROLE`); + + let hash = await wallet.writeContract({ + chain: publicClient.chain, + address: orderChain.orderPortalAddress, + abi: ORDER_PORTAL_ABI, + functionName: 'setChain', + args: [BigInt(adChain.chainId), adChain.adManagerAddress, true], + }); + + let receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `OrderPortal.setChain tx failed revert: ${receipt.transactionHash}`, + ); + } + + console.log('OrderPortal set'); + + hash = await wallet.writeContract({ + chain: publicClient.chain, + address: orderChain.orderPortalAddress, + abi: ORDER_PORTAL_ABI, + functionName: 'setTokenRoute', + args: [ + orderChain.tokenAddress, + BigInt(adChain.chainId), + adChain.tokenAddress, + ], + }); + + receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `OrderPortal.setTokenRoute tx failed revert: ${receipt.transactionHash}`, + ); + } + + console.log('OrderPortal route set'); +} + +export async function adminSetup( + publicClient: PublicClient, + account: PrivateKeyAccount, + chain1: EthChainData, + chain2: EthChainData, +) { + // On the same provider chain (chain 1) + // Setup roles and contracts + await grantManagerRole(publicClient, account, chain1); + // chain 1 is the ad chain, chain 2 is the order chain for setupAdManager + await setupAdManager(publicClient, account, chain1, chain2); + // chain 2 is the ad chain, chain 1 is the order chain for setupOrderPortal + await setupOrderPortal(publicClient, account, chain2, chain1); +} + +export async function createAd( + publicClient: PublicClient, + account: PrivateKeyAccount, + signature: `0x${string}`, + authToken: `0x${string}`, + timeToExpire: number, + adId: string, + adToken: `0x${string}`, + fundAmount: string, + orderChainId: string, + adRecipient: `0x${string}`, + adManagerAddress: string, +) { + console.log(adManagerAddress); + + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: adManagerAddress as AddressLike, + abi: AD_MANAGER_ABI, + functionName: 'createAd', + args: [ + signature, + authToken, + BigInt(timeToExpire), + adId, + adToken, + BigInt(fundAmount), + BigInt(orderChainId), + adRecipient, + ], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `AdManager.setChain tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function fundAd( + publicClient: PublicClient, + account: PrivateKeyAccount, + signature: `0x${string}`, + authToken: `0x${string}`, + timeToExpire: number, + adId: string, + amount: bigint, + adManagerAddress: string, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: adManagerAddress as AddressLike, + abi: AD_MANAGER_ABI, + functionName: 'fundAd', + args: [signature, authToken, BigInt(timeToExpire), adId, amount], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `AdManager.fundAd tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function withdrawAdFunds( + publicClient: PublicClient, + account: PrivateKeyAccount, + signature: `0x${string}`, + authToken: `0x${string}`, + timeToExpire: number, + adId: string, + amount: bigint, + to: `0x${string}`, + adManagerAddress: string, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: adManagerAddress as AddressLike, + abi: AD_MANAGER_ABI, + functionName: 'withdrawFromAd', + args: [signature, authToken, BigInt(timeToExpire), adId, amount, to], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `AdManager.withdrawFromAd tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function closeAd( + publicClient: PublicClient, + account: PrivateKeyAccount, + signature: `0x${string}`, + authToken: `0x${string}`, + timeToExpire: number, + adId: string, + to: `0x${string}`, + adManagerAddress: string, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: adManagerAddress as AddressLike, + abi: AD_MANAGER_ABI, + functionName: 'closeAd', + args: [signature, authToken, BigInt(timeToExpire), adId, to], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `AdManager.closeAd tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function createOrder( + publicClient: PublicClient, + account: PrivateKeyAccount, + signature: `0x${string}`, + authToken: `0x${string}`, + timeToExpire: number, + orderParams: T_OrderPortalParams, + orderPortalAddress: string, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: orderPortalAddress as AddressLike, + abi: ORDER_PORTAL_ABI, + functionName: 'createOrder', + args: [ + signature, + authToken, + BigInt(timeToExpire), + { + ...orderParams, + amount: BigInt(orderParams.amount), + adChainId: BigInt(orderParams.adChainId), + salt: BigInt(orderParams.salt), + }, + ], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `OrderPortal.createOrder tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function lockForOrder( + publicClient: PublicClient, + account: PrivateKeyAccount, + signature: `0x${string}`, + authToken: `0x${string}`, + timeToExpire: number, + orderParams: T_AdManagerOrderParams, + adManagerAddress: AddressLike, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + if (!adManagerAddress) { + throw new Error('adManagerAddress is undefined'); + } + + const formattedAddress = getAddress(adManagerAddress); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: formattedAddress, + abi: AD_MANAGER_ABI, + functionName: 'lockForOrder', + args: [ + signature, + authToken, + BigInt(timeToExpire), + { + orderChainToken: orderParams.orderChainToken, + adChainToken: orderParams.adChainToken, + amount: BigInt(orderParams.amount), + bridger: orderParams.bridger, + orderChainId: BigInt(orderParams.orderChainId), + srcOrderPortal: orderParams.srcOrderPortal, + orderRecipient: orderParams.orderRecipient, + adId: orderParams.adId, + adCreator: orderParams.adCreator, + adRecipient: orderParams.adRecipient, + salt: BigInt(orderParams.salt), + }, + ], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `AdManager.lockForOrder tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function unlockAdChain( + publicClient: PublicClient, + account: PrivateKeyAccount, + signature: `0x${string}`, + authToken: `0x${string}`, + timeToExpire: number, + orderParams: T_AdManagerOrderParams, + nullifierHash: `0x${string}`, + targetRoot: `0x${string}`, + proof: `0x${string}`, + adManagerAddress: string, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: adManagerAddress as AddressLike, + abi: AD_MANAGER_ABI, + functionName: 'unlock', + args: [ + signature, + authToken, + BigInt(timeToExpire), + { + ...orderParams, + amount: BigInt(orderParams.amount), + orderChainId: BigInt(orderParams.orderChainId), + salt: BigInt(orderParams.salt), + }, + nullifierHash, + targetRoot, + proof, + ], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `AdManager.unlockOrder tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function unlockOrderChain( + publicClient: PublicClient, + account: PrivateKeyAccount, + signature: `0x${string}`, + authToken: `0x${string}`, + timeToExpire: number, + orderParams: T_OrderPortalParams, + nullifierHash: `0x${string}`, + targetRoot: `0x${string}`, + proof: `0x${string}`, + orderPortalAddress: string, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: orderPortalAddress as AddressLike, + abi: ORDER_PORTAL_ABI, + functionName: 'unlock', + + args: [ + signature, + authToken, + BigInt(timeToExpire), + { + ...orderParams, + amount: BigInt(orderParams.amount), + adChainId: BigInt(orderParams.adChainId), + salt: BigInt(orderParams.salt), + }, + nullifierHash, + targetRoot, + proof, + ], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `OrderPortal.unlockOrder tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function mintToken( + publicClient: PublicClient, + account: PrivateKeyAccount, + tokenAddress: AddressLike, + to: AddressLike, + amount: bigint, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: tokenAddress, + abi: ERC20_MOCK_ABI, + functionName: 'mint', + args: [to, amount], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `ERC20Mock.mint tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} + +export async function approveToken( + publicClient: PublicClient, + account: PrivateKeyAccount, + tokenAddress: AddressLike, + spender: AddressLike, + amount: bigint, +) { + const wallet = createWalletClient({ + chain: publicClient.chain, + transport: http(), + account, + }); + + const hash = await wallet.writeContract({ + chain: publicClient.chain, + address: tokenAddress, + abi: ERC20_MOCK_ABI, + functionName: 'approve', + args: [spender, amount], + }); + + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + + if (receipt.status !== 'success') { + throw new Error( + `ERC20Mock.approve tx failed revert: ${receipt.transactionHash}`, + ); + } + + return hash; +} diff --git a/scripts/relayer-e2e/lib/seed.ts b/scripts/relayer-e2e/lib/seed.ts new file mode 100644 index 0000000..f396932 --- /dev/null +++ b/scripts/relayer-e2e/lib/seed.ts @@ -0,0 +1,154 @@ +import { PrismaClient } from "@prisma/client"; +import { hash as argon2hash } from "@node-rs/argon2"; +import { ethers } from "ethers"; + +export interface DeployedContracts { + eth: { + name: string; + chainId: string; + adManagerAddress: string; + orderPortalAddress: string; + merkleManagerAddress: string; + verifierAddress: string; + tokenName: string; + tokenSymbol: string; + tokenAddress: string; + }; + stellar?: { + name: string; + chainId: string; + adManagerAddress: string; // 0x + 64 hex + orderPortalAddress: string; + merkleManagerAddress: string; + verifierAddress: string; + tokenName: string; + tokenSymbol: string; + tokenAddress: string; + adminSecret: string; + }; +} + +export async function seedDb(deployed: DeployedContracts): Promise { + const prisma = new PrismaClient(); + try { + await prisma.$connect(); + + // Admin. + const passwordHash = await argon2hash("ChangeMe123!"); + await prisma.admin.upsert({ + where: { email: "admin@x.com" }, + create: { email: "admin@x.com", passwordHash }, + update: { passwordHash }, + }); + + // EVM chain + token. + const ethChain = await prisma.chain.upsert({ + where: { chainId: BigInt(deployed.eth.chainId) }, + create: { + name: deployed.eth.name, + chainId: BigInt(deployed.eth.chainId), + kind: "EVM", + adManagerAddress: deployed.eth.adManagerAddress, + orderPortalAddress: deployed.eth.orderPortalAddress, + mmr: { create: { chainId: deployed.eth.chainId } }, + }, + update: { + name: deployed.eth.name, + adManagerAddress: deployed.eth.adManagerAddress, + orderPortalAddress: deployed.eth.orderPortalAddress, + }, + select: { id: true }, + }); + + const ethToken = await prisma.token.upsert({ + where: { + chainUid_address: { + chainUid: ethChain.id, + address: deployed.eth.tokenAddress, + }, + }, + create: { + chainUid: ethChain.id, + symbol: deployed.eth.tokenSymbol, + name: deployed.eth.tokenName, + address: deployed.eth.tokenAddress, + decimals: 18, + kind: "ERC20", + }, + update: { + symbol: deployed.eth.tokenSymbol, + name: deployed.eth.tokenName, + decimals: 18, + kind: "ERC20", + }, + select: { id: true }, + }); + + // Stellar chain + token (optional). + if (deployed.stellar) { + const s = deployed.stellar; + const stellarChain = await prisma.chain.upsert({ + where: { chainId: BigInt(s.chainId) }, + create: { + name: s.name, + chainId: BigInt(s.chainId), + kind: "STELLAR", + adManagerAddress: s.adManagerAddress, + orderPortalAddress: s.orderPortalAddress, + mmr: { create: { chainId: s.chainId } }, + }, + update: { + name: s.name, + adManagerAddress: s.adManagerAddress, + orderPortalAddress: s.orderPortalAddress, + }, + select: { id: true }, + }); + + const stellarToken = await prisma.token.upsert({ + where: { + chainUid_address: { + chainUid: stellarChain.id, + address: s.tokenAddress, + }, + }, + create: { + chainUid: stellarChain.id, + symbol: s.tokenSymbol, + name: s.tokenName, + address: s.tokenAddress, + decimals: 7, + kind: "NATIVE", + }, + update: { + symbol: s.tokenSymbol, + name: s.tokenName, + decimals: 7, + kind: "NATIVE", + }, + select: { id: true }, + }); + await prisma.route.upsert({ + where: { + orderTokenId_adTokenId: { + orderTokenId: ethToken.id, + adTokenId: stellarToken.id, + }, + }, + create: { + adTokenId: stellarToken.id, + orderTokenId: ethToken.id, + }, + update: {}, + }); + } + + console.log("[seed] done"); + } finally { + await prisma.$disconnect(); + } +} + +export function randomAddress(): string { + return ethers.Wallet.createRandom().address; +} diff --git a/scripts/relayer-e2e/lib/stellar-actions.ts b/scripts/relayer-e2e/lib/stellar-actions.ts new file mode 100644 index 0000000..1cfcc7e --- /dev/null +++ b/scripts/relayer-e2e/lib/stellar-actions.ts @@ -0,0 +1,263 @@ +// Stellar contract-action helpers — Soroban analogue of contract-actions.ts. +// +// Each wrapper takes the relayer's signed-request payload (signature + signer +// public key + authToken + timeToExpire) plus the raw contract args, builds a +// Soroban invocation, prepares + signs + submits, and polls for success. + +import { + Address, + Contract, + Keypair, + Networks, + TransactionBuilder, + nativeToScVal, + rpc, + xdr, +} from '@stellar/stellar-sdk'; +import { + hex32ToBuffer, + hex32ToContractId, +} from '../../../apps/backend-relayer/src/providers/stellar/utils/address.js'; + +const BASE_FEE = '1000'; + +function getServer(): rpc.Server { + const url = process.env.STELLAR_RPC_URL; + if (!url) throw new Error('STELLAR_RPC_URL not set'); + return new rpc.Server(url, { allowHttp: url.startsWith('http://') }); +} + +function passphrase(): string { + return process.env.STELLAR_NETWORK_PASSPHRASE || Networks.TESTNET; +} + +async function invoke( + signer: Keypair, + contractHex: string, + method: string, + args: xdr.ScVal[], +): Promise { + const server = getServer(); + const contract = new Contract(hex32ToContractId(contractHex)); + const source = await server.getAccount(signer.publicKey()); + const tx = new TransactionBuilder(source, { + fee: BASE_FEE, + networkPassphrase: passphrase(), + }) + .addOperation(contract.call(method, ...args)) + .setTimeout(60) + .build(); + const prepared = await server.prepareTransaction(tx); + prepared.sign(signer); + const sent = await server.sendTransaction(prepared); + if (sent.status === 'ERROR') { + throw new Error( + `Stellar send failed [${method}]: ${JSON.stringify(sent.errorResult)}`, + ); + } + for (let i = 0; i < 20; i++) { + const got = await server.getTransaction(sent.hash); + if (got.status === rpc.Api.GetTransactionStatus.SUCCESS) return sent.hash; + if (got.status === rpc.Api.GetTransactionStatus.FAILED) { + throw new Error(`Stellar tx [${method}] FAILED hash=${sent.hash}`); + } + await new Promise((r) => setTimeout(r, 1000)); + } + throw new Error(`Stellar tx [${method}] timed out hash=${sent.hash}`); +} + +// ── scval helpers ─────────────────────────────────────────────────── + +function bytesN(hex: string): xdr.ScVal { + return nativeToScVal(hex32ToBuffer(hex), { type: 'bytes' }); +} + +function bytes(buf: Buffer): xdr.ScVal { + return nativeToScVal(buf, { type: 'bytes' }); +} + +function u64(n: number | bigint): xdr.ScVal { + return nativeToScVal(BigInt(n), { type: 'u64' }); +} + +function u128(n: string | bigint): xdr.ScVal { + return nativeToScVal(BigInt(n), { type: 'u128' }); +} + +function strVal(s: string): xdr.ScVal { + return nativeToScVal(s, { type: 'string' }); +} + +// Shared auth quadruple (signature, public_key, auth_token, time_to_expire). +function authArgs( + signatureHex: string, + publicKeyHex: string, + authTokenHex: string, + timeToExpire: number, +): xdr.ScVal[] { + return [ + bytes(Buffer.from(signatureHex.replace(/^0x/, ''), 'hex')), + bytesN(publicKeyHex), + bytesN(authTokenHex), + u64(timeToExpire), + ]; +} + +// ── ad-manager wrappers ───────────────────────────────────────────── + +export async function createAdSoroban( + signer: Keypair, + signatureHex: string, + signerPublicKeyHex: string, + authTokenHex: string, + timeToExpire: number, + creatorPublicKey: string, // G-strkey of the ad creator (signer) + adId: string, + adTokenHex: string, + initialAmount: string, + orderChainId: string, + adRecipientHex: string, + adManagerHex: string, +): Promise { + const args = [ + ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), + new Address(creatorPublicKey).toScVal(), + strVal(adId), + bytesN(adTokenHex), + u128(initialAmount), + u128(orderChainId), + bytesN(adRecipientHex), + ]; + return invoke(signer, adManagerHex, 'create_ad', args); +} + +export async function fundAdSoroban( + signer: Keypair, + signatureHex: string, + signerPublicKeyHex: string, + authTokenHex: string, + timeToExpire: number, + adId: string, + amount: string, + adManagerHex: string, +): Promise { + const args = [ + ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), + strVal(adId), + u128(amount), + ]; + return invoke(signer, adManagerHex, 'fund_ad', args); +} + +export async function withdrawFromAdSoroban( + signer: Keypair, + signatureHex: string, + signerPublicKeyHex: string, + authTokenHex: string, + timeToExpire: number, + adId: string, + amount: string, + toPublicKey: string, // G-strkey + adManagerHex: string, +): Promise { + const args = [ + ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), + strVal(adId), + u128(amount), + new Address(toPublicKey).toScVal(), + ]; + return invoke(signer, adManagerHex, 'withdraw_from_ad', args); +} + +export async function closeAdSoroban( + signer: Keypair, + signatureHex: string, + signerPublicKeyHex: string, + authTokenHex: string, + timeToExpire: number, + adId: string, + toPublicKey: string, // G-strkey + adManagerHex: string, +): Promise { + const args = [ + ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), + strVal(adId), + new Address(toPublicKey).toScVal(), + ]; + return invoke(signer, adManagerHex, 'close_ad', args); +} + +// Matches contracts/stellar/contracts/ad-manager/src/types.rs::OrderParams. +export interface StellarOrderParams { + orderChainToken: string; // 0x + 64 hex + adChainToken: string; + amount: string; + bridger: string; + orderChainId: string; + srcOrderPortal: string; + orderRecipient: string; + adId: string; + adCreator: string; + adRecipient: string; + salt: string; +} + +function orderParamsScVal(p: StellarOrderParams): xdr.ScVal { + // Soroban struct is encoded as an ScMap with entries sorted by key. + const entries: Array<[string, xdr.ScVal]> = [ + ['ad_chain_token', bytesN(p.adChainToken)], + ['ad_creator', bytesN(p.adCreator)], + ['ad_id', strVal(p.adId)], + ['ad_recipient', bytesN(p.adRecipient)], + ['amount', u128(p.amount)], + ['bridger', bytesN(p.bridger)], + ['order_chain_id', u128(p.orderChainId)], + ['order_chain_token', bytesN(p.orderChainToken)], + ['order_recipient', bytesN(p.orderRecipient)], + ['salt', u128(p.salt)], + ['src_order_portal', bytesN(p.srcOrderPortal)], + ]; + return xdr.ScVal.scvMap( + entries.map( + ([k, v]) => new xdr.ScMapEntry({ key: xdr.ScVal.scvSymbol(k), val: v }), + ), + ); +} + +export async function lockForOrderSoroban( + signer: Keypair, + signatureHex: string, + signerPublicKeyHex: string, + authTokenHex: string, + timeToExpire: number, + params: StellarOrderParams, + adManagerHex: string, +): Promise { + const args = [ + ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), + orderParamsScVal(params), + ]; + return invoke(signer, adManagerHex, 'lock_for_order', args); +} + +export async function unlockSoroban( + signer: Keypair, + signatureHex: string, + signerPublicKeyHex: string, + authTokenHex: string, + timeToExpire: number, + params: StellarOrderParams, + nullifierHashHex: string, + targetRootHex: string, + proof: Buffer, + adManagerHex: string, +): Promise { + const args = [ + ...authArgs(signatureHex, signerPublicKeyHex, authTokenHex, timeToExpire), + orderParamsScVal(params), + bytesN(nullifierHashHex), + bytesN(targetRootHex), + bytes(proof), + ]; + return invoke(signer, adManagerHex, 'unlock', args); +} diff --git a/scripts/relayer-e2e/package.json b/scripts/relayer-e2e/package.json new file mode 100644 index 0000000..c64f962 --- /dev/null +++ b/scripts/relayer-e2e/package.json @@ -0,0 +1,23 @@ +{ + "name": "relayer-e2e", + "private": true, + "type": "module", + "scripts": { + "cli": "tsx cli.ts", + "e2e": "bash e2e.sh" + }, + "dependencies": { + "@node-rs/argon2": "^2.0.2", + "@prisma/client": "6.16.1", + "@stellar/stellar-sdk": "^15.0.1", + "cross-chain-e2e": "workspace:*", + "ethers": "^6.15.0", + "siwe": "^3.0.0", + "viem": "^2.37.7" + }, + "devDependencies": { + "@types/node": "^22.10.7", + "tsx": "^4.21.0", + "typescript": "^5.7.3" + } +} diff --git a/scripts/relayer-e2e/tsconfig.json b/scripts/relayer-e2e/tsconfig.json new file mode 100644 index 0000000..b1fb4b9 --- /dev/null +++ b/scripts/relayer-e2e/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "allowImportingTsExtensions": false, + "noEmit": true + }, + "include": [ + "cli.ts", + "lib/**/*.ts", + "flows/**/*.ts" + ] +} \ No newline at end of file diff --git a/scripts/run_cross_chain_e2e.sh b/scripts/start_chains.sh similarity index 57% rename from scripts/run_cross_chain_e2e.sh rename to scripts/start_chains.sh index dbd3b65..ec91d07 100755 --- a/scripts/run_cross_chain_e2e.sh +++ b/scripts/start_chains.sh @@ -1,12 +1,23 @@ #!/usr/bin/env bash set -euo pipefail -# Cross-chain E2E test orchestrator. +# Cross-chain dev-environment bring-up. # Starts a Stellar localnet (Docker) and Anvil (EVM), builds all prerequisites, -# then runs the TypeScript test that exercises the full bridge flow. +# funds keypairs, and exposes the resulting addresses/keys/secrets as +# environment variables. This script does NOT run any test — the caller is +# expected to consume the exported env and run whichever command they want +# (e.g. `npx tsx scripts/cross-chain-e2e/run.ts` or +# `pnpm --filter backend-relayer test:integrations`). +# +# Outputs: +# - Writes all exports to `/.chains.env` (source it in another shell). +# - Appends to $GITHUB_ENV when running under GitHub Actions so subsequent +# job steps inherit the values automatically. +# - Writes Anvil's PID to `/.chains.anvil.pid` so a teardown script +# can stop it later (Stellar container is stopped via `stellar container +# stop $STELLAR_CONTAINER_NAME`). ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -SCRIPT_DIR="$ROOT_DIR/scripts/cross-chain-e2e" # ── configuration ──────────────────────────────────────────────────── @@ -15,9 +26,9 @@ NETWORK_NAME="${STELLAR_NETWORK_NAME:-local}" STELLAR_RPC_URL="${STELLAR_RPC_URL:-http://localhost:8000/soroban/rpc}" NETWORK_PASSPHRASE="${STELLAR_NETWORK_PASSPHRASE:-Standalone Network ; February 2017}" -ANVIL_PORT="${ANVIL_PORT:-8545}" -# Use 127.0.0.1 explicitly — on GitHub runners, Node resolves "localhost" -# to ::1 first, which fails to reach Anvil (bound on IPv4 0.0.0.0). +# apps/backend-relayer/src/providers/viem/ethers/localnet.ts pins 9545; +# scripts/cross-chain-e2e/run.ts reads EVM_RPC_URL so it is port-agnostic. +ANVIL_PORT="${ANVIL_PORT:-9545}" EVM_RPC_URL="http://127.0.0.1:$ANVIL_PORT" # ── user roles (4 actors drive the flow) ───────────────────────────── @@ -33,29 +44,15 @@ STELLAR_ADMIN_ACCOUNT="${STELLAR_ADMIN_ACCOUNT:-admin}" STELLAR_AD_CREATOR_ACCOUNT="${STELLAR_AD_CREATOR_ACCOUNT:-alice}" STELLAR_ORDER_CREATOR_ACCOUNT="${STELLAR_ORDER_CREATOR_ACCOUNT:-bridger}" -# Anvil prefunded keys — #0 admin, #1 order creator. +# Anvil prefunded keys — #0 admin, #1 order creator, #2 ad-creator EVM side. EVM_ADMIN_PRIVATE_KEY="${EVM_ADMIN_PRIVATE_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80}" EVM_ORDER_CREATOR_PRIVATE_KEY="${EVM_ORDER_CREATOR_PRIVATE_KEY:-0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d}" -# Ad creator's EVM recipient — receive-only, no key needed. Defaults to -# Anvil prefunded account #2's address. +EVM_AD_CREATOR_PRIVATE_KEY="${EVM_AD_CREATOR_PRIVATE_KEY:-0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a}" AD_CREATOR_EVM_RECIPIENT="${AD_CREATOR_EVM_RECIPIENT:-0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC}" # stellar.ts reads STELLAR_SOURCE_ACCOUNT as the default CLI source. export STELLAR_SOURCE_ACCOUNT="$STELLAR_ADMIN_ACCOUNT" -ANVIL_PID="" - -# ── cleanup ────────────────────────────────────────────────────────── - -cleanup() { - echo "" - echo "Cleaning up..." - [[ -n "$ANVIL_PID" ]] && kill "$ANVIL_PID" 2>/dev/null || true - stellar container stop "$CONTAINER_NAME" >/dev/null 2>&1 || true - echo "Done." -} -trap cleanup EXIT - # ── start Stellar localnet ─────────────────────────────────────────── echo "=== Starting Stellar quickstart container ===" @@ -109,19 +106,29 @@ for acct in "$STELLAR_ADMIN_ACCOUNT" "$STELLAR_AD_CREATOR_ACCOUNT" "$STELLAR_ORD done echo "Stellar accounts funded." +# Dump Stellar secret seeds so Node-side tests can sign without re-running +# the CLI. `stellar keys show` prints just the S… strkey. +STELLAR_ADMIN_SECRET="$(stellar keys show "$STELLAR_ADMIN_ACCOUNT")" +STELLAR_AD_CREATOR_SECRET="$(stellar keys show "$STELLAR_AD_CREATOR_ACCOUNT")" +STELLAR_ORDER_CREATOR_SECRET="$(stellar keys show "$STELLAR_ORDER_CREATOR_ACCOUNT")" + # ── start Anvil ────────────────────────────────────────────────────── echo "" echo "=== Starting Anvil (EVM devnet) ===" -# Kill any existing Anvil on the target port lsof -ti :"$ANVIL_PORT" | xargs -r kill -9 2>/dev/null || true sleep 1 -anvil --host 0.0.0.0 --port "$ANVIL_PORT" --block-time 2 --silent & +# nohup + disown keeps Anvil alive after this script exits, so downstream +# CI steps (or a separate dev shell) can talk to it. +nohup anvil --host 0.0.0.0 --port "$ANVIL_PORT" --block-time 2 --silent \ + > "$ROOT_DIR/.chains.anvil.log" 2>&1 & ANVIL_PID=$! +disown "$ANVIL_PID" || true +echo "$ANVIL_PID" > "$ROOT_DIR/.chains.anvil.pid" sleep 2 if ! kill -0 "$ANVIL_PID" 2>/dev/null; then - echo "Anvil failed to start" >&2 + echo "Anvil failed to start — see $ROOT_DIR/.chains.anvil.log" >&2 exit 1 fi echo "Anvil running on port $ANVIL_PORT (PID $ANVIL_PID)." @@ -148,25 +155,50 @@ cd "$ROOT_DIR/contracts/evm" forge build --silent cd "$ROOT_DIR" -# ── run the TypeScript test ────────────────────────────────────────── +# ── expose addresses, keys, and secrets ────────────────────────────── + +ENV_FILE="$ROOT_DIR/.chains.env" +: > "$ENV_FILE" + +emit() { + local key="$1" + local val="$2" + # Quote the value so passphrases with spaces/semicolons survive a round-trip + # through `source`. Single-quote-safe: escape any ' by closing, escaping, reopening. + local escaped="${val//\'/\'\\\'\'}" + echo "export ${key}='${escaped}'" >> "$ENV_FILE" + if [[ -n "${GITHUB_ENV:-}" ]]; then + # $GITHUB_ENV uses plain KEY=VALUE (no export, no quoting). + # Multiline-safe form isn't needed here since none of these contain newlines. + echo "${key}=${val}" >> "$GITHUB_ENV" + fi +} -echo "" -echo "=== Running cross-chain E2E test ===" - -export STELLAR_RPC_URL -export STELLAR_NETWORK="$NETWORK_NAME" -export STELLAR_NETWORK_PASSPHRASE="$NETWORK_PASSPHRASE" -export STELLAR_ADMIN_ACCOUNT -export STELLAR_AD_CREATOR_ACCOUNT -export STELLAR_ORDER_CREATOR_ACCOUNT -export EVM_RPC_URL -export EVM_ADMIN_PRIVATE_KEY -export EVM_ORDER_CREATOR_PRIVATE_KEY -export AD_CREATOR_EVM_RECIPIENT -export ROOT_DIR - -cd "$SCRIPT_DIR" -npx tsx run.ts +emit STELLAR_RPC_URL "$STELLAR_RPC_URL" +emit STELLAR_NETWORK "$NETWORK_NAME" +emit STELLAR_NETWORK_PASSPHRASE "$NETWORK_PASSPHRASE" +emit STELLAR_CONTAINER_NAME "$CONTAINER_NAME" +emit STELLAR_SOURCE_ACCOUNT "$STELLAR_ADMIN_ACCOUNT" +emit STELLAR_ADMIN_ACCOUNT "$STELLAR_ADMIN_ACCOUNT" +emit STELLAR_AD_CREATOR_ACCOUNT "$STELLAR_AD_CREATOR_ACCOUNT" +emit STELLAR_ORDER_CREATOR_ACCOUNT "$STELLAR_ORDER_CREATOR_ACCOUNT" +emit STELLAR_ADMIN_SECRET "$STELLAR_ADMIN_SECRET" +emit STELLAR_AD_CREATOR_SECRET "$STELLAR_AD_CREATOR_SECRET" +emit STELLAR_ORDER_CREATOR_SECRET "$STELLAR_ORDER_CREATOR_SECRET" +emit EVM_RPC_URL "$EVM_RPC_URL" +emit EVM_ADMIN_PRIVATE_KEY "$EVM_ADMIN_PRIVATE_KEY" +emit EVM_ORDER_CREATOR_PRIVATE_KEY "$EVM_ORDER_CREATOR_PRIVATE_KEY" +emit EVM_AD_CREATOR_PRIVATE_KEY "$EVM_AD_CREATOR_PRIVATE_KEY" +emit AD_CREATOR_EVM_RECIPIENT "$AD_CREATOR_EVM_RECIPIENT" +emit ROOT_DIR "$ROOT_DIR" echo "" -echo "=== Cross-chain E2E test passed! ===" +echo "=== Chains ready ===" +echo "Env written to: $ENV_FILE" +echo "Anvil log: $ROOT_DIR/.chains.anvil.log" +echo "Anvil PID file: $ROOT_DIR/.chains.anvil.pid" +echo "" +echo "Next steps:" +echo " - local dev: source $ENV_FILE && " +echo " - CI: env inherited via \$GITHUB_ENV in subsequent steps" +echo " - teardown: bash scripts/stop_chains.sh" diff --git a/scripts/stop_chains.sh b/scripts/stop_chains.sh new file mode 100755 index 0000000..621392a --- /dev/null +++ b/scripts/stop_chains.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -uo pipefail + +# Tears down the chains brought up by start_chains.sh. +# Stops Anvil (via PID file) and the Stellar quickstart container. + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +PID_FILE="$ROOT_DIR/.chains.anvil.pid" +if [[ -f "$PID_FILE" ]]; then + ANVIL_PID="$(cat "$PID_FILE")" + if [[ -n "$ANVIL_PID" ]] && kill -0 "$ANVIL_PID" 2>/dev/null; then + echo "Stopping Anvil (PID $ANVIL_PID)..." + kill "$ANVIL_PID" 2>/dev/null || true + fi + rm -f "$PID_FILE" +fi + +CONTAINER_NAME="${STELLAR_CONTAINER_NAME:-stellar-e2e}" +echo "Stopping Stellar container '$CONTAINER_NAME'..." +stellar container stop "$CONTAINER_NAME" >/dev/null 2>&1 || true + +rm -f "$ROOT_DIR/.chains.env" "$ROOT_DIR/.chains.anvil.log" + +echo "Done." From 00632da1113dc57e7cd5421ec3c09978e9c07efd Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Sun, 12 Apr 2026 23:46:02 +0100 Subject: [PATCH 03/12] chore: fix cis --- .github/workflows/backend-relayer-e2e.yml | 56 +++++++++++++++++++++-- .github/workflows/cross-chain-e2e.yml | 2 +- apps/backend-relayer/Dockerfile | 6 ++- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/.github/workflows/backend-relayer-e2e.yml b/.github/workflows/backend-relayer-e2e.yml index b1697e2..32ef4e6 100644 --- a/.github/workflows/backend-relayer-e2e.yml +++ b/.github/workflows/backend-relayer-e2e.yml @@ -26,6 +26,13 @@ on: env: CARGO_TERM_COLOR: always + SNAPSHOT_PATH: ${{ github.workspace }}/scripts/relayer-e2e/deployed.json + COMPOSE_FILE: apps/backend-relayer/docker-compose.e2e.yaml + COMPOSE_PROJECT_NAME: relayer-e2e + HOST_DATABASE_URL: postgresql://relayer:relayer@localhost:5433/relayer + RELAYER_URL: http://localhost:2005 + STELLAR_CHAIN_ID: "1000001" + EVM_CHAIN_ID: "31337" jobs: backend-relayer-e2e: @@ -78,16 +85,59 @@ jobs: - name: Generate Prisma client run: pnpm --filter backend-relayer exec prisma generate - - name: Run backend-relayer e2e - run: bash scripts/relayer-e2e/e2e.sh + # ── 1. chains ──────────────────────────────────────────────────── + - name: Start chains (Stellar + Anvil) and build contracts + run: bash scripts/start_chains.sh - - name: Upload relayer logs + - name: Export chain env + run: | + # .chains.env uses `export KEY='value'`; $GITHUB_ENV expects KEY=value. + sed -E "s/^export[[:space:]]+([A-Za-z_][A-Za-z0-9_]*)='(.*)'$/\1=\2/" .chains.env >> "$GITHUB_ENV" + + # ── 2. deploy ──────────────────────────────────────────────────── + - name: Deploy contracts + run: pnpm --filter relayer-e2e exec tsx cli.ts deploy --out "$SNAPSHOT_PATH" + + # ── 3. postgres + backend-relayer containers ───────────────────── + - name: Start postgres + backend-relayer + env: + STELLAR_ADMIN_SECRET: ${{ env.STELLAR_ADMIN_SECRET }} + STELLAR_NETWORK_PASSPHRASE: "Standalone Network ; February 2017" + run: docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" up -d --build --wait + + # ── 4. seed ────────────────────────────────────────────────────── + - name: Seed database + env: + DATABASE_URL: ${{ env.HOST_DATABASE_URL }} + run: pnpm --filter relayer-e2e exec tsx cli.ts seed --in "$SNAPSHOT_PATH" + + # ── 5. flows ───────────────────────────────────────────────────── + - name: Run lifecycle flows + run: pnpm --filter relayer-e2e exec tsx cli.ts flows + + # ── teardown ───────────────────────────────────────────────────── + - name: Dump relayer logs + if: always() + run: | + docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" logs backend-relayer > .relayer.log 2>&1 || true + docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" logs postgres > .postgres.log 2>&1 || true + + - name: Stop containers + if: always() + run: docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" down --remove-orphans --volumes || true + + - name: Stop chains + if: always() + run: bash scripts/stop_chains.sh || true + + - name: Upload logs + snapshot if: failure() uses: actions/upload-artifact@v4 with: name: relayer-logs path: | .relayer.log + .postgres.log .chains.anvil.log scripts/relayer-e2e/deployed.json if-no-files-found: ignore diff --git a/.github/workflows/cross-chain-e2e.yml b/.github/workflows/cross-chain-e2e.yml index 64aa640..23f18de 100644 --- a/.github/workflows/cross-chain-e2e.yml +++ b/.github/workflows/cross-chain-e2e.yml @@ -71,7 +71,7 @@ jobs: run: bash scripts/start_chains.sh - name: Run cross-chain E2E test - run: npx tsx scripts/cross-chain-e2e/run.ts + run: pnpm --filter cross-chain-e2e exec tsx run.ts - name: Stop chains if: always() diff --git a/apps/backend-relayer/Dockerfile b/apps/backend-relayer/Dockerfile index 85608a0..6f7edb4 100644 --- a/apps/backend-relayer/Dockerfile +++ b/apps/backend-relayer/Dockerfile @@ -30,7 +30,11 @@ RUN pnpm install --frozen-lockfile --offline --ignore-scripts \ && pnpm --filter backend-relayer build # Produce a self-contained runtime dir with only the relayer's prod deps. -RUN pnpm --filter backend-relayer deploy --prod --legacy /out +# `pnpm deploy` only copies files listed in the package's `files` field, so +# the built `dist/` and `prisma/` dirs have to be copied explicitly. +RUN pnpm --filter backend-relayer deploy --prod --legacy /out \ + && cp -r apps/backend-relayer/dist /out/dist \ + && cp -r apps/backend-relayer/prisma /out/prisma # ----------------------------------------------------------------------------- # Stage 2 — runtime From 5d2d9e3012a66351642e1465b8e35a9d3ae17fad Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 00:08:28 +0100 Subject: [PATCH 04/12] chore: fix raised issues --- .github/workflows/backend-relayer-e2e.yml | 13 +- .github/workflows/backend-relayer-tests.yml | 2 + apps/backend-relayer/.env.example | 26 +- apps/backend-relayer/Dockerfile | 3 + apps/backend-relayer/package.json | 2 +- .../src/modules/ads/ad.service.ts | 1 - .../src/modules/trades/trade.service.ts | 13 +- pnpm-lock.yaml | 228 +++++++++--------- scripts/cross-chain-e2e/lib/deploy.ts | 9 +- scripts/relayer-e2e/lib/eth.ts | 13 +- scripts/relayer-e2e/lib/seed.ts | 43 +++- scripts/stop_chains.sh | 6 + 12 files changed, 203 insertions(+), 156 deletions(-) diff --git a/.github/workflows/backend-relayer-e2e.yml b/.github/workflows/backend-relayer-e2e.yml index 32ef4e6..b35a19f 100644 --- a/.github/workflows/backend-relayer-e2e.yml +++ b/.github/workflows/backend-relayer-e2e.yml @@ -9,6 +9,7 @@ on: - "proof_circuits/**" - "scripts/**" - "packages/**" + - "package.json" - "pnpm-workspace.yaml" - "pnpm-lock.yaml" - ".github/workflows/backend-relayer-e2e.yml" @@ -19,6 +20,7 @@ on: - "proof_circuits/**" - "scripts/**" - "packages/**" + - "package.json" - "pnpm-workspace.yaml" - "pnpm-lock.yaml" - ".github/workflows/backend-relayer-e2e.yml" @@ -119,8 +121,15 @@ jobs: - name: Dump relayer logs if: always() run: | - docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" logs backend-relayer > .relayer.log 2>&1 || true - docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" logs postgres > .postgres.log 2>&1 || true + echo "::group::backend-relayer logs" + docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" logs --no-color --tail=500 backend-relayer | tee .relayer.log || true + echo "::endgroup::" + echo "::group::postgres logs" + docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" logs --no-color --tail=200 postgres | tee .postgres.log || true + echo "::endgroup::" + echo "::group::container state" + docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" ps -a || true + echo "::endgroup::" - name: Stop containers if: always() diff --git a/.github/workflows/backend-relayer-tests.yml b/.github/workflows/backend-relayer-tests.yml index eb78850..420d520 100644 --- a/.github/workflows/backend-relayer-tests.yml +++ b/.github/workflows/backend-relayer-tests.yml @@ -6,6 +6,7 @@ on: paths: - "apps/backend-relayer/**" - "packages/**" + - "package.json" - "pnpm-workspace.yaml" - "pnpm-lock.yaml" - ".github/workflows/backend-relayer-tests.yml" @@ -13,6 +14,7 @@ on: paths: - "apps/backend-relayer/**" - "packages/**" + - "package.json" - "pnpm-workspace.yaml" - "pnpm-lock.yaml" - ".github/workflows/backend-relayer-tests.yml" diff --git a/apps/backend-relayer/.env.example b/apps/backend-relayer/.env.example index df85990..cb26e61 100644 --- a/apps/backend-relayer/.env.example +++ b/apps/backend-relayer/.env.example @@ -1,16 +1,16 @@ -EVM_ADMIN_PRIVATE_KEY="" -DATABASE_URL="" -EVM_RPC_API_KEY="" -JWT_EXPIRY="" -JWT_REFRESH_EXPIRY="" -JWT_SECRET="" -NODE_ENV="" -PORT="" -SECRET_KEY="" +EVM_ADMIN_PRIVATE_KEY= +DATABASE_URL= +EVM_RPC_API_KEY= +JWT_EXPIRY= +JWT_REFRESH_EXPIRY= +JWT_SECRET= +NODE_ENV= +PORT= +SECRET_KEY= SIGN_DOMAIN=proof-bridge.vercel.app SIGN_URI=https://proof-bridge.vercel.app # Stellar SEP-10 server signing key. Generate with: Keypair.random().secret() -STELLAR_AUTH_SECRET="" -STELLAR_RPC_URL="" -STELLAR_NETWORK_PASSPHRASE="" -STELLAR_ADMIN_SECRET="" \ No newline at end of file +STELLAR_AUTH_SECRET= +STELLAR_RPC_URL= +STELLAR_NETWORK_PASSPHRASE= +STELLAR_ADMIN_SECRET= diff --git a/apps/backend-relayer/Dockerfile b/apps/backend-relayer/Dockerfile index 6f7edb4..e841b8a 100644 --- a/apps/backend-relayer/Dockerfile +++ b/apps/backend-relayer/Dockerfile @@ -50,6 +50,9 @@ COPY --from=builder /out/node_modules ./node_modules COPY --from=builder /out/dist ./dist COPY --from=builder /out/prisma ./prisma +RUN chown -R node:node /app +USER node + ENV NODE_ENV=production ENV PORT=2005 EXPOSE 2005 diff --git a/apps/backend-relayer/package.json b/apps/backend-relayer/package.json index 57714ad..e2f060b 100644 --- a/apps/backend-relayer/package.json +++ b/apps/backend-relayer/package.json @@ -34,6 +34,7 @@ "@node-rs/argon2": "^2.0.2", "@noir-lang/noir_js": "1.0.0-beta.9", "@prisma/client": "6.16.1", + "prisma": "6.16.1", "@stellar/stellar-sdk": "^15.0.1", "@types/morgan": "^1.9.10", "@zkpassport/poseidon2": "^0.6.2", @@ -77,7 +78,6 @@ "globals": "^16.0.0", "jest": "^30.0.0", "prettier": "^3.4.2", - "prisma": "6.16.1", "siwe": "^3.0.0", "source-map-support": "^0.5.21", "supertest": "^7.1.4", diff --git a/apps/backend-relayer/src/modules/ads/ad.service.ts b/apps/backend-relayer/src/modules/ads/ad.service.ts index 499616c..4314f09 100644 --- a/apps/backend-relayer/src/modules/ads/ad.service.ts +++ b/apps/backend-relayer/src/modules/ads/ad.service.ts @@ -1059,7 +1059,6 @@ export class AdsService { return { adId: adId, success: true, - dto: dto, }; } catch (e) { if (e instanceof Error) { diff --git a/apps/backend-relayer/src/modules/trades/trade.service.ts b/apps/backend-relayer/src/modules/trades/trade.service.ts index cd0a3f8..04d82e2 100644 --- a/apps/backend-relayer/src/modules/trades/trade.service.ts +++ b/apps/backend-relayer/src/modules/trades/trade.service.ts @@ -141,10 +141,14 @@ export class TradesService { if (q.routeId) where.routeId = q.routeId; if (q.adId) where.adId = q.adId; - if (q.adCreatorAddress) - where.adCreatorAddress = normalizeChainAddress(q.adCreatorAddress); - if (q.bridgerAddress) - where.bridgerAddress = normalizeChainAddress(q.bridgerAddress); + try { + if (q.adCreatorAddress) + where.adCreatorAddress = normalizeChainAddress(q.adCreatorAddress); + if (q.bridgerAddress) + where.bridgerAddress = normalizeChainAddress(q.bridgerAddress); + } catch { + throw new BadRequestException('Invalid address filter'); + } if (q.adTokenId || q.orderTokenId) { where.route = { @@ -1145,6 +1149,7 @@ export class TradesService { where: { tradeId: tradeId, userAddress: normalizeChainAddress(user.walletAddress), + ...(dto.signature ? { signature: dto.signature } : {}), }, orderBy: { createdAt: 'desc' }, include: { trade: true }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82ac488..6d45531 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,6 +112,9 @@ importers: nest-winston: specifier: ^1.10.2 version: 1.10.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(winston@3.19.0) + prisma: + specifier: 6.16.1 + version: 6.16.1(typescript@5.9.3) proofbridge-mmr: specifier: 1.0.8 version: 1.0.8(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -194,9 +197,6 @@ importers: prettier: specifier: ^3.4.2 version: 3.8.2 - prisma: - specifier: 6.16.1 - version: 6.16.1(typescript@5.9.3) siwe: specifier: ^3.0.0 version: 3.0.0(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) @@ -235,10 +235,10 @@ importers: version: 2.1.2(gsap@3.14.2)(react@19.1.0) '@rainbow-me/rainbowkit': specifier: ^2.2.8 - version: 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) + version: 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) '@rainbow-me/rainbowkit-siwe-next-auth': specifier: ^0.5.0 - version: 0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0) '@tanstack/react-query': specifier: ^5.89.0 version: 5.98.0(react@19.1.0) @@ -304,10 +304,10 @@ importers: version: 4.0.0(tailwindcss@4.2.2) viem: specifier: ^2.37.7 - version: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + version: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: specifier: ^2.17.1 - version: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) + version: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) devDependencies: '@eslint/eslintrc': specifier: ^3 @@ -8347,16 +8347,16 @@ snapshots: '@balena/dockerignore@1.0.2': {} - '@base-org/account@2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6)': + '@base-org/account@2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@coinbase/cdp-sdk': 1.47.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@4.3.6) + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) preact: 10.24.2 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) transitivePeerDependencies: - '@types/react' @@ -8419,15 +8419,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@coinbase/wallet-sdk@4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6)': + '@coinbase/wallet-sdk@4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@4.3.6) + ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) preact: 10.24.2 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) transitivePeerDependencies: - '@types/react' @@ -8636,11 +8636,11 @@ snapshots: ethereum-cryptography: 2.2.1 micro-ftch: 0.3.1 - '@gemini-wallet/core@0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))': + '@gemini-wallet/core@0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: '@metamask/rpc-errors': 7.0.2 eventemitter3: 5.0.1 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -9763,13 +9763,13 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@rainbow-me/rainbowkit-siwe-next-auth@0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0)': + '@rainbow-me/rainbowkit-siwe-next-auth@0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0)': dependencies: - '@rainbow-me/rainbowkit': 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) + '@rainbow-me/rainbowkit': 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) next-auth: 4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0) react: 19.1.0 - '@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))': + '@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))': dependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) '@vanilla-extract/css': 1.17.3 @@ -9781,8 +9781,8 @@ snapshots: react-dom: 19.2.5(react@19.1.0) react-remove-scroll: 2.6.2(@types/react@19.2.14)(react@19.1.0) ua-parser-js: 1.0.41 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) transitivePeerDependencies: - '@types/react' - babel-plugin-macros @@ -9867,24 +9867,24 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@reown/appkit-controllers@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9913,12 +9913,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@reown/appkit-pay@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) lit: 3.3.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) transitivePeerDependencies: @@ -9953,12 +9953,12 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6)': + '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) lit: 3.3.0 transitivePeerDependencies: @@ -9990,10 +9990,10 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@reown/appkit-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) lit: 3.3.0 qrcode: 1.5.3 @@ -10025,16 +10025,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6)': + '@reown/appkit-utils@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -10074,21 +10074,21 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@reown/appkit@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-pay': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-pay': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/types': 2.21.0 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -10119,9 +10119,9 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -10129,10 +10129,10 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript @@ -11163,19 +11163,19 @@ snapshots: dependencies: '@vanilla-extract/css': 1.17.3 - '@wagmi/connectors@6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6)': + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': dependencies: - '@base-org/account': 2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6) - '@gemini-wallet/core': 0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@base-org/account': 2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76) + '@gemini-wallet/core': 0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + porto: 0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -11216,11 +11216,11 @@ snapshots: - wagmi - zod - '@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))': + '@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.9.3) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.0(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/query-core': 5.98.0 @@ -11231,7 +11231,7 @@ snapshots: - react - use-sync-external-store - '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -11245,7 +11245,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -11275,7 +11275,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -11289,7 +11289,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -11323,18 +11323,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/ethereum-provider@2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.21.1 - '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11457,16 +11457,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11493,16 +11493,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11591,7 +11591,7 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -11600,9 +11600,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -11631,7 +11631,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -11640,9 +11640,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -11671,7 +11671,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -11689,7 +11689,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11715,7 +11715,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': + '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -11733,7 +11733,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11855,10 +11855,10 @@ snapshots: typescript: 5.9.3 zod: 3.25.76 - abitype@1.0.8(typescript@5.9.3)(zod@4.3.6): + abitype@1.0.8(typescript@5.9.3)(zod@3.25.76): optionalDependencies: typescript: 5.9.3 - zod: 4.3.6 + zod: 3.25.76 abitype@1.2.3(typescript@5.9.3)(zod@3.22.4): optionalDependencies: @@ -13085,7 +13085,7 @@ snapshots: eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1)) @@ -13122,7 +13122,7 @@ snapshots: tinyglobby: 0.2.16 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -13136,7 +13136,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -15178,28 +15178,28 @@ snapshots: transitivePeerDependencies: - zod - ox@0.6.7(typescript@5.9.3)(zod@4.3.6): + ox@0.6.7(typescript@5.9.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.9.3)(zod@4.3.6) + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.6.9(typescript@5.9.3)(zod@4.3.6): + ox@0.6.9(typescript@5.9.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) + abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 @@ -15333,21 +15333,21 @@ snapshots: pony-cause@2.1.11: {} - porto@0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)): + porto@0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) hono: 4.12.12 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.9.3) ox: 0.9.17(typescript@5.9.3)(zod@4.3.6) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 4.3.6 zustand: 5.0.12(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) react: 19.1.0 typescript: 5.9.3 - wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) + wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) transitivePeerDependencies: - '@types/react' - immer @@ -16957,15 +16957,15 @@ snapshots: vary@1.1.2: {} - viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6): + viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.9.3)(zod@4.3.6) + abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) isows: 1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.9.3)(zod@4.3.6) + ox: 0.6.7(typescript@5.9.3)(zod@3.25.76) ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.3 @@ -17025,14 +17025,14 @@ snapshots: - utf-8-validate - zod - wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6): + wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): dependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) - '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) react: 19.1.0 use-sync-external-store: 1.4.0(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: diff --git a/scripts/cross-chain-e2e/lib/deploy.ts b/scripts/cross-chain-e2e/lib/deploy.ts index de61e57..5621cd2 100644 --- a/scripts/cross-chain-e2e/lib/deploy.ts +++ b/scripts/cross-chain-e2e/lib/deploy.ts @@ -258,11 +258,15 @@ export function writeDeployedSnapshot( outPath: string, { stellar, evm }: DeployAllResult, ): void { + // Undeployed roles are emitted as null so consumers fail fast on a real use + // (instead of silently contract-calling the zero address). In this flow the + // EVM side only plays the order role and the Stellar side only plays the ad + // role, so the counterpart address on each chain stays null. const snapshot = { eth: { name: "AnvilLocal", chainId: evm.chainId.toString(), - adManagerAddress: "0x" + "0".repeat(40), // EVM AdManager not deployed in this flow + adManagerAddress: null as string | null, orderPortalAddress: evm.addresses.orderPortal, merkleManagerAddress: evm.addresses.merkleManager, verifierAddress: evm.addresses.verifier, @@ -274,13 +278,12 @@ export function writeDeployedSnapshot( name: "StellarLocal", chainId: stellar.chainId.toString(), adManagerAddress: stellar.adManagerHex, - orderPortalAddress: "0x" + "0".repeat(64), // Stellar OrderPortal not deployed here + orderPortalAddress: null as string | null, merkleManagerAddress: strkeyToHex(stellar.merkleManager), verifierAddress: strkeyToHex(stellar.verifier), tokenName: "XLM", tokenSymbol: "XLM", tokenAddress: stellar.adTokenHex, - adminSecret: process.env.STELLAR_ADMIN_SECRET ?? "", }, }; fs.writeFileSync(outPath, JSON.stringify(snapshot, null, 2)); diff --git a/scripts/relayer-e2e/lib/eth.ts b/scripts/relayer-e2e/lib/eth.ts index 0a20e09..09e1c1b 100644 --- a/scripts/relayer-e2e/lib/eth.ts +++ b/scripts/relayer-e2e/lib/eth.ts @@ -55,6 +55,7 @@ export async function fundEthAddress( const needed = parseEther(minBalanceEther); const current = await client.getBalance({ address: to }); if (current >= needed) return; + const missing = needed - current; const funderKey = process.env.FUNDER_KEY as `0x${string}` | undefined; @@ -65,18 +66,20 @@ export async function fundEthAddress( account: privateKeyToAccount(funderKey), }); - const hash = await wallet.sendTransaction({ - to, - value: parseEther("10"), - }); + const hash = await wallet.sendTransaction({ to, value: missing }); await client.waitForTransactionReceipt({ hash }); return; } - const ok = await tryTopUpViaRpc(to, "0x8AC7230489E80000"); // 10 ETH + const ok = await tryTopUpViaRpc(to, `0x${needed.toString(16)}`); if (!ok) { throw new Error( "Unable to fund address. Set FUNDER_KEY in env, or run against Anvil/Hardhat and allow *_setBalance.", ); } + + const funded = await client.getBalance({ address: to }); + if (funded < needed) { + throw new Error(`Unable to fund ${to} to ${minBalanceEther} ETH.`); + } } diff --git a/scripts/relayer-e2e/lib/seed.ts b/scripts/relayer-e2e/lib/seed.ts index f396932..ea43549 100644 --- a/scripts/relayer-e2e/lib/seed.ts +++ b/scripts/relayer-e2e/lib/seed.ts @@ -2,12 +2,15 @@ import { PrismaClient } from "@prisma/client"; import { hash as argon2hash } from "@node-rs/argon2"; import { ethers } from "ethers"; +// `null` means the role wasn't deployed for this chain in the current flow; +// the Prisma columns are non-null so we substitute a recognizable sentinel at +// the DB boundary (see `sentinelFor`) and log a warning. export interface DeployedContracts { eth: { name: string; chainId: string; - adManagerAddress: string; - orderPortalAddress: string; + adManagerAddress: string | null; + orderPortalAddress: string | null; merkleManagerAddress: string; verifierAddress: string; tokenName: string; @@ -17,17 +20,23 @@ export interface DeployedContracts { stellar?: { name: string; chainId: string; - adManagerAddress: string; // 0x + 64 hex - orderPortalAddress: string; + adManagerAddress: string | null; // 0x + 64 hex + orderPortalAddress: string | null; merkleManagerAddress: string; verifierAddress: string; tokenName: string; tokenSymbol: string; tokenAddress: string; - adminSecret: string; }; } +function sentinelFor(role: string, chain: string, width: 40 | 64): string { + console.warn( + `[seed] ${chain} has no ${role} deployed — writing zero-address sentinel.`, + ); + return "0x" + "0".repeat(width); +} + export async function seedDb(deployed: DeployedContracts): Promise { const prisma = new PrismaClient(); try { @@ -42,20 +51,24 @@ export async function seedDb(deployed: DeployedContracts): Promise { }); // EVM chain + token. + const ethAdManager = + deployed.eth.adManagerAddress ?? sentinelFor("adManager", "eth", 40); + const ethOrderPortal = + deployed.eth.orderPortalAddress ?? sentinelFor("orderPortal", "eth", 40); const ethChain = await prisma.chain.upsert({ where: { chainId: BigInt(deployed.eth.chainId) }, create: { name: deployed.eth.name, chainId: BigInt(deployed.eth.chainId), kind: "EVM", - adManagerAddress: deployed.eth.adManagerAddress, - orderPortalAddress: deployed.eth.orderPortalAddress, + adManagerAddress: ethAdManager, + orderPortalAddress: ethOrderPortal, mmr: { create: { chainId: deployed.eth.chainId } }, }, update: { name: deployed.eth.name, - adManagerAddress: deployed.eth.adManagerAddress, - orderPortalAddress: deployed.eth.orderPortalAddress, + adManagerAddress: ethAdManager, + orderPortalAddress: ethOrderPortal, }, select: { id: true }, }); @@ -87,20 +100,24 @@ export async function seedDb(deployed: DeployedContracts): Promise { // Stellar chain + token (optional). if (deployed.stellar) { const s = deployed.stellar; + const stellarAdManager = + s.adManagerAddress ?? sentinelFor("adManager", "stellar", 64); + const stellarOrderPortal = + s.orderPortalAddress ?? sentinelFor("orderPortal", "stellar", 64); const stellarChain = await prisma.chain.upsert({ where: { chainId: BigInt(s.chainId) }, create: { name: s.name, chainId: BigInt(s.chainId), kind: "STELLAR", - adManagerAddress: s.adManagerAddress, - orderPortalAddress: s.orderPortalAddress, + adManagerAddress: stellarAdManager, + orderPortalAddress: stellarOrderPortal, mmr: { create: { chainId: s.chainId } }, }, update: { name: s.name, - adManagerAddress: s.adManagerAddress, - orderPortalAddress: s.orderPortalAddress, + adManagerAddress: stellarAdManager, + orderPortalAddress: stellarOrderPortal, }, select: { id: true }, }); diff --git a/scripts/stop_chains.sh b/scripts/stop_chains.sh index 621392a..8053637 100755 --- a/scripts/stop_chains.sh +++ b/scripts/stop_chains.sh @@ -6,6 +6,12 @@ set -uo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENV_FILE="$ROOT_DIR/.chains.env" +if [[ -f "$ENV_FILE" ]]; then + # shellcheck disable=SC1090 + source "$ENV_FILE" +fi + PID_FILE="$ROOT_DIR/.chains.anvil.pid" if [[ -f "$PID_FILE" ]]; then ANVIL_PID="$(cat "$PID_FILE")" From db52c8436cde44f6a1901906f97a185dd2835074 Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 02:01:07 +0100 Subject: [PATCH 05/12] chore: fix ci --- apps/backend-relayer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend-relayer/Dockerfile b/apps/backend-relayer/Dockerfile index e841b8a..67d2e85 100644 --- a/apps/backend-relayer/Dockerfile +++ b/apps/backend-relayer/Dockerfile @@ -60,4 +60,4 @@ EXPOSE 2005 # `tini` keeps signal forwarding sane. Migrations run on every boot — idempotent # for an already-migrated DB. ENTRYPOINT ["/sbin/tini", "--"] -CMD ["sh", "-c", "npx prisma migrate deploy && node dist/main.js"] +CMD ["sh", "-c", "npx prisma migrate deploy && node dist/src/main.js"] From c9f198cbf8e54b769126d1f72e1cf0ce655e7588 Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 05:17:05 +0100 Subject: [PATCH 06/12] chore: fix ci build issue --- apps/backend-relayer/Dockerfile | 23 +-- apps/backend-relayer/package.json | 9 +- pnpm-lock.yaml | 247 +++++++++++++++--------------- 3 files changed, 144 insertions(+), 135 deletions(-) diff --git a/apps/backend-relayer/Dockerfile b/apps/backend-relayer/Dockerfile index 67d2e85..efc8eaa 100644 --- a/apps/backend-relayer/Dockerfile +++ b/apps/backend-relayer/Dockerfile @@ -3,9 +3,11 @@ # ----------------------------------------------------------------------------- # Stage 1 — builder # ----------------------------------------------------------------------------- -FROM node:20-alpine AS builder +FROM node:20-slim AS builder -RUN apk add --no-cache python3 make g++ openssl libc6-compat +RUN apt-get update \ + && apt-get install -y --no-install-recommends python3 make g++ openssl ca-certificates \ + && rm -rf /var/lib/apt/lists/* RUN corepack enable && corepack prepare pnpm@10.10.0 --activate @@ -25,23 +27,26 @@ COPY packages/proofbridge_mmr packages/proofbridge_mmr # Install & build the relayer (and only the relayer — avoid compiling every # workspace package). -RUN pnpm install --frozen-lockfile --offline --ignore-scripts \ +RUN pnpm install --frozen-lockfile --offline \ && pnpm --filter backend-relayer exec prisma generate \ && pnpm --filter backend-relayer build -# Produce a self-contained runtime dir with only the relayer's prod deps. # `pnpm deploy` only copies files listed in the package's `files` field, so -# the built `dist/` and `prisma/` dirs have to be copied explicitly. +# copy dist/ and prisma/ explicitly, then re-run prisma generate inside /out +# so .prisma/client lands in the final node_modules. RUN pnpm --filter backend-relayer deploy --prod --legacy /out \ && cp -r apps/backend-relayer/dist /out/dist \ - && cp -r apps/backend-relayer/prisma /out/prisma + && cp -r apps/backend-relayer/prisma /out/prisma \ + && cd /out && npx prisma generate --schema=prisma/schema.prisma # ----------------------------------------------------------------------------- # Stage 2 — runtime # ----------------------------------------------------------------------------- -FROM node:20-alpine AS runtime +FROM node:20-slim AS runtime -RUN apk add --no-cache openssl libc6-compat tini +RUN apt-get update \ + && apt-get install -y --no-install-recommends openssl ca-certificates tini \ + && rm -rf /var/lib/apt/lists/* WORKDIR /app @@ -59,5 +64,5 @@ EXPOSE 2005 # `tini` keeps signal forwarding sane. Migrations run on every boot — idempotent # for an already-migrated DB. -ENTRYPOINT ["/sbin/tini", "--"] +ENTRYPOINT ["/usr/bin/tini", "--"] CMD ["sh", "-c", "npx prisma migrate deploy && node dist/src/main.js"] diff --git a/apps/backend-relayer/package.json b/apps/backend-relayer/package.json index e2f060b..76e1c9b 100644 --- a/apps/backend-relayer/package.json +++ b/apps/backend-relayer/package.json @@ -11,7 +11,7 @@ "start": "nest start", "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", + "start:prod": "node dist/src/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest", "test:watch": "jest --watch", @@ -46,6 +46,9 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "date-fns": "^4.1.0", + "dotenv": "^17.2.2", + "ethers": "^6.15.0", + "express": "^5.1.0", "level": "^10.0.0", "morgan": "^1.10.1", "nest-winston": "^1.10.2", @@ -53,6 +56,7 @@ "rave-level": "^1.0.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", + "siwe": "^3.0.0", "unique-names-generator": "^4.7.1", "uuid": "^13.0.0", "viem": "^2.37.7", @@ -69,16 +73,13 @@ "@types/jest": "^30.0.0", "@types/node": "^22.10.7", "@types/supertest": "^6.0.2", - "dotenv": "^17.2.2", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", "eslint-plugin-prettier": "^5.2.2", - "ethers": "^6.15.0", "execa": "^9.6.0", "globals": "^16.0.0", "jest": "^30.0.0", "prettier": "^3.4.2", - "siwe": "^3.0.0", "source-map-support": "^0.5.21", "supertest": "^7.1.4", "ts-jest": "^29.2.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d45531..8a40e00 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -103,6 +103,15 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 + dotenv: + specifier: ^17.2.2 + version: 17.4.1 + ethers: + specifier: ^6.15.0 + version: 6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) + express: + specifier: ^5.1.0 + version: 5.2.1 level: specifier: ^10.0.0 version: 10.0.0 @@ -127,6 +136,9 @@ importers: rxjs: specifier: ^7.8.1 version: 7.8.2 + siwe: + specifier: ^3.0.0 + version: 3.0.0(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) unique-names-generator: specifier: ^4.7.1 version: 4.7.1 @@ -170,9 +182,6 @@ importers: '@types/supertest': specifier: ^6.0.2 version: 6.0.3 - dotenv: - specifier: ^17.2.2 - version: 17.4.1 eslint: specifier: ^9.18.0 version: 9.39.4(jiti@2.6.1) @@ -182,9 +191,6 @@ importers: eslint-plugin-prettier: specifier: ^5.2.2 version: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(prettier@3.8.2) - ethers: - specifier: ^6.15.0 - version: 6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) execa: specifier: ^9.6.0 version: 9.6.1 @@ -197,9 +203,6 @@ importers: prettier: specifier: ^3.4.2 version: 3.8.2 - siwe: - specifier: ^3.0.0 - version: 3.0.0(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -235,10 +238,10 @@ importers: version: 2.1.2(gsap@3.14.2)(react@19.1.0) '@rainbow-me/rainbowkit': specifier: ^2.2.8 - version: 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + version: 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) '@rainbow-me/rainbowkit-siwe-next-auth': specifier: ^0.5.0 - version: 0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0) '@tanstack/react-query': specifier: ^5.89.0 version: 5.98.0(react@19.1.0) @@ -304,10 +307,10 @@ importers: version: 4.0.0(tailwindcss@4.2.2) viem: specifier: ^2.37.7 - version: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) wagmi: specifier: ^2.17.1 - version: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + version: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) devDependencies: '@eslint/eslintrc': specifier: ^3 @@ -8347,16 +8350,16 @@ snapshots: '@balena/dockerignore@1.0.2': {} - '@base-org/account@2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@base-org/account@2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@coinbase/cdp-sdk': 1.47.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + ox: 0.6.9(typescript@5.9.3)(zod@4.3.6) preact: 10.24.2 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.3(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) transitivePeerDependencies: - '@types/react' @@ -8419,15 +8422,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@coinbase/wallet-sdk@4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@coinbase/wallet-sdk@4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@3.25.76) + ox: 0.6.9(typescript@5.9.3)(zod@4.3.6) preact: 10.24.2 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.3(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) transitivePeerDependencies: - '@types/react' @@ -8636,11 +8639,11 @@ snapshots: ethereum-cryptography: 2.2.1 micro-ftch: 0.3.1 - '@gemini-wallet/core@0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@gemini-wallet/core@0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))': dependencies: '@metamask/rpc-errors': 7.0.2 eventemitter3: 5.0.1 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - supports-color @@ -9763,13 +9766,13 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@rainbow-me/rainbowkit-siwe-next-auth@0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0)': + '@rainbow-me/rainbowkit-siwe-next-auth@0.5.0(@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)))(next-auth@4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react@19.1.0)': dependencies: - '@rainbow-me/rainbowkit': 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) + '@rainbow-me/rainbowkit': 2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) next-auth: 4.24.11(next@15.5.15(@babel/core@7.29.0)(react-dom@19.2.5(react@19.1.0))(react@19.1.0))(react-dom@19.2.5(react@19.1.0))(react@19.1.0) react: 19.1.0 - '@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))': + '@rainbow-me/rainbowkit@2.2.10(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(react-dom@19.2.5(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))': dependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) '@vanilla-extract/css': 1.17.3 @@ -9781,8 +9784,8 @@ snapshots: react-dom: 19.2.5(react@19.1.0) react-remove-scroll: 2.6.2(@types/react@19.2.14)(react@19.1.0) ua-parser-js: 1.0.41 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) transitivePeerDependencies: - '@types/react' - babel-plugin-macros @@ -9867,24 +9870,24 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9913,12 +9916,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-pay@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) lit: 3.3.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) transitivePeerDependencies: @@ -9953,12 +9956,12 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) lit: 3.3.0 transitivePeerDependencies: @@ -9990,10 +9993,10 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) lit: 3.3.0 qrcode: 1.5.3 @@ -10025,16 +10028,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76)': + '@reown/appkit-utils@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -10074,21 +10077,21 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-pay': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.14)(react@19.1.0))(zod@4.3.6) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@walletconnect/types': 2.21.0 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -10119,9 +10122,9 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -10129,10 +10132,10 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - bufferutil - typescript @@ -11163,19 +11166,19 @@ snapshots: dependencies: '@vanilla-extract/css': 1.17.3 - '@wagmi/connectors@6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6)': dependencies: - '@base-org/account': 2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.76) - '@gemini-wallet/core': 0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@base-org/account': 2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@4.3.6) + '@gemini-wallet/core': 0.3.2(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + porto: 0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -11216,11 +11219,11 @@ snapshots: - wagmi - zod - '@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.9.3) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) zustand: 5.0.0(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/query-core': 5.98.0 @@ -11231,7 +11234,7 @@ snapshots: - react - use-sync-external-store - '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -11245,7 +11248,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -11275,7 +11278,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -11289,7 +11292,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -11323,18 +11326,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/ethereum-provider@2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@reown/appkit': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/types': 2.21.1 - '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11457,16 +11460,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11493,16 +11496,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: - '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -11591,7 +11594,7 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -11600,9 +11603,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -11631,7 +11634,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -11640,9 +11643,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -11671,7 +11674,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -11689,7 +11692,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11715,7 +11718,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -11733,7 +11736,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -11855,10 +11858,10 @@ snapshots: typescript: 5.9.3 zod: 3.25.76 - abitype@1.0.8(typescript@5.9.3)(zod@3.25.76): + abitype@1.0.8(typescript@5.9.3)(zod@4.3.6): optionalDependencies: typescript: 5.9.3 - zod: 3.25.76 + zod: 4.3.6 abitype@1.2.3(typescript@5.9.3)(zod@3.22.4): optionalDependencies: @@ -13084,7 +13087,7 @@ snapshots: '@next/eslint-plugin-next': 16.2.3 eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) @@ -13111,7 +13114,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -13126,13 +13129,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -13147,7 +13150,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -15178,28 +15181,28 @@ snapshots: transitivePeerDependencies: - zod - ox@0.6.7(typescript@5.9.3)(zod@3.25.76): + ox@0.6.7(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + abitype: 1.0.8(typescript@5.9.3)(zod@4.3.6) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.6.9(typescript@5.9.3)(zod@3.25.76): + ox@0.6.9(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 @@ -15333,21 +15336,21 @@ snapshots: pony-cause@2.1.11: {} - porto@0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)): + porto@0.2.35(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) hono: 4.12.12 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.9.3) ox: 0.9.17(typescript@5.9.3)(zod@4.3.6) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) zod: 4.3.6 zustand: 5.0.12(@types/react@19.2.14)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) react: 19.1.0 typescript: 5.9.3 - wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6) transitivePeerDependencies: - '@types/react' - immer @@ -16957,15 +16960,15 @@ snapshots: vary@1.1.2: {} - viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.9.3)(zod@3.25.76) + abitype: 1.0.8(typescript@5.9.3)(zod@4.3.6) isows: 1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.9.3)(zod@3.25.76) + ox: 0.6.7(typescript@5.9.3)(zod@4.3.6) ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.3 @@ -17025,14 +17028,14 @@ snapshots: - utf-8-validate - zod - wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): + wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6): dependencies: '@tanstack/react-query': 5.98.0(react@19.1.0) - '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.98.0)(@tanstack/react-query@5.98.0(react@19.1.0))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6))(zod@4.3.6))(zod@4.3.6) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.98.0)(@types/react@19.2.14)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6)) react: 19.1.0 use-sync-external-store: 1.4.0(react@19.1.0) - viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: From 16cc190c7a76c8c7ed80a3e87ae06d3d3641bf7b Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 05:29:37 +0100 Subject: [PATCH 07/12] chore: fix ci build issue --- apps/backend-relayer/docker-compose.e2e.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend-relayer/docker-compose.e2e.yaml b/apps/backend-relayer/docker-compose.e2e.yaml index 0e981bc..8bd6cfb 100644 --- a/apps/backend-relayer/docker-compose.e2e.yaml +++ b/apps/backend-relayer/docker-compose.e2e.yaml @@ -40,7 +40,7 @@ services: ports: - "2005:2005" healthcheck: - test: ["CMD-SHELL", "wget -q -O- http://localhost:2005/health || exit 1"] + test: ["CMD", "node", "-e", "fetch('http://localhost:2005/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] interval: 3s timeout: 5s retries: 40 From eb19efa5fcc38e466c89deeb452229f2e7484ba4 Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 08:19:08 +0100 Subject: [PATCH 08/12] chore: fix bad test url in scripts --- scripts/relayer-e2e/lib/api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/relayer-e2e/lib/api.ts b/scripts/relayer-e2e/lib/api.ts index e882c39..5052cf6 100644 --- a/scripts/relayer-e2e/lib/api.ts +++ b/scripts/relayer-e2e/lib/api.ts @@ -69,7 +69,7 @@ export const apiCreateAd = ( creatorDstAddress: string, fundAmount: string ) => - request("POST", "/v1/ads", { + request("POST", "/v1/ads/create", { token, body: { routeId, creatorDstAddress, fundAmount }, }); @@ -106,7 +106,7 @@ export const apiCreateOrder = ( amount: string; bridgerDstAddress: string; } -) => request("POST", "/v1/trades", { token, body }); +) => request("POST", "/v1/trades/create", { token, body }); export const apiGetTrade = (tradeId: string) => request("GET", `/v1/trades/${tradeId}`); From fa5c3eb3df58dab59b15ab805fa91ba0c0b5aad3 Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 08:33:19 +0100 Subject: [PATCH 09/12] chore: fix api req body mismatch --- scripts/relayer-e2e/lib/api.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/relayer-e2e/lib/api.ts b/scripts/relayer-e2e/lib/api.ts index 5052cf6..6e7b5ba 100644 --- a/scripts/relayer-e2e/lib/api.ts +++ b/scripts/relayer-e2e/lib/api.ts @@ -78,7 +78,10 @@ export const apiConfirm = (adId: string, token: string, txHash: `0x${string}`) = request("POST", `/v1/ads/${adId}/confirm`, { token, body: { txHash } }); export const apiFundAd = (adId: string, token: string, amount: string) => - request("POST", `/v1/ads/${adId}/fund`, { token, body: { amount } }); + request("POST", `/v1/ads/${adId}/fund`, { + token, + body: { poolAmountTopUp: amount }, + }); export const apiWithdraw = ( adId: string, @@ -88,7 +91,7 @@ export const apiWithdraw = ( ) => request("POST", `/v1/ads/${adId}/withdraw`, { token, - body: { amount, to }, + body: { poolAmountWithdraw: amount, to }, }); export const apiGetAd = (adId: string) => request("GET", `/v1/ads/${adId}`); From 6054050f685e0e0372b94f0f097303f9a5fe3c1b Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 08:45:41 +0100 Subject: [PATCH 10/12] chore: fix fund issue and optimizations --- .github/workflows/backend-relayer-e2e.yml | 13 ++- .../src/modules/ads/ad.service.ts | 106 +++++++++++------- scripts/stop_chains.sh | 9 +- 3 files changed, 80 insertions(+), 48 deletions(-) diff --git a/.github/workflows/backend-relayer-e2e.yml b/.github/workflows/backend-relayer-e2e.yml index b35a19f..5b71731 100644 --- a/.github/workflows/backend-relayer-e2e.yml +++ b/.github/workflows/backend-relayer-e2e.yml @@ -56,8 +56,14 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 - name: Install Stellar CLI + env: + STELLAR_CLI_VERSION: "23.3.0" + STELLAR_CLI_SHA256: "b3a5455d7113a53a8bd0f1ba0148a14b7aa7a46ee2f49c3b9277424775b309ad" run: | - curl -Ls https://github.com/stellar/stellar-cli/releases/download/v23.3.0/stellar-cli-23.3.0-x86_64-unknown-linux-gnu.tar.gz -o /tmp/stellar-cli.tar.gz + set -euo pipefail + url="https://github.com/stellar/stellar-cli/releases/download/v${STELLAR_CLI_VERSION}/stellar-cli-${STELLAR_CLI_VERSION}-x86_64-unknown-linux-gnu.tar.gz" + curl -fsSL "$url" -o /tmp/stellar-cli.tar.gz + echo "${STELLAR_CLI_SHA256} /tmp/stellar-cli.tar.gz" | sha256sum -c - mkdir -p "$HOME/.local/bin" tar -xzf /tmp/stellar-cli.tar.gz -C "$HOME/.local/bin" stellar chmod +x "$HOME/.local/bin/stellar" @@ -103,15 +109,12 @@ jobs: # ── 3. postgres + backend-relayer containers ───────────────────── - name: Start postgres + backend-relayer env: - STELLAR_ADMIN_SECRET: ${{ env.STELLAR_ADMIN_SECRET }} STELLAR_NETWORK_PASSPHRASE: "Standalone Network ; February 2017" run: docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" up -d --build --wait # ── 4. seed ────────────────────────────────────────────────────── - name: Seed database - env: - DATABASE_URL: ${{ env.HOST_DATABASE_URL }} - run: pnpm --filter relayer-e2e exec tsx cli.ts seed --in "$SNAPSHOT_PATH" + run: DATABASE_URL="$HOST_DATABASE_URL" pnpm --filter relayer-e2e exec tsx cli.ts seed --in "$SNAPSHOT_PATH" # ── 5. flows ───────────────────────────────────────────────────── - name: Run lifecycle flows diff --git a/apps/backend-relayer/src/modules/ads/ad.service.ts b/apps/backend-relayer/src/modules/ads/ad.service.ts index 4314f09..b80cf21 100644 --- a/apps/backend-relayer/src/modules/ads/ad.service.ts +++ b/apps/backend-relayer/src/modules/ads/ad.service.ts @@ -16,7 +16,7 @@ import { ConfirmAdActionDto, CloseAdDto, } from './dto/ad.dto'; -import { AdStatus, Prisma } from '@prisma/client'; +import { AdStatus, ChainKind, Prisma } from '@prisma/client'; import { Request } from 'express'; import { ChainAdapterService } from '../../chain-adapters/chain-adapter.service'; import { @@ -25,6 +25,28 @@ import { } from '../../providers/viem/ethers/typedData'; import { randomUUID } from 'crypto'; +// Caller-owns-ad check bound to the ad's chain kind. +function assertCallerOwnsAd( + walletAddress: string, + ad: { + creatorAddress: string; + route: { adToken: { chain: { kind: ChainKind } } }; + }, +): void { + let normalized: string; + try { + normalized = normalizeChainAddress( + walletAddress, + ad.route.adToken.chain.kind, + ); + } catch { + throw new ForbiddenException('Unauthorized'); + } + if (normalized !== ad.creatorAddress) { + throw new ForbiddenException('Unauthorized'); + } +} + type AdQueryInput = { routeId?: string; creatorAddress?: string; @@ -416,11 +438,23 @@ export class AdsService { adRecipient: toBytes32(normalizedCreatorDst), }); + let normalizedCreator: string; + try { + normalizedCreator = normalizeChainAddress( + user.walletAddress, + route.adToken.chain.kind, + ); + } catch { + throw new BadRequestException( + 'Authenticated wallet does not match ad chain', + ); + } + const requestDetails = await this.prisma.$transaction(async (prisma) => { const ad = await prisma.ad.create({ data: { id: adId, - creatorAddress: normalizeChainAddress(user.walletAddress), + creatorAddress: normalizedCreator, creatorDstAddress: normalizedCreatorDst, routeId: route.id, adTokenId: route.adToken.id, @@ -496,10 +530,7 @@ export class AdsService { if (!user) throw new ForbiddenException('Unauthorized'); const ad = await this.prisma.ad.findUnique({ - where: { - id, - creatorAddress: normalizeChainAddress(user.walletAddress), - }, + where: { id }, select: { id: true, creatorAddress: true, @@ -526,6 +557,8 @@ export class AdsService { if (!ad) throw new NotFoundException('Ad not found'); + assertCallerOwnsAd(user.walletAddress, ad); + if (ad.adUpdateLog) { throw new BadRequestException( 'Ad has pending update; please wait a few minutes and try again', @@ -618,10 +651,7 @@ export class AdsService { if (!user) throw new ForbiddenException('Unauthorized'); const ad = await this.prisma.ad.findUnique({ - where: { - id, - creatorAddress: normalizeChainAddress(user.walletAddress), - }, + where: { id }, select: { id: true, creatorAddress: true, @@ -648,6 +678,8 @@ export class AdsService { if (!ad) throw new NotFoundException('Ad not found'); + assertCallerOwnsAd(user.walletAddress, ad); + if (ad.adUpdateLog) { throw new BadRequestException( 'Ad has pending update; please wait a few minutes and try again', @@ -840,10 +872,7 @@ export class AdsService { if (!user) throw new ForbiddenException('Unauthorized'); const ad = await this.prisma.ad.findFirst({ - where: { - id, - creatorAddress: normalizeChainAddress(user.walletAddress), - }, + where: { id }, select: { id: true, creatorAddress: true, @@ -869,6 +898,8 @@ export class AdsService { }); if (!ad) throw new NotFoundException('Ad not found'); + assertCallerOwnsAd(user.walletAddress, ad); + if (ad.adUpdateLog) { throw new BadRequestException( 'Ad has pending update; please wait a few minutes and try again', @@ -967,7 +998,8 @@ export class AdsService { async confirmChainAction( req: Request, adId: string, - dto: ConfirmAdActionDto, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _dto: ConfirmAdActionDto, ) { try { const reqUser = req.user; @@ -982,43 +1014,35 @@ export class AdsService { const adLogUpdate = await this.prisma.adUpdateLog.findUnique({ where: { adId }, - include: { ad: true, log: true }, - }); - - if (!adLogUpdate) throw new NotFoundException('Ad update log not found'); - - if ( - normalizeChainAddress(adLogUpdate.ad.creatorAddress) !== - normalizeChainAddress(user.walletAddress) - ) { - throw new ForbiddenException('Unauthorized'); - } - - // get ad details - const ad = await this.prisma.ad.findUnique({ - where: { id: adId }, - select: { - poolAmount: true, - status: true, - route: { - select: { - adToken: { + include: { + ad: { + include: { + route: { select: { - chain: { + adToken: { select: { - adManagerAddress: true, - chainId: true, - kind: true, + chain: { + select: { + adManagerAddress: true, + chainId: true, + kind: true, + }, + }, }, }, }, }, }, }, + log: true, }, }); - if (!ad) throw new NotFoundException('Ad for Ad Id not found'); + if (!adLogUpdate) throw new NotFoundException('Ad update log not found'); + + const ad = adLogUpdate.ad; + + assertCallerOwnsAd(user.walletAddress, ad); // // verify adLog const isValidated = await this.chainAdapters diff --git a/scripts/stop_chains.sh b/scripts/stop_chains.sh index 8053637..f4f311b 100755 --- a/scripts/stop_chains.sh +++ b/scripts/stop_chains.sh @@ -16,8 +16,13 @@ PID_FILE="$ROOT_DIR/.chains.anvil.pid" if [[ -f "$PID_FILE" ]]; then ANVIL_PID="$(cat "$PID_FILE")" if [[ -n "$ANVIL_PID" ]] && kill -0 "$ANVIL_PID" 2>/dev/null; then - echo "Stopping Anvil (PID $ANVIL_PID)..." - kill "$ANVIL_PID" 2>/dev/null || true + CMD="$(ps -o comm= -p "$ANVIL_PID" 2>/dev/null || true)" + if [[ "$CMD" == anvil* ]]; then + echo "Stopping Anvil (PID $ANVIL_PID)..." + kill "$ANVIL_PID" 2>/dev/null || true + else + echo "Skipping stale PID $ANVIL_PID (now: '${CMD:-unknown}')." + fi fi rm -f "$PID_FILE" fi From a968c00797ae6cb1c7fc940a63ba8e7aae951fdf Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 09:00:08 +0100 Subject: [PATCH 11/12] chore: fix env setup issue --- apps/backend-relayer/docker-compose.e2e.yaml | 1 + apps/backend-relayer/src/providers/viem/ethers/localnet.ts | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/backend-relayer/docker-compose.e2e.yaml b/apps/backend-relayer/docker-compose.e2e.yaml index 8bd6cfb..e37a620 100644 --- a/apps/backend-relayer/docker-compose.e2e.yaml +++ b/apps/backend-relayer/docker-compose.e2e.yaml @@ -35,6 +35,7 @@ services: STELLAR_RPC_URL: http://host.docker.internal:8000/soroban/rpc STELLAR_NETWORK_PASSPHRASE: ${STELLAR_NETWORK_PASSPHRASE:-Standalone Network ; February 2017} STELLAR_ADMIN_SECRET: ${STELLAR_ADMIN_SECRET} + EVM_ADMIN_PRIVATE_KEY: ${EVM_ADMIN_PRIVATE_KEY} extra_hosts: - "host.docker.internal:host-gateway" ports: diff --git a/apps/backend-relayer/src/providers/viem/ethers/localnet.ts b/apps/backend-relayer/src/providers/viem/ethers/localnet.ts index 11c56a3..196b7af 100644 --- a/apps/backend-relayer/src/providers/viem/ethers/localnet.ts +++ b/apps/backend-relayer/src/providers/viem/ethers/localnet.ts @@ -1,5 +1,7 @@ import { Chain, defineChain } from 'viem'; +// Defaults target a host-local anvil/hedera. In containerized e2e the host is +// reachable via `host.docker.internal`, so honor env overrides when present. export const ethLocalnet: Chain = defineChain({ id: 31337, name: 'ETH LOCALNET', @@ -10,7 +12,7 @@ export const ethLocalnet: Chain = defineChain({ }, rpcUrls: { default: { - http: ['http://localhost:9545'], + http: [process.env.ETHEREUM_RPC_URL || 'http://localhost:9545'], }, }, }); @@ -25,7 +27,7 @@ export const hederaLocalnet: Chain = defineChain({ }, rpcUrls: { default: { - http: ['http://localhost:7546'], + http: [process.env.HEDERA_RPC_URL || 'http://localhost:7546'], }, }, }); From f39c57142ba3669038b6b242acbee88ef7d1e7d1 Mon Sep 17 00:00:00 2001 From: JoE11-y Date: Mon, 13 Apr 2026 09:18:24 +0100 Subject: [PATCH 12/12] chore: add more observability to lifecycles --- scripts/relayer-e2e/flows/ad-lifecycle.ts | 263 ++++++------ scripts/relayer-e2e/flows/trade-lifecycle.ts | 399 +++++++++++-------- scripts/relayer-e2e/lib/assert.ts | 17 + 3 files changed, 386 insertions(+), 293 deletions(-) diff --git a/scripts/relayer-e2e/flows/ad-lifecycle.ts b/scripts/relayer-e2e/flows/ad-lifecycle.ts index 873f25e..ec18b72 100644 --- a/scripts/relayer-e2e/flows/ad-lifecycle.ts +++ b/scripts/relayer-e2e/flows/ad-lifecycle.ts @@ -19,7 +19,7 @@ import { } from "../lib/api.js"; import { loginStellar } from "../lib/auth.js"; import { toBaseUnits } from "../lib/amount.js"; -import { assert, assertObject, phase } from "../lib/assert.js"; +import { assert, assertObject, note, phase, step } from "../lib/assert.js"; import { createAdSoroban, fundAdSoroban, @@ -39,131 +39,152 @@ export async function runAdLifecycle(): Promise { const stellarChainId = process.env.STELLAR_CHAIN_ID!; const evmChainId = process.env.EVM_CHAIN_ID!; - const routes = expectStatus( - await getRoutes(stellarChainId, evmChainId), - 200, - "getRoutes", - ); - assert(routes.body.data.length > 0, "no routes seeded"); - const route = routes.body.data[0]; + note(`ad creator stellar: ${adCreator.publicKey()}`); + note(`ad creator evm dst: ${adCreatorEvm.address}`); + note(`stellar chain ${stellarChainId} → evm chain ${evmChainId}`); - const access = await loginStellar(adCreator); + const route = await step("fetch routes", async () => { + const routes = expectStatus( + await getRoutes(stellarChainId, evmChainId), + 200, + "getRoutes", + ); + assert(routes.body.data.length > 0, "no routes seeded"); + const r = routes.body.data[0]; + note(`route ${r.id}`); + return r; + }); + + const access = await step("login (stellar SEP-10)", () => loginStellar(adCreator)); - // Create ad — 50 XLM. const INITIAL = toBaseUnits("50", "STELLAR"); - const create = expectStatus( - await apiCreateAd(access, route.id, adCreatorEvm.address, INITIAL), - 201, - "apiCreateAd", - ); - const req = create.body as any; - const adId = req.adId; - - const txCreate = await createAdSoroban( - adCreator, - req.signature, - req.signerPublicKey, - req.authToken, - req.timeToExpire, - adCreator.publicKey(), - req.adId, - req.adToken, - req.initialAmount, - req.orderChainId, - req.adRecipient, - req.contractAddress, - ); - expectStatus( - await apiConfirm(adId, access, txCreate as `0x${string}`), - 200, - "apiConfirm(create)", - ); - - const adAfterCreate = expectStatus(await apiGetAd(adId), 200, "apiGetAd(create)"); - assertObject(adAfterCreate.body, { - id: adId, - status: "ACTIVE", - poolAmount: INITIAL, - }); + const adId = await step(`create ad (${INITIAL} base units)`, async () => { + const create = expectStatus( + await apiCreateAd(access, route.id, adCreatorEvm.address, INITIAL), + 201, + "apiCreateAd", + ); + const req = create.body as any; + note(`adId ${req.adId}`); - // Fund — 5 XLM. - const topup = expectStatus( - await apiFundAd(adId, access, toBaseUnits("5", "STELLAR")), - 200, - "apiFundAd", - ); - const txFund = await fundAdSoroban( - adCreator, - topup.body.signature, - topup.body.signerPublicKey, - topup.body.authToken, - topup.body.timeToExpire, - topup.body.adId, - topup.body.amount, - topup.body.contractAddress, - ); - expectStatus( - await apiConfirm(adId, access, txFund as `0x${string}`), - 200, - "apiConfirm(fund)", - ); - - // Withdraw — 1 XLM to the ad creator. - const withdraw = expectStatus( - await apiWithdraw( - adId, - access, - toBaseUnits("1", "STELLAR"), + const txCreate = await createAdSoroban( + adCreator, + req.signature, + req.signerPublicKey, + req.authToken, + req.timeToExpire, adCreator.publicKey(), - ), - 200, - "apiWithdraw", - ); - const txW = await withdrawFromAdSoroban( - adCreator, - withdraw.body.signature, - withdraw.body.signerPublicKey, - withdraw.body.authToken, - withdraw.body.timeToExpire, - withdraw.body.adId, - withdraw.body.amount, - StrKey.isValidEd25519PublicKey(withdraw.body.to) - ? withdraw.body.to - : adCreator.publicKey(), - withdraw.body.contractAddress, - ); - expectStatus( - await apiConfirm(adId, access, txW as `0x${string}`), - 200, - "apiConfirm(withdraw)", - ); - - // Close. - const close = expectStatus( - await apiCloseAd(adId, access, { to: adCreator.publicKey() }), - 200, - "apiCloseAd", - ); - const txClose = await closeAdSoroban( - adCreator, - close.body.signature, - close.body.signerPublicKey, - close.body.authToken, - close.body.timeToExpire, - close.body.adId, - StrKey.isValidEd25519PublicKey(close.body.to) - ? close.body.to - : adCreator.publicKey(), - close.body.contractAddress, - ); - expectStatus( - await apiConfirm(adId, access, txClose as `0x${string}`), - 200, - "apiConfirm(close)", - ); - - const finalAd = expectStatus(await apiGetAd(adId), 200, "apiGetAd(final)"); - assertObject(finalAd.body, { status: "CLOSED", poolAmount: "0" }); + req.adId, + req.adToken, + req.initialAmount, + req.orderChainId, + req.adRecipient, + req.contractAddress, + ); + note(`soroban tx ${txCreate}`); + expectStatus( + await apiConfirm(req.adId, access, txCreate as `0x${string}`), + 200, + "apiConfirm(create)", + ); + return req.adId as string; + }); + + await step("verify ad ACTIVE", async () => { + const adAfterCreate = expectStatus(await apiGetAd(adId), 200, "apiGetAd(create)"); + assertObject(adAfterCreate.body, { + id: adId, + status: "ACTIVE", + poolAmount: INITIAL, + }); + }); + + await step("fund ad (+5 XLM)", async () => { + const topup = expectStatus( + await apiFundAd(adId, access, toBaseUnits("5", "STELLAR")), + 200, + "apiFundAd", + ); + const txFund = await fundAdSoroban( + adCreator, + topup.body.signature, + topup.body.signerPublicKey, + topup.body.authToken, + topup.body.timeToExpire, + topup.body.adId, + topup.body.amount, + topup.body.contractAddress, + ); + note(`soroban tx ${txFund}`); + expectStatus( + await apiConfirm(adId, access, txFund as `0x${string}`), + 200, + "apiConfirm(fund)", + ); + }); + + await step("withdraw (-1 XLM)", async () => { + const withdraw = expectStatus( + await apiWithdraw( + adId, + access, + toBaseUnits("1", "STELLAR"), + adCreator.publicKey(), + ), + 200, + "apiWithdraw", + ); + const txW = await withdrawFromAdSoroban( + adCreator, + withdraw.body.signature, + withdraw.body.signerPublicKey, + withdraw.body.authToken, + withdraw.body.timeToExpire, + withdraw.body.adId, + withdraw.body.amount, + StrKey.isValidEd25519PublicKey(withdraw.body.to) + ? withdraw.body.to + : adCreator.publicKey(), + withdraw.body.contractAddress, + ); + note(`soroban tx ${txW}`); + expectStatus( + await apiConfirm(adId, access, txW as `0x${string}`), + 200, + "apiConfirm(withdraw)", + ); + }); + + await step("close ad", async () => { + const close = expectStatus( + await apiCloseAd(adId, access, { to: adCreator.publicKey() }), + 200, + "apiCloseAd", + ); + const txClose = await closeAdSoroban( + adCreator, + close.body.signature, + close.body.signerPublicKey, + close.body.authToken, + close.body.timeToExpire, + close.body.adId, + StrKey.isValidEd25519PublicKey(close.body.to) + ? close.body.to + : adCreator.publicKey(), + close.body.contractAddress, + ); + note(`soroban tx ${txClose}`); + expectStatus( + await apiConfirm(adId, access, txClose as `0x${string}`), + 200, + "apiConfirm(close)", + ); + }); + + await step("verify ad CLOSED", async () => { + const finalAd = expectStatus(await apiGetAd(adId), 200, "apiGetAd(final)"); + assertObject(finalAd.body, { status: "CLOSED", poolAmount: "0" }); + }); console.log("[ad-lifecycle] passed"); } diff --git a/scripts/relayer-e2e/flows/trade-lifecycle.ts b/scripts/relayer-e2e/flows/trade-lifecycle.ts index 67b9c1c..285103f 100644 --- a/scripts/relayer-e2e/flows/trade-lifecycle.ts +++ b/scripts/relayer-e2e/flows/trade-lifecycle.ts @@ -23,7 +23,7 @@ import { } from "../lib/api.js"; import { loginStellar, loginEvm } from "../lib/auth.js"; import { toBaseUnits } from "../lib/amount.js"; -import { assert, assertObject, phase } from "../lib/assert.js"; +import { assert, assertObject, note, phase, step } from "../lib/assert.js"; import { createAdSoroban, lockForOrderSoroban, @@ -60,192 +60,247 @@ export async function runTradeLifecycle(): Promise { const stellarChainId = process.env.STELLAR_CHAIN_ID!; const evmChainId = process.env.EVM_CHAIN_ID!; + note(`ad creator stellar: ${adCreator.publicKey()}`); + note(`ad creator evm dst: ${adCreatorEvm.address}`); + note(`bridger evm: ${bridger.address}`); + note(`bridger stellar dst: ${bridgerStellar.publicKey()}`); + note(`stellar chain ${stellarChainId} → evm chain ${evmChainId}`); + const ethClient = makeEthClient(); - await fundEthAddress(ethClient, bridger.address); - await fundEthAddress(ethClient, adCreatorEvm.address); + await step("fund evm participants", async () => { + await fundEthAddress(ethClient, bridger.address); + await fundEthAddress(ethClient, adCreatorEvm.address); + }); - const routes = expectStatus( - await getRoutes(stellarChainId, evmChainId), - 200, - "getRoutes", - ); - assert(routes.body.data.length > 0, "no routes seeded"); - const route = routes.body.data[0]; + const route = await step("fetch routes", async () => { + const routes = expectStatus( + await getRoutes(stellarChainId, evmChainId), + 200, + "getRoutes", + ); + assert(routes.body.data.length > 0, "no routes seeded"); + const r = routes.body.data[0]; + note(`route ${r.id}`); + return r; + }); - const adAccess = await loginStellar(adCreator); + const adAccess = await step("login ad creator (stellar)", () => + loginStellar(adCreator), + ); - // Seed the ad. const INITIAL = toBaseUnits("50", "STELLAR"); - const create = expectStatus( - await apiCreateAd(adAccess, route.id, adCreatorEvm.address, INITIAL), - 201, - "apiCreateAd", - ); - const req = create.body as any; - const adId = req.adId; - const txCreate = await createAdSoroban( - adCreator, - req.signature, - req.signerPublicKey, - req.authToken, - req.timeToExpire, - adCreator.publicKey(), - req.adId, - req.adToken, - req.initialAmount, - req.orderChainId, - req.adRecipient, - req.contractAddress, - ); - expectStatus( - await apiConfirm(adId, adAccess, txCreate as `0x${string}`), - 200, - "apiConfirm(create)", - ); + const adId = await step(`seed ad (${INITIAL} base units)`, async () => { + const create = expectStatus( + await apiCreateAd(adAccess, route.id, adCreatorEvm.address, INITIAL), + 201, + "apiCreateAd", + ); + const req = create.body as any; + note(`adId ${req.adId}`); + const txCreate = await createAdSoroban( + adCreator, + req.signature, + req.signerPublicKey, + req.authToken, + req.timeToExpire, + adCreator.publicKey(), + req.adId, + req.adToken, + req.initialAmount, + req.orderChainId, + req.adRecipient, + req.contractAddress, + ); + note(`soroban tx ${txCreate}`); + expectStatus( + await apiConfirm(req.adId, adAccess, txCreate as `0x${string}`), + 200, + "apiConfirm(create)", + ); + return req.adId as string; + }); - // Bridger creates the order on EVM. - const bridgerAccess = await loginEvm(bridgerKey); - const order = expectStatus( - await apiCreateOrder(bridgerAccess, { - adId, - routeId: route.id, - amount: toBaseUnits("10", "STELLAR"), - bridgerDstAddress: bridgerStellar.publicKey(), - }), - 201, - "apiCreateOrder", - ); - const orderReq = order.body.reqContractDetails; - const tradeId = order.body.tradeId as string; - - // Mint + approve test tokens to the order portal. - const evmTokenAddress = orderReq.orderParams.orderChainToken; // bytes32 - // addresses in orderParams are bytes32 — the portal address is the contractAddress field. - const orderPortalAddress = orderReq.contractAddress as `0x${string}`; - - // Need the real (20-byte) token address — recover from the orderParams - // bytes32 by slicing the last 20 bytes. - const tokenAddr20 = getAddress(`0x${evmTokenAddress.slice(-40)}`); - - await mintToken(ethClient, bridger, tokenAddr20, bridger.address, parseEther("1000")); - await approveToken(ethClient, bridger, tokenAddr20, orderPortalAddress, parseEther("100")); - - const orderCreateTx = await createOrder( - ethClient, - bridger, - orderReq.signature, - orderReq.authToken as `0x${string}`, - orderReq.timeToExpire, - orderReq.orderParams, - orderPortalAddress, - ); - expectStatus( - await apiTradeConfirm(tradeId, bridgerAccess, orderCreateTx), - 200, - "apiTradeConfirm(order)", + const bridgerAccess = await step("login bridger (evm SIWE)", () => + loginEvm(bridgerKey), ); - // Lock on the Stellar ad chain. - const lockOrder = expectStatus( - await apiLockOrder(adAccess, tradeId), - 200, - "apiLockOrder", - ); - const lockReq = lockOrder.body; - const lockTxn = await lockForOrderSoroban( - adCreator, - lockReq.signature as `0x${string}`, - lockReq.signerPublicKey, - lockReq.authToken as `0x${string}`, - lockReq.timeToExpire, - lockReq.orderParams, - lockReq.contractAddress, - ); - expectStatus( - await apiTradeConfirm(tradeId, adAccess, lockTxn as `0x${string}`), - 200, - "apiTradeConfirm(lock)", + const { tradeId, orderReq, orderPortalAddress, tokenAddr20 } = await step( + "create order on EVM", + async () => { + const order = expectStatus( + await apiCreateOrder(bridgerAccess, { + adId, + routeId: route.id, + amount: toBaseUnits("10", "STELLAR"), + bridgerDstAddress: bridgerStellar.publicKey(), + }), + 201, + "apiCreateOrder", + ); + const orderReq = order.body.reqContractDetails; + const tradeId = order.body.tradeId as string; + const orderPortalAddress = orderReq.contractAddress as `0x${string}`; + const tokenAddr20 = getAddress( + `0x${orderReq.orderParams.orderChainToken.slice(-40)}`, + ); + note(`tradeId ${tradeId}`); + note(`orderPortal ${orderPortalAddress}`); + note(`orderChainToken ${tokenAddr20}`); + return { tradeId, orderReq, orderPortalAddress, tokenAddr20 }; + }, ); - const afterLock = expectStatus( - await apiGetTrade(tradeId), - 200, - "apiGetTrade(after lock)", - ); - assertObject(afterLock.body, { status: "LOCKED" }); + await step("mint + approve EVM test token for bridger", async () => { + await mintToken( + ethClient, + bridger, + tokenAddr20, + bridger.address, + parseEther("1000"), + ); + await approveToken( + ethClient, + bridger, + tokenAddr20, + orderPortalAddress, + parseEther("100"), + ); + }); - // Ad-creator unlocks on EVM: sign with the EVM destination key (ECDSA). - const adCreatorParams = expectStatus( - await apiTradeParams(adAccess, tradeId), - 200, - "apiTradeParams(adCreator)", - ); - const adCreatorOrderParams = adCreatorParams.body; - const adCreatorSig = await signTypedOrder(adCreatorEvmKey, adCreatorOrderParams); + await step("submit + confirm EVM createOrder", async () => { + const orderCreateTx = await createOrder( + ethClient, + bridger, + orderReq.signature, + orderReq.authToken as `0x${string}`, + orderReq.timeToExpire, + orderReq.orderParams, + orderPortalAddress, + ); + note(`evm tx ${orderCreateTx}`); + expectStatus( + await apiTradeConfirm(tradeId, bridgerAccess, orderCreateTx), + 200, + "apiTradeConfirm(order)", + ); + }); - const unlockOnOrder = expectStatus( - await apiUnlockOrder(adAccess, tradeId, adCreatorSig), - 200, - "apiUnlockOrder(adCreator)", - ); - const unlockOrderReq = unlockOnOrder.body; - const unlockOrderTx = await unlockOrderChain( - ethClient, - adCreatorEvm, - unlockOrderReq.signature, - unlockOrderReq.authToken as `0x${string}`, - unlockOrderReq.timeToExpire, - unlockOrderReq.orderParams, - unlockOrderReq.nullifierHash as `0x${string}`, - unlockOrderReq.targetRoot as `0x${string}`, - unlockOrderReq.proof as `0x${string}`, - unlockOrderReq.contractAddress, - ); - expectStatus( - await apiTradeUnlockConfirm(adAccess, tradeId, unlockOrderTx), - 200, - "apiTradeUnlockConfirm(adCreator)", - ); + await step("lock on Stellar ad chain", async () => { + const lockOrder = expectStatus( + await apiLockOrder(adAccess, tradeId), + 200, + "apiLockOrder", + ); + const lockReq = lockOrder.body; + const lockTxn = await lockForOrderSoroban( + adCreator, + lockReq.signature as `0x${string}`, + lockReq.signerPublicKey, + lockReq.authToken as `0x${string}`, + lockReq.timeToExpire, + lockReq.orderParams, + lockReq.contractAddress, + ); + note(`soroban tx ${lockTxn}`); + expectStatus( + await apiTradeConfirm(tradeId, adAccess, lockTxn as `0x${string}`), + 200, + "apiTradeConfirm(lock)", + ); + }); - // Bridger unlocks on Stellar: ed25519 over the TypedDataEncoder hash. - const bridgerParams = expectStatus( - await apiTradeParams(bridgerAccess, tradeId), - 200, - "apiTradeParams(bridger)", - ); - const bridgerOrderParams = bridgerParams.body; - const bridgerOrderHash = TypedDataEncoder.hash(domain, orderTypes, { - ...bridgerOrderParams, - salt: BigInt(bridgerOrderParams.salt), + await step("verify trade LOCKED", async () => { + const afterLock = expectStatus( + await apiGetTrade(tradeId), + 200, + "apiGetTrade(after lock)", + ); + assertObject(afterLock.body, { status: "LOCKED" }); }); - const bridgerSigBytes = bridgerStellar.sign( - Buffer.from(bridgerOrderHash.replace(/^0x/, ""), "hex"), - ); - const bridgerSig = `0x${bridgerSigBytes.toString("hex")}`; - const unlockOnAd = expectStatus( - await apiUnlockOrder(bridgerAccess, tradeId, bridgerSig), - 200, - "apiUnlockOrder(bridger)", - ); - const unlockAdReq = unlockOnAd.body; - const unlockAdTx = await unlockSoroban( - bridgerStellar, - unlockAdReq.signature, - unlockAdReq.signerPublicKey, - unlockAdReq.authToken, - unlockAdReq.timeToExpire, - unlockAdReq.orderParams, - unlockAdReq.nullifierHash, - unlockAdReq.targetRoot, - Buffer.from(unlockAdReq.proof.replace(/^0x/, ""), "hex"), - unlockAdReq.contractAddress, - ); - expectStatus( - await apiTradeUnlockConfirm(bridgerAccess, tradeId, unlockAdTx as `0x${string}`), - 200, - "apiTradeUnlockConfirm(bridger)", - ); + await step("ad-creator unlocks on EVM (ECDSA)", async () => { + const adCreatorParams = expectStatus( + await apiTradeParams(adAccess, tradeId), + 200, + "apiTradeParams(adCreator)", + ); + const adCreatorOrderParams = adCreatorParams.body; + const adCreatorSig = await signTypedOrder( + adCreatorEvmKey, + adCreatorOrderParams, + ); + + const unlockOnOrder = expectStatus( + await apiUnlockOrder(adAccess, tradeId, adCreatorSig), + 200, + "apiUnlockOrder(adCreator)", + ); + const unlockOrderReq = unlockOnOrder.body; + const unlockOrderTx = await unlockOrderChain( + ethClient, + adCreatorEvm, + unlockOrderReq.signature, + unlockOrderReq.authToken as `0x${string}`, + unlockOrderReq.timeToExpire, + unlockOrderReq.orderParams, + unlockOrderReq.nullifierHash as `0x${string}`, + unlockOrderReq.targetRoot as `0x${string}`, + unlockOrderReq.proof as `0x${string}`, + unlockOrderReq.contractAddress, + ); + note(`evm tx ${unlockOrderTx}`); + expectStatus( + await apiTradeUnlockConfirm(adAccess, tradeId, unlockOrderTx), + 200, + "apiTradeUnlockConfirm(adCreator)", + ); + }); + + await step("bridger unlocks on Stellar (ed25519)", async () => { + const bridgerParams = expectStatus( + await apiTradeParams(bridgerAccess, tradeId), + 200, + "apiTradeParams(bridger)", + ); + const bridgerOrderParams = bridgerParams.body; + const bridgerOrderHash = TypedDataEncoder.hash(domain, orderTypes, { + ...bridgerOrderParams, + salt: BigInt(bridgerOrderParams.salt), + }); + const bridgerSigBytes = bridgerStellar.sign( + Buffer.from(bridgerOrderHash.replace(/^0x/, ""), "hex"), + ); + const bridgerSig = `0x${bridgerSigBytes.toString("hex")}`; + + const unlockOnAd = expectStatus( + await apiUnlockOrder(bridgerAccess, tradeId, bridgerSig), + 200, + "apiUnlockOrder(bridger)", + ); + const unlockAdReq = unlockOnAd.body; + const unlockAdTx = await unlockSoroban( + bridgerStellar, + unlockAdReq.signature, + unlockAdReq.signerPublicKey, + unlockAdReq.authToken, + unlockAdReq.timeToExpire, + unlockAdReq.orderParams, + unlockAdReq.nullifierHash, + unlockAdReq.targetRoot, + Buffer.from(unlockAdReq.proof.replace(/^0x/, ""), "hex"), + unlockAdReq.contractAddress, + ); + note(`soroban tx ${unlockAdTx}`); + expectStatus( + await apiTradeUnlockConfirm( + bridgerAccess, + tradeId, + unlockAdTx as `0x${string}`, + ), + 200, + "apiTradeUnlockConfirm(bridger)", + ); + }); console.log("[trade-lifecycle] passed"); } diff --git a/scripts/relayer-e2e/lib/assert.ts b/scripts/relayer-e2e/lib/assert.ts index 783f07a..666aff3 100644 --- a/scripts/relayer-e2e/lib/assert.ts +++ b/scripts/relayer-e2e/lib/assert.ts @@ -32,3 +32,20 @@ export function phase(n: number | string, title: string): void { const bar = "=".repeat(60); console.log(`\n${bar}\nPhase ${n}: ${title}\n${bar}`); } + +export async function step(label: string, fn: () => Promise): Promise { + const t0 = Date.now(); + console.log(` → ${label}`); + try { + const out = await fn(); + console.log(` ✓ ${label} (${Date.now() - t0}ms)`); + return out; + } catch (e) { + console.log(` ✗ ${label} (${Date.now() - t0}ms)`); + throw e; + } +} + +export function note(msg: string): void { + console.log(` · ${msg}`); +}