diff --git a/.github/workflows/bridge-ui--docker-build.yml b/.github/workflows/bridge-ui--docker-build.yml new file mode 100644 index 00000000000..49bf7d8859e --- /dev/null +++ b/.github/workflows/bridge-ui--docker-build.yml @@ -0,0 +1,65 @@ +name: "Nethermind - Bridge UI - Docker build and push" + +on: + workflow_dispatch: + push: + branches: [main] + tags: + - "bridge-ui-v*" + paths: + - "packages/bridge-ui/**" + +env: + DOCKER_REGISTRY: nethermind.jfrog.io + DOCKER_USERNAME: core + DOCKER_REPOSITORY: core-oci-local-dev/bridge-ui + +jobs: + build: + name: Build and push docker image + runs-on: ubuntu-latest + if: github.repository == 'NethermindEth/surge-taiko-mono' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: ${{ env.DOCKER_USERNAME }} + password: ${{ secrets.ARTIFACTORY_CORE_TOKEN_CONTRIBUTOR }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_REPOSITORY }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=ref,event=tag + type=sha + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64,linux/arm64 + context: packages/bridge-ui + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Summary + run: | + echo "## Docker build completed :green_circle:" >> $GITHUB_STEP_SUMMARY + echo "### Tags" >> $GITHUB_STEP_SUMMARY + echo "${{ steps.meta.outputs.tags }}" | while IFS= read -r TAG; do + echo "- $TAG" >> $GITHUB_STEP_SUMMARY + done diff --git a/Dockerfile b/Dockerfile index e27049140ef..3963377be39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG PACKAGE=eventindexer +ARG PACKAGE=relayer FROM golang:1.23.0 as builder diff --git a/packages/bridge-ui/.env.example b/packages/bridge-ui/.env.example index a17db647151..4890b890238 100644 --- a/packages/bridge-ui/.env.example +++ b/packages/bridge-ui/.env.example @@ -11,7 +11,7 @@ export PUBLIC_GUIDE_URL=https:// export PUBLIC_TESTNET_NAME="" # WalletConnect Project ID -export PUBLIC_WALLETCONNECT_PROJECT_ID="" +export PUBLIC_WALLETCONNECT_PROJECT_ID="Testnet" # Enable NFT Bridge ("true" or "false") export PUBLIC_NFT_BRIDGE_ENABLED="" diff --git a/packages/bridge-ui/.gitignore b/packages/bridge-ui/.gitignore index fa4bf0d56d6..f376946475e 100644 --- a/packages/bridge-ui/.gitignore +++ b/packages/bridge-ui/.gitignore @@ -11,7 +11,6 @@ vite.config.js.timestamp-* vite.config.ts.timestamp-* # Bridge config files -/config/* !/config/sample/ !/config/schemas/ diff --git a/packages/bridge-ui/Dockerfile b/packages/bridge-ui/Dockerfile new file mode 100644 index 00000000000..0606ed35eae --- /dev/null +++ b/packages/bridge-ui/Dockerfile @@ -0,0 +1,42 @@ +# Step 1: Build the application +FROM node:20 AS builder + +RUN npm install -g pnpm + +WORKDIR /app + +# Install dependencies +COPY package.json ./ + +# Install dependencies without the lock file, generating a new one +RUN pnpm install + +# Copy the app source code and build +COPY . . + +RUN cp .env.example .env + +RUN pnpm export:config +RUN pnpm build + +# Step 2: Set up the final production environment +FROM node:20 + +# Install pnpm globally +RUN npm install -g pnpm + +WORKDIR /app + +# Copy only necessary build files +COPY --from=builder /app/package.json ./ +COPY --from=builder /app/pnpm-lock.yaml ./ +RUN pnpm install --prod + +# Copy the build output +COPY --from=builder /app/build ./build + +# Expose port +EXPOSE 3000 + +# Start the SvelteKit app +CMD ["node", "build"] diff --git a/packages/bridge-ui/config/configuredBridges.json b/packages/bridge-ui/config/configuredBridges.json new file mode 100644 index 00000000000..a3d5c481801 --- /dev/null +++ b/packages/bridge-ui/config/configuredBridges.json @@ -0,0 +1,30 @@ +{ + "configuredBridges": [ + { + "source": "3151908", + "destination": "763374", + "addresses": { + "bridgeAddress": "0x72bCbB3f339aF622c28a26488Eed9097a2977404", + "erc20VaultAddress": "0x38435Ac0E0e9Bd8737c476F8F39a24b0735e00dc", + "erc721VaultAddress": "0x9ECB6f04D47FA2599449AaA523bF84476f7aD80f", + "erc1155VaultAddress": "0xB29dB8A6b1C596B64f7E1dD5358d59Db73648E17", + "crossChainSyncAddress": "", + "signalServiceAddress": "0x00c042C4D5D913277CE16611a2ce6e9003554aD5", + "quotaManagerAddress": "" + } + }, + { + "source": "763374", + "destination": "3151908", + "addresses": { + "bridgeAddress": "0x7633740000000000000000000000000000000001", + "erc20VaultAddress": "0x7633740000000000000000000000000000000002", + "erc721VaultAddress": "0x7633740000000000000000000000000000000003", + "erc1155VaultAddress": "0x7633740000000000000000000000000000000004", + "crossChainSyncAddress": "", + "signalServiceAddress": "0x7633740000000000000000000000000000000005", + "quotaManagerAddress": "" + } + } + ] +} diff --git a/packages/bridge-ui/config/configuredChains.json b/packages/bridge-ui/config/configuredChains.json new file mode 100644 index 00000000000..5673894eb02 --- /dev/null +++ b/packages/bridge-ui/config/configuredChains.json @@ -0,0 +1,54 @@ +{ + "configuredChains": [ + { + "3151908": { + "name": "Kurtosis", + "type": "L1", + "icon": "https://cdn.worldvectorlogo.com/logos/ethereum-eth.svg", + "rpcUrls": { + "default": { + "http": [ + "https://l1-rpc/" + ] + } + }, + "nativeCurrency": { + "name": "ETH", + "symbol": "ETH", + "decimals": 18 + }, + "blockExplorers": { + "default": { + "name": "Explorer 1", + "url": "" + } + } + } + }, + { + "763374": { + "name": "Taiko", + "type": "L2", + "icon": "https://cdn.worldvectorlogo.com/logos/ethereum-eth.svg", + "rpcUrls": { + "default": { + "http": [ + "https://l2-rpc/" + ] + } + }, + "nativeCurrency": { + "name": "TaikoETH", + "symbol": "tETH", + "decimals": 18 + }, + "blockExplorers": { + "default": { + "name": "Explorer 2", + "url": "" + } + } + } + } + ] +} diff --git a/packages/bridge-ui/config/configuredCustomTokens.json b/packages/bridge-ui/config/configuredCustomTokens.json new file mode 100644 index 00000000000..0d4f101c7a3 --- /dev/null +++ b/packages/bridge-ui/config/configuredCustomTokens.json @@ -0,0 +1,2 @@ +[ +] diff --git a/packages/bridge-ui/config/configuredEventIndexer.json b/packages/bridge-ui/config/configuredEventIndexer.json new file mode 100644 index 00000000000..f908b821d41 --- /dev/null +++ b/packages/bridge-ui/config/configuredEventIndexer.json @@ -0,0 +1,12 @@ +{ + "configuredEventIndexer": [ + { + "chainIds": [3151908, 763374], + "url": "" + }, + { + "chainIds": [763374, 3151908], + "url": "" + } + ] +} diff --git a/packages/bridge-ui/config/configuredRelayer.json b/packages/bridge-ui/config/configuredRelayer.json new file mode 100644 index 00000000000..89fb79d9159 --- /dev/null +++ b/packages/bridge-ui/config/configuredRelayer.json @@ -0,0 +1,12 @@ +{ + "configuredRelayer": [ + { + "chainIds": [3151908, 763374], + "url": "https://relayer:4102" + }, + { + "chainIds": [763374, 3151908], + "url": "https://relayer:4102" + } + ] +} diff --git a/packages/bridge-ui/docker-compose.yml b/packages/bridge-ui/docker-compose.yml new file mode 100644 index 00000000000..02224608d98 --- /dev/null +++ b/packages/bridge-ui/docker-compose.yml @@ -0,0 +1,30 @@ +services: + bridge-ui: + build: + context: . + dockerfile: Dockerfile + container_name: bridge-ui + ports: + - "3000:3000" + profiles: + - bridge-ui + networks: + - app-network + + nginx: + image: nginx:latest + container_name: nginx + ports: + - "80:80" + - "443:443" + profiles: + - nginx + networks: + - app-network + volumes: + - ./nginx/ssl:/etc/nginx/ssl:ro + - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro + +networks: + app-network: + driver: bridge diff --git a/packages/bridge-ui/nginx/generate-certs.sh b/packages/bridge-ui/nginx/generate-certs.sh new file mode 100644 index 00000000000..7573f8b764a --- /dev/null +++ b/packages/bridge-ui/nginx/generate-certs.sh @@ -0,0 +1,6 @@ +#!/bin/bash +mkdir -p ssl +openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout ssl/key.pem \ + -out ssl/cert.pem \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" diff --git a/packages/bridge-ui/nginx/nginx.conf b/packages/bridge-ui/nginx/nginx.conf new file mode 100644 index 00000000000..66ecbca208c --- /dev/null +++ b/packages/bridge-ui/nginx/nginx.conf @@ -0,0 +1,70 @@ +server { + listen 80; + listen 443 ssl; + server_name devnet.domain; + + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + + # L1 RPC endpoint + location /l1-rpc/ { + proxy_pass http://172.17.0.1:32002/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # L1 RPC block explorer + location /l1-blocksscout/ { + proxy_pass http://172.17.0.1:35001/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # L2 RPC endpoint + location /l2-rpc/ { + proxy_pass http://172.17.0.1:8547/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Relayer endpoint + location /l1-relayer/ { + proxy_pass http://172.17.0.1:4102/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /l2-relayer/ { + proxy_pass http://172.17.0.1:4103/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Main application (should be last) + location / { + proxy_pass http://bridge-ui:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } +} diff --git a/packages/bridge-ui/package.json b/packages/bridge-ui/package.json index c44dfca11bd..59f6722b831 100644 --- a/packages/bridge-ui/package.json +++ b/packages/bridge-ui/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@playwright/test": "^1.43.1", - "@sveltejs/adapter-auto": "^3.2.0", + "@sveltejs/adapter-node": "5.2.9", "@sveltejs/kit": "^2.5.21", "@sveltejs/vite-plugin-svelte": "^3.1.0", "@types/debug": "^4.1.12", diff --git a/packages/bridge-ui/svelte.config.js b/packages/bridge-ui/svelte.config.js index e2427361c03..8071d2e9f80 100644 --- a/packages/bridge-ui/svelte.config.js +++ b/packages/bridge-ui/svelte.config.js @@ -1,4 +1,4 @@ -import adapter from '@sveltejs/adapter-auto'; +import adapter from '@sveltejs/adapter-node'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; /** @type {import('@sveltejs/kit').Config} */ @@ -10,7 +10,11 @@ const config = { kit: { // https://kit.svelte.dev/docs/single-page-apps adapter: adapter({ + pages: 'build', + assets: 'build', fallback: 'index.html', + precompress: false, + strict: true }), }, }; diff --git a/packages/relayer/docker-compose.yml b/packages/relayer/docker-compose.yml new file mode 100644 index 00000000000..7c5350f35d5 --- /dev/null +++ b/packages/relayer/docker-compose.yml @@ -0,0 +1,243 @@ +services: + db: + image: mysql:8.0 + container_name: "db" + cap_add: + - SYS_NICE + restart: always + environment: + - MYSQL_DATABASE=relayer + - MYSQL_ROOT_PASSWORD=root + ports: + - "3306:3306" + volumes: + - mysql_data:/var/lib/mysql + networks: + - rabbitmq_go_net + profiles: + - init + + migrations: + image: ghcr.io/kukymbr/goose-docker:3.22.1 + environment: + - GOOSE_DRIVER=mysql + - GOOSE_DBSTRING=root:root@tcp(db:3306)/relayer + volumes: + - ./migrations:/migrations + networks: + - rabbitmq_go_net + profiles: + - migrations + + rabbitmq: + image: rabbitmq:3-management-alpine + container_name: "rabbitmq" + environment: + - RABBITMQ_DEFAULT_USER=root + - RABBITMQ_DEFAULT_PASS=root + ports: + - 5672:5672 + - 15672:15672 + - 15692:15692 + volumes: + - ./rabbitmq/data/:/var/lib/rabbitmq/ + - ./rabbitmq/log/:/var/log/rabbitmq + networks: + - rabbitmq_go_net + profiles: + - init + + relayer-l1-processor: + image: nethsurge/taiko-relayer:latest + container_name: relayer-l1-processor + restart: always + entrypoint: ["/usr/local/bin/relayer"] + command: + - processor + - --queue.prefetch=100 + - --processorPrivateKey=bcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31 + - --destBridgeAddress=0x7633740000000000000000000000000000000001 + - --destERC20VaultAddress=0x7633740000000000000000000000000000000002 + - --destERC721Address=0x7633740000000000000000000000000000000003 + - --destERC1155Address=0x7633740000000000000000000000000000000004 + - --destTaikoAddress=0x7633740000000000000000000000000000010001 + - --srcSignalServiceAddress=0x00c042C4D5D913277CE16611a2ce6e9003554aD5 + - --srcRpcUrl= + - --destRpcUrl= + - --confirmations=0 + - --headerSyncInterval=2 + - --profitableOnly=false + - --tx.minTipCap=0.01 + - --queue.host=rabbitmq + - --queue.password=root + - --queue.port=5672 + - --queue.username=root + - --db.host=db + - --db.maxIdleConns=50 + - --db.maxOpenConns=3000 + - --db.name=relayer + - --db.password=root + - --db.username=root + networks: + - rabbitmq_go_net + profiles: + - l1 + + relayer-l1-indexer: + image: nethsurge/taiko-relayer:latest + container_name: relayer-l1-indexer + restart: always + entrypoint: ["/usr/local/bin/relayer"] + command: + - indexer + - --db.host=db + - --db.connMaxLifetime=100000 + - --db.maxIdleConns=50 + - --db.maxOpenConns=3000 + - --db.name=relayer + - --db.password=root + - --db.username=root + - --srcBridgeAddress=0x72bCbB3f339aF622c28a26488Eed9097a2977404 + - --destBridgeAddress=0x7633740000000000000000000000000000000001 + - --srcTaikoAddress=0xaE37C7A711bcab9B0f8655a97B738d6ccaB6560B + - --srcSignalServiceAddress=0x00c042C4D5D913277CE16611a2ce6e9003554aD5 + - --srcRpcUrl= + - --destRpcUrl= + - --queue.host=rabbitmq + - --queue.password=root + - --queue.port=5672 + - --queue.username=root + - --maxNumGoroutines=50 + - --blockBatchSize=100 + - --event=MessageSent + - --confirmations=0 + networks: + - rabbitmq_go_net + profiles: + - l1 + + relayer-l1-api: + image: nethsurge/taiko-relayer:latest + container_name: relayer-l1-api + restart: always + entrypoint: ["/usr/local/bin/relayer"] + ports: + - 4102:4102 + command: + - api + - --db.host=db + - --db.connMaxLifetime=100000 + - --db.maxIdleConns=50 + - --db.maxOpenConns=3000 + - --db.name=relayer + - --db.password=root + - --db.username=root + - --srcRpcUrl= + - --destRpcUrl= + - --destTaikoAddress=0x7633740000000000000000000000000000010001 + - --processingFeeMultiplier=1.75 + networks: + - rabbitmq_go_net + profiles: + - api + + relayer-l2-api: + image: nethsurge/taiko-relayer:latest + container_name: relayer-l2-api + restart: always + entrypoint: ["/usr/local/bin/relayer"] + ports: + - 4103:4102 + command: + - api + - --db.host=db + - --db.connMaxLifetime=100000 + - --db.maxIdleConns=50 + - --db.maxOpenConns=3000 + - --db.name=relayer + - --db.password=root + - --db.username=root + - --srcRpcUrl= + - --destRpcUrl= + - --destTaikoAddress=0xaE37C7A711bcab9B0f8655a97B738d6ccaB6560B + - --processingFeeMultiplier=1.75 + networks: + - rabbitmq_go_net + profiles: + - api + + relayer-l2-processor: + image: nethsurge/taiko-relayer:latest + container_name: relayer-l2-processor + restart: always + entrypoint: ["/usr/local/bin/relayer"] + command: + - processor + - --queue.prefetch=100 + - --processorPrivateKey=bcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31 + - --destBridgeAddress=0x72bCbB3f339aF622c28a26488Eed9097a2977404 + - --destERC20VaultAddress=0x38435Ac0E0e9Bd8737c476F8F39a24b0735e00dc + - --destERC721Address=0x9ECB6f04D47FA2599449AaA523bF84476f7aD80f + - --destERC1155Address=0xB29dB8A6b1C596B64f7E1dD5358d59Db73648E17 + - --destTaikoAddress=0xaE37C7A711bcab9B0f8655a97B738d6ccaB6560B + - --srcSignalServiceAddress=0x7633740000000000000000000000000000000005 + - --srcRpcUrl= + - --destRpcUrl= + - --confirmations=0 + - --headerSyncInterval=2 + - --profitableOnly=false + - --tx.minTipCap=0.01 + - --queue.host=rabbitmq + - --queue.password=root + - --queue.port=5672 + - --queue.username=root + - --db.host=db + - --db.maxIdleConns=50 + - --db.maxOpenConns=3000 + - --db.name=relayer + - --db.password=root + - --db.username=root + networks: + - rabbitmq_go_net + profiles: + - l2 + + relayer-l2-indexer: + image: nethsurge/taiko-relayer:latest + container_name: relayer-l2-indexer + restart: always + entrypoint: ["/usr/local/bin/relayer"] + command: + - indexer + - --db.host=db + - --db.connMaxLifetime=100000 + - --db.maxIdleConns=50 + - --db.maxOpenConns=3000 + - --db.name=relayer + - --db.password=root + - --db.username=root + - --srcBridgeAddress=0x7633740000000000000000000000000000000001 + - --destBridgeAddress=0x72bCbB3f339aF622c28a26488Eed9097a2977404 + - --srcSignalServiceAddress=0x7633740000000000000000000000000000000005 + - --srcRpcUrl= + - --destRpcUrl= + - --queue.host=rabbitmq + - --queue.password=root + - --queue.port=5672 + - --queue.username=root + - --maxNumGoroutines=50 + - --blockBatchSize=100 + - --event=MessageSent + - --confirmations=0 + networks: + - rabbitmq_go_net + profiles: + - l2 + +volumes: + mysql_data: + driver: local + +networks: + rabbitmq_go_net: + driver: bridge