Compare commits
33 Commits
107d2ae5c0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d207ef6dca | |||
| 0e5921255e | |||
| 1ac26aeec0 | |||
| a2a036818d | |||
|
|
1c2e267136 | ||
|
|
b2abf2073e | ||
| 9835d67e54 | |||
| b0b050f4be | |||
| f43db3391b | |||
| 334e9f4f53 | |||
|
|
9d2ee39d1a | ||
| d4e41821a6 | |||
| d07ff55c13 | |||
| 4523195b78 | |||
| 068286919a | |||
| 7b8114267e | |||
| f3277b45ab | |||
| fe02d94d11 | |||
| ff9a718522 | |||
| e36f6011c9 | |||
| 99929d8db8 | |||
| 85a7b58d55 | |||
|
|
882e271040 | ||
|
|
a3053d1f7b | ||
| c8c23a4f54 | |||
| 8cc9d00521 | |||
|
|
519a22847b | ||
| aeb90f2e0f | |||
| d47f3d566a | |||
| 4214f94ab8 | |||
| ea44e0d941 | |||
|
|
5b450ab303 | ||
|
|
2585873d8a |
11
.env-secret
11
.env-secret
@@ -1,11 +0,0 @@
|
||||
# Secret environment variables - DO NOT COMMIT TO GIT
|
||||
# Add this file to .gitignore
|
||||
|
||||
# RPC Node Connection
|
||||
MAINNET_RPC_URL=https://eth-1.dxod.org/joEnzz51UH6Bc2yU
|
||||
ALCHEMY_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/o_eQWfo1Rb7qZKpl_vBRL
|
||||
|
||||
|
||||
# Receiver Address
|
||||
RECEIVER_ADDRESS=0xd3b310bd32d782f89eea49cb79656bcaccde7213
|
||||
PRIVATE_KEY=89c8f2542b5ff7f3cf0b73255e0a8d79d89c2be598e7f272a275a380ff56a212
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,6 +5,9 @@
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
*secret*
|
||||
.env
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
|
||||
4
EthereumTransactionCall.sh
Normal file
4
EthereumTransactionCall.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
cast call 0x8f98B899F4135408Fe03228cE942Ad6BF8E40f22 \
|
||||
"working(address)" \
|
||||
0x3EDE1eE859A72aEc85ff04d305B6Ffe89f2Cb4eb \
|
||||
--rpc-url https://eth-sepolia.g.alchemy.com/v2/demo
|
||||
@@ -7,15 +7,9 @@ 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)"
|
||||
@@ -32,12 +26,11 @@ 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 buildx build --platform linux/amd64 --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
|
||||
|
||||
80
package-lock.json
generated
80
package-lock.json
generated
@@ -17,7 +17,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"i18next": "^23.15.0",
|
||||
"lucide-react": "^0.460.0",
|
||||
"next": "^15.1.3",
|
||||
"next": "15.5.7",
|
||||
"next-themes": "^0.4.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
@@ -1405,15 +1405,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.4.tgz",
|
||||
"integrity": "sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.7.tgz",
|
||||
"integrity": "sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.4.tgz",
|
||||
"integrity": "sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz",
|
||||
"integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1427,9 +1427,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.4.tgz",
|
||||
"integrity": "sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz",
|
||||
"integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1443,9 +1443,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.4.tgz",
|
||||
"integrity": "sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz",
|
||||
"integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1459,9 +1459,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.4.tgz",
|
||||
"integrity": "sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz",
|
||||
"integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1475,9 +1475,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.4.tgz",
|
||||
"integrity": "sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz",
|
||||
"integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1491,9 +1491,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.4.tgz",
|
||||
"integrity": "sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz",
|
||||
"integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1507,9 +1507,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.4.tgz",
|
||||
"integrity": "sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz",
|
||||
"integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1523,9 +1523,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.4.tgz",
|
||||
"integrity": "sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz",
|
||||
"integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -6179,12 +6179,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "15.5.4",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.4.tgz",
|
||||
"integrity": "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==",
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.7.tgz",
|
||||
"integrity": "sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/env": "15.5.4",
|
||||
"@next/env": "15.5.7",
|
||||
"@swc/helpers": "0.5.15",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
"postcss": "8.4.31",
|
||||
@@ -6197,14 +6197,14 @@
|
||||
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "15.5.4",
|
||||
"@next/swc-darwin-x64": "15.5.4",
|
||||
"@next/swc-linux-arm64-gnu": "15.5.4",
|
||||
"@next/swc-linux-arm64-musl": "15.5.4",
|
||||
"@next/swc-linux-x64-gnu": "15.5.4",
|
||||
"@next/swc-linux-x64-musl": "15.5.4",
|
||||
"@next/swc-win32-arm64-msvc": "15.5.4",
|
||||
"@next/swc-win32-x64-msvc": "15.5.4",
|
||||
"@next/swc-darwin-arm64": "15.5.7",
|
||||
"@next/swc-darwin-x64": "15.5.7",
|
||||
"@next/swc-linux-arm64-gnu": "15.5.7",
|
||||
"@next/swc-linux-arm64-musl": "15.5.7",
|
||||
"@next/swc-linux-x64-gnu": "15.5.7",
|
||||
"@next/swc-linux-x64-musl": "15.5.7",
|
||||
"@next/swc-win32-arm64-msvc": "15.5.7",
|
||||
"@next/swc-win32-x64-msvc": "15.5.7",
|
||||
"sharp": "^0.34.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"i18next": "^23.15.0",
|
||||
"lucide-react": "^0.460.0",
|
||||
"next": "^15.1.3",
|
||||
"next": "15.5.7",
|
||||
"next-themes": "^0.4.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { config } from 'dotenv';
|
||||
import IPartyPlannerABI from "../src/contracts/IPartyPlannerABI.ts";
|
||||
|
||||
// Load environment variables from .env-secret
|
||||
config({ path: new URL('../.env-secret', import.meta.url).pathname });
|
||||
@@ -16,89 +15,137 @@ config({ path: new URL('../.env-secret', import.meta.url).pathname });
|
||||
// CONFIGURATION
|
||||
// ============================================================================
|
||||
|
||||
const RPC_URL = process.env.MAINNET_RPC_URL;
|
||||
const PRIVATE_KEY = process.env.PRIVATE_KEY;
|
||||
const RECEIVER_ADDRESS = process.env.RECEIVER_ADDRESS;
|
||||
// Network flag: 'mockchain' or 'mainnet'
|
||||
const NETWORK = process.env.NETWORK || 'mainnet';
|
||||
|
||||
if (!RPC_URL || !PRIVATE_KEY || !RECEIVER_ADDRESS) {
|
||||
console.error('[!] Missing required environment variables in .env-secret file');
|
||||
console.error(' Required: MAINNET_RPC_URL, PRIVATE_KEY, RECEIVER_ADDRESS');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Curated token list with real addresses (Ethereum Mainnet)
|
||||
const TEST_TOKENS = {
|
||||
USDT: {
|
||||
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // Mainnet USDT
|
||||
coingeckoId: 'tether',
|
||||
decimals: 6,
|
||||
feePpm: 400 // 0.00004 = 0.004% = 40 ppm
|
||||
// Network-specific configuration
|
||||
const NETWORK_CONFIG = {
|
||||
mockchain: {
|
||||
rpcUrl: 'http://localhost:8545',
|
||||
privateKey: '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a', // Dev wallet #4 (sender)
|
||||
receiverPrivateKey: '0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356', // Receiver wallet
|
||||
receiverAddress: '0x14dC79964da2C08b23698B3D3cc7Ca32193d9955', // Receiver public key
|
||||
chainId: '31337',
|
||||
tokens: {
|
||||
USDT: {
|
||||
address: '0xbdEd0D2bf404bdcBa897a74E6657f1f12e5C6fb6', // USXD on mockchain
|
||||
coingeckoId: 'tether',
|
||||
decimals: 6,
|
||||
feePpm: 400
|
||||
},
|
||||
USDC: {
|
||||
address: '0x2910E325cf29dd912E3476B61ef12F49cb931096', // FUSD on mockchain
|
||||
coingeckoId: 'usd-coin',
|
||||
decimals: 6,
|
||||
feePpm: 400
|
||||
}
|
||||
}
|
||||
},
|
||||
USDC: {
|
||||
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // Mainnet USDC
|
||||
coingeckoId: 'usd-coin',
|
||||
decimals: 6,
|
||||
feePpm: 400 // 0.00004 = 0.004% = 40 ppm
|
||||
},
|
||||
WBTC: {
|
||||
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', // Mainnet WBTC
|
||||
coingeckoId: 'wrapped-bitcoin',
|
||||
decimals: 8,
|
||||
feePpm: 3000 // 0.00030 = 0.03% = 300 ppm
|
||||
},
|
||||
WETH: {
|
||||
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // Mainnet WETH
|
||||
coingeckoId: 'weth',
|
||||
decimals: 18,
|
||||
feePpm: 3500 // 0.00035 = 0.035% = 350 ppm
|
||||
},
|
||||
BNB: {
|
||||
address: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', // Mainnet BNB (Binance-Peg)
|
||||
coingeckoId: 'binancecoin',
|
||||
decimals: 18,
|
||||
feePpm: 4500 // 0.00045 = 0.045% = 450 ppm
|
||||
},
|
||||
WSOL: {
|
||||
address: '0xD31a59c85aE9D8edEFeC411D448f90841571b89c', // Mainnet Wormhole Wrapped SOL
|
||||
coingeckoId: 'solana',
|
||||
decimals: 9,
|
||||
feePpm: 9500 // 0.00095 = 0.095% = 950 ppm
|
||||
},
|
||||
TRX: {
|
||||
address: '0x50327c6c5a14DCaDE707ABad2E27eB517df87AB5', // Mainnet TRX (Wrapped)
|
||||
coingeckoId: 'tron',
|
||||
decimals: 6,
|
||||
feePpm: 9500 // 0.00095 = 0.095% = 950 ppm
|
||||
},
|
||||
AAVE: {
|
||||
address: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', // Mainnet AAVE
|
||||
coingeckoId: 'aave',
|
||||
decimals: 18,
|
||||
feePpm: 14500 // 0.00145 = 0.145% = 1450 ppm
|
||||
},
|
||||
PEPE: {
|
||||
address: '0x6982508145454Ce325dDbE47a25d4ec3d2311933', // Mainnet PEPE
|
||||
coingeckoId: 'pepe',
|
||||
decimals: 18,
|
||||
feePpm: 21500 // 0.00215 = 0.215% = 2150 ppm
|
||||
},
|
||||
SHIB: {
|
||||
address: '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE', // Mainnet SHIB
|
||||
coingeckoId: 'shiba-inu',
|
||||
decimals: 18,
|
||||
feePpm: 21500 // 0.00215 = 0.215% = 2150 ppm
|
||||
mainnet: {
|
||||
rpcUrl: process.env.MAINNET_RPC_URL,
|
||||
privateKey: process.env.PRIVATE_KEY,
|
||||
receiverAddress: '0xd3b310bd32d782f89eea49cb79656bcaccde7213', // Same as payer for mainnet
|
||||
chainId: '1',
|
||||
tokens: {
|
||||
USDT: {
|
||||
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
||||
coingeckoId: 'tether',
|
||||
decimals: 6,
|
||||
feePpm: 40 // 0.0004%
|
||||
},
|
||||
USDC: {
|
||||
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
||||
coingeckoId: 'usd-coin',
|
||||
decimals: 6,
|
||||
feePpm: 40 // 0.0004%
|
||||
},
|
||||
WBTC: {
|
||||
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
|
||||
coingeckoId: 'wrapped-bitcoin',
|
||||
decimals: 8,
|
||||
feePpm: 300 // 0.00030%
|
||||
},
|
||||
WETH: {
|
||||
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
||||
coingeckoId: 'weth',
|
||||
decimals: 18,
|
||||
feePpm: 350 // 0.0035%
|
||||
},
|
||||
UNI: {
|
||||
address: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984',
|
||||
coingeckoId: 'uniswap',
|
||||
decimals: 18,
|
||||
feePpm: 1450 // 0.00145%
|
||||
},
|
||||
WSOL: {
|
||||
address: '0xD31a59c85aE9D8edEFeC411D448f90841571b89c', // Wormhole Wrapped SOL
|
||||
coingeckoId: 'solana',
|
||||
decimals: 9,
|
||||
feePpm: 950 // 0.00095%
|
||||
},
|
||||
TRX: {
|
||||
address: '0x50327c6c5a14DCaDE707ABad2E27eB517df87AB5',
|
||||
coingeckoId: 'tron',
|
||||
decimals: 6,
|
||||
feePpm: 950 // 0.00095%
|
||||
},
|
||||
AAVE: {
|
||||
address: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9',
|
||||
coingeckoId: 'aave',
|
||||
decimals: 18,
|
||||
feePpm: 1450 // 0.00145%
|
||||
},
|
||||
PEPE: {
|
||||
address: '0x6982508145454Ce325dDbE47a25d4ec3d2311933',
|
||||
coingeckoId: 'pepe',
|
||||
decimals: 18,
|
||||
feePpm: 2150 // 0.00215%
|
||||
},
|
||||
SHIB: {
|
||||
address: '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE',
|
||||
coingeckoId: 'shiba-inu',
|
||||
decimals: 18,
|
||||
feePpm: 2150 // 0.00215%
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Get current network config
|
||||
const currentConfig = NETWORK_CONFIG[NETWORK];
|
||||
if (!currentConfig) {
|
||||
console.error(`[!] Invalid NETWORK: ${NETWORK}. Must be 'mockchain' or 'mainnet'`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const RPC_URL = currentConfig.rpcUrl;
|
||||
const PRIVATE_KEY = currentConfig.privateKey;
|
||||
const RECEIVER_ADDRESS = currentConfig.receiverAddress || process.env.RECEIVER_ADDRESS;
|
||||
|
||||
// Validate required config for mainnet
|
||||
if (NETWORK === 'mainnet' && (!RPC_URL || !PRIVATE_KEY)) {
|
||||
console.error('[!] Missing required environment variables for mainnet');
|
||||
console.error(' Required: MAINNET_RPC_URL, PRIVATE_KEY');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!RECEIVER_ADDRESS) {
|
||||
console.error('[!] Missing RECEIVER_ADDRESS in .env-secret file');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Use network-specific tokens
|
||||
const TEST_TOKENS = currentConfig.tokens;
|
||||
|
||||
// Default pool parameters
|
||||
const DEFAULT_POOL_PARAMS = {
|
||||
name: 'Staging LP Pool',
|
||||
symbol: 'SLP',
|
||||
kappa: ethers.BigNumber.from('10000000000000000'), // 0.01 * 1e18
|
||||
name: 'Original Genesis of Liquidity Party',
|
||||
symbol: 'OG.LP',
|
||||
kappa: ethers.BigNumber.from('184467440737095520'), //0.01 * 2^64
|
||||
swapFeesPpm: Object.values(TEST_TOKENS).map(t => t.feePpm),
|
||||
flashFeePpm: 5, // 0.0005%
|
||||
stable: false,
|
||||
initialLpAmount: ethers.utils.parseUnits('100', 18) // 100 USD in 18 decimals
|
||||
initialLpAmount: ethers.utils.parseUnits('1', 18) // 100 USD in 18 decimals
|
||||
};
|
||||
|
||||
// Input amount in USD
|
||||
@@ -110,7 +157,7 @@ const INPUT_USD_AMOUNT = 1;
|
||||
// ============================================================================
|
||||
|
||||
const chainInfoData = JSON.parse(await readFile(new URL('../src/contracts/liqp-deployments.json', import.meta.url), 'utf-8'));
|
||||
const PARTY_PLANNER_ADDRESS = chainInfoData['1'].v1.PartyPlanner;
|
||||
const PARTY_PLANNER_ADDRESS = chainInfoData[currentConfig.chainId].v1.PartyPlanner;
|
||||
|
||||
const ERC20ABI = [
|
||||
{ "type": "function", "name": "balanceOf", "stateMutability": "view", "inputs": [{ "name": "account", "type": "address" }], "outputs": [{ "name": "", "type": "uint256" }] },
|
||||
@@ -189,15 +236,15 @@ function calculateTokenAmounts(prices, usdAmount) {
|
||||
/**
|
||||
* Check token balances
|
||||
*/
|
||||
async function checkBalances(provider, wallet, tokenAmounts) {
|
||||
console.log(`\n[~] Checking token balances for wallet: ${wallet.address}`);
|
||||
async function checkBalances(provider, wallet, tokenAmounts, receiverAddress) {
|
||||
console.log(`\n[~] Checking token balances for receiver wallet: ${receiverAddress}`);
|
||||
|
||||
const balances = {};
|
||||
let hasEnoughBalance = true;
|
||||
|
||||
for (const [symbol, tokenInfo] of Object.entries(TEST_TOKENS)) {
|
||||
const tokenContract = new ethers.Contract(tokenInfo.address, ERC20ABI, provider);
|
||||
const balance = await tokenContract.balanceOf(wallet.address);
|
||||
const balance = await tokenContract.balanceOf(receiverAddress);
|
||||
const requiredAmount = tokenAmounts[symbol];
|
||||
|
||||
balances[symbol] = balance;
|
||||
@@ -214,7 +261,7 @@ async function checkBalances(provider, wallet, tokenAmounts) {
|
||||
}
|
||||
|
||||
if (!hasEnoughBalance) {
|
||||
console.log(`\n[!] Insufficient token balance. Please ensure your wallet has enough tokens.`);
|
||||
console.log(`\n[!] Insufficient token balance. Please ensure receiver wallet has enough tokens.`);
|
||||
throw new Error('Insufficient token balance');
|
||||
}
|
||||
|
||||
@@ -225,22 +272,26 @@ async function checkBalances(provider, wallet, tokenAmounts) {
|
||||
/**
|
||||
* Approve tokens for the PartyPlanner contract
|
||||
*/
|
||||
async function approveTokens(wallet, tokenAmounts) {
|
||||
async function approveTokens(provider, tokenAmounts, receiverPrivateKey) {
|
||||
console.log(`\n[~] Approving tokens for PartyPlanner contract...`);
|
||||
|
||||
// Connect with receiver wallet for approvals
|
||||
const receiverWallet = new ethers.Wallet(receiverPrivateKey, provider);
|
||||
console.log(`[~] Using receiver wallet for approvals: ${receiverWallet.address}`);
|
||||
|
||||
for (const [symbol, tokenInfo] of Object.entries(TEST_TOKENS)) {
|
||||
const tokenContract = new ethers.Contract(tokenInfo.address, ERC20ABI, wallet);
|
||||
const tokenContract = new ethers.Contract(tokenInfo.address, ERC20ABI, receiverWallet);
|
||||
|
||||
// Approve 1% more than needed to account for fees/slippage
|
||||
const requiredAmount = tokenAmounts[symbol];
|
||||
const approvalAmount = requiredAmount.mul(101).div(100); // 1% buffer
|
||||
|
||||
console.log(` [~] Approving ${symbol} (1% buffer)...`);
|
||||
console.log(` [~] Approving ${symbol} ${tokenInfo.address} ${approvalAmount} (1% buffer)...`);
|
||||
|
||||
try {
|
||||
// USDT and some tokens require setting allowance to 0 before setting a new value
|
||||
// Skip for BNB as it has a broken approve function
|
||||
if (symbol !== 'BNB') {
|
||||
if (symbol == 'USDT') {
|
||||
const resetTx = await tokenContract.approve(PARTY_PLANNER_ADDRESS, 0);
|
||||
await resetTx.wait();
|
||||
console.log(` [+] ${symbol} allowance reset to 0`);
|
||||
@@ -268,16 +319,16 @@ async function createPool(wallet, tokenAmounts) {
|
||||
const tokenAddresses = Object.values(TEST_TOKENS).map(t => t.address);
|
||||
const initialDeposits = Object.keys(TEST_TOKENS).map(symbol => tokenAmounts[symbol].toString());
|
||||
|
||||
// Set deadline to 1 hour from now
|
||||
const deadline = Math.floor(Date.now() / 1000) + 3600;
|
||||
// Set deadline to 5 minutes from now
|
||||
const deadline = Math.floor(Date.now() / 1000) + 300;
|
||||
|
||||
console.log(`[~] Pool parameters:`);
|
||||
console.log(` Name: ${DEFAULT_POOL_PARAMS.name}`);
|
||||
console.log(` Symbol: ${DEFAULT_POOL_PARAMS.symbol}`);
|
||||
console.log(` Tokens: ${tokenAddresses.join(', ')}`);
|
||||
console.log(` Swap Fees PPM: [${DEFAULT_POOL_PARAMS.swapFeesPpm.join(', ')}]`);
|
||||
console.log(` Payer: ${wallet.address}`);
|
||||
console.log(` Receiver: ${RECEIVER_ADDRESS}`);
|
||||
console.log(` Payer (provides tokens): ${RECEIVER_ADDRESS}`);
|
||||
console.log(` Receiver (gets LP tokens): ${wallet.address}`);
|
||||
console.log(` Deadline: ${new Date(deadline * 1000).toISOString()}`);
|
||||
|
||||
// Build cast send command
|
||||
@@ -290,15 +341,14 @@ async function createPool(wallet, tokenAmounts) {
|
||||
"[${DEFAULT_POOL_PARAMS.swapFeesPpm.join(',')}]" \
|
||||
${DEFAULT_POOL_PARAMS.flashFeePpm} \
|
||||
${DEFAULT_POOL_PARAMS.stable} \
|
||||
${wallet.address} \
|
||||
${RECEIVER_ADDRESS} \
|
||||
${wallet.address} \
|
||||
"[${initialDeposits.join(',')}]" \
|
||||
${DEFAULT_POOL_PARAMS.initialLpAmount.toString()} \
|
||||
${deadline} \
|
||||
--rpc-url http://localhost:8545 \
|
||||
--rpc-url '${RPC_URL}' \
|
||||
--from 0x12db90820dafed100e40e21128e40dcd4ff6b331 \
|
||||
--trezor --mnemonic-index 0`;
|
||||
// --private-key ${PRIVATE_KEY};
|
||||
--trezor --mnemonic-index 0`
|
||||
|
||||
console.log(`\n[~] Cast command:\n${castCommand}\n`);
|
||||
|
||||
@@ -346,6 +396,7 @@ Example:
|
||||
async function main() {
|
||||
console.log(`${'='.repeat(70)}`);
|
||||
console.log(`Create Pool from Real-Time Prices`);
|
||||
console.log(`Network: ${NETWORK}`);
|
||||
console.log(`${'='.repeat(70)}\n`);
|
||||
|
||||
// Parse command line arguments
|
||||
@@ -385,16 +436,23 @@ async function main() {
|
||||
const tokenAmounts = calculateTokenAmounts(prices, usdAmount);
|
||||
//
|
||||
// // Step 3: Connect to wallet
|
||||
console.log(`\n[~] Connecting to test wallet at ${RPC_URL}...`);
|
||||
console.log(`\n[~] Connecting to sender wallet at ${RPC_URL}...`);
|
||||
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
|
||||
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
|
||||
console.log(`[+] Connected. Using wallet: ${wallet.address}`);
|
||||
console.log(`[+] Connected. Using sender wallet: ${wallet.address}`);
|
||||
console.log(`[+] Receiver wallet: ${RECEIVER_ADDRESS}`);
|
||||
//
|
||||
// Step 4: Check balances
|
||||
await checkBalances(provider, wallet, tokenAmounts);
|
||||
await checkBalances(provider, wallet, tokenAmounts, RECEIVER_ADDRESS);
|
||||
|
||||
// Step 5: Approve tokens
|
||||
await approveTokens(wallet, tokenAmounts);
|
||||
// // Step 5: Approve tokens
|
||||
if (NETWORK === 'mockchain' && currentConfig.receiverPrivateKey) {
|
||||
// On mockchain, use receiver wallet for approvals
|
||||
await approveTokens(provider, tokenAmounts, currentConfig.receiverPrivateKey);
|
||||
} else if (NETWORK === 'mainnet') {
|
||||
// On mainnet, use the main wallet (payer and receiver are the same)
|
||||
await approveTokens(provider, tokenAmounts, PRIVATE_KEY);
|
||||
}
|
||||
|
||||
// Step 6: Create pool
|
||||
await createPool(wallet, tokenAmounts);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"create-pool": "node create_pool_from_prices.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^17.2.3",
|
||||
"ethers": "^5.7.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ export default function AboutPage() {
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
Verify our contracts on{' '}
|
||||
<a
|
||||
href="https://sepolia.etherscan.io/address/0x081aA8AB1984680087c01a5Cd50fC9f49742434D#code"
|
||||
href="https://etherscan.io/address/0x42977f565971F6D288a05ddEbC87A17276F71A29#code"
|
||||
target="liqp_etherscan"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary hover:underline"
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
'https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd',
|
||||
{
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
cache: 'no-store',
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`CoinGecko API error: ${response.status} ${response.statusText}`);
|
||||
throw new Error(`CoinGecko API error: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return NextResponse.json(data, {
|
||||
headers: {
|
||||
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=120',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching gas price:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to fetch gas price', details: error instanceof Error ? error.message : 'Unknown error' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import '@rainbow-me/rainbowkit/styles.css';
|
||||
import '@/app/globals.css';
|
||||
import { Providers } from '@/components/providers';
|
||||
import { Metadata } from 'next';
|
||||
import Script from "next/script";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL('https://liquidity.party'),
|
||||
@@ -37,6 +38,19 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body className="min-h-screen">
|
||||
{/* Google tag (gtag.js) */}
|
||||
<Script
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-GH2R6NTLC3"
|
||||
strategy="afterInteractive"
|
||||
/>
|
||||
<Script id="gtag-init" strategy="afterInteractive">
|
||||
{`
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){window.dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-GH2R6NTLC3');
|
||||
`}
|
||||
</Script>
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
5
src/app/terms/page.tsx
Normal file
5
src/app/terms/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import TosCard from '@/components/tos-card';
|
||||
|
||||
export default function TermsPage() {
|
||||
return <TosCard />;
|
||||
}
|
||||
@@ -101,6 +101,16 @@ export function Providers({ children }: { children: React.ReactNode }) {
|
||||
<main className="flex-1 container mx-auto px-4 py-8">
|
||||
{children}
|
||||
</main>
|
||||
<footer className="py-4 text-center text-sm text-muted-foreground border-t">
|
||||
<div className="mb-2">
|
||||
©{new Date().getFullYear()} Dexorder Trading Services, Ltd. (BVI)
|
||||
</div>
|
||||
<div>
|
||||
<a href="/terms" className="hover:text-foreground underline transition-colors">
|
||||
Terms of Service
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</ToastProvider>
|
||||
</Web3Provider>
|
||||
|
||||
@@ -20,6 +20,38 @@ interface StakeFormProps {
|
||||
defaultMode?: Mode;
|
||||
}
|
||||
|
||||
// Helper component for slippage warnings
|
||||
function SlippageWarning({
|
||||
slippage,
|
||||
action,
|
||||
isError
|
||||
}: {
|
||||
slippage: number;
|
||||
action: string;
|
||||
isError: boolean;
|
||||
}) {
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="px-4 py-3 bg-destructive/10 border border-destructive/20 rounded-lg">
|
||||
<p className="text-sm text-destructive font-medium">⚠️ Slippage Exceeds 5%</p>
|
||||
<p className="text-xs text-destructive/80 mt-1">
|
||||
The estimated slippage for this {action} is {Math.abs(slippage).toFixed(2)}%.
|
||||
We cannot process this {action} as you may lose too much money due to the high slippage.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="px-4 py-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
|
||||
<p className="text-sm text-yellow-600 dark:text-yellow-500 font-medium">⚠️ High Slippage Warning</p>
|
||||
<p className="text-xs text-yellow-600/80 dark:text-yellow-500/80 mt-1">
|
||||
The estimated slippage for this {action} is {Math.abs(slippage).toFixed(2)}%. You may lose money due to low liquidity in this pool.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface TokenInfo {
|
||||
address: `0x${string}`;
|
||||
symbol: string;
|
||||
@@ -141,19 +173,49 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
||||
}, [stakeAmount, selectedToken, mode]);
|
||||
|
||||
// Fetch swap mint amounts (for stake mode)
|
||||
const { swapMintAmounts, loading: swapMintLoading } = useSwapMintAmounts(
|
||||
const { swapMintAmounts, loading: swapMintLoading, error: swapMintError } = useSwapMintAmounts(
|
||||
mode === 'stake' ? selectedPool?.address : undefined,
|
||||
mode === 'stake' ? inputTokenIndex : undefined,
|
||||
mode === 'stake' ? maxAmountIn : undefined
|
||||
mode === 'stake' ? maxAmountIn : undefined,
|
||||
mode === 'stake' && selectedToken ? selectedToken.decimals : undefined
|
||||
);
|
||||
|
||||
// Fetch burn swap amounts (for unstake mode, only when not redeeming all)
|
||||
const { burnSwapAmounts, loading: burnSwapLoading } = useBurnSwapAmounts(
|
||||
const { burnSwapAmounts, loading: burnSwapLoading, error: burnSwapError } = useBurnSwapAmounts(
|
||||
mode === 'unstake' && !redeemAll ? selectedPool?.address : undefined,
|
||||
mode === 'unstake' && !redeemAll ? maxAmountIn : undefined,
|
||||
mode === 'unstake' && !redeemAll ? inputTokenIndex : undefined
|
||||
mode === 'unstake' && !redeemAll ? inputTokenIndex : undefined,
|
||||
mode === 'unstake' && !redeemAll && selectedToken ? selectedToken.decimals : undefined
|
||||
);
|
||||
|
||||
// Check if calculated slippage exceeds 5%
|
||||
const slippageExceedsLimit = useMemo(() => {
|
||||
if (mode === 'stake' && swapMintAmounts?.calculatedSlippage !== undefined) {
|
||||
return Math.abs(swapMintAmounts.calculatedSlippage) > 5;
|
||||
}
|
||||
return false;
|
||||
}, [mode, swapMintAmounts]);
|
||||
|
||||
// Check if slippage is high (> 2%)
|
||||
const slippageIsHigh = useMemo(() => {
|
||||
if (mode === 'stake' && swapMintAmounts?.calculatedSlippage !== undefined) {
|
||||
return Math.abs(swapMintAmounts.calculatedSlippage) > 2;
|
||||
}
|
||||
|
||||
if (mode === 'unstake' && !redeemAll && burnSwapAmounts?.calculatedSlippage !== undefined) {
|
||||
return Math.abs(burnSwapAmounts.calculatedSlippage) > 2;
|
||||
}
|
||||
return false;
|
||||
}, [mode, swapMintAmounts, burnSwapAmounts, redeemAll]);
|
||||
|
||||
// Check if unstake slippage exceeds 5%
|
||||
const unstakeSlippageExceedsLimit = useMemo(() => {
|
||||
if (mode === 'unstake' && !redeemAll && burnSwapAmounts?.calculatedSlippage !== undefined) {
|
||||
return Math.abs(burnSwapAmounts.calculatedSlippage) > 5;
|
||||
}
|
||||
return false;
|
||||
}, [mode, burnSwapAmounts, redeemAll]);
|
||||
|
||||
// Fetch burn amounts (for unstake mode when redeeming all)
|
||||
const { burnAmounts, loading: burnAmountsLoading } = useBurnAmounts(
|
||||
mode === 'unstake' && redeemAll ? selectedPool?.address : undefined,
|
||||
@@ -664,6 +726,58 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error messages for output zero */}
|
||||
{mode === 'stake' && swapMintError && stakeAmount && (
|
||||
<div className="px-4 py-3 bg-destructive/10 border border-destructive/20 rounded-lg">
|
||||
<p className="text-sm text-destructive font-medium">⚠️ Cannot Process Stake</p>
|
||||
<p className="text-xs text-destructive/80 mt-1">
|
||||
{swapMintError}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{mode === 'unstake' && !redeemAll && burnSwapError && stakeAmount && (
|
||||
<div className="px-4 py-3 bg-destructive/10 border border-destructive/20 rounded-lg">
|
||||
<p className="text-sm text-destructive font-medium">⚠️ Cannot Process Unstake</p>
|
||||
<p className="text-xs text-destructive/80 mt-1">
|
||||
{burnSwapError}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Slippage warnings - consolidated for both stake and unstake modes */}
|
||||
{mode === 'stake' && !swapMintError && slippageExceedsLimit && swapMintAmounts?.calculatedSlippage !== undefined && (
|
||||
<SlippageWarning
|
||||
slippage={swapMintAmounts.calculatedSlippage}
|
||||
action="stake"
|
||||
isError={true}
|
||||
/>
|
||||
)}
|
||||
|
||||
{mode === 'stake' && !swapMintError && !slippageExceedsLimit && slippageIsHigh && swapMintAmounts?.calculatedSlippage !== undefined && (
|
||||
<SlippageWarning
|
||||
slippage={swapMintAmounts.calculatedSlippage}
|
||||
action="stake"
|
||||
isError={false}
|
||||
/>
|
||||
)}
|
||||
|
||||
{mode === 'unstake' && !redeemAll && !burnSwapError && unstakeSlippageExceedsLimit && burnSwapAmounts?.calculatedSlippage !== undefined && (
|
||||
<SlippageWarning
|
||||
slippage={burnSwapAmounts.calculatedSlippage}
|
||||
action="unstake"
|
||||
isError={true}
|
||||
/>
|
||||
)}
|
||||
|
||||
{mode === 'unstake' && !redeemAll && !burnSwapError && !unstakeSlippageExceedsLimit && slippageIsHigh && burnSwapAmounts?.calculatedSlippage !== undefined && (
|
||||
<SlippageWarning
|
||||
slippage={burnSwapAmounts.calculatedSlippage}
|
||||
action="unstake"
|
||||
isError={false}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Burn Swap Amounts Display (Unstake Mode) */}
|
||||
{mode === 'unstake' && !redeemAll && burnSwapAmounts && selectedToken && !isAmountExceedingBalance && (
|
||||
<div className="px-4 py-3 bg-muted/30 rounded-lg space-y-2">
|
||||
@@ -712,10 +826,10 @@ export function StakeForm({ defaultMode = 'stake' }: StakeFormProps) {
|
||||
!selectedPool ||
|
||||
isAmountExceedingBalance ||
|
||||
(mode === 'stake'
|
||||
? (!selectedToken || isSwapMinting)
|
||||
? (!selectedToken || isSwapMinting || slippageExceedsLimit || !!swapMintError)
|
||||
: (redeemAll
|
||||
? isBurning
|
||||
: (!selectedToken || inputTokenIndex === undefined || isBurnSwapping)))
|
||||
: (!selectedToken || inputTokenIndex === undefined || isBurnSwapping || unstakeSlippageExceedsLimit || !!burnSwapError)))
|
||||
}
|
||||
>
|
||||
{!isConnected
|
||||
|
||||
@@ -6,17 +6,19 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowDownUp, ChevronDown, Settings, CheckCircle, XCircle, Loader2 } from 'lucide-react';
|
||||
import { useAccount } from 'wagmi';
|
||||
import { useAccount, useChainId } from 'wagmi';
|
||||
import { useTokenDetails, useGetPoolsByToken, type TokenDetails } from '@/hooks/usePartyPlanner';
|
||||
import { useSwapAmounts, useSwap, selectBestSwapRoute, type ActualSwapAmounts } from '@/hooks/usePartyPool';
|
||||
import { formatUnits, parseUnits } from 'viem';
|
||||
import { SwapReviewModal } from './swap-review-modal';
|
||||
import UniswapQuote from './uniswap-quote';
|
||||
|
||||
type TransactionStatus = 'idle' | 'pending' | 'success' | 'error';
|
||||
|
||||
export function SwapForm() {
|
||||
const { t } = useTranslation();
|
||||
const { isConnected, address } = useAccount();
|
||||
const chainId = useChainId();
|
||||
const [fromAmount, setFromAmount] = useState('');
|
||||
const [toAmount, setToAmount] = useState('');
|
||||
const [selectedFromToken, setSelectedFromToken] = useState<TokenDetails | null>(null);
|
||||
@@ -60,17 +62,44 @@ export function SwapForm() {
|
||||
currentSlippage
|
||||
);
|
||||
|
||||
// Check if user has insufficient balance
|
||||
const hasInsufficientBalance = useMemo(() => {
|
||||
if (!selectedFromToken || !fromAmount || fromAmount === '') {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const inputAmount = parseUnits(fromAmount, selectedFromToken.decimals);
|
||||
return inputAmount > selectedFromToken.balance;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}, [selectedFromToken, fromAmount]);
|
||||
|
||||
// Check if calculated slippage exceeds 5%
|
||||
const slippageExceedsLimit = useMemo(() => {
|
||||
if (!swapAmounts || swapAmounts.length === 0 || swapAmounts[0].calculatedSlippage === undefined) {
|
||||
return false;
|
||||
}
|
||||
return Math.abs(swapAmounts[0].calculatedSlippage) > 5;
|
||||
}, [swapAmounts]);
|
||||
|
||||
// Initialize swap hook
|
||||
const { executeSwap, estimateGas, isSwapping, gasEstimate, isEstimatingGas } = useSwap();
|
||||
|
||||
// Update "You Receive" amount when swap calculation completes
|
||||
useEffect(() => {
|
||||
if (hasInsufficientBalance) {
|
||||
setToAmount('');
|
||||
return;
|
||||
}
|
||||
if (swapAmounts && swapAmounts.length > 0 && selectedToToken) {
|
||||
const swapResult = swapAmounts[0]; // Get the first (and should be only) result
|
||||
const formattedAmount = formatUnits(swapResult.amountOut, selectedToToken.decimals);
|
||||
setToAmount(formattedAmount);
|
||||
} else {
|
||||
setToAmount('');
|
||||
}
|
||||
}, [swapAmounts, selectedToToken]);
|
||||
}, [swapAmounts, selectedToToken, hasInsufficientBalance]);
|
||||
|
||||
// Close dropdowns when clicking outside
|
||||
useEffect(() => {
|
||||
@@ -284,6 +313,7 @@ export function SwapForm() {
|
||||
onChange={(e) => setToAmount(e.target.value)}
|
||||
className="text-2xl h-16"
|
||||
disabled={!selectedFromToken}
|
||||
readOnly
|
||||
/>
|
||||
<div className="relative min-w-[160px] space-y-1" ref={toDropdownRef}>
|
||||
<Button
|
||||
@@ -346,6 +376,49 @@ export function SwapForm() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error message for insufficient balance */}
|
||||
{hasInsufficientBalance && (
|
||||
<div className="px-4 py-3 bg-destructive/10 border border-destructive/20 rounded-lg">
|
||||
<p className="text-sm text-destructive">Insufficient balance</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error message for slippage exceeding 5% */}
|
||||
{slippageExceedsLimit && (
|
||||
<div className="px-4 py-3 bg-destructive/10 border border-destructive/20 rounded-lg">
|
||||
<p className="text-sm text-destructive font-medium">⚠️ Slippage Exceeds 5%</p>
|
||||
<p className="text-xs text-destructive/80 mt-1">
|
||||
The estimated slippage for this swap is {Math.abs(swapAmounts![0].calculatedSlippage!).toFixed(2)}%.
|
||||
We cannot process this swap as you may lose too much money due to the high slippage.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* High slippage warning - show if calculated slippage exceeds max slippage but is under 5% */}
|
||||
{!slippageExceedsLimit && swapAmounts && swapAmounts.length > 0 && swapAmounts[0].calculatedSlippage !== undefined && (
|
||||
Math.abs(swapAmounts[0].calculatedSlippage) > currentSlippage
|
||||
) && (
|
||||
<div className="px-4 py-3 bg-yellow-500/10 border border-yellow-500/20 rounded-lg">
|
||||
<p className="text-sm text-yellow-600 dark:text-yellow-500 font-medium">⚠️ Slippage Exceeds Your Tolerance</p>
|
||||
<p className="text-xs text-yellow-600/80 dark:text-yellow-500/80 mt-1">
|
||||
The estimated slippage for this swap is {Math.abs(swapAmounts[0].calculatedSlippage).toFixed(2)}%, which exceeds your maximum slippage setting of {currentSlippage}%. This swap may result in less favorable pricing than expected due to low liquidity.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/*/!* Uniswap Quote - Hidden (under construction) *!/*/}
|
||||
{/*{false && fromAmount && selectedFromToken && selectedToToken && (*/}
|
||||
{/* <UniswapQuote*/}
|
||||
{/* amountIn={fromAmount}*/}
|
||||
{/* tokenInAddress={selectedFromToken.address}*/}
|
||||
{/* tokenOutAddress={selectedToToken.address}*/}
|
||||
{/* tokenInDecimals={selectedFromToken.decimals}*/}
|
||||
{/* tokenOutDecimals={selectedToToken.decimals}*/}
|
||||
{/* tokenOutSymbol={selectedToToken.symbol}*/}
|
||||
{/* chainId={chainId || 1}*/}
|
||||
{/* />*/}
|
||||
{/*)}*/}
|
||||
|
||||
{/* Gas Estimate, Slippage, and Fees */}
|
||||
{isConnected && fromAmount && toAmount && (
|
||||
<div className="px-4 py-2 bg-muted/30 rounded-lg space-y-2">
|
||||
@@ -384,10 +457,14 @@ export function SwapForm() {
|
||||
<Button
|
||||
className="w-full h-14 text-lg"
|
||||
onClick={() => setIsReviewModalOpen(true)}
|
||||
disabled={!isConnected || !fromAmount || !toAmount || !!poolsError}
|
||||
disabled={!isConnected || !fromAmount || !toAmount || !!poolsError || hasInsufficientBalance || slippageExceedsLimit}
|
||||
>
|
||||
{!isConnected
|
||||
? t('swap.connectWalletToSwap')
|
||||
: hasInsufficientBalance
|
||||
? 'Insufficient Balance'
|
||||
: slippageExceedsLimit
|
||||
? 'Slippage Too High'
|
||||
: 'Review'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
@@ -431,7 +508,7 @@ export function SwapForm() {
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-2">
|
||||
Your transaction will revert if the price changes unfavorably by more than this percentage.
|
||||
You will be warned if the slippage exceeds this setting, and you can choose whether to proceed with the trade.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
675
src/components/tos-card.tsx
Normal file
675
src/components/tos-card.tsx
Normal file
@@ -0,0 +1,675 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function TosCard() {
|
||||
return (
|
||||
<div className="max-w-5xl mx-auto p-5">
|
||||
<div className="bg-card rounded-lg shadow-md p-6 border">
|
||||
<h1 className="text-2xl font-semibold text-center mb-4">Terms of Service</h1>
|
||||
{/* MAKE SURE TO UPDATE THE VERSION VARIABLE AS WELL */}
|
||||
<p className="text-center mb-4">Last Updated November 18, 2024</p>
|
||||
|
||||
<div className="mb-4 leading-relaxed">
|
||||
Please read these Terms of Service (the "<b>Terms</b>")
|
||||
carefully because they govern your use of the website (and all subdomains and subpages
|
||||
thereon) located at liquidity.party, including without limitation the subdomains
|
||||
app.liquidity.party and www.liquidity.party (collectively, the "<b>Site</b>"), and the
|
||||
Liquidity Party
|
||||
web application graphical user interface and any other services accessible via the Site
|
||||
(together with the Site, web application, and other services, collectively, the "
|
||||
<b>Dexorder Service</b>") offered by Dexorder Trading Services Ltd. ("<b>Dexorder</b>," "
|
||||
<b>we</b>," "<b>our</b>," or "<b>us</b>").
|
||||
</div>
|
||||
|
||||
<div className="mb-4 leading-relaxed">
|
||||
<b>
|
||||
BY USING THE DEXORDER SERVICE, YOU REPRESENT THAT (I) YOU ARE NOT LOCATED WITHIN THE
|
||||
UNITED STATES; AND (II) YOU ARE NOT A PERSON OR ENTITY WHO IS RESIDENT IN, A CITIZEN OF,
|
||||
IS LOCATED IN, IS INCORPORATED IN, OR HAS A REGISTERED OFFICE IN ANY RESTRICTED
|
||||
TERRITORY, AS DEFINED BELOW (ANY SUCH PERSON OR ENTITY FROM WITHIN THE UNITED STATES OR
|
||||
A RESTRICTED TERRITORY, IS REFERRED TO HEREIN AS A "RESTRICTED PERSON").
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<div className="mb-4 leading-relaxed">
|
||||
WHEN YOU AGREE TO THESE TERMS, YOU ARE AGREEING (WITH LIMITED EXCEPTION) TO RESOLVE ANY
|
||||
DISPUTE BETWEEN YOU AND DEXORDER THROUGH BINDING, INDIVIDUAL ARBITRATION RATHER THAN IN
|
||||
COURT. PLEASE REVIEW CAREFULLY SECTION 16 (DISPUTE RESOLUTION) BELOW FOR DETAILS REGARDING
|
||||
ARBITRATION. HOWEVER, IF YOU ARE A RESIDENT OF A JURISDICTION WHERE APPLICABLE LAW
|
||||
PROHIBITS ARBITRATION OF DISPUTES, THE AGREEMENT TO ARBITRATE IN SECTION 16 WILL NOT APPLY
|
||||
TO YOU, BUT THE PROVISIONS OF SECTION 15 (GOVERNING LAW) WILL APPLY INSTEAD.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">1. Description of Dexorder Service</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) The Dexorder Service allows you to access an online web application graphical user
|
||||
interface (the "<b>App</b>") which enables you to interact with a protocol consisting of a
|
||||
set of smart contracts (the "<b>Protocol</b>").
|
||||
You may use the <b>App</b> to send signals to, interact with, and initiate actions on the
|
||||
decentralized exchange ("<b>DEX</b>").
|
||||
</div>
|
||||
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) <b>Interface</b>. The Dexorder Service provides you with access to the Protocol.
|
||||
All information provided in
|
||||
connection with your access and use of the Dexorder Service is for informational purposes
|
||||
only. You should not take, or refrain from taking, any action based on any information
|
||||
contained on the Dexorder Service or any other information that we make available at any
|
||||
time, including blog posts, data, articles, links to third-party content, Discord content,
|
||||
news feeds, tutorials, tweets, and videos. Before you make any financial, legal, technical,
|
||||
or other decisions involving the Dexorder Service, you should seek independent professional
|
||||
advice from a licensed and qualified individual in the area for which such advice would be
|
||||
appropriate. Because the Dexorder Service provides information about the Protocol, these
|
||||
Terms also provide some information about the use of the Protocol. This information is not
|
||||
intended to be comprehensive or address all aspects of the Protocol.
|
||||
</div>
|
||||
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(c) <b>Our Relationship</b>. You acknowledge and agree that Dexorder is an online platform
|
||||
provider and not a financial institution, broker dealer, exchange or money services
|
||||
business. Dexorder does not direct or control the day-to-day activities of users accessing
|
||||
the Dexorder Service. Neither we nor any affiliated entity is a party to any transaction on
|
||||
the blockchain network underlying the Protocol; we do not have possession, custody or
|
||||
control over any cryptoassets appearing on the Dexorder Service or on the Protocol; and we
|
||||
do not have possession, custody, or control over any user's funds or cryptoassets. Further,
|
||||
we do not store, send, or receive any funds or cryptoassets on your behalf. You understand
|
||||
that when you interact with any Protocol smart contracts, you retain control over your
|
||||
cryptoassets at all times. You are solely responsible for evaluating any proposed technical
|
||||
changes and how such changes may alter current or future Interactions. Furthermore, you
|
||||
understand and acknowledge that only you have absolute and ultimate authority over the
|
||||
implementation of any such changes and the responsibility therefor. The private key
|
||||
associated with your Vault is the only private key that can control the cryptoassets in the
|
||||
Vault. You alone are responsible for securing your private keys. We do not have access to
|
||||
your private keys. Because the Protocol is non-custodial, we are not intermediaries, agents,
|
||||
advisors, or custodians, and we do not have a fiduciary relationship or obligation to you
|
||||
regarding any other decisions or activities that you affect when using the Dexorder Service
|
||||
or interacting with the Protocol. You acknowledge that we, for the avoidance of doubt, do
|
||||
not have any information regarding any users, users' identities, or services beyond what is
|
||||
available, obtainable publicly via the blockchain, or shared by you when you access the
|
||||
Dexorder Service. We are not responsible for any activities you engage in when using the
|
||||
Dexorder Service, and you should understand the risks associated with cryptoassets,
|
||||
blockchain technology generally, and the Interface.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">2. Agreement to Terms</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
By using our Dexorder Service, you agree to be bound by these Terms. If you don't agree to
|
||||
be bound by these Terms, do not use the Dexorder Service.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">3. Changes to these Terms or the Dexorder Service</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
We may update the Terms, including any addendum terms, from time to time in our sole
|
||||
discretion. If we do, we'll let you know by posting the updated Terms on the Site and/or may
|
||||
also send other communications. It's important that you review the Terms whenever we update
|
||||
them or you use the Dexorder Service. If you continue to use the Dexorder Service after we
|
||||
have posted updated Terms it means that you accept and agree to the changes. If you don't
|
||||
agree to be bound by the changes, you may not use the Dexorder Service anymore. Because our
|
||||
Dexorder Service is evolving over time we may change or discontinue all or any part of the
|
||||
Dexorder Service, at any time and without notice, at our sole discretion.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">4. Who May Use the Dexorder Service?</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) <u>Eligibility</u>. The Dexorder Service is only available to users in certain
|
||||
jurisdictions outside of the United States and that are at least 18 years old, capable of
|
||||
forming a binding contract with the Dexorder and not otherwise barred from using the
|
||||
Dexorder Service under Applicable Law. You may not attempt to access or use the Dexorder
|
||||
Service if you are not permitted to do so (including without limitation if you are a
|
||||
Restricted Person).
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) <u>Compliance</u>. You certify that you will comply with all Applicable Law when using
|
||||
the Dexorder Service. You are solely responsible for ensuring that your access and use of
|
||||
the Dexorder Service in such country, territory, or jurisdiction does not violate any
|
||||
Applicable Laws. You must not use any software or networking techniques, including use of a
|
||||
virtual private network ("<b>VPN</b>") to circumvent or attempt to circumvent this
|
||||
prohibition. We reserve the right to monitor the locations from which our Dexorder Service
|
||||
is accessed. Furthermore, we reserve the right, at any time, in our sole discretion, to
|
||||
block access to the Dexorder Service, in whole or in part, from any geographic location, IP
|
||||
addresses, and unique device identifiers
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">5. Use of the Dexorder Service</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) <u>User Representations and Warranties</u>. As a condition to accessing or using the
|
||||
Dexorder Service, you represent and warrant to Dexorder that:
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(i) if you are entering into these Terms as an individual, then you are of legal age in the
|
||||
jurisdiction in which you reside and you have the legal capacity to enter into these Terms
|
||||
and be bound by them and if you are entering into these Terms as an entity, then you must
|
||||
have the legal authority to accept these Terms on that entity's behalf, in which case "you"
|
||||
(except as used in this paragraph) will mean that entity;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">(ii) you are not in or residing in the United States;</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(iii) you are not in or residing in Cuba, Iran, North Korea, Syria, Belarus, Russia, and the
|
||||
Crimea, Luhansk, Donetsk, Zaporizhzhia, and Kherson regions of Ukraine, or any other country
|
||||
or jurisdiction to which the Cayman Islands, the United Kingdom, United States, the United
|
||||
Nations Security Council, or the European Union embargoes goods or imposes similar sanctions
|
||||
(collectively, "<b>Restricted Territories</b>");
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(iv) you are not on any sanctions list or equivalent maintained by the Cayman Islands, the
|
||||
United Kingdom, United States, the United Nations Security Council, or the European Union
|
||||
(collectively, "<b>Sanctions Lists Persons</b>") and you do not intend to transact with any
|
||||
Restricted Person or Sanctions List Person;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(v) you do not, and will not, use VPN software or any other privacy or anonymization tools
|
||||
or techniques to circumvent, or attempt to circumvent, any restrictions that apply to the
|
||||
Dexorder Service;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(vi) you have obtained all required consents from any individual whose personal information
|
||||
you transfer to us in connection with your use of the Dexorder Service; and
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(vii) all information that you provide through the Dexorder Service is current, complete,
|
||||
true, and accurate and you will maintain the security and confidentiality of your private
|
||||
keys associated with your public wallet address, passwords, API keys, passwords or other
|
||||
information associated with your Vault or otherwise, as applicable.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(viii) your access to the Dexorder Service is not: (a) prohibited by and does not otherwise
|
||||
violate or assist you to violate any domestic or foreign law, rule, statute, regulation,
|
||||
by-law, order, protocol, code, decree, or another directive, requirement, or guideline,
|
||||
published or in force that applies to or is otherwise intended to govern or regulate any
|
||||
person, property, transaction, activity, event or other matter, including any rule, order,
|
||||
judgment, directive or other requirement or guideline issued by any domestic or foreign
|
||||
federal, provincial or state, municipal, local or other governmental, regulatory, judicial or
|
||||
administrative authority having jurisdiction over Dexorder, you, the Site or the Dexorder
|
||||
Service, or as otherwise duly enacted, enforceable by law, the common law or equity
|
||||
(collectively, "<b>Applicable Laws</b>"); or (b) contribute to or facilitate any illegal
|
||||
activity.
|
||||
</div>
|
||||
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) <u>Limitations</u>. As a condition to accessing or using the Dexorder Service or the
|
||||
Site, you acknowledge, understand, and agree to the following:
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(i) Subject to your compliance with these Terms, Dexorder will use its commercially
|
||||
reasonable efforts to provide you with access to the Dexorder Service and to cause your
|
||||
Interactions to be executed on the Protocol, however from time to time the Site and
|
||||
the Dexorder Service may
|
||||
be inaccessible or inoperable for any reason, including, without limitation: (a) if an
|
||||
Interaction repeatedly fails to be executed (such as due to an error in Interaction
|
||||
execution or a malfunction in the Dexorder Service); (b) equipment malfunctions; (c)
|
||||
periodic maintenance procedures or repairs that Dexorder or any of its suppliers or
|
||||
contractors may undertake from time to time; (d) causes beyond Dexorder's control or that
|
||||
Dexorder could not reasonably foresee; (e) disruptions and temporary or permanent
|
||||
unavailability of underlying blockchain infrastructure; (f) unavailability of third-party
|
||||
service providers or external partners for any reason.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(ii) the Site and the Dexorder Service may evolve, which means Dexorder may apply changes,
|
||||
replace, or discontinue (temporarily or permanently) the Dexorder Service at any time in its
|
||||
sole discretion;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(iii) Dexorder does not act as an agent for you or any other user of the Site or the
|
||||
Dexorder Service;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(iv) you are solely responsible for your use of the Dexorder Service; and
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(v) we owe no fiduciary duties or liabilities to you or any other party, and that to the
|
||||
extent any such duties or liabilities may exist at law or in equity, you hereby irrevocably
|
||||
disclaim, waive, and eliminate those duties and liabilities.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">6. Interactions; Fees</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) <u>Interactions</u>. In order to effectuate Interactions, you may need to transfer
|
||||
digital assets (e.g., tokens). You acknowledge that you may use the Dexorder
|
||||
Services to process and cause Interactions to operate on the Protocol.
|
||||
Dexorder is an interface to that smart contract, and does not offer a digital
|
||||
wallet and has no custody or control over your digital wallet or any digital assets or
|
||||
cryptocurrency, which is never accessible by Dexorder.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) <u>Fees</u>.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(i) Dexorder charges fees for usage of the Dexorder Services at the time of user
|
||||
Interactions ("<b>Fees</b>"). You agree to pay all applicable Fees to Dexorder, in
|
||||
the amounts communicated or presented to you via the Dexorder Service in connection with
|
||||
usage of the Dexorder Service. Each party shall be responsible for all Taxes imposed on its
|
||||
income or property.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed ml-8">
|
||||
(ii) There may be associated fees in connection with transactions enacted on a blockchain.
|
||||
All transactions using blockchains require the payment of gas fees, which are essentially
|
||||
transaction fees paid on every transaction that occurs on the selected blockchain network.
|
||||
We do not collect any such fees. Please note that accessing the Protocol may result in you
|
||||
incurring gas fees, which are non-refundable, and are paid by you in all circumstances. You
|
||||
pay all gas fees incurred by you as relating to interacting with the Protocol.
|
||||
</div>
|
||||
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(c) <u>Tax Records and Reporting</u>. You are solely responsible for all costs incurred by
|
||||
you in using the Dexorder Service, and for determining, collecting, reporting, and paying
|
||||
all applicable Taxes that you may be required by law to collect and remit to any
|
||||
governmental or regulatory agencies. As used herein, "<b>Taxes</b>" means the taxes, duties,
|
||||
levies, tariffs, and other charges imposed by any federal, state, multinational or local
|
||||
governmental or regulatory authority. We reserve the right to report any activity occurring
|
||||
using the Dexorder Service to relevant tax authorities as required under Applicable Law. You
|
||||
are solely responsible for maintaining all relevant Tax records and complying with any
|
||||
reporting requirements you may have as related to our Dexorder Service. You are further
|
||||
solely responsible for independently maintaining the accuracy of any record submitted to any
|
||||
tax authority including any information derived from the Dexorder Service.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(d) Suspensions or Terminations. In addition to the other suspension and termination rights
|
||||
in these Terms, we may suspend or terminate your access to the Dexorder Service, or any and
|
||||
all Interactions, at any time in connection with any Interaction or transaction (i) as
|
||||
required by Applicable Law or any governmental authority, (ii) if we are unable to process
|
||||
or execute an Interaction or transaction after several attempts (as described in the
|
||||
Execution Policy or otherwise in Dexorder's reasonable discretion), or (iii) if we in our
|
||||
sole and reasonable discretion determine you are violating the terms of any third-party
|
||||
service provider or these Terms, including, without limitation, if we reasonably believe any
|
||||
of your representations and warranties may be untrue or inaccurate or you are violating or
|
||||
have violated any of the geographical restrictions that apply to the Dexorder Service, and
|
||||
in any case we will not be liable to you for any losses or damages you may suffer as a
|
||||
result of or in connection with the Dexorder Service being inaccessible to you at any time
|
||||
or for any reason. Such suspension or termination shall not constitute a breach of these
|
||||
Terms by Dexorder. In accordance with its anti- money laundering, anti-terrorism,
|
||||
anti-fraud, and other compliance policies and practices, we may impose limitations and
|
||||
controls on the ability of you or any beneficiary to utilize the Dexorder Service. Such
|
||||
limitations may include rejecting transaction requests, freezing funds in any case where
|
||||
Dexorder has such ability, or otherwise restricting you from using the Dexorder Service, all
|
||||
to the extent of our ability to do so.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">7. General Prohibitions and Dexorder's Enforcement Rights.</h2>
|
||||
<div className="mb-4 leading-relaxed">You agree not to do any of the following:</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) Engage in or induce others to engage in any form of unauthorized access, hacking, or
|
||||
social engineering, including without limitation any distributed denial or service or DDoS
|
||||
attack, of Dexorder, the Dexorder Service, or any users of the foregoing;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) Use, display, mirror or frame the Dexorder Service or any individual element within the
|
||||
Dexorder Service, Dexorder's name, any Dexorder trademark, logo or other proprietary
|
||||
information, or the layout and design of any page or form contained on a page, without
|
||||
Dexorder's express written consent;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(c) Access, tamper with, or use non-public areas of the Dexorder Service, Dexorder's
|
||||
computer systems, or the technical delivery systems of Dexorder's providers;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(d) Attempt to probe, scan or test the vulnerability of any Dexorder system or network or
|
||||
breach any security or authentication measures;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(e) Avoid, bypass, remove, deactivate, impair, descramble or otherwise circumvent any
|
||||
technological measure implemented by Dexorder or any of Dexorder's providers or any other
|
||||
third party (including another user) to protect the Dexorder Service;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(f) Attempt to access or search the Dexorder Service or download content from the Dexorder
|
||||
Service using any engine, software, tool, agent, device or mechanism (including spiders,
|
||||
robots, crawlers, data mining tools or the like) other than the software and/or search
|
||||
agents provided by Dexorder or other generally available third-party web browsers;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(g) Use any meta tags or other hidden text or metadata utilizing a Dexorder trademark, logo,
|
||||
URL or product name without Dexorder's express written consent;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(h) Forge any TCP/IP packet header or any part of the header information in any email or
|
||||
newsgroup posting, or in any way use the Dexorder Service to send altered, deceptive or
|
||||
false source-identifying information;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(i) Attempt to decipher, decompile, disassemble or reverse engineer any of the software used
|
||||
to provide the Dexorder Service;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(j) Interfere with, or attempt to interfere with, the access of any user, host or network,
|
||||
including, without limitation, sending a virus, exploiting any bug, overloading, flooding,
|
||||
spamming, or mail- bombing the Dexorder Service;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(k) Use the Dexorder Service for benchmarking or analysis in a manner that could, directly
|
||||
or indirectly, interfere with, detract from, or otherwise harm the Dexorder Service or DEX;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(l) Collect or store any personally identifiable information from the Dexorder Service from
|
||||
other users of the Dexorder Service without their express permission;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(m) Impersonate or misrepresent your affiliation with any person or entity;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(n) Create or list any counterfeit items (including digital assets);
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(o) Fabricate in any way any transaction or process related thereto;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(p) Engage or assist in any activity that violates any law, statute, ordinance, regulation,
|
||||
or sanctions program, , or that involves proceeds of any unlawful activity (including but
|
||||
not limited to money laundering, terrorist financing or deliberately engaging in activities
|
||||
designed to adversely affect the performance of the Dexorder Service);
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">(q) Engage in deceptive or manipulative trading activities;</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(r) Disguise or interfere in any way with the IP address of the computer you are using to
|
||||
access or use the Dexorder Service or that otherwise prevents us from correctly identifying
|
||||
the IP address of the computer you are using to access the Dexorder Service;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(s) Transmit, exchange, or otherwise support the direct or indirect proceeds of criminal or
|
||||
fraudulent activity;
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">(t) Violate any Applicable Law or regulation; or</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(u) Encourage or enable any other individual to do any of the foregoing.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
Dexorder is not obligated to monitor access to or use of the Dexorder Service or to review
|
||||
or edit any content. However, we have the right to do so for the purpose of operating the
|
||||
Dexorder Service, to ensure compliance with these Terms and to comply with Applicable Law or
|
||||
other legal requirements. We reserve the right, but are not obligated, to suspend or
|
||||
terminate access to the Dexorder Service at any time if we believe you are violating these
|
||||
Terms. We have the right to investigate violations of these Terms or conduct that affects
|
||||
the Dexorder Service. We may also consult and cooperate with law enforcement authorities to
|
||||
prosecute users who violate the law.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">8. Feedback</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
We appreciate feedback, comments, ideas, proposals and suggestions for improvements to the
|
||||
Dexorder Service ("<b>Feedback</b>"). If you choose to submit Feedback, you agree that we
|
||||
are free to use it (and permit others to use it) without any restriction or compensation to
|
||||
you.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">9. Links to Third Party Websites or Resources</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
The Dexorder Service may allow you to access third-party websites, integrations, or other
|
||||
resources, including the Protocol (collectively, "<b>Third Party Resources</b>"). We
|
||||
provide access only as a convenience and are not responsible for the content, products or
|
||||
services on or available from those resources or links displayed on such websites. You
|
||||
acknowledge sole responsibility for and assume all risk arising from, your use of any
|
||||
third-party resources. Our provision of access to Third Party Resources does not constitute
|
||||
approval, endorsement, or control of such Third Party Resource.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">10. Termination</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
We may suspend or terminate your access to and use of the Dexorder Service, at our sole
|
||||
discretion, at any time and without notice to you. You acknowledge and agree that we shall
|
||||
have no liability or obligation to you in such event and that you will not be entitled to a
|
||||
refund of any amounts that you have already paid to us or any third party, to the fullest
|
||||
extent permitted by Applicable Law. Upon any termination, discontinuation, or cancellation
|
||||
of the Dexorder Service or your account, the following Sections will survive: 6.(d) , 7 , 8
|
||||
, 10 , 11 , 13 , 14 , 15 , 16 , and 17 .
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">11. Warranty Disclaimers</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
THE DEXORDER SERVICE (INCLUDING WITHOUT LIMITATION THE VAULT) AND ANY CONTENT CONTAINED
|
||||
THEREIN, AS WELL AS THE PROTOCOL, THE DEXORDER ORACLE, AND ANY ASSOCIATED PROTOCOL OR
|
||||
BLOCKCHAIN MESSAGING FUNCTIONALITY UNDERLYING THE DEXORDER
|
||||
SERVICE (TOGETHER, THE "<b>UTILITIES</b>"), ARE PROVIDED "AS IS," WITHOUT WARRANTY OF ANY
|
||||
KIND. WITHOUT LIMITING THE FOREGOING, WE EXPLICITLY DISCLAIM ANY IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT AND NON-INFRINGEMENT, AND
|
||||
ANY WARRANTIES ARISING OUT OF COURSE OF DEALING OR USAGE OF TRADE. WE MAKE NO WARRANTY THAT
|
||||
THE UTILITIES WILL MEET YOUR REQUIREMENTS, BE AVAILABLE ON AN UNINTERRUPTED, SECURE, OR
|
||||
ERROR-FREE BASIS. WE MAKE NO WARRANTY REGARDING THE QUALITY, ACCURACY, TIMELINESS,
|
||||
TRUTHFULNESS, COMPLETENESS OR RELIABILITY OF ANY INFORMATION OR CONTENT ON THE UTILITIES.
|
||||
DEXORDER FURTHER EXPRESSLY DISCLAIMS ALL LIABILITY OR RESPONSIBILITY IN CONNECTION WITH
|
||||
THIRD PARTY SERVICES. NOTHING HEREIN NOR ANY USE OF THE UTILITIES IN CONNECTION WITH THIRD
|
||||
PARTY SERVICES CONSTITUTES OUR ENDORSEMENT, RECOMMENDATION OR ANY OTHER AFFILIATION OF OR
|
||||
WITH ANY THIRD PARTY SERVICES.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
DEXORDER DOES NOT REPRESENT OR WARRANT THAT ANY CONTENT ON THE UTILITIES IS ACCURATE,
|
||||
COMPLETE, RELIABLE, CURRENT OR ERROR-FREE. WE WILL NOT BE LIABLE FOR ANY LOSS OF ANY KIND
|
||||
FROM ANY ACTION TAKEN OR TAKEN IN RELIANCE ON MATERIAL OR INFORMATION CONTAINED ON THE
|
||||
UTILITIES. DEXORDER CANNOT AND DOES NOT REPRESENT OR WARRANT THAT THE UTILITIES, ANY CONTENT
|
||||
THEREIN, OR OUR SERVERS ARE FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
WE CANNOT GUARANTEE THE SECURITY OF ANY DATA THAT YOU DISCLOSE ONLINE. YOU ACCEPT THE
|
||||
INHERENT SECURITY RISKS OF PROVIDING INFORMATION AND DEALING ONLINE OVER THE INTERNET AND
|
||||
WILL NOT HOLD US RESPONSIBLE FOR ANY BREACH OF SECURITY.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
DEXORDER WILL NOT BE RESPONSIBLE OR LIABLE TO YOU FOR ANY LOSS AND TAKES NO RESPONSIBILITY
|
||||
FOR, AND WILL NOT BE LIABLE TO YOU FOR, ANY USE OF THE UTILITIES, INCLUDING BUT NOT LIMITED
|
||||
TO ANY LOSSES, DAMAGES OR CLAIMS ARISING FROM: (I) USER ERROR SUCH AS FORGOTTEN PASSWORDS,
|
||||
INCORRECTLY CONSTRUCTED TRANSACTIONS, EXCEEDING TRANSFER LIMITS OF THIRD PARTY RESOURCES OR
|
||||
THE DEX, OR MISTYPED WALLET ADDRESSES; (II) SERVER FAILURE OR DATA LOSS; (III) BLOCKCHAIN
|
||||
NETWORKS, CRYPTOCURRENCY WALLETS, CORRUPT FILES, SOFTWARE ERRORS, OR BUGS; (IV) UNAUTHORIZED
|
||||
ACCESS TO THE UTILITIES; OR (V) ANY THIRD PARTY UTILITIES, INCLUDING WITHOUT LIMITATION THE
|
||||
USE OF VIRUSES, PHISHING, BRUTEFORCING OR OTHER MEANS OF ATTACK AGAINST ANY BLOCKCHAIN
|
||||
NETWORK UNDERLYING THE UTILITIES.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
THE UTILITIES MAY NOT BE AVAILABLE DUE TO ANY NUMBER OF FACTORS INCLUDING, BUT NOT LIMITED
|
||||
TO, PERIODIC SYSTEM MAINTENANCE, SCHEDULED OR UNSCHEDULED, ACTS OF GOD, UNAUTHORIZED ACCESS,
|
||||
VIRUSES, DENIAL OF SERVICE OR OTHER ATTACKS, TECHNICAL FAILURE OF THE UTILITIES AND/OR
|
||||
TELECOMMUNICATIONS INFRASTRUCTURE OR DISRUPTION, AND THEREFORE WE EXPRESSLY DISCLAIM ANY
|
||||
EXPRESS OR IMPLIED WARRANTY REGARDING THE USE AND/OR AVAILABILITY, ACCESSIBILITY, SECURITY
|
||||
OR PERFORMANCE OF THE UTILITIES CAUSED BY SUCH FACTORS. WE DO NOT MAKE ANY REPRESENTATIONS
|
||||
OR WARRANTIES AGAINST THE POSSIBILITY OF DELETION, MISDELIVERY OR FAILURE TO STORE
|
||||
COMMUNICATIONS, PERSONALIZED SETTINGS OR OTHER DATA. SOME JURISDICTIONS DO NOT ALLOW THE
|
||||
EXCLUSION OF CERTAIN WARRANTIES. ACCORDINGLY, SOME OF THE ABOVE DISCLAIMERS OF WARRANTIES
|
||||
MAY NOT APPLY TO YOU BUT OTHERS REMAIN IN EFFECT.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
You understand that your use of the Utilities is entirely at your own risk. You assume all
|
||||
risks associated with using the Utilities, and digital assets and decentralized systems
|
||||
generally, including but not limited to, that digital assets are highly volatile; you may
|
||||
not have ready access to assets; and you may lose some or all of your tokens or other
|
||||
assets, including with respect to an Interaction or the Vault. You agree that you will have
|
||||
no recourse against Dexorder for any losses due to your use of the Utilities. For example,
|
||||
these losses may arise from or relate to: (i) lost funds; (ii) server failure or data loss;
|
||||
(iii) corrupted digital wallet files; (iv) unauthorized access; (v) errors, mistakes, or
|
||||
inaccuracies; or (vi) third-party activities.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">12. Assumption of Risk.</h2>
|
||||
<div className="mb-4 leading-relaxed">You accept, acknowledge and assume the following risks:</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) You are solely responsible for determining what, if any, Taxes apply to your
|
||||
transactions through the Utilities. Neither Dexorder nor any Dexorder affiliates are
|
||||
responsible for determining the Taxes that apply to such transactions.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) A lack of use or public interest in the creation and development of distributed
|
||||
ecosystems could negatively impact the development of those ecosystems and related
|
||||
applications, and could therefore also negatively impact the potential utility or value of
|
||||
certain digital assets.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(c) By accessing and using the Utilities, you represent that you understand the inherent
|
||||
risks associated with using cryptographic and blockchain-based systems, and that you have a
|
||||
working knowledge of the usage and intricacies of tokens such as, bitcoin (BTC), ether
|
||||
(ETH), and other digital tokens such as those following the Ethereum Token Standard
|
||||
(ERC-20). You further understand that the markets for tokens can be highly volatile due to
|
||||
factors including (but not limited to) adoption, speculation, technology, security, and
|
||||
regulation. You acknowledge that the cost and speed of transacting with cryptographic and
|
||||
blockchain-based systems are variable and may increase at any time. Accordingly, you
|
||||
understand and agree to assume full responsibility for all of the risks of accessing and
|
||||
using and engaging with the Utilities.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">13. Indemnity</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
You will indemnify, defend (at Dexorder's option) and hold Dexorder and its affiliates and
|
||||
their respective officers, directors, employees and agents, harmless from and against any
|
||||
claims, disputes, demands, liabilities, damages, losses, and costs and expenses, including,
|
||||
without limitation, reasonable legal and accounting fees arising out of or in any way
|
||||
connected with: (a) your access to or use of the Utilities, (b) Interactions and the Vault,
|
||||
(c) your violation of these Terms, or (d) your negligence, willful misconduct, fraud, or
|
||||
violation of Applicable Laws. You may not settle or otherwise compromise any claim subject
|
||||
to this Section without Dexorder's prior written approval.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">14. Limitation of Liability</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) TO THE MAXIMUM EXTENT PERMITTED BY LAW, NEITHER DEXORDER NOR ITS SERVICE PROVIDERS
|
||||
INVOLVED IN CREATING, PRODUCING, OR DELIVERING THE UTILITIES WILL BE LIABLE FOR ANY
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES, OR DAMAGES FOR LOST PROFITS, LOST
|
||||
REVENUES, LOST SAVINGS, LOST BUSINESS OPPORTUNITY, LOSS OF DATA OR GOODWILL, SERVICE
|
||||
INTERRUPTION, COMPUTER DAMAGE OR SYSTEM FAILURE OR THE COST OF SUBSTITUTE SERVICES OF ANY
|
||||
KIND ARISING OUT OF OR IN CONNECTION WITH THESE TERMS OR FROM THE USE OF OR INABILITY TO USE
|
||||
THE UTILITIES, WHETHER BASED ON WARRANTY, CONTRACT, TORT (INCLUDING NEGLIGENCE), PRODUCT
|
||||
LIABILITY OR ANY OTHER LEGAL THEORY, AND WHETHER OR NOT DEXORDER OR ITS SERVICE PROVIDERS
|
||||
HAS BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGE, EVEN IF A LIMITED REMEDY SET FORTH
|
||||
HEREIN IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) TO THE MAXIMUM EXTENT PERMITTED BY THE LAW, IN NO EVENT WILL DEXORDER'S TOTAL LIABILITY
|
||||
ARISING OUT OF OR IN CONNECTION WITH THESE TERMS OR FROM THE USE OF OR INABILITY TO USE THE
|
||||
UTILITIES EXCEED THE TOTAL FEES YOU HAVE PAID OR ARE PAYABLE BY YOU TO DEXORDER FOR USE OF
|
||||
THE UTILITIES (EXCLUDING GAS FEES), OR ONE HUNDRED DOLLARS ($100) IF YOU HAVE NOT HAD ANY
|
||||
PAYMENT OBLIGATIONS TO DEXORDER, AS APPLICABLE.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(c) THE EXCLUSIONS AND LIMITATIONS OF DAMAGES SET FORTH ABOVE ARE FUNDAMENTAL ELEMENTS OF
|
||||
THE BASIS OF THE BARGAIN BETWEEN DEXORDER AND YOU.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">15. Governing Law and Forum Choice</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
These Terms will be governed by and construed in accordance with the laws of the British
|
||||
Virgin Islands without regard to its conflict of laws provisions.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">16. Dispute Resolution</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) <u>Mandatory Arbitration of Disputes</u>. We each agree that any dispute, claim or
|
||||
controversy arising out of or relating to these Terms or the breach, termination,
|
||||
enforcement, interpretation or validity thereof or the use of the Utilities (collectively, "
|
||||
<b>Disputes</b>") will be resolved{' '}
|
||||
<b>
|
||||
solely by binding, individual arbitration and not in a class, representative or
|
||||
consolidated action or proceeding.
|
||||
</b>{' '}
|
||||
You and Dexorder agree that the Cayman Islands Arbitration Law governs the interpretation
|
||||
and enforcement of these Terms, and that you and Dexorder are each waiving the right to a
|
||||
trial by jury or to participate in a class action. This arbitration provision shall survive
|
||||
termination of these Terms.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) <u>Exceptions</u>. As limited exceptions to Section 16.(a) above: (i) each party may
|
||||
seek to resolve a Dispute in small claims court if it qualifies; and (ii) each party retains
|
||||
the right to seek injunctive or other equitable relief from a court to prevent (or enjoin)
|
||||
the infringement or misappropriation of our intellectual property rights.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(c) <u>Conducting Arbitration and Arbitration Rules</u>. The arbitration will be conducted
|
||||
by the Cayman International Mediation & Arbitration Centre (CI-MAC) in accordance with
|
||||
its arbitration rules in force at the time of the dispute ("<b>CI-MAC Rules</b>"), except as
|
||||
modified by these Terms. The CI-MAC Rules are available at{' '}
|
||||
<a href="https://www.caymanarbitration.com/arbitrationrules2023">
|
||||
https://www.caymanarbitration.com/arbitrationrules2023
|
||||
</a>
|
||||
. A party who wishes to start arbitration must submit a written request for arbitration to
|
||||
CI-MAC and give notice to the other party as specified in the CI-MAC Rules. CI-MAC provides
|
||||
instructions on submitting a request for arbitration under Section 3 (Request for
|
||||
arbitration) of the CI-MAC Rules.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
Any arbitration hearings will take place in the county (or parish) where you live, unless
|
||||
the parties agree to a different location. The parties agree that the arbitrator shall have
|
||||
exclusive authority to decide all issues relating to the interpretation, applicability,
|
||||
enforceability and scope of this arbitration agreement.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(d) <u>Arbitration Costs</u>. Payment of all filing, administration and arbitrator fees will
|
||||
be governed by the JAMS Rules, and we won't seek to recover the administration and
|
||||
arbitrator fees we are responsible for paying, unless the arbitrator finds your Dispute
|
||||
frivolous. If we prevail in arbitration we'll pay all of our attorneys' fees and costs and
|
||||
won't seek to recover them from you. If you prevail in arbitration you will be entitled to
|
||||
an award of attorneys' fees and expenses to the extent provided under Applicable Law.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(e) <u>Injunctive and Declaratory Relief</u>. Except as provided in Section 16.(b) above,
|
||||
the arbitrator shall determine all issues of liability on the merits of any claim asserted
|
||||
by either party and may award declaratory or injunctive relief only in favor of the
|
||||
individual party seeking relief and only to the extent necessary to provide relief warranted
|
||||
by that party's individual claim. To the extent that you or we prevail on a claim and seek
|
||||
public injunctive relief (that is, injunctive relief that has the primary purpose and effect
|
||||
of prohibiting unlawful acts that threaten future injury to the public), the entitlement to
|
||||
and extent of such relief must be litigated in a civil court of competent jurisdiction and
|
||||
not in arbitration. The parties agree that litigation of any issues of public injunctive
|
||||
relief shall be stayed pending the outcome of the merits of any individual claims in
|
||||
arbitration.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(f) <u>Class Action Waiver</u>.{' '}
|
||||
<b>
|
||||
YOU AND DEXORDER AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS
|
||||
INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR
|
||||
REPRESENTATIVE PROCEEDING.
|
||||
</b>{' '}
|
||||
Further, if the parties' Dispute is resolved through arbitration, the arbitrator may not
|
||||
consolidate another person's claims with your claims, and may not otherwise preside over any
|
||||
form of a representative or class proceeding. If this specific provision is found to be
|
||||
unenforceable, then the entirety of this Dispute Resolution section shall be null and void.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(g) <u>Severability</u>. With the exception of any of the provisions in Section 16.(f) of
|
||||
these Terms ("<b>Class Action Waiver</b>"), if an arbitrator or court of competent
|
||||
jurisdiction decides that any part of these Terms is invalid or unenforceable, the other
|
||||
parts of these Terms will still apply.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">17. General Terms</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(a) <u>Reservation of Rights</u>. Dexorder and its licensors exclusively own all right,
|
||||
title and interest in and to the Dexorder Service, including all associated intellectual
|
||||
property rights. You acknowledge that the Dexorder Service is protected by copyright,
|
||||
trademark, and other laws of the United States and foreign countries. You agree not to
|
||||
remove, alter or obscure any copyright, trademark, service mark or other proprietary rights
|
||||
notices incorporated in or accompanying the Dexorder Service.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(b) <u>Entire Agreement</u>. These Terms, including any addendum terms, constitute the
|
||||
entire and exclusive understanding and agreement between Dexorder and you regarding the
|
||||
Dexorder Service, and these Terms supersede and replace all prior oral or written
|
||||
understandings or agreements between Dexorder and you regarding the Dexorder Service. If any
|
||||
provision of these Terms is held invalid or unenforceable by an arbitrator or a court of
|
||||
competent jurisdiction, that provision will be enforced to the maximum extent permissible
|
||||
and the other provisions of these Terms will remain in full force and effect. Except where
|
||||
provided by Applicable Law in your jurisdiction, you may not assign or transfer these Terms,
|
||||
by operation of law or otherwise, without Dexorder's prior written consent. Any attempt by
|
||||
you to assign or transfer these Terms absent our consent or your statutory right, will be
|
||||
null. Dexorder may freely assign or transfer these Terms without restriction. Subject to the
|
||||
foregoing, these Terms will bind and inure to the benefit of the parties, their successors
|
||||
and permitted assigns.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(c) <u>Notices</u>. Any notices or other communications provided by Dexorder under these
|
||||
Terms will be given: (i) via email; or (ii) by posting to the Dexorder Service. For notices
|
||||
made by email, the date of receipt will be deemed the date on which such notice is
|
||||
transmitted.
|
||||
</div>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
(d) <u>Waiver of Rights</u>. Dexorder's failure to enforce any right or provision of these
|
||||
Terms will not be considered a waiver of such right or provision. The waiver of any such
|
||||
right or provision will be effective only if in writing and signed by a duly authorized
|
||||
representative of Dexorder. Except as expressly set forth in these Terms, the exercise by
|
||||
either party of any of its remedies under these Terms will be without prejudice to its other
|
||||
remedies under these Terms or otherwise.
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl font-semibold mt-6 mb-4">18. Contact Information</h2>
|
||||
<div className="mb-4 leading-relaxed">
|
||||
If you have any questions about these Terms or the Dexorder Service, please contact Dexorder
|
||||
at <a href="mailto:legal@dexorder.com">legal@dexorder.com</a> or{' '}
|
||||
<a href="mailto:support@dexorder.com">support@dexorder.com</a>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
248
src/components/uniswap-quote.tsx
Normal file
248
src/components/uniswap-quote.tsx
Normal file
@@ -0,0 +1,248 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { parseUnits, formatUnits } from 'viem';
|
||||
|
||||
interface UniswapQuoteProps {
|
||||
amountIn: string;
|
||||
tokenInAddress: string | null;
|
||||
tokenOutAddress: string | null;
|
||||
tokenInDecimals: number;
|
||||
tokenOutDecimals: number;
|
||||
tokenOutSymbol: string;
|
||||
chainId: number;
|
||||
}
|
||||
|
||||
interface TokenPrices {
|
||||
[key: string]: number;
|
||||
}
|
||||
|
||||
export default function UniswapQuote({
|
||||
amountIn,
|
||||
tokenInAddress,
|
||||
tokenOutAddress,
|
||||
tokenInDecimals,
|
||||
tokenOutDecimals,
|
||||
tokenOutSymbol,
|
||||
chainId
|
||||
}: UniswapQuoteProps) {
|
||||
const [quote, setQuote] = useState<any>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [prices, setPrices] = useState<TokenPrices | null>(null);
|
||||
|
||||
// Only show on mainnet
|
||||
if (chainId !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't fetch quote if tokens aren't selected
|
||||
if (!tokenInAddress || !tokenOutAddress) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fetch token prices from CoinGecko using contract addresses
|
||||
useEffect(() => {
|
||||
if (!tokenInAddress || !tokenOutAddress) return;
|
||||
|
||||
const fetchPrices = async () => {
|
||||
try {
|
||||
// Fetch both token prices separately using the correct endpoint
|
||||
const [tokenInResponse, tokenOutResponse] = await Promise.all([
|
||||
fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=${tokenInAddress}&vs_currencies=usd`),
|
||||
fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=${tokenOutAddress}&vs_currencies=usd`)
|
||||
]);
|
||||
|
||||
const tokenInData = await tokenInResponse.json();
|
||||
const tokenOutData = await tokenOutResponse.json();
|
||||
|
||||
const tokenInPrice = tokenInData[tokenInAddress.toLowerCase()]?.usd || 0;
|
||||
const tokenOutPrice = tokenOutData[tokenOutAddress.toLowerCase()]?.usd || 0;
|
||||
|
||||
setPrices({
|
||||
tokenIn: tokenInPrice,
|
||||
tokenOut: tokenOutPrice
|
||||
});
|
||||
|
||||
console.log('Token prices:', { tokenInPrice, tokenOutPrice, tokenInData, tokenOutData });
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch prices:', err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPrices();
|
||||
// Refresh prices every 30 seconds
|
||||
const interval = setInterval(fetchPrices, 30000);
|
||||
return () => clearInterval(interval);
|
||||
}, [tokenInAddress, tokenOutAddress]);
|
||||
|
||||
const getQuote = async () => {
|
||||
if (!amountIn || parseFloat(amountIn) <= 0) {
|
||||
setError('Please enter a valid amount');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
// Convert amount to smallest unit based on token decimals using viem
|
||||
const amountInSmallestUnit = parseUnits(amountIn, tokenInDecimals).toString();
|
||||
|
||||
const response = await fetch('https://api.uniswap.org/v2/quote', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': 'https://app.uniswap.org'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
amount: amountInSmallestUnit,
|
||||
tokenIn: tokenInAddress,
|
||||
tokenInChainId: chainId,
|
||||
tokenOut: tokenOutAddress,
|
||||
tokenOutChainId: chainId,
|
||||
type: 'EXACT_INPUT',
|
||||
configs: [
|
||||
{
|
||||
protocols: ['V2', 'V3', 'V4'],
|
||||
enableUniversalRouter: true,
|
||||
routingType: 'CLASSIC'
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to fetch quote');
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Uniswap Quote:', data);
|
||||
setQuote(data);
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formatTokenAmount = (amount: string) => {
|
||||
const formatted = formatUnits(BigInt(amount), tokenOutDecimals);
|
||||
return parseFloat(formatted).toLocaleString('en-US', {
|
||||
maximumFractionDigits: tokenOutDecimals >= 18 ? 0 : 6
|
||||
});
|
||||
};
|
||||
|
||||
const getQuoteAmount = () => {
|
||||
if (!quote) return '0';
|
||||
// Handle nested quote structure
|
||||
return quote.quote?.quote || quote.quote || '0';
|
||||
};
|
||||
|
||||
const calculateCostBreakdown = () => {
|
||||
if (!quote || !prices || !prices.tokenIn) return null;
|
||||
|
||||
const tokenAmount = parseFloat(amountIn);
|
||||
const tradeValueUSD = tokenAmount * prices.tokenIn;
|
||||
|
||||
// Access nested quote object
|
||||
const quoteData = quote.quote || quote;
|
||||
|
||||
// 1. Gas Cost
|
||||
const gasCostUSD = parseFloat(quoteData.gasUseEstimateUSD || '0');
|
||||
|
||||
// 2. Uniswap UX Fee (0.25%)
|
||||
const uniswapFeePercent = 0.25;
|
||||
const uniswapFeeUSD = (uniswapFeePercent / 100) * tradeValueUSD;
|
||||
|
||||
const totalCostUSD = gasCostUSD + uniswapFeeUSD;
|
||||
|
||||
console.log('Cost breakdown calc:', {
|
||||
gasCostUSD,
|
||||
uniswapFeeUSD,
|
||||
totalCostUSD,
|
||||
tradeValueUSD,
|
||||
quoteData
|
||||
});
|
||||
|
||||
return {
|
||||
gasCostUSD,
|
||||
uniswapFeePercent,
|
||||
uniswapFeeUSD,
|
||||
totalCostUSD,
|
||||
tradeValueUSD
|
||||
};
|
||||
};
|
||||
|
||||
const costBreakdown = calculateCostBreakdown();
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-md p-4">
|
||||
<button
|
||||
onClick={getQuote}
|
||||
disabled={loading}
|
||||
className="w-full bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white font-semibold py-2 px-4 rounded"
|
||||
>
|
||||
{loading ? 'Getting Quote...' : 'Get Quote'}
|
||||
</button>
|
||||
|
||||
{error && (
|
||||
<div className="mt-3 p-3 bg-red-50 text-red-700 rounded text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{quote && costBreakdown && (
|
||||
<div className="mt-4 space-y-3">
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="text-sm text-gray-600">You Get ({tokenOutSymbol})</div>
|
||||
<div className="text-xl font-bold">
|
||||
{formatTokenAmount(getQuoteAmount())}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Total Costs Breakdown */}
|
||||
<div className="p-4 bg-blue-50 rounded-lg space-y-3">
|
||||
<h3 className="font-semibold text-lg text-gray-800">Total Costs Breakdown</h3>
|
||||
|
||||
{/* 1. Gas Cost */}
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm font-medium text-gray-700">1. Gas Cost (Network Fee)</span>
|
||||
<span className="text-sm font-bold text-gray-900">
|
||||
${costBreakdown.gasCostUSD.toFixed(2)} USD
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 2. Uniswap UX Fee */}
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm font-medium text-gray-700">2. Uniswap UX Fee</span>
|
||||
<span className="text-sm font-bold text-gray-900">
|
||||
{costBreakdown.uniswapFeePercent}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 pl-4">
|
||||
${costBreakdown.uniswapFeeUSD.toFixed(2)} USD
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Total */}
|
||||
<div className="pt-2 border-t border-gray-300">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm font-bold text-gray-800">Total Estimated Cost</span>
|
||||
<span className="text-base font-bold text-red-600">
|
||||
${costBreakdown.totalCostUSD.toFixed(2)} USD
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Trade Value Reference */}
|
||||
<div className="text-xs text-gray-500 text-center pt-1">
|
||||
Trade Value: ${costBreakdown.tradeValueUSD.toFixed(2)} USD
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -183,8 +183,8 @@ const IPartyInfoABI = [
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "int128",
|
||||
"internalType": "int128"
|
||||
"type": "uint256",
|
||||
"internalType": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view"
|
||||
|
||||
@@ -415,6 +415,19 @@ const IPartyPoolABI = [
|
||||
],
|
||||
"stateMutability": "payable"
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "mintImpl",
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address",
|
||||
"internalType": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view"
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "name",
|
||||
@@ -605,6 +618,19 @@ const IPartyPoolABI = [
|
||||
],
|
||||
"stateMutability": "view"
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "swapImpl",
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "address",
|
||||
"internalType": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view"
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "swapMint",
|
||||
|
||||
@@ -49,7 +49,8 @@ export function useGetAllTokens(offset: number = 0, limit: number = 100) {
|
||||
|
||||
// Get chain ID and contract address
|
||||
const chainId = await publicClient.getChainId();
|
||||
const address = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyPoolViewer: string } }>)[chainId.toString()]?.v1?.PartyPlanner;
|
||||
// @ts-ignore
|
||||
const address = (chainInfo as Record<string, { v1: { PartyPlanner: string } }>)[chainId.toString()]?.v1?.PartyPlanner;
|
||||
|
||||
if (!address) {
|
||||
setError('IPartyPlanner contract not found for current chain');
|
||||
@@ -98,6 +99,8 @@ export interface SwapRoute {
|
||||
poolAddress: `0x${string}`;
|
||||
inputTokenIndex: number;
|
||||
outputTokenIndex: number;
|
||||
inputTokenDecimal: number;
|
||||
outputTokenDecimal: number;
|
||||
}
|
||||
|
||||
export interface AvailableToken {
|
||||
@@ -195,68 +198,131 @@ export function useGetPoolsByToken(tokenAddress: `0x${string}` | undefined, offs
|
||||
return;
|
||||
}
|
||||
|
||||
// First, fetch all tokens from all working pools
|
||||
const poolTokensContracts = workingPools.map(poolAddress => ({
|
||||
address: poolAddress,
|
||||
abi: IPartyPoolABI,
|
||||
functionName: 'allTokens',
|
||||
}));
|
||||
|
||||
const poolTokensResults = await publicClient.multicall({
|
||||
contracts: poolTokensContracts as any,
|
||||
allowFailure: true,
|
||||
});
|
||||
|
||||
// Build a flat list of all unique token addresses we need to query
|
||||
const uniqueTokenAddresses = new Set<`0x${string}`>();
|
||||
uniqueTokenAddresses.add(tokenAddress); // Add input token
|
||||
|
||||
poolTokensResults.forEach((result) => {
|
||||
if (result.status === 'success') {
|
||||
const tokens = result.result as readonly `0x${string}`[];
|
||||
tokens.forEach(token => uniqueTokenAddresses.add(token));
|
||||
}
|
||||
});
|
||||
|
||||
const tokenAddressesArray = Array.from(uniqueTokenAddresses);
|
||||
|
||||
// Build multicall for all token symbols and decimals
|
||||
const tokenDataContracts = tokenAddressesArray.flatMap(addr => [
|
||||
{
|
||||
address: addr,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'symbol',
|
||||
},
|
||||
{
|
||||
address: addr,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'decimals',
|
||||
},
|
||||
]);
|
||||
|
||||
const tokenDataResults = await publicClient.multicall({
|
||||
contracts: tokenDataContracts as any,
|
||||
allowFailure: true,
|
||||
});
|
||||
|
||||
// Parse token data into a map
|
||||
const tokenDataMap = new Map<string, { symbol: string | null; decimals: number | null }>();
|
||||
for (let i = 0; i < tokenAddressesArray.length; i++) {
|
||||
const symbolResult = tokenDataResults[i * 2];
|
||||
const decimalsResult = tokenDataResults[i * 2 + 1];
|
||||
tokenDataMap.set(tokenAddressesArray[i].toLowerCase(), {
|
||||
symbol: symbolResult.status === 'success' ? (symbolResult.result as string) : null,
|
||||
decimals: decimalsResult.status === 'success' ? Number(decimalsResult.result) : null,
|
||||
});
|
||||
}
|
||||
|
||||
// Map to store available tokens with their swap routes
|
||||
const tokenRoutesMap = new Map<string, AvailableToken>();
|
||||
|
||||
// For each working pool, fetch all tokens and track indices
|
||||
for (const poolAddress of workingPools) {
|
||||
try {
|
||||
const tokensInPool = await publicClient.readContract({
|
||||
address: poolAddress,
|
||||
abi: IPartyPoolABI,
|
||||
functionName: 'allTokens',
|
||||
}) as readonly `0x${string}`[];
|
||||
// For each working pool, process tokens
|
||||
for (let poolIdx = 0; poolIdx < workingPools.length; poolIdx++) {
|
||||
const poolAddress = workingPools[poolIdx];
|
||||
const poolTokensResult = poolTokensResults[poolIdx];
|
||||
|
||||
// Find the input token index in this pool
|
||||
const inputTokenIndex = tokensInPool.findIndex(
|
||||
(token) => token.toLowerCase() === tokenAddress.toLowerCase()
|
||||
);
|
||||
if (poolTokensResult.status !== 'success') {
|
||||
console.error('Failed to fetch tokens for pool', poolAddress);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inputTokenIndex === -1) {
|
||||
console.error('Input token not found in pool', poolAddress);
|
||||
const tokensInPool = poolTokensResult.result as readonly `0x${string}`[];
|
||||
|
||||
// Find the input token index in this pool
|
||||
const inputTokenIndex = tokensInPool.findIndex(
|
||||
(token) => token.toLowerCase() === tokenAddress.toLowerCase()
|
||||
);
|
||||
|
||||
if (inputTokenIndex === -1) {
|
||||
console.error('Input token not found in pool', poolAddress);
|
||||
continue;
|
||||
}
|
||||
|
||||
const inputTokenData = tokenDataMap.get(tokenAddress.toLowerCase());
|
||||
const inputTokenDecimal = inputTokenData?.decimals ?? null;
|
||||
|
||||
// Process each token in the pool
|
||||
for (let outputTokenIndex = 0; outputTokenIndex < tokensInPool.length; outputTokenIndex++) {
|
||||
const outputTokenAddress = tokensInPool[outputTokenIndex];
|
||||
|
||||
// Skip if it's the same as the input token
|
||||
if (outputTokenIndex === inputTokenIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process each token in the pool
|
||||
for (let outputTokenIndex = 0; outputTokenIndex < tokensInPool.length; outputTokenIndex++) {
|
||||
const outputTokenAddress = tokensInPool[outputTokenIndex];
|
||||
const outputTokenData = tokenDataMap.get(outputTokenAddress.toLowerCase());
|
||||
const outputTokenSymbol = outputTokenData?.symbol ?? null;
|
||||
const outputTokenDecimal = outputTokenData?.decimals ?? null;
|
||||
|
||||
// Skip if it's the same as the input token
|
||||
if (outputTokenIndex === inputTokenIndex) {
|
||||
continue;
|
||||
}
|
||||
// Skip tokens with the same symbol as the selected token
|
||||
if (!outputTokenSymbol || outputTokenSymbol === selectedTokenSymbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the symbol of this token
|
||||
const outputTokenSymbol = await publicClient.readContract({
|
||||
// Skip tokens if decimals failed to load
|
||||
if (inputTokenDecimal === null || outputTokenDecimal === null) {
|
||||
console.error(`Failed to load decimals for token ${outputTokenAddress} or ${tokenAddress}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create or update the available token entry
|
||||
const tokenKey = outputTokenAddress.toLowerCase();
|
||||
if (!tokenRoutesMap.has(tokenKey)) {
|
||||
tokenRoutesMap.set(tokenKey, {
|
||||
address: outputTokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'symbol',
|
||||
}).catch(() => null);
|
||||
|
||||
// Skip tokens with the same symbol as the selected token
|
||||
if (!outputTokenSymbol || outputTokenSymbol === selectedTokenSymbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create or update the available token entry
|
||||
const tokenKey = outputTokenAddress.toLowerCase();
|
||||
if (!tokenRoutesMap.has(tokenKey)) {
|
||||
tokenRoutesMap.set(tokenKey, {
|
||||
address: outputTokenAddress,
|
||||
symbol: outputTokenSymbol,
|
||||
swapRoutes: [],
|
||||
});
|
||||
}
|
||||
|
||||
// Add this swap route
|
||||
tokenRoutesMap.get(tokenKey)!.swapRoutes.push({
|
||||
poolAddress,
|
||||
inputTokenIndex,
|
||||
outputTokenIndex,
|
||||
symbol: outputTokenSymbol,
|
||||
swapRoutes: [],
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching tokens from pool', poolAddress, err);
|
||||
|
||||
// Add this swap route
|
||||
tokenRoutesMap.get(tokenKey)!.swapRoutes.push({
|
||||
poolAddress,
|
||||
inputTokenIndex,
|
||||
outputTokenIndex,
|
||||
inputTokenDecimal,
|
||||
outputTokenDecimal,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,55 +369,54 @@ export function useTokenDetails(userAddress: `0x${string}` | undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build multicall contracts array - 4 calls per token (name, symbol, decimals, balanceOf)
|
||||
const contracts = tokens.flatMap((tokenAddress) => [
|
||||
{
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'name',
|
||||
},
|
||||
{
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'symbol',
|
||||
},
|
||||
{
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'decimals',
|
||||
},
|
||||
{
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [userAddress],
|
||||
},
|
||||
]);
|
||||
|
||||
// Execute multicall
|
||||
const results = await publicClient.multicall({
|
||||
contracts: contracts as any,
|
||||
allowFailure: true,
|
||||
});
|
||||
|
||||
// Parse results
|
||||
const details: TokenDetails[] = [];
|
||||
|
||||
// Make individual calls for each token
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const tokenAddress = tokens[i];
|
||||
try {
|
||||
const [name, symbol, decimals, balance] = await Promise.all([
|
||||
publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'name',
|
||||
}).catch(() => 'Unknown'),
|
||||
publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'symbol',
|
||||
}).catch(() => '???'),
|
||||
publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'decimals',
|
||||
}).catch(() => 18),
|
||||
publicClient.readContract({
|
||||
address: tokenAddress,
|
||||
abi: ERC20ABI,
|
||||
functionName: 'balanceOf',
|
||||
args: [userAddress],
|
||||
}).catch(() => BigInt(0)),
|
||||
]);
|
||||
const baseIndex = i * 4;
|
||||
const nameResult = results[baseIndex];
|
||||
const symbolResult = results[baseIndex + 1];
|
||||
const decimalsResult = results[baseIndex + 2];
|
||||
const balanceResult = results[baseIndex + 3];
|
||||
|
||||
details.push({
|
||||
address: tokenAddress,
|
||||
name: name as string,
|
||||
symbol: symbol as string,
|
||||
decimals: Number(decimals),
|
||||
balance: balance as bigint,
|
||||
index: i,
|
||||
});
|
||||
} catch (err) {
|
||||
// Add token with fallback values if individual call fails
|
||||
details.push({
|
||||
address: tokenAddress,
|
||||
name: 'Unknown',
|
||||
symbol: '???',
|
||||
decimals: 18,
|
||||
balance: BigInt(0),
|
||||
index: i,
|
||||
});
|
||||
}
|
||||
details.push({
|
||||
address: tokens[i],
|
||||
name: nameResult.status === 'success' ? (nameResult.result as string) : 'Unknown',
|
||||
symbol: symbolResult.status === 'success' ? (symbolResult.result as string) : '???',
|
||||
decimals: decimalsResult.status === 'success' ? Number(decimalsResult.result) : 18,
|
||||
balance: balanceResult.status === 'success' ? (balanceResult.result as bigint) : BigInt(0),
|
||||
index: i,
|
||||
});
|
||||
}
|
||||
|
||||
setTokenDetails(details);
|
||||
@@ -463,41 +528,12 @@ export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
||||
let tvlStr: string | undefined;
|
||||
|
||||
if (isWorking) {
|
||||
// Fetch pool price (use first token as quote, index 0)
|
||||
try {
|
||||
const priceRaw = await publicClient.readContract({
|
||||
address: partyInfoAddress as `0x${string}`,
|
||||
abi: IPartyInfoABI,
|
||||
functionName: 'poolPrice',
|
||||
args: [poolAddress, BigInt(0)],
|
||||
});
|
||||
|
||||
const price = BigInt(priceRaw as bigint | number);
|
||||
|
||||
if (price === 0n) {
|
||||
priceStr = undefined;
|
||||
} else {
|
||||
// Convert Q64 format to decimal (price = priceValue / 2^64)
|
||||
const Q64 = 2n ** 64n;
|
||||
const isNegative = price < 0n;
|
||||
const absPrice = isNegative ? -price : price;
|
||||
const priceFloat = Number(absPrice) / Number(Q64);
|
||||
const finalPrice = isNegative ? -priceFloat : priceFloat;
|
||||
|
||||
priceStr = `$${finalPrice.toFixed(4)}`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error fetching pool price for ${poolAddress}:`, err);
|
||||
priceStr = undefined;
|
||||
}
|
||||
|
||||
// Calculate TVL (approximate by getting first token balance and doubling it)
|
||||
// Fetch token decimals and balance first (needed for both price and TVL)
|
||||
try {
|
||||
if (tokens && tokens.length > 0) {
|
||||
const firstTokenAddress = tokens[0];
|
||||
|
||||
// Get token decimals and balance
|
||||
const [decimals, balance] = await Promise.all([
|
||||
// Get token decimals, balance, and pool price in parallel
|
||||
const [decimals, balance, priceRaw] = await Promise.all([
|
||||
publicClient.readContract({
|
||||
address: firstTokenAddress as `0x${string}`,
|
||||
abi: ERC20ABI,
|
||||
@@ -509,16 +545,36 @@ export function useGetAllPools(offset: number = 0, limit: number = 100) {
|
||||
functionName: 'balanceOf',
|
||||
args: [poolAddress],
|
||||
}) as Promise<bigint>,
|
||||
publicClient.readContract({
|
||||
address: partyInfoAddress as `0x${string}`,
|
||||
abi: IPartyInfoABI,
|
||||
functionName: 'poolPrice',
|
||||
args: [poolAddress, BigInt(0)],
|
||||
}),
|
||||
]);
|
||||
|
||||
// Convert balance to float and double it for total TVL approximation
|
||||
const tokenBalance = Number(balance) / Math.pow(10, decimals);
|
||||
const approximateTVL = tokenBalance * 3;
|
||||
// Calculate pool price using actual token decimals
|
||||
const price = BigInt(priceRaw as bigint | number);
|
||||
if (price === 0n) {
|
||||
priceStr = undefined;
|
||||
} else {
|
||||
// Convert Q64 format to decimal (price = priceValue / 2^64)
|
||||
const Q64 = 2n ** 64n;
|
||||
const priceFloat = Number(price) / Number(Q64);
|
||||
|
||||
// Adjust for token decimals (poolPrice assumes 18 decimals, adjust based on actual token decimals)
|
||||
const finalPrice = priceFloat * (Math.pow(10, 18) / Math.pow(10, decimals));
|
||||
priceStr = `$${finalPrice.toFixed(4)}`;
|
||||
}
|
||||
|
||||
// Calculate TVL (approximate by getting first token balance and multiplying by number of tokens)
|
||||
const tokenBalance = Number(balance) / Math.pow(10, decimals);
|
||||
const approximateTVL = tokenBalance * tokens.length;
|
||||
tvlStr = formatTVL(approximateTVL);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error fetching TVL for ${poolAddress}:`, err);
|
||||
console.error(`Error fetching pool price/TVL for ${poolAddress}:`, err);
|
||||
priceStr = undefined;
|
||||
tvlStr = undefined;
|
||||
}
|
||||
}
|
||||
@@ -564,17 +620,20 @@ export interface SwapMintAmounts {
|
||||
amountInUsed: bigint;
|
||||
fee: bigint;
|
||||
lpMinted: bigint;
|
||||
calculatedSlippage?: number; // Percentage, e.g. 5.5 means 5.5%
|
||||
}
|
||||
|
||||
export interface BurnSwapAmounts {
|
||||
amountOut: bigint;
|
||||
outFee: bigint;
|
||||
calculatedSlippage?: number; // Percentage, e.g. 5.5 means 5.5%
|
||||
}
|
||||
|
||||
export function useSwapMintAmounts(
|
||||
poolAddress: `0x${string}` | undefined,
|
||||
inputTokenIndex: number | undefined,
|
||||
maxAmountIn: bigint | undefined
|
||||
maxAmountIn: bigint | undefined,
|
||||
inputTokenDecimals: number | undefined // Decimals of the input token
|
||||
) {
|
||||
const publicClient = usePublicClient();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
@@ -595,8 +654,11 @@ export function useSwapMintAmounts(
|
||||
}
|
||||
|
||||
const fetchSwapMintAmounts = async () => {
|
||||
if (!publicClient) {
|
||||
setLoading(false);
|
||||
if (!publicClient) return;
|
||||
|
||||
// Early validation
|
||||
if (inputTokenDecimals === undefined) {
|
||||
setError('inputTokenDecimals is required but was undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -604,32 +666,58 @@ export function useSwapMintAmounts(
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
// Get chain ID and contract address
|
||||
const chainId = await publicClient.getChainId();
|
||||
const viewerAddress = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyPoolViewer: string } }>)[chainId.toString()]?.v1?.PartyPoolViewer;
|
||||
const partyInfoAddress = (chainInfo as Record<string, { v1: { PartyInfo: string } }>)[chainId.toString()]?.v1?.PartyInfo;
|
||||
|
||||
if (!viewerAddress) {
|
||||
setError('IPartyPoolViewer contract not found for current chain');
|
||||
if (!partyInfoAddress) {
|
||||
setError('PartyInfo contract not found for current chain');
|
||||
setSwapMintAmounts(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call swapMintAmounts function
|
||||
const result = await publicClient.readContract({
|
||||
address: viewerAddress as `0x${string}`,
|
||||
address: partyInfoAddress as `0x${string}`,
|
||||
abi: IPartyPoolViewerABI,
|
||||
functionName: 'swapMintAmounts',
|
||||
args: [poolAddress, BigInt(inputTokenIndex), maxAmountIn],
|
||||
}) as readonly [bigint, bigint, bigint];
|
||||
|
||||
// Fetch and calculate pool price
|
||||
let poolPrice: number | undefined;
|
||||
let calculatedSlippage: number | undefined;
|
||||
|
||||
try {
|
||||
const poolPriceInt128 = await publicClient.readContract({
|
||||
address: partyInfoAddress as `0x${string}`,
|
||||
abi: IPartyInfoABI,
|
||||
functionName: 'poolPrice',
|
||||
args: [poolAddress, BigInt(inputTokenIndex)],
|
||||
}) as bigint;
|
||||
|
||||
const basePrice = Number(poolPriceInt128) / (2 ** 64);
|
||||
poolPrice = 1 / (basePrice * Math.pow(10, 18 - inputTokenDecimals));
|
||||
// Calculate slippage
|
||||
const decimalsMultiplier = Math.pow(10, inputTokenDecimals);
|
||||
const lpMinted = Number(result[1]) / Math.pow(10, 18);
|
||||
const amountIn = Number(result[0]) / decimalsMultiplier;
|
||||
const fee = Number(result[2]) / decimalsMultiplier;
|
||||
|
||||
const swapPrice = lpMinted / (amountIn - fee);
|
||||
calculatedSlippage = ((poolPrice - swapPrice) / poolPrice) * 100;
|
||||
} catch (priceErr) {
|
||||
console.error('Error fetching poolPrice or calculating slippage:', priceErr);
|
||||
}
|
||||
|
||||
setSwapMintAmounts({
|
||||
amountInUsed: result[0],
|
||||
fee: result[1],
|
||||
lpMinted: result[2],
|
||||
fee: result[2],
|
||||
lpMinted: result[1],
|
||||
calculatedSlippage,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Error calling swapMintAmounts:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch swap mint amounts');
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch swap mint amounts';
|
||||
setError(errorMessage);
|
||||
setSwapMintAmounts(null);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -637,7 +725,7 @@ export function useSwapMintAmounts(
|
||||
};
|
||||
|
||||
fetchSwapMintAmounts();
|
||||
}, [publicClient, mounted, poolAddress, inputTokenIndex, maxAmountIn]);
|
||||
}, [publicClient, mounted, poolAddress, inputTokenIndex, maxAmountIn, inputTokenDecimals]);
|
||||
|
||||
return {
|
||||
swapMintAmounts,
|
||||
@@ -650,7 +738,8 @@ export function useSwapMintAmounts(
|
||||
export function useBurnSwapAmounts(
|
||||
poolAddress: `0x${string}` | undefined,
|
||||
lpAmount: bigint | undefined,
|
||||
inputTokenIndex: number | undefined
|
||||
inputTokenIndex: number | undefined,
|
||||
tokenDecimals: number | undefined // Decimals of the output token
|
||||
) {
|
||||
const publicClient = usePublicClient();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
@@ -679,57 +768,66 @@ export function useBurnSwapAmounts(
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
// Get chain ID and contract address
|
||||
const chainId = await publicClient.getChainId();
|
||||
const viewerAddress = (chainInfo as Record<string, { v1: { PartyPlanner: string; PartyPoolViewer: string } }>)[chainId.toString()]?.v1?.PartyPoolViewer;
|
||||
// @ts-ignore
|
||||
const partyInfoAddress = (chainInfo as Record<string, { v1: { PartyInfo: string } }>)[chainId.toString()]?.v1?.PartyInfo;
|
||||
|
||||
if (!viewerAddress) {
|
||||
setError('IPartyPoolViewer contract not found for current chain');
|
||||
if (!partyInfoAddress) {
|
||||
setError('PartyInfo contract not found for current chain');
|
||||
setBurnSwapAmounts(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Log inputs
|
||||
console.log('🔍 burnSwapAmounts INPUTS:', {
|
||||
chainId: chainId.toString(),
|
||||
rpcUrl: publicClient.transport?.url || 'Unknown',
|
||||
poolAddress,
|
||||
lpAmount: lpAmount.toString(),
|
||||
inputTokenIndex,
|
||||
viewerAddress,
|
||||
});
|
||||
|
||||
// Call burnSwapAmounts function - returns [amountOut, outFee]
|
||||
const result = await publicClient.readContract({
|
||||
address: viewerAddress as `0x${string}`,
|
||||
address: partyInfoAddress as `0x${string}`,
|
||||
abi: IPartyPoolViewerABI,
|
||||
functionName: 'burnSwapAmounts',
|
||||
args: [poolAddress, lpAmount, BigInt(inputTokenIndex)],
|
||||
}) as readonly [bigint, bigint];
|
||||
|
||||
// Log raw result
|
||||
console.log('📊 burnSwapAmounts RAW RESULT:', {
|
||||
resultArray: result,
|
||||
amountOut: result[0].toString(),
|
||||
outFee: result[1].toString(),
|
||||
});
|
||||
// Calculate slippage for burnSwap using poolPrice
|
||||
let calculatedSlippage: number | undefined;
|
||||
if (tokenDecimals !== undefined) {
|
||||
try {
|
||||
// Get the market price from poolPrice (quoteTokenIndex = 0)
|
||||
const poolPriceInt128 = await publicClient.readContract({
|
||||
address: partyInfoAddress as `0x${string}`,
|
||||
abi: IPartyInfoABI,
|
||||
functionName: 'poolPrice',
|
||||
args: [poolAddress, BigInt(inputTokenIndex)],
|
||||
}) as bigint;
|
||||
|
||||
// Convert Q64 format to decimal (price = priceValue / 2^64)
|
||||
let poolPrice = Number(poolPriceInt128) / 2 ** 64;
|
||||
poolPrice = (poolPrice * Math.pow(10, 18 - tokenDecimals));
|
||||
|
||||
// For burnSwap: swapPrice = (outAmount + fee) / lpAmount
|
||||
// Need to apply decimal corrections: outAmount and fee are in token decimals, lpAmount is in 18 decimals
|
||||
const outAmountDecimal = Number(result[0]) / Math.pow(10, tokenDecimals);
|
||||
const feeDecimal = Number(result[1]) / Math.pow(10, tokenDecimals);
|
||||
const lpAmountDecimal = Number(lpAmount) / Math.pow(10, 18); // LP tokens have 18 decimals
|
||||
|
||||
const swapPrice = (outAmountDecimal + feeDecimal) / lpAmountDecimal;
|
||||
calculatedSlippage = ((poolPrice - swapPrice) / poolPrice) * 100;
|
||||
|
||||
} catch (slippageErr) {
|
||||
console.error(`Error calculating slippage for burnSwap:`, slippageErr);
|
||||
}
|
||||
}
|
||||
|
||||
const parsedAmounts = {
|
||||
amountOut: result[0],
|
||||
outFee: result[1],
|
||||
calculatedSlippage,
|
||||
};
|
||||
|
||||
// Log parsed result
|
||||
console.log('✅ burnSwapAmounts PARSED:', {
|
||||
amountOut: parsedAmounts.amountOut.toString(),
|
||||
outFee: parsedAmounts.outFee.toString(),
|
||||
});
|
||||
|
||||
setBurnSwapAmounts(parsedAmounts);
|
||||
} catch (err) {
|
||||
console.error('Error calling burnSwapAmounts:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch burn swap amounts');
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to fetch burn swap amounts';
|
||||
setError(errorMessage);
|
||||
setBurnSwapAmounts(null);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -737,7 +835,7 @@ export function useBurnSwapAmounts(
|
||||
};
|
||||
|
||||
fetchBurnSwapAmounts();
|
||||
}, [publicClient, mounted, poolAddress, lpAmount, inputTokenIndex]);
|
||||
}, [publicClient, mounted, poolAddress, lpAmount, inputTokenIndex, tokenDecimals]);
|
||||
|
||||
return {
|
||||
burnSwapAmounts,
|
||||
|
||||
@@ -2,14 +2,37 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { usePublicClient, useWalletClient } from 'wagmi';
|
||||
import { decodeEventLog } from 'viem';
|
||||
import { decodeEventLog, parseUnits } from 'viem';
|
||||
import IPartyPoolABI from '@/contracts/IPartyPoolABI';
|
||||
import chainInfo from '../../../lmsr-amm/liqp-deployments.json';
|
||||
import chainInfo from '../contracts/liqp-deployments.json';
|
||||
import IPartyInfoABI from '@/contracts/IPartyInfoABI';
|
||||
import type { AvailableToken } from './usePartyPlanner';
|
||||
|
||||
// Q96 constant for price calculations
|
||||
const Q96 = 1n << 96n;
|
||||
|
||||
/**
|
||||
* Calculate slippage percentage based on market price and actual swap execution price
|
||||
* @param marketPrice The current market price from the pool (in Q64 format, already converted to decimal)
|
||||
* @param swapOutputAmount The output amount from the swap
|
||||
* @param swapInputAmount The input amount for the swap
|
||||
* @param swapFee The fee charged for the swap
|
||||
* @returns Slippage as a percentage (e.g., 5.5 means 5.5%)
|
||||
*/
|
||||
export function calculateSlippage(
|
||||
marketPrice: number,
|
||||
swapOutputAmount: number,
|
||||
swapInputAmount: number,
|
||||
swapFee: number
|
||||
): number {
|
||||
// Calculate actual swap price with decimal correction
|
||||
const swapPrice = swapOutputAmount / ((swapInputAmount) - (swapFee));
|
||||
// Calculate slippage percentage: ((swapPrice - marketPrice) / marketPrice) * 100
|
||||
const slippage = ((marketPrice - swapPrice) / marketPrice) * 100;
|
||||
|
||||
return slippage;
|
||||
}
|
||||
|
||||
export interface SwapAmountResult {
|
||||
tokenAddress: `0x${string}`;
|
||||
tokenSymbol: string;
|
||||
@@ -20,6 +43,7 @@ export interface SwapAmountResult {
|
||||
kappa: bigint;
|
||||
inputTokenIndex: number;
|
||||
outputTokenIndex: number;
|
||||
calculatedSlippage?: number; // Percentage, e.g. 5.5 means 5.5%
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,22 +119,11 @@ export function useSwapAmounts(
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const amountInWei = BigInt(Math.floor(parseFloat(fromAmount) * Math.pow(10, fromTokenDecimals)));
|
||||
|
||||
// Calculate limit price based on slippage tolerance
|
||||
// limitPrice in Q96 format = Q96 * (100 + slippage%) / 100
|
||||
// This represents the maximum acceptable price ratio (1 + slippage%)
|
||||
const slippageBasisPoints = BigInt(Math.floor(slippagePercent * 100)); // Convert to basis points (0.5% = 50)
|
||||
const limitPrice = (Q96 * (10000n + slippageBasisPoints)) / 10000n;
|
||||
|
||||
console.log('Limit Price Calculation:', {
|
||||
slippagePercent,
|
||||
slippageBasisPoints: slippageBasisPoints.toString(),
|
||||
limitPriceQ96: limitPrice.toString(),
|
||||
Q96: Q96.toString(),
|
||||
});
|
||||
const amountInTokenUnits = parseUnits(fromAmount, fromTokenDecimals);
|
||||
|
||||
const results: SwapAmountResult[] = [];
|
||||
const chainId = await publicClient.getChainId();
|
||||
const partyInfoAddress = (chainInfo as any)[chainId]?.v1?.PartyInfo as `0x${string}` | undefined;
|
||||
|
||||
// Calculate swap amounts for ALL routes of each token
|
||||
for (const token of availableTokens) {
|
||||
@@ -121,21 +134,19 @@ export function useSwapAmounts(
|
||||
// Evaluate ALL routes for this token
|
||||
for (const route of token.swapRoutes) {
|
||||
try {
|
||||
// Get swap amounts
|
||||
const swapResult = await publicClient.readContract({
|
||||
// Get swap amounts with NO LIMIT
|
||||
const [swapInputAmount, swapOutputAmount, inFee] = await publicClient.readContract({
|
||||
address: route.poolAddress,
|
||||
abi: IPartyPoolABI,
|
||||
functionName: 'swapAmounts',
|
||||
args: [
|
||||
BigInt(route.inputTokenIndex),
|
||||
BigInt(route.outputTokenIndex),
|
||||
amountInWei,
|
||||
limitPrice,
|
||||
amountInTokenUnits,
|
||||
0n, // NO LIMIT
|
||||
],
|
||||
}) as readonly [bigint, bigint, bigint];
|
||||
|
||||
const [amountIn, amountOut, fee] = swapResult;
|
||||
|
||||
// Get kappa for this pool
|
||||
const kappa = await publicClient.readContract({
|
||||
address: route.poolAddress,
|
||||
@@ -143,16 +154,46 @@ export function useSwapAmounts(
|
||||
functionName: 'kappa',
|
||||
}) as bigint;
|
||||
|
||||
// Calculate slippage for this route
|
||||
let calculatedSlippage: number | undefined;
|
||||
if (partyInfoAddress) {
|
||||
try {
|
||||
// Get the current market price from PoolInfo
|
||||
const priceInt128 = await publicClient.readContract({
|
||||
address: partyInfoAddress,
|
||||
abi: IPartyInfoABI,
|
||||
functionName: 'price',
|
||||
args: [route.poolAddress, BigInt(route.inputTokenIndex), BigInt(route.outputTokenIndex)],
|
||||
}) as bigint;
|
||||
|
||||
// Convert Q128 format to decimal (price = priceValue / 2^128)
|
||||
// Then apply decimal conversion: 10^18 / 10^outputTokenDecimals
|
||||
const priceQ128 = Number(priceInt128) / 2 ** 128;
|
||||
const decimalAdjustment = 10 ** (route.outputTokenDecimal - route.inputTokenDecimal);
|
||||
const marketPrice = priceQ128 / decimalAdjustment;
|
||||
const swapOutputAmountDecimal = Number(swapOutputAmount) / 10 ** route.outputTokenDecimal;
|
||||
const swapInputAmountDecimal = Number(swapInputAmount) / 10 ** route.inputTokenDecimal;
|
||||
const swapFeeDecimal = Number(inFee) / 10 ** route.inputTokenDecimal;
|
||||
// Calculate slippage using the reusable function
|
||||
|
||||
calculatedSlippage = calculateSlippage(marketPrice, swapOutputAmountDecimal, swapInputAmountDecimal, swapFeeDecimal);
|
||||
console.log('calculatedSlippage', calculatedSlippage)
|
||||
} catch (slippageErr) {
|
||||
console.error(`Error calculating slippage for ${token.symbol}:`, slippageErr);
|
||||
}
|
||||
}
|
||||
|
||||
routeResults.push({
|
||||
tokenAddress: token.address,
|
||||
tokenSymbol: token.symbol,
|
||||
amountIn,
|
||||
amountOut,
|
||||
fee,
|
||||
amountIn: swapInputAmount,
|
||||
amountOut: swapOutputAmount,
|
||||
fee: inFee,
|
||||
poolAddress: route.poolAddress,
|
||||
kappa,
|
||||
inputTokenIndex: route.inputTokenIndex,
|
||||
outputTokenIndex: route.outputTokenIndex,
|
||||
calculatedSlippage,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Error calculating swap for ${token.symbol} via ${route.poolAddress}:`, err);
|
||||
@@ -319,9 +360,7 @@ export function useSwap() {
|
||||
await publicClient.waitForTransactionReceipt({ hash: approvalHash });
|
||||
console.log('✅ Approval confirmed');
|
||||
|
||||
// STEP 2: Calculate limit price and deadline
|
||||
const slippageBasisPoints = BigInt(Math.floor(slippagePercent * 100));
|
||||
const limitPrice = (Q96 * (10000n + slippageBasisPoints)) / 10000n;
|
||||
// STEP 2: Calculate deadline
|
||||
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1200);
|
||||
|
||||
console.log('🚀 Executing swap with params:', {
|
||||
@@ -331,12 +370,12 @@ export function useSwap() {
|
||||
inputTokenIndex,
|
||||
outputTokenIndex,
|
||||
maxAmountIn: maxAmountIn.toString(),
|
||||
limitPrice: limitPrice.toString(),
|
||||
limitPrice: '0 (no limit)',
|
||||
deadline: deadline.toString(),
|
||||
unwrap: false,
|
||||
});
|
||||
|
||||
// STEP 3: Execute the swap transaction
|
||||
// STEP 3: Execute the swap transaction with no limit price
|
||||
const hash = await walletClient.writeContract({
|
||||
address: poolAddress,
|
||||
abi: IPartyPoolABI,
|
||||
@@ -348,7 +387,7 @@ export function useSwap() {
|
||||
BigInt(inputTokenIndex),
|
||||
BigInt(outputTokenIndex),
|
||||
maxAmountIn,
|
||||
limitPrice,
|
||||
0n, // no limit price
|
||||
deadline,
|
||||
false, // unwrap
|
||||
'0x', // cbData (empty bytes)
|
||||
@@ -815,6 +854,7 @@ export function useBurn() {
|
||||
});
|
||||
|
||||
if (decodedLog.eventName === 'Burn') {
|
||||
// @ts-ignore
|
||||
const { amounts, lpBurned } = decodedLog.args as {
|
||||
amounts: bigint[];
|
||||
lpBurned: bigint;
|
||||
|
||||
Reference in New Issue
Block a user