diff --git a/stack_orchestrator/data/compose/docker-compose-uniswap-interface.yml b/stack_orchestrator/data/compose/docker-compose-uniswap-interface.yml new file mode 100644 index 00000000..f6a5c53f --- /dev/null +++ b/stack_orchestrator/data/compose/docker-compose-uniswap-interface.yml @@ -0,0 +1,49 @@ +version: "3.2" + +services: + uniswap-interface: + image: cerc/uniswap-interface:local + restart: on-failure + environment: + - REACT_APP_INFURA_KEY=${CERC_INFURA_KEY} + - REACT_APP_AWS_API_ENDPOINT=${CERC_UNISWAP_GQL} + command: ["./build-app.sh"] + volumes: + - app_builds:/app-builds + - ../config/uniswap-interface/build-app.sh:/app/build-app.sh + + uniswap-glob-host: + image: cerc/urbit-globs-host:local + restart: unless-stopped + depends_on: + uniswap-interface: + condition: service_completed_successfully + command: ["./host-uniswap-glob.sh"] + volumes: + - app_globs:/app-globs + - ../config/uniswap-interface/host-uniswap-glob.sh:/app/host-uniswap-glob.sh + ports: + - "3000" + healthcheck: + test: ["CMD", "nc", "-v", "localhost", "3000"] + interval: 20s + timeout: 5s + retries: 15 + start_period: 10s + + uniswap-gql-proxy: + image: cerc/uniswap-interface:local + restart: on-failure + command: ["bash", "-c", "yarn proxy-gql"] + ports: + - "4000" + healthcheck: + test: ["CMD", "nc", "-v", "localhost", "4000"] + interval: 20s + timeout: 5s + retries: 15 + start_period: 10s + +volumes: + app_builds: + app_globs: diff --git a/stack_orchestrator/data/compose/docker-compose-uniswap-urbit.yml b/stack_orchestrator/data/compose/docker-compose-uniswap-urbit.yml new file mode 100644 index 00000000..ae0b3709 --- /dev/null +++ b/stack_orchestrator/data/compose/docker-compose-uniswap-urbit.yml @@ -0,0 +1,26 @@ +version: '3.7' + +services: + urbit-fake-ship: + restart: unless-stopped + image: tloncorp/vere + entrypoint: ["bash", "-c", "./run-urbit-ship.sh && ./deploy-uniswap-app.sh && tail -f /dev/null"] + volumes: + - urbit_data:/urbit + - app_builds:/app-builds + - app_globs:/app-globs + - ../config/urbit/run-urbit-ship.sh:/urbit/run-urbit-ship.sh + - ../config/uniswap-interface/deploy-uniswap-app.sh:/urbit/deploy-uniswap-app.sh + ports: + - "80" + healthcheck: + test: ["CMD", "nc", "-v", "localhost", "80"] + interval: 20s + timeout: 5s + retries: 15 + start_period: 10s + +volumes: + urbit_data: + app_builds: + app_globs: diff --git a/stack_orchestrator/data/config/uniswap-interface/build-app.sh b/stack_orchestrator/data/config/uniswap-interface/build-app.sh new file mode 100755 index 00000000..d3b012e6 --- /dev/null +++ b/stack_orchestrator/data/config/uniswap-interface/build-app.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +# Check and exit if a deployment already exists (on restarts) +if [ -d /app-builds/uniswap/build ]; then + echo "Build already exists, remove volume to rebuild" + exit 0 +fi + +yarn build + +# Move build to app-builds so urbit can deploy it +mkdir /app-builds/uniswap +cp -r ./build /app-builds/uniswap/ diff --git a/stack_orchestrator/data/config/uniswap-interface/deploy-uniswap-app.sh b/stack_orchestrator/data/config/uniswap-interface/deploy-uniswap-app.sh new file mode 100755 index 00000000..6c147083 --- /dev/null +++ b/stack_orchestrator/data/config/uniswap-interface/deploy-uniswap-app.sh @@ -0,0 +1,142 @@ +#!/bin/bash + +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +uniswap_app_build='/app-builds/uniswap/build' +uniswap_desk_dir='/urbit/zod/uniswap' + +if [ -d ${uniswap_desk_dir} ]; then + echo "Uniswap desk dir already exists, skipping deployment..." + exit 0 +fi + +# Fire curl requests to perform operations on the ship +dojo () { + curl -s --data '{"source":{"dojo":"'"$1"'"},"sink":{"stdout":null}}' http://localhost:12321 +} + +hood () { + curl -s --data '{"source":{"dojo":"+hood/'"$1"'"},"sink":{"app":"hood"}}' http://localhost:12321 +} + +# Create/mount a uniswap desk +hood "merge %uniswap our %landscape" +hood "mount %uniswap" + +# Loop until the uniswap build appears +while [ ! -d ${uniswap_app_build} ]; do + echo "Uniswap app build not found, retrying in 5s..." + sleep 5 +done +echo "Build found..." + +# Copy over build to desk data dir +cp -r ${uniswap_app_build} ${uniswap_desk_dir} + +# Create a mark file for .map file type +cat << EOF > "${uniswap_desk_dir}/mar/map.hoon" +:: +:::: /hoon/map/mar + :: Mark for js source maps +/? 310 +:: +=, eyre +|_ mud=@ +++ grow + |% + ++ mime [/application/octet-stream (as-octs:mimes:html (@t mud))] + -- +++ grab + |% :: convert from + ++ mime |=([p=mite q=octs] (@t q.q)) + ++ noun cord :: clam from %noun + -- +++ grad %mime +-- +EOF + +# Create a mark file for .woff file type +cat << EOF > "${uniswap_desk_dir}/mar/woff.hoon" +|_ dat=octs +++ grow + |% + ++ mime [/font/woff dat] + -- +++ grab + |% + ++ mime |=([=mite =octs] octs) + ++ noun octs + -- +++ grad %mime +-- +EOF + +# Create a mark file for .ttf file type +cat << EOF > "${uniswap_desk_dir}/mar/ttf.hoon" +|_ dat=octs +++ grow + |% + ++ mime [/font/ttf dat] + -- +++ grab + |% + ++ mime |=([=mite =octs] octs) + ++ noun octs + -- +++ grad %mime +-- +EOF + +rm "${uniswap_desk_dir}/desk.bill" +rm "${uniswap_desk_dir}/desk.ship" + +# Commit changes and create a glob +hood "commit %uniswap" +dojo "-landscape!make-glob %uniswap /build" + +echo "Copying over glob file to mounted volume" +mkdir -p /app-globs/uniswap +cp /urbit/zod/.urb/put/* /app-globs/uniswap/ + +glob_file=$(ls -1 -c zod/.urb/put | head -1) +echo "Glob filename: ${glob_file}" + +# Curl and wait for the glob to be hosted +glob_url="http://uniswap-glob-host:3000/${glob_file}" + +echo "Checking if glob file hosted at ${glob_url}" +while true; do + response=$(curl -sL -w "%{http_code}" -o /dev/null "$glob_url") + + if [ $response -eq 200 ]; then + echo "File found at $glob_url" + break # Exit the loop if the file is found + else + echo "File not found. Retrying in a few seconds..." + sleep 5 + fi +done + +glob_hash=$(echo "$glob_file" | sed "s/glob-\([a-z0-9\.]*\).glob/\1/") + +# Update the docket file +cat << EOF > "${uniswap_desk_dir}/desk.docket-0" +:~ title+'Uniswap' + info+'Self-hosted uniswap frontend.' + color+0xcd.75df + image+'https://logowik.com/content/uploads/images/uniswap-uni7403.jpg' + base+'uniswap' + glob-http+['http://uniswap-glob-host:3000/${glob_file}' ${glob_hash}] + version+[0 0 1] + website+'https://uniswap.org/' + license+'MIT' +== +EOF + +# Commit changes and install the app +hood "commit %uniswap" +hood "install our %uniswap" + +echo "Uniswap app installed" diff --git a/stack_orchestrator/data/config/uniswap-interface/host-uniswap-glob.sh b/stack_orchestrator/data/config/uniswap-interface/host-uniswap-glob.sh new file mode 100755 index 00000000..37605794 --- /dev/null +++ b/stack_orchestrator/data/config/uniswap-interface/host-uniswap-glob.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +# Use config from mounted volume (when running web-app along with watcher stack) +echo "Waiting for uniswap app glob" +while [ ! -d /app-globs/uniswap ]; do + echo "Glob directory not found, retrying in 5 seconds..." + sleep 5 +done + + +# Copy to a new globs directory +mkdir -p globs +cp -r /app-globs/uniswap/* ./globs + +# Serve the glob file +cd globs +echo "Hosting glob file at port 3000" +python3 -m http.server 3000 --bind 0.0.0.0 diff --git a/stack_orchestrator/data/config/uniswap-interface/install-uniswap-app.sh b/stack_orchestrator/data/config/uniswap-interface/install-uniswap-app.sh new file mode 100755 index 00000000..679bc27e --- /dev/null +++ b/stack_orchestrator/data/config/uniswap-interface/install-uniswap-app.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +# $1: Glob file URL (eg. https://xyz.com/glob-abcd.glob) +# $2: Uniswap desk dir (default: ./zod/uniswap) + +if [ -z "$1" ]; then + echo "Glob file URL arg not provided" + exit 0 +fi + +glob_url=$1 +glob_file=$(basename "$glob_url") +glob_hash=$(echo "$glob_file" | sed "s/glob-\([a-z0-9\.]*\).glob/\1/") +echo "Using glob file ${glob_file}" + +# Default desk dir: ./zod/uniswap +uniswap_desk_dir="${2:-./zod/uniswap}" + +echo "Using ${uniswap_desk_dir} as the Uniswap desk dir path" + +# Fire curl requests to perform operations on the ship +dojo () { + curl -s --data '{"source":{"dojo":"'"$1"'"},"sink":{"stdout":null}}' http://localhost:12321 +} + +hood () { + curl -s --data '{"source":{"dojo":"+hood/'"$1"'"},"sink":{"app":"hood"}}' http://localhost:12321 +} + +# Create/mount a uniswap desk +hood "merge %uniswap our %landscape" +hood "mount %uniswap" + +# Create a mark file for .map file type +cat << EOF > "${uniswap_desk_dir}/mar/map.hoon" +:: +:::: /hoon/map/mar + :: Mark for js source maps +/? 310 +:: +=, eyre +|_ mud=@ +++ grow + |% + ++ mime [/application/octet-stream (as-octs:mimes:html (@t mud))] + -- +++ grab + |% :: convert from + ++ mime |=([p=mite q=octs] (@t q.q)) + ++ noun cord :: clam from %noun + -- +++ grad %mime +-- +EOF + +# Create a mark file for .woff file type +cat << EOF > "${uniswap_desk_dir}/mar/woff.hoon" +|_ dat=octs +++ grow + |% + ++ mime [/font/woff dat] + -- +++ grab + |% + ++ mime |=([=mite =octs] octs) + ++ noun octs + -- +++ grad %mime +-- +EOF + +# Create a mark file for .ttf file type +cat << EOF > "${uniswap_desk_dir}/mar/ttf.hoon" +|_ dat=octs +++ grow + |% + ++ mime [/font/ttf dat] + -- +++ grab + |% + ++ mime |=([=mite =octs] octs) + ++ noun octs + -- +++ grad %mime +-- +EOF + +rm "${uniswap_desk_dir}/desk.bill" +rm "${uniswap_desk_dir}/desk.ship" + +# Update the docket file +cat << EOF > "${uniswap_desk_dir}/desk.docket-0" +:~ title+'Uniswap' + info+'Self-hosted uniswap frontend.' + color+0xcd.75df + image+'https://logowik.com/content/uploads/images/uniswap-uni7403.jpg' + base+'uniswap' + glob-http+['${glob_url}' ${glob_hash}] + version+[0 0 1] + website+'https://uniswap.org/' + license+'MIT' +== +EOF + +# Commit changes and install the app +hood "commit %uniswap" +hood "install our %uniswap" + +echo "Uniswap app installed" diff --git a/stack_orchestrator/data/config/uniswap-interface/remote-deploy-uniswap.sh b/stack_orchestrator/data/config/uniswap-interface/remote-deploy-uniswap.sh new file mode 100755 index 00000000..528151e9 --- /dev/null +++ b/stack_orchestrator/data/config/uniswap-interface/remote-deploy-uniswap.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# $1: Remote user host +# $2: Path to run the app installation in (where urbit ship dir is located) +# $3: Glob file URL (eg. https://xyz.com/glob-abcd.glob) + +if [ "$#" -ne 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +remote_user_host="$1" +remote_folder="$2" +glob_url="$3" + +installation_script="./install-uniswap-app.sh" + +ssh "$remote_user_host" "cd $remote_folder && bash -s $glob_url" < "$installation_script" diff --git a/stack_orchestrator/data/config/urbit/run-urbit-ship.sh b/stack_orchestrator/data/config/urbit/run-urbit-ship.sh new file mode 100755 index 00000000..bb301c81 --- /dev/null +++ b/stack_orchestrator/data/config/urbit/run-urbit-ship.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +pier_dir="/urbit/zod" + +# Run urbit ship in daemon mode +# Check if the directory exists +if [ -d "$pier_dir" ]; then + echo "Pier directory already exists, rebooting..." + urbit -d zod +else + echo "Creating a new fake ship..." + urbit -d -F zod +fi diff --git a/stack_orchestrator/data/container-build/cerc-uniswap-interface/Dockerfile b/stack_orchestrator/data/container-build/cerc-uniswap-interface/Dockerfile new file mode 100644 index 00000000..59804896 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-uniswap-interface/Dockerfile @@ -0,0 +1,10 @@ +FROM node:18.17.1-alpine3.18 + +RUN apk --update --no-cache add git make alpine-sdk bash + +WORKDIR /app + +COPY . . + +RUN echo "Building uniswap-interface" && \ + yarn diff --git a/stack_orchestrator/data/container-build/cerc-uniswap-interface/build.sh b/stack_orchestrator/data/container-build/cerc-uniswap-interface/build.sh new file mode 100755 index 00000000..af1971b5 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-uniswap-interface/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Build the uniswap-interface image +source ${CERC_CONTAINER_BASE_DIR}/build-base.sh + +# See: https://stackoverflow.com/a/246128/1701505 +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +docker build -t cerc/uniswap-interface:local -f ${SCRIPT_DIR}/Dockerfile ${build_command_args} ${CERC_REPO_BASE_DIR}/uniswap-interface diff --git a/stack_orchestrator/data/container-build/cerc-urbit-globs-host/Dockerfile b/stack_orchestrator/data/container-build/cerc-urbit-globs-host/Dockerfile new file mode 100644 index 00000000..7a3ca9b7 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-urbit-globs-host/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.13.0a2-alpine3.18 + +RUN apk --update --no-cache add alpine-sdk jq bash curl wget + +WORKDIR /app + +ENTRYPOINT [ "bash" ] diff --git a/stack_orchestrator/data/container-build/cerc-urbit-globs-host/build.sh b/stack_orchestrator/data/container-build/cerc-urbit-globs-host/build.sh new file mode 100755 index 00000000..ebd396f1 --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-urbit-globs-host/build.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Build the urbit-globs-host image + +source ${CERC_CONTAINER_BASE_DIR}/build-base.sh + +# See: https://stackoverflow.com/a/246128/1701505 +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +docker build -t cerc/urbit-globs-host:local -f ${SCRIPT_DIR}/Dockerfile ${build_command_args} ${SCRIPT_DIR} diff --git a/stack_orchestrator/data/container-image-list.txt b/stack_orchestrator/data/container-image-list.txt index 41dd8b21..fd295be5 100644 --- a/stack_orchestrator/data/container-image-list.txt +++ b/stack_orchestrator/data/container-image-list.txt @@ -59,3 +59,4 @@ cerc/ponder cerc/nitro-rpc-client cerc/watcher-merkl-sushiswap-v3 cerc/watcher-sushiswap-v3 +cerc/uniswap-interface diff --git a/stack_orchestrator/data/repository-list.txt b/stack_orchestrator/data/repository-list.txt index 192a831e..cddaccce 100644 --- a/stack_orchestrator/data/repository-list.txt +++ b/stack_orchestrator/data/repository-list.txt @@ -49,3 +49,4 @@ github.com/cerc-io/mobymask-snap github.com/cerc-io/ponder github.com/cerc-io/merkl-sushiswap-v3-watcher-ts github.com/cerc-io/sushiswap-v3-watcher-ts +github.com/cerc-io/uniswap-interface diff --git a/stack_orchestrator/data/stacks/uniswap-urbit-app/README.md b/stack_orchestrator/data/stacks/uniswap-urbit-app/README.md new file mode 100644 index 00000000..cd6a9f3e --- /dev/null +++ b/stack_orchestrator/data/stacks/uniswap-urbit-app/README.md @@ -0,0 +1,122 @@ +# Self-hosted Uniswap Frontend + +Instructions to setup and deploy Uniswap app on Urbit + +Build and deploy: + +- Urbit +- Uniswap app + +## Setup + +Clone required repositories: + +```bash +laconic-so --stack uniswap-urbit-app setup-repositories --pull + +# If this throws an error as a result of being already checked out to a branch/tag in a repo, remove the repositories mentioned below and re-run the command +``` + +Build the container images: + +```bash +laconic-so --stack uniswap-urbit-app build-containers +``` + +## Create a deployment + +First, create a spec file for the deployment, which will map the stack's ports and volumes to the host: + +```bash +laconic-so --stack uniswap-urbit-app deploy init --output uniswap-urbit-app-spec.yml +``` + +### Ports + +Edit `network` in spec file to map container ports to same ports in host + +``` +... +network: + ports: + urbit-fake-ship: + - '8080:80' + uniswap-glob-host: + - '3000:3000' + uniswap-gql-proxy: + - '4000:4000' +... +``` + +### Data volumes + +Container data volumes are bind-mounted to specified paths in the host filesystem. +The default setup (generated by `laconic-so deploy init`) places the volumes in the `./data` subdirectory of the deployment directory. The default mappings can be customized by editing the "spec" file generated by `laconic-so deploy init`. + +--- + +Once you've made any needed changes to the spec file, create a deployment from it: + +```bash +laconic-so --stack uniswap-urbit-app deploy create --spec-file uniswap-urbit-app-spec.yml --deployment-dir uniswap-urbit-app-deployment +``` + +## Set env variables + +Inside the deployment directory, open the file `config.env` and add variable for infura key : + + ```bash + # External RPC endpoints + # https://docs.infura.io/getting-started#2-create-an-api-key + CERC_INFURA_KEY= + + # Uniswap API GQL Endpoint + # Set this to GQL proxy server endpoint for uniswap app + # (Eg. http://localhost:4000/graphql) + CERC_UNISWAP_GQL= + ``` + +## Start the stack + +Start the deployment: + +```bash +laconic-so deployment --dir uniswap-urbit-app-deployment start +``` + +* List and check the health status of all the containers using `docker ps` and wait for them to be `healthy` + +* Run the following to get login password for Urbit web interface: + + ```bash + laconic-so deployment --dir uniswap-urbit-app-deployment exec urbit-fake-ship "curl -s --data '{\"source\":{\"dojo\":\"+code\"},\"sink\":{\"stdout\":null}}' http://localhost:12321" + + # Expected output: "\n"% + ``` + +* Open the Urbit web UI at http://localhost:8080 and use the `PASSWORD` from previous step to login + +* The uniswap app is not available when starting stack for the first time. Check `urbit-fake-ship` logs to see that app has installed + ``` + laconic-so deployment --dir uniswap-urbit-app-deployment logs -f + + # Expected output: + # laconic-3ccf7ee79bdae874-urbit-fake-ship-1 | docket: fetching %http glob for %uniswap desk + # laconic-3ccf7ee79bdae874-urbit-fake-ship-1 | ">="">="Uniswap app installed + ``` + +* The uniswap app will be now visible at http://localhost:8080 + +## Clean up + +To stop all uniswap-urbit-app services running in the background, while preserving chain data: + +```bash +laconic-so deployment --dir uniswap-urbit-app-deployment stop +``` + +To stop all uniswap-urbit-app services and also delete data: + +```bash +laconic-so deployment --dir uniswap-urbit-app-deployment stop --delete-volumes +``` diff --git a/stack_orchestrator/data/stacks/uniswap-urbit-app/stack.yml b/stack_orchestrator/data/stacks/uniswap-urbit-app/stack.yml new file mode 100644 index 00000000..1077b557 --- /dev/null +++ b/stack_orchestrator/data/stacks/uniswap-urbit-app/stack.yml @@ -0,0 +1,10 @@ +version: "0.1" +name: uniswap-urbit-app +repos: + - github.com/cerc-io/uniswap-interface@laconic # TODO: Use release +containers: + - cerc/uniswap-interface + - cerc/urbit-globs-host +pods: + - uniswap-interface + - uniswap-urbit