diff --git a/.gitignore b/.gitignore index 30ac0b0..e3fc63d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/src/contracts/liqp-deployments.json /.idea/ # dependencies /node_modules diff --git a/bin/build b/bin/build new file mode 100755 index 0000000..2e8f0d7 --- /dev/null +++ b/bin/build @@ -0,0 +1,2 @@ +#!/bin/bash +BUILD=1 bin/deploy "$1" diff --git a/bin/deploy b/bin/deploy new file mode 100755 index 0000000..dd767b4 --- /dev/null +++ b/bin/deploy @@ -0,0 +1,50 @@ +#!/bin/bash + +if [ "$1" = "dev" ]; then + DEV=1 + shift +else + DEV=0 +fi + +CHAIN_ID=${1:-11155111} # Defaults to Sepolia +PROJECT=liquidity-party +REMOTE=git.dxod.org/dexorder/dexorder + +if [ "$BUILD" != "1" ] && [ "$CHAIN_ID" = "31337" ]; then + echo bin/deploy must specify a production chain ID + exit 1 +fi + +if [ "$BUILD" != "1" ]; then + if [ "$DEV" = "1" ]; then + TAG="dev$(date +%Y%m%d%H%M%S)" + else + DIRTY="$( git status | grep "Changes " )" + if [ "$DIRTY" != "" ]; then + echo This project has uncommited changes: deployment aborted. + exit 1 + fi + TAG="$( git log --oneline | head -1 | cut -d ' ' -f 1 )" + fi + echo Deploying $TAG +else + echo Building +fi + +bin/generate-contracts "$CHAIN_ID" || exit 1 +npm run build || exit 1 + +if [ "$BUILD" != "1" ]; then + echo Deploying with tag $TAG + docker build --no-cache -f deploy/Dockerfile -t dexorder/$PROJECT:latest . || exit 1 + docker tag dexorder/$PROJECT:latest dexorder/$PROJECT:$TAG + docker tag dexorder/$PROJECT:$TAG $REMOTE/$PROJECT:$TAG + docker tag $REMOTE/$PROJECT:$TAG $REMOTE/$PROJECT:latest + docker push $REMOTE/$PROJECT:$TAG + YAML=$(sed "s#image: dexorder/$PROJECT*#image: $REMOTE/$PROJECT:$TAG#" deploy/liquidity-party.k8s.yaml) + echo "$YAML" | kubectl apply -f - || echo "$YAML" "\nkubectl apply failed" && exit 1 + echo "$(date)" deployed $REMOTE/$PROJECT:$TAG +else + echo "$(date)" built +fi diff --git a/bin/generate-abi b/bin/generate-abi deleted file mode 100755 index 6522a6b..0000000 --- a/bin/generate-abi +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -generate() { - ABI=$(jq '.abi' ../lmsr-amm/out/$1.sol/$1.json) -# echo "import {useReadContract} from \"wagmi\";\n\nconst {data} = useReadContract({abi: ${ABI}});\n\nexport default data;" > ./src/contracts/$1.ts - echo "/* GENERATED FILE: DO NOT EDIT! */\n\nconst ${1}ABI = ${ABI} as const;\n\nexport default ${1}ABI;" > ./src/contracts/$1ABI.ts -} - -generate IPartyPlanner -generate IPartyPool -generate IPartyPoolViewer diff --git a/bin/generate-contracts b/bin/generate-contracts new file mode 100755 index 0000000..b7801c3 --- /dev/null +++ b/bin/generate-contracts @@ -0,0 +1,31 @@ +#!/bin/sh + +CHAIN_ID=${1:-31337} + +if [ "$CHAIN_ID" = "31337" ]; then + ABI_PATH=../lmsr-amm/out + METADATA_PATH=../lmsr-amm/liqp-deployments.json +else + ABI_PATH=../lmsr-amm/deployment/$1/v1/out + METADATA_PATH=../lmsr-amm/deployment/liqp-deployments.json +fi + +if [ ! -f "$ABI_PATH/IPartyPool.sol/IPartyPool.json" ]; then + echo "Invalid chain ID $CHAIN_ID" + exit 1 +fi + +echo Chain $CHAIN_ID + +generate() { + ABI=$(jq '.abi' $ABI_PATH/$1.sol/$1.json) + echo "/* GENERATED FILE: DO NOT EDIT! */\n\nconst ${1}ABI = ${ABI} as const;\n\nexport default ${1}ABI;" > ./src/contracts/$1ABI.ts + echo "src/contracts/$1ABI.ts" +} + +generate IPartyPlanner +generate IPartyPool +generate IPartyPoolViewer + +cp "$METADATA_PATH" src/contracts/ +echo liqp-deployments.json diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..bbefe45 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,10 @@ +FROM nginx:stable-alpine + +RUN apk update && apk upgrade + +WORKDIR /liquidity.party/web +COPY out ./out +RUN sed -i '1idaemon off;' /etc/nginx/nginx.conf +COPY deploy/nginx.conf /etc/nginx/conf.d/default.conf + +CMD ["nginx"] diff --git a/deploy/liquidity-party.k8s.yaml b/deploy/liquidity-party.k8s.yaml new file mode 100644 index 0000000..6c0a041 --- /dev/null +++ b/deploy/liquidity-party.k8s.yaml @@ -0,0 +1,103 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: liquidity-party + labels: + app: liquidity-party +spec: + replicas: 1 + selector: + matchLabels: + app: liquidity-party + template: + metadata: + labels: + app: liquidity-party + annotations: + prometheus.io/scrape: "false" + spec: + containers: + - name: liquidity-party + image: dexorder/liquidity-party + ports: + - name: www + containerPort: 80 + protocol: TCP + resources: + requests: + cpu: 10m + memory: 100M + + +--- +apiVersion: v1 +kind: Service +metadata: + name: liquidity-party + labels: + app: liquidity-party +spec: + selector: + app: liquidity-party + ports: + - name: liquidity-party + port: 80 + targetPort: 80 + protocol: TCP + + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: liquidity-party + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/ssl-redirect: "true" # Redirects HTTP to HTTPS +spec: + ingressClassName: nginx + tls: + - secretName: liquidity-party-tls + hosts: + - liquidity.party + rules: + - host: liquidity.party + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: liquidity-party + port: + number: 80 + + +# www redirects to apex domain +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: www-liquidity-party + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/ssl-redirect: "true" # Redirects HTTP to HTTPS + nginx.ingress.kubernetes.io/permanent-redirect: "https://liquidity.party/" +spec: + ingressClassName: nginx + tls: + - secretName: www-liquidity-party-tls + hosts: + - www.liquidity.party + rules: + - host: www.liquidity.party + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: liquidity-party + port: + number: 80 diff --git a/deploy/nginx.conf b/deploy/nginx.conf new file mode 100644 index 0000000..e13ba65 --- /dev/null +++ b/deploy/nginx.conf @@ -0,0 +1,23 @@ +server { + listen 80 default_server; + + root /liquidity.party/web/out; + gzip on; + gzip_min_length 1000; + gzip_types text/plain text/xml application/javascript text/css; + + location / { + try_files $uri $uri.html $uri/ /index.html; + } + + # This is necessary when `trailingSlash: false`. + # You can omit this when `trailingSlash: true`. + location /blog/ { + rewrite ^/blog/(.*)$ /blog/$1.html break; + } + + error_page 404 /404.html; + location = /404.html { + internal; + } +} diff --git a/src/app/liquidity-party.json b/src/app/liquidity-party.json deleted file mode 100644 index 4f57d58..0000000 --- a/src/app/liquidity-party.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "31337": { - "IPartyPlanner": "0xFc18426b71EDa3dC001dcc36ADC9C67bC6f38747", - "IPartyPoolViewer": "0xd85BdcdaE4db1FAEB8eF93331525FE68D7C8B3f0" - } -} \ No newline at end of file diff --git a/src/components/providers.tsx b/src/components/providers.tsx index 142b62e..f2f7938 100644 --- a/src/components/providers.tsx +++ b/src/components/providers.tsx @@ -2,7 +2,7 @@ import { getDefaultConfig, RainbowKitProvider, darkTheme, lightTheme } from '@rainbow-me/rainbowkit'; import { WagmiProvider } from 'wagmi'; -import { mainnet, polygon, optimism, arbitrum, base } from 'wagmi/chains'; +import { mainnet, polygon, optimism, arbitrum, base, sepolia } from 'wagmi/chains'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; import { ThemeProvider, useTheme } from 'next-themes'; import { useEffect, useState } from 'react'; @@ -12,28 +12,35 @@ import { TranslationsProvider } from '@/providers/translations-provider'; import { Header } from '@/components/header'; import { ToastProvider } from '@/components/ui/toast'; +import deployments from '@/contracts/liqp-deployments.json'; + const mockchain = defineChain({ id: 31337, name: 'Mockchain', nativeCurrency: { decimals: 18, - name: 'Ether', - symbol: 'ETH', + name: 'Test Ether', + symbol: 'TETH', }, rpcUrls: { default: { http: ['http://localhost:8545'] }, }, - blockExplorers: { - default: { name: 'Explorer', url: 'http://localhost:8545' }, - }, testnet: true, }); +const allChains = [mainnet, polygon, optimism, arbitrum, base, sepolia, mockchain]; +const chains = allChains.filter(chain => + Object.keys(deployments || {}).includes(chain.id.toString()) +); +if (chains.length === 0) { + console.error('No chains found with deployed contracts.'); +} + const config = getDefaultConfig({ - appName: 'Liquidity Party', - projectId: 'YOUR_PROJECT_ID', // Get this from https://cloud.walletconnect.com - chains: [mainnet, polygon, optimism, arbitrum, base, mockchain], - ssr: false, + appName: 'Liquidity Party', + projectId: 'YOUR_PROJECT_ID', // Get this from https://cloud.walletconnect.com + chains: chains as any, + ssr: false, }); const queryClient = new QueryClient({ diff --git a/src/contracts/IPartyPlannerABI.ts b/src/contracts/IPartyPlannerABI.ts index 0d33dc2..bc64b3b 100644 --- a/src/contracts/IPartyPlannerABI.ts +++ b/src/contracts/IPartyPlannerABI.ts @@ -115,42 +115,42 @@ const IPartyPlannerABI = [ "name": "newPool", "inputs": [ { - "name": "name_", + "name": "name", "type": "string", "internalType": "string" }, { - "name": "symbol_", + "name": "symbol", "type": "string", "internalType": "string" }, { - "name": "_tokens", + "name": "tokens", "type": "address[]", "internalType": "contract IERC20[]" }, { - "name": "_bases", + "name": "bases", "type": "uint256[]", "internalType": "uint256[]" }, { - "name": "_kappa", + "name": "kappa", "type": "int128", "internalType": "int128" }, { - "name": "_swapFeePpm", + "name": "swapFeePpm", "type": "uint256", "internalType": "uint256" }, { - "name": "_flashFeePpm", + "name": "flashFeePpm", "type": "uint256", "internalType": "uint256" }, { - "name": "_stable", + "name": "stable", "type": "bool", "internalType": "bool" }, @@ -199,47 +199,47 @@ const IPartyPlannerABI = [ "name": "newPool", "inputs": [ { - "name": "name_", + "name": "name", "type": "string", "internalType": "string" }, { - "name": "symbol_", + "name": "symbol", "type": "string", "internalType": "string" }, { - "name": "_tokens", + "name": "tokens", "type": "address[]", "internalType": "contract IERC20[]" }, { - "name": "_bases", + "name": "bases", "type": "uint256[]", "internalType": "uint256[]" }, { - "name": "_tradeFrac", + "name": "tradeFrac", "type": "int128", "internalType": "int128" }, { - "name": "_targetSlippage", + "name": "targetSlippage", "type": "int128", "internalType": "int128" }, { - "name": "_swapFeePpm", + "name": "swapFeePpm", "type": "uint256", "internalType": "uint256" }, { - "name": "_flashFeePpm", + "name": "flashFeePpm", "type": "uint256", "internalType": "uint256" }, { - "name": "_stable", + "name": "stable", "type": "bool", "internalType": "bool" }, @@ -283,6 +283,19 @@ const IPartyPlannerABI = [ ], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "poolCount", @@ -315,6 +328,13 @@ const IPartyPlannerABI = [ ], "stateMutability": "view" }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "swapImpl", @@ -341,6 +361,38 @@ const IPartyPlannerABI = [ ], "stateMutability": "view" }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, { "type": "event", "name": "PartyStarted", @@ -371,6 +423,28 @@ const IPartyPlannerABI = [ } ], "anonymous": false + }, + { + "type": "error", + "name": "OwnableInvalidOwner", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "OwnableUnauthorizedAccount", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ] } ] as const; diff --git a/src/contracts/IPartyPoolABI.ts b/src/contracts/IPartyPoolABI.ts index fe19721..20b5805 100644 --- a/src/contracts/IPartyPoolABI.ts +++ b/src/contracts/IPartyPoolABI.ts @@ -343,6 +343,26 @@ const IPartyPoolABI = [ ], "stateMutability": "view" }, + { + "type": "function", + "name": "kill", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "killed", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "mint", @@ -403,6 +423,19 @@ const IPartyPoolABI = [ ], "stateMutability": "view" }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "protocolFeeAddress", @@ -429,6 +462,13 @@ const IPartyPoolABI = [ ], "stateMutability": "view" }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "swap", @@ -727,6 +767,19 @@ const IPartyPoolABI = [ ], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "wrapperToken", @@ -735,7 +788,7 @@ const IPartyPoolABI = [ { "name": "", "type": "address", - "internalType": "contract IWETH9" + "internalType": "contract NativeWrapper" } ], "stateMutability": "view" @@ -813,13 +866,31 @@ const IPartyPoolABI = [ "internalType": "address" }, { - "name": "targetTokenIndex", - "type": "uint256", + "name": "tokenOut", + "type": "address", "indexed": true, + "internalType": "contract IERC20" + }, + { + "name": "amountIn", + "type": "uint256", + "indexed": false, "internalType": "uint256" }, { - "name": "payoutUint", + "name": "amountOut", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "lpFee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "protocolFee", "type": "uint256", "indexed": false, "internalType": "uint256" @@ -827,6 +898,55 @@ const IPartyPoolABI = [ ], "anonymous": false }, + { + "type": "event", + "name": "Flash", + "inputs": [ + { + "name": "initiator", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "contract IERC3156FlashBorrower" + }, + { + "name": "token", + "type": "address", + "indexed": true, + "internalType": "contract IERC20" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "lpFee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "protocolFee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Killed", + "inputs": [], + "anonymous": false + }, { "type": "event", "name": "Mint", @@ -858,6 +978,31 @@ const IPartyPoolABI = [ ], "anonymous": false }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ProtocolFeesCollected", + "inputs": [], + "anonymous": false + }, { "type": "event", "name": "Swap", @@ -897,6 +1042,18 @@ const IPartyPoolABI = [ "type": "uint256", "indexed": false, "internalType": "uint256" + }, + { + "name": "lpFee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "protocolFee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" } ], "anonymous": false @@ -918,25 +1075,31 @@ const IPartyPoolABI = [ "internalType": "address" }, { - "name": "inputTokenIndex", - "type": "uint256", + "name": "tokenIn", + "type": "address", "indexed": true, - "internalType": "uint256" + "internalType": "contract IERC20" }, { - "name": "grossTransfer", + "name": "amountIn", "type": "uint256", "indexed": false, "internalType": "uint256" }, { - "name": "netInput", + "name": "amountOut", "type": "uint256", "indexed": false, "internalType": "uint256" }, { - "name": "feeTaken", + "name": "lpFee", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "protocolFee", "type": "uint256", "indexed": false, "internalType": "uint256" @@ -968,6 +1131,28 @@ const IPartyPoolABI = [ } ], "anonymous": false + }, + { + "type": "error", + "name": "OwnableInvalidOwner", + "inputs": [ + { + "name": "owner", + "type": "address", + "internalType": "address" + } + ] + }, + { + "type": "error", + "name": "OwnableUnauthorizedAccount", + "inputs": [ + { + "name": "account", + "type": "address", + "internalType": "address" + } + ] } ] as const; diff --git a/src/hooks/usePartyPlanner.ts b/src/hooks/usePartyPlanner.ts index 158120b..d9af328 100644 --- a/src/hooks/usePartyPlanner.ts +++ b/src/hooks/usePartyPlanner.ts @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { usePublicClient } from 'wagmi'; -import chainInfo from '../../../lmsr-amm/liqp-deployments.json'; +import chainInfo from '@/contracts/liqp-deployments.json'; import IPartyPlannerABI from '@/contracts/IPartyPlannerABI'; import IPartyPoolABI from '@/contracts/IPartyPoolABI'; import { ERC20ABI } from '@/contracts/ERC20ABI';