diff --git a/.env.local b/.env.local
index 008bb05..f20b6c7 100644
--- a/.env.local
+++ b/.env.local
@@ -1,3 +1,5 @@
-NEXT_PUBLIC_WC_PROJECT_ID=your_project_id_here
+NEXT_PUBLIC_WC_PROJECT_ID=1ac26acf6371d43254aa7c9d285252e2
NEXT_PUBLIC_ENABLE_TESTNETS=false
-NEXT_PUBLIC_BETA_ACCESS_CODE=your_code
\ No newline at end of file
+NEXT_PUBLIC_BETA_ACCESS_CODE=your_code
+JWT_SECRET=your_super_secret_jwt_key
+SERVER_SECRET_ACCESS_CODE=yieldsogood
\ No newline at end of file
diff --git a/next.config.js b/next.config.js
index bea0abf..36c2313 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,6 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
+ transpilePackages: ['@vanilla-extract', '@rainbow-me'],
reactStrictMode: true,
+ experimental: {
+ esmExternals: 'loose',
+ },
webpack: (config) => {
config.externals.push('pino-pretty', 'lokijs', 'encoding');
return config;
diff --git a/package-lock.json b/package-lock.json
index 99374db..02fdb68 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,29 +10,34 @@
"dependencies": {
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
- "@rainbow-me/rainbowkit": "^2.2.4",
+ "@rainbow-me/rainbowkit": "^2.2.8",
"@tanstack/react-query": "^5.55.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "cookie": "^1.0.2",
"ethers": "^6.13.5",
+ "jsonwebtoken": "^9.0.2",
"lucide-react": "^0.484.0",
"next": "^15.1.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-error-boundary": "^5.0.0",
"react-icons": "^5.5.0",
- "tailwind-merge": "^3.0.2",
+ "recharts": "^2.15.3",
+ "tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7",
"viem": "2.23.3",
"wagmi": "^2.14.11"
},
"devDependencies": {
"@shadcn/ui": "^0.0.4",
+ "@types/jsonwebtoken": "^9.0.9",
"@types/node": "^20.14.8",
"@types/react": "^19.0.6",
"autoprefixer": "^10.4.21",
"eslint-config-next": "^15.1.7",
"postcss": "^8.4.35",
+ "postinstall-postinstall": "^2.1.0",
"tailwindcss": "^3.4.1",
"typescript": "5.5.4"
}
@@ -138,8 +143,7 @@
"node_modules/@emotion/hash": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
- "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
- "license": "MIT"
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.5.1",
@@ -1996,16 +2000,15 @@
"license": "MIT"
},
"node_modules/@rainbow-me/rainbowkit": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/@rainbow-me/rainbowkit/-/rainbowkit-2.2.4.tgz",
- "integrity": "sha512-LUYBcB5bzLf6/BMdnW3dEFHVqoPkTGcFN3u6WamaIHXuqD9HT+HVAeNlcYvKENBXldN2zNBs1Bt3k8Oy7y5bTw==",
- "license": "MIT",
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/@rainbow-me/rainbowkit/-/rainbowkit-2.2.8.tgz",
+ "integrity": "sha512-EdNIK2cdAT6GJ9G11wx7nCVfjqBfxh7dx/1DhPYrB+yg+VFrII6cM1PiMFVC9evD4mqVHe9mmLAt3nvlwDdiPQ==",
"dependencies": {
- "@vanilla-extract/css": "1.15.5",
- "@vanilla-extract/dynamic": "2.1.2",
- "@vanilla-extract/sprinkles": "1.6.3",
+ "@vanilla-extract/css": "1.17.3",
+ "@vanilla-extract/dynamic": "2.1.4",
+ "@vanilla-extract/sprinkles": "1.6.4",
"clsx": "2.1.1",
- "qrcode": "1.5.4",
+ "cuer": "0.0.2",
"react-remove-scroll": "2.6.2",
"ua-parser-js": "^1.0.37"
},
@@ -2020,23 +2023,6 @@
"wagmi": "^2.9.0"
}
},
- "node_modules/@rainbow-me/rainbowkit/node_modules/qrcode": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
- "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
- "license": "MIT",
- "dependencies": {
- "dijkstrajs": "^1.0.1",
- "pngjs": "^5.0.0",
- "yargs": "^15.3.1"
- },
- "bin": {
- "qrcode": "bin/qrcode"
- },
- "engines": {
- "node": ">=10.13.0"
- }
- },
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -2236,6 +2222,60 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+ "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
+ },
"node_modules/@types/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -2268,6 +2308,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/jsonwebtoken": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz",
+ "integrity": "sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*",
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/ms": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
@@ -2694,13 +2745,12 @@
]
},
"node_modules/@vanilla-extract/css": {
- "version": "1.15.5",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/css/-/css-1.15.5.tgz",
- "integrity": "sha512-N1nQebRWnXvlcmu9fXKVUs145EVwmWtMD95bpiEKtvehHDpUhmO1l2bauS7FGYKbi3dU1IurJbGpQhBclTr1ng==",
- "license": "MIT",
+ "version": "1.17.3",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/css/-/css-1.17.3.tgz",
+ "integrity": "sha512-jHivr1UPoJTX5Uel4AZSOwrCf4mO42LcdmnhJtUxZaRWhW4FviFbIfs0moAWWld7GOT+2XnuVZjjA/K32uUnMQ==",
"dependencies": {
"@emotion/hash": "^0.9.0",
- "@vanilla-extract/private": "^1.0.6",
+ "@vanilla-extract/private": "^1.0.8",
"css-what": "^6.1.0",
"cssesc": "^3.0.0",
"csstype": "^3.0.7",
@@ -2714,25 +2764,22 @@
}
},
"node_modules/@vanilla-extract/dynamic": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/dynamic/-/dynamic-2.1.2.tgz",
- "integrity": "sha512-9BGMciD8rO1hdSPIAh1ntsG4LPD3IYKhywR7VOmmz9OO4Lx1hlwkSg3E6X07ujFx7YuBfx0GDQnApG9ESHvB2A==",
- "license": "MIT",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/dynamic/-/dynamic-2.1.4.tgz",
+ "integrity": "sha512-7+Ot7VlP3cIzhJnTsY/kBtNs21s0YD7WI1rKJJKYP56BkbDxi/wrQUWMGEczKPUDkJuFcvbye+E2ub1u/mHH9w==",
"dependencies": {
- "@vanilla-extract/private": "^1.0.6"
+ "@vanilla-extract/private": "^1.0.8"
}
},
"node_modules/@vanilla-extract/private": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/private/-/private-1.0.6.tgz",
- "integrity": "sha512-ytsG/JLweEjw7DBuZ/0JCN4WAQgM9erfSTdS1NQY778hFQSZ6cfCDEZZ0sgVm4k54uNz6ImKB33AYvSR//fjxw==",
- "license": "MIT"
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/private/-/private-1.0.9.tgz",
+ "integrity": "sha512-gT2jbfZuaaCLrAxwXbRgIhGhcXbRZCG3v4TTUnjw0EJ7ArdBRxkq4msNJkbuRkCgfIK5ATmprB5t9ljvLeFDEA=="
},
"node_modules/@vanilla-extract/sprinkles": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/@vanilla-extract/sprinkles/-/sprinkles-1.6.3.tgz",
- "integrity": "sha512-oCHlQeYOBIJIA2yWy2GnY5wE2A7hGHDyJplJo4lb+KEIBcJWRnDJDg8ywDwQS5VfWJrBBO3drzYZPFpWQjAMiQ==",
- "license": "MIT",
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/@vanilla-extract/sprinkles/-/sprinkles-1.6.4.tgz",
+ "integrity": "sha512-lW3MuIcdIeHKX81DzhTnw68YJdL1ial05exiuvTLJMdHXQLKcVB93AncLPajMM6mUhaVVx5ALZzNHMTrq/U9Hg==",
"peerDependencies": {
"@vanilla-extract/css": "^1.0.0"
}
@@ -3815,6 +3862,12 @@
"ieee754": "^1.2.1"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/bufferutil": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
@@ -4131,6 +4184,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cookie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
+ "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/cookie-es": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz",
@@ -4188,10 +4250,9 @@
}
},
"node_modules/css-what": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
- "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
- "license": "BSD-2-Clause",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
"engines": {
"node": ">= 6"
},
@@ -4217,6 +4278,140 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
+ "node_modules/cuer": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/cuer/-/cuer-0.0.2.tgz",
+ "integrity": "sha512-MG1BYnnSLqBnO0dOBS1Qm/TEc9DnFa9Sz2jMA24OF4hGzs8UuPjpKBMkRPF3lrpC+7b3EzULwooX9djcvsM8IA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/wevm"
+ }
+ ],
+ "dependencies": {
+ "qr": "~0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18",
+ "typescript": ">=5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@@ -4330,6 +4525,11 @@
"node": ">=0.10.0"
}
},
+ "node_modules/decimal.js-light": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
+ },
"node_modules/decode-uri-component": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
@@ -4340,10 +4540,9 @@
}
},
"node_modules/dedent": {
- "version": "1.5.3",
- "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
- "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==",
- "license": "MIT",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz",
+ "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==",
"peerDependencies": {
"babel-plugin-macros": "^3.1.0"
},
@@ -4364,14 +4563,12 @@
"node_modules/deep-object-diff": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz",
- "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==",
- "license": "MIT"
+ "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA=="
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -4489,6 +4686,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -4521,6 +4727,15 @@
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"license": "MIT"
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/eciesjs": {
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.14.tgz",
@@ -5612,6 +5827,14 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
+ "node_modules/fast-equals": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
+ "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/fast-glob": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@@ -6352,6 +6575,14 @@
"node": ">= 0.4"
}
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/iron-webcrypto": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz",
@@ -6897,7 +7128,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
@@ -6989,6 +7219,28 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -7005,6 +7257,27 @@
"node": ">=4.0"
}
},
+ "node_modules/jwa": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/keccak": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz",
@@ -7148,6 +7421,47 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -7156,6 +7470,12 @@
"license": "MIT",
"peer": true
},
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
"node_modules/log-symbols": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz",
@@ -7190,7 +7510,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
@@ -7227,7 +7546,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/media-query-parser/-/media-query-parser-2.0.2.tgz",
"integrity": "sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==",
- "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5"
}
@@ -7347,8 +7665,7 @@
"node_modules/modern-ahocorasick": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/modern-ahocorasick/-/modern-ahocorasick-1.1.0.tgz",
- "integrity": "sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ==",
- "license": "MIT"
+ "integrity": "sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ=="
},
"node_modules/motion": {
"version": "10.16.2",
@@ -8317,6 +8634,13 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT"
},
+ "node_modules/postinstall-postinstall": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz",
+ "integrity": "sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==",
+ "dev": true,
+ "hasInstallScript": true
+ },
"node_modules/preact": {
"version": "10.26.4",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.4.tgz",
@@ -8368,7 +8692,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
@@ -8403,6 +8726,17 @@
"node": ">=6"
}
},
+ "node_modules/qr": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/qr/-/qr-0.5.0.tgz",
+ "integrity": "sha512-LtnyJsepKCMzfmHBZKVNo1g29kS+8ZbuxE9294EsRhHgVVpy4x8eFw9o4J9SIolDHoDYuaEIY+z8UjiCv/eudA==",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/qrcode": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz",
@@ -8517,7 +8851,6 @@
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/react-remove-scroll": {
@@ -8567,6 +8900,20 @@
}
}
},
+ "node_modules/react-smooth": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
+ "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
+ "dependencies": {
+ "fast-equals": "^5.0.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
@@ -8589,6 +8936,21 @@
}
}
},
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -8643,6 +9005,46 @@
"node": ">= 12.13.0"
}
},
+ "node_modules/recharts": {
+ "version": "2.15.3",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.3.tgz",
+ "integrity": "sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "eventemitter3": "^4.0.1",
+ "lodash": "^4.17.21",
+ "react-is": "^18.3.1",
+ "react-smooth": "^4.0.4",
+ "recharts-scale": "^0.4.4",
+ "tiny-invariant": "^1.3.1",
+ "victory-vendor": "^36.6.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/recharts-scale": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
+ "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
+ "dependencies": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
+ "node_modules/recharts/node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
+ },
+ "node_modules/recharts/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -9639,10 +10041,9 @@
}
},
"node_modules/tailwind-merge": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.0.2.tgz",
- "integrity": "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==",
- "license": "MIT",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
+ "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/dcastil"
@@ -9800,6 +10201,11 @@
"real-require": "^0.1.0"
}
},
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
+ },
"node_modules/tinyglobby": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
@@ -10350,6 +10756,27 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/victory-vendor": {
+ "version": "36.9.2",
+ "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
+ "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
+ "dependencies": {
+ "@types/d3-array": "^3.0.3",
+ "@types/d3-ease": "^3.0.0",
+ "@types/d3-interpolate": "^3.0.1",
+ "@types/d3-scale": "^4.0.2",
+ "@types/d3-shape": "^3.1.0",
+ "@types/d3-time": "^3.0.0",
+ "@types/d3-timer": "^3.0.0",
+ "d3-array": "^3.1.6",
+ "d3-ease": "^3.0.1",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.1.0",
+ "d3-time": "^3.0.0",
+ "d3-timer": "^3.0.1"
+ }
+ },
"node_modules/viem": {
"version": "2.23.3",
"resolved": "https://registry.npmjs.org/viem/-/viem-2.23.3.tgz",
diff --git a/package.json b/package.json
index 370e2c6..a8b2b1f 100644
--- a/package.json
+++ b/package.json
@@ -11,29 +11,34 @@
"dependencies": {
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8",
- "@rainbow-me/rainbowkit": "^2.2.4",
+ "@rainbow-me/rainbowkit": "^2.2.8",
"@tanstack/react-query": "^5.55.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
+ "cookie": "^1.0.2",
"ethers": "^6.13.5",
+ "jsonwebtoken": "^9.0.2",
"lucide-react": "^0.484.0",
"next": "^15.1.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-error-boundary": "^5.0.0",
"react-icons": "^5.5.0",
- "tailwind-merge": "^3.0.2",
+ "recharts": "^2.15.3",
+ "tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7",
"viem": "2.23.3",
"wagmi": "^2.14.11"
},
"devDependencies": {
"@shadcn/ui": "^0.0.4",
+ "@types/jsonwebtoken": "^9.0.9",
"@types/node": "^20.14.8",
"@types/react": "^19.0.6",
"autoprefixer": "^10.4.21",
"eslint-config-next": "^15.1.7",
"postcss": "^8.4.35",
+ "postinstall-postinstall": "^2.1.0",
"tailwindcss": "^3.4.1",
"typescript": "5.5.4"
}
diff --git a/public/images/icons/sUSDS.svg b/public/images/icons/sUSDS.svg
new file mode 100644
index 0000000..66835d0
--- /dev/null
+++ b/public/images/icons/sUSDS.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/images/icons/usds.svg b/public/images/icons/usds.svg
new file mode 100644
index 0000000..d3d5fca
--- /dev/null
+++ b/public/images/icons/usds.svg
@@ -0,0 +1,26 @@
+
+
\ No newline at end of file
diff --git a/public/images/icons/usdt.svg b/public/images/icons/usdt.svg
new file mode 100644
index 0000000..4174696
--- /dev/null
+++ b/public/images/icons/usdt.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/public/images/logo/logo.svg b/public/images/logo/logo.svg
new file mode 100644
index 0000000..063daa6
--- /dev/null
+++ b/public/images/logo/logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index b73b7d5..5b3fa6b 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,9 +1,24 @@
import { TooltipProvider } from "@/components/ui/tooltip"
import { ReactNode } from "react"
+export const metadata = {
+ title: 'Advanced Yield Platform',
+ description: 'Advanced Yield Platform for DeFi',
+ icons: {
+ icon: '/images/logo/logo.svg',
+ shortcut: '/images/logo/logo.svg',
+ apple: '/images/logo/logo.svg',
+ },
+}
+
export default function RootLayout({ children }: { children: ReactNode }) {
return (
-
+
+
+
+
+
+
{children}
diff --git a/src/components/deposit-view.tsx b/src/components/deposit-view.tsx
index 7760186..2f3f23b 100644
--- a/src/components/deposit-view.tsx
+++ b/src/components/deposit-view.tsx
@@ -14,6 +14,8 @@ import {
useWatchContractEvent,
useTransaction,
useReadContracts,
+ useSwitchChain,
+ useChainId
} from "wagmi";
import { USD_STRATEGIES, BTC_STRATEGIES, ETH_STRATEGIES } from "../config/env";
import {
@@ -24,6 +26,7 @@ import {
http,
parseUnits,
} from "viem";
+import { RATE_PROVIDER_ABI } from "../config/abi/rateProvider";
type DurationType = "30_DAYS" | "60_DAYS" | "180_DAYS" | "PERPETUAL_DURATION";
type StrategyType = "STABLE" | "INCENTIVE";
@@ -31,20 +34,41 @@ type StrategyType = "STABLE" | "INCENTIVE";
interface StrategyConfig {
network: string;
contract: string;
- deposit_token: string;
- deposit_contract: string;
- deposit_token_contract?: string; // Optional field for backward compatibility
- deposit_token_image?: string; // Optional field for deposit token image
- deposit_token_decimal?: number; // Optional field for deposit token decimal
+ base: {
+ tokens: Array<{
+ name: string;
+ contract: string;
+ decimal: number;
+ image: string;
+ }>;
+ };
+ ethereum: {
+ tokens: Array<{
+ name: string;
+ contract: string;
+ decimal: number;
+ image: string;
+ }>;
+ };
+ arbitrum: {
+ tokens: Array<{
+ name: string;
+ contract: string;
+ decimal: number;
+ image: string;
+ }>;
+ };
description: string;
apy: string;
incentives: string;
tvl: string;
- rpc?: string; // Optional field for backward compatibility
+ rpc?: string;
show_cap: boolean;
filled_cap: string;
cap_limit: string;
- boringVaultAddress?: string; // Optional field for boring vault address
+ boringVaultAddress?: string;
+ rateProvider: string;
+ shareAddress: string;
}
// ERC20 ABI for token operations
@@ -136,10 +160,37 @@ const VAULT_ABI = [
inputs: [
{ name: "depositAsset", type: "address" },
{ name: "depositAmount", type: "uint256" },
- { name: "minimumMint", type: "uint256" }
+ { name: "minimumMint", type: "uint256" },
],
outputs: [{ name: "shares", type: "uint256" }],
},
+ {
+ name: "depositAndBridge",
+ type: "function",
+ stateMutability: "payable",
+ inputs: [
+ { name: "depositAsset", type: "address" },
+ { name: "depositAmount", type: "uint256" },
+ { name: "minimumMint", type: "uint256" },
+ { name: "to", type: "address" },
+ { name: "bridgeWildCard", type: "bytes" },
+ { name: "feeToken", type: "address" },
+ { name: "maxFee", type: "uint256" },
+ ],
+ outputs: [{ name: "shares", type: "uint256" }],
+ },
+ {
+ name: "previewFee",
+ type: "function",
+ stateMutability: "view",
+ inputs: [
+ { name: "shareAmount", type: "uint96" },
+ { name: "to", type: "address" },
+ { name: "bridgeWildCard", type: "bytes" },
+ { name: "feeToken", type: "address" },
+ ],
+ outputs: [{ name: "fee", type: "uint256" }],
+ },
{
name: "totalAssets",
type: "function",
@@ -220,6 +271,12 @@ const DepositView: React.FC = ({
| "depositing"
| "idle"
>("idle");
+ const [isMultiChain, setIsMultiChain] = useState(false);
+ const [bridgeFee, setBridgeFee] = useState("0");
+ const [isLoadingFee, setIsLoadingFee] = useState(false);
+ const [targetChain, setTargetChain] = useState("arbitrum"); // Default target chain
+ const { switchChain } = useSwitchChain();
+ const { chain } = useAccount(); // Get connected chain info
// Get strategy config based on asset type
const strategyConfigs = {
@@ -229,13 +286,39 @@ const DepositView: React.FC = ({
};
// Explicitly access the strategies for the selected asset first
- const assetStrategies = strategyConfigs[selectedAsset as keyof typeof strategyConfigs];
+ const assetStrategies =
+ strategyConfigs[selectedAsset as keyof typeof strategyConfigs];
// Now access the specific duration and strategy type
- const strategyConfig = (assetStrategies as any)[duration][ // Cast assetStrategies to any for indexing
+ const strategyConfig = (assetStrategies as any)[duration][
strategy === "stable" ? "STABLE" : "INCENTIVE"
] as StrategyConfig;
+ // Get the appropriate network tokens based on the selected target chain
+ const getNetworkTokens = () => {
+ switch (targetChain) {
+ case "arbitrum":
+ return strategyConfig.arbitrum.tokens;
+ case "ethereum":
+ return strategyConfig.ethereum.tokens;
+ case "base":
+ default:
+ return strategyConfig.base.tokens;
+ }
+ };
+
+ // Parse all available deposit assets from strategyConfig, filtered by targetChain
+ const assetOptions = useMemo(() => {
+ return getNetworkTokens();
+ }, [strategyConfig, targetChain]);
+
+ const [selectedAssetIdx, setSelectedAssetIdx] = useState(0);
+ const selectedAssetOption = assetOptions[selectedAssetIdx] || assetOptions[0];
+
+ // Update token contract address and decimals
+ const tokenContractAddress = selectedAssetOption.contract;
+ const depositTokenDecimals = selectedAssetOption.decimal;
+
// Calculate deposit cap values from env config
const showDepositCap = strategyConfig.show_cap;
const depositCap = useMemo(
@@ -262,14 +345,6 @@ const DepositView: React.FC = ({
const { address } = useAccount();
- // Get deposit token name, image, and decimals (only if defined in config)
- const depositToken = strategyConfig.deposit_token;
- const depositTokenImage = strategyConfig.deposit_token_image;
- const depositTokenDecimals = strategyConfig.deposit_token_decimal || 6;
-
- // USDC token contract for approvals
- const tokenContractAddress =
- strategyConfig.deposit_token_contract || strategyConfig.deposit_contract;
// Vault contract for deposits
const vaultContractAddress = strategyConfig.contract;
@@ -297,9 +372,14 @@ const DepositView: React.FC = ({
allowance: allowance?.toString(),
hasAllowance: !!allowance,
amount: amount ? parseUnits(amount, depositTokenDecimals).toString() : "0",
- needsApproval: amount ? (BigInt(allowance?.toString() || "0") < parseUnits(amount, depositTokenDecimals)) : false,
- currentAllowanceFormatted: allowance ? formatUnits(BigInt(allowance.toString()), depositTokenDecimals) : "0",
- requestedAmountFormatted: amount || "0"
+ needsApproval: amount
+ ? BigInt(allowance?.toString() || "0") <
+ parseUnits(amount, depositTokenDecimals)
+ : false,
+ currentAllowanceFormatted: allowance
+ ? formatUnits(BigInt(allowance.toString()), depositTokenDecimals)
+ : "0",
+ requestedAmountFormatted: amount || "0",
});
// Watch approve transaction
@@ -324,7 +404,7 @@ const DepositView: React.FC = ({
useEffect(() => {
const checkTokenContract = async () => {
if (!tokenContractAddress || !address) return;
-
+
try {
const client = createPublicClient({
transport: http(strategyConfig.rpc || "https://base.llamarpc.com"),
@@ -338,29 +418,39 @@ const DepositView: React.FC = ({
symbol: "ETH",
},
rpcUrls: {
- default: { http: [strategyConfig.rpc || "https://base.llamarpc.com"] },
- public: { http: [strategyConfig.rpc || "https://base.llamarpc.com"] },
+ default: {
+ http: [strategyConfig.rpc || "https://base.llamarpc.com"],
+ },
+ public: {
+ http: [strategyConfig.rpc || "https://base.llamarpc.com"],
+ },
},
},
});
// Try to read basic token info
const [name, symbol, decimals] = await Promise.all([
- client.readContract({
- address: tokenContractAddress as Address,
- abi: ERC20_ABI,
- functionName: "name",
- }).catch(() => "Error reading name"),
- client.readContract({
- address: tokenContractAddress as Address,
- abi: ERC20_ABI,
- functionName: "symbol",
- }).catch(() => "Error reading symbol"),
- client.readContract({
- address: tokenContractAddress as Address,
- abi: ERC20_ABI,
- functionName: "decimals",
- }).catch(() => "Error reading decimals"),
+ client
+ .readContract({
+ address: tokenContractAddress as Address,
+ abi: ERC20_ABI,
+ functionName: "name",
+ })
+ .catch(() => "Error reading name"),
+ client
+ .readContract({
+ address: tokenContractAddress as Address,
+ abi: ERC20_ABI,
+ functionName: "symbol",
+ })
+ .catch(() => "Error reading symbol"),
+ client
+ .readContract({
+ address: tokenContractAddress as Address,
+ abi: ERC20_ABI,
+ functionName: "decimals",
+ })
+ .catch(() => "Error reading decimals"),
]);
console.log("Token contract debug info:", {
@@ -418,10 +508,10 @@ const DepositView: React.FC = ({
console.log("Deposit successful!", {
hash: transactionHash,
amount,
- token: depositToken,
+ token: selectedAssetOption.name,
});
}
- }, [isDepositSuccess, transactionHash, amount, depositToken]);
+ }, [isDepositSuccess, transactionHash, amount, selectedAssetOption.name]);
useEffect(() => {
const checkApproval = async () => {
@@ -475,6 +565,85 @@ const DepositView: React.FC = ({
console.log("Status changed:", status);
}, [status]);
+ // Add preview fee function
+ const previewBridgeFee = async (amount: bigint) => {
+ if (!address || !amount || !isMultiChain) return;
+
+ setIsLoadingFee(true);
+ try {
+ const client = createPublicClient({
+ transport: http(strategyConfig.rpc || "https://base.llamarpc.com"),
+ chain: {
+ id: 8453,
+ name: "Base",
+ network: "base",
+ nativeCurrency: {
+ decimals: 18,
+ name: "Ethereum",
+ symbol: "ETH",
+ },
+ rpcUrls: {
+ default: {
+ http: [strategyConfig.rpc || "https://base.llamarpc.com"],
+ },
+ public: {
+ http: [strategyConfig.rpc || "https://base.llamarpc.com"],
+ },
+ },
+ },
+ });
+
+ // Get bridge wildcard based on target chain
+ const bridgeWildCard = getBridgeWildCard(targetChain);
+
+ // Convert amount to uint96 for previewFee
+ const shareAmount = amount as unknown as bigint;
+
+ // Call previewFee function with exact parameters from your example
+ const fee = await client.readContract({
+ address: vaultContractAddress as Address,
+ abi: VAULT_ABI,
+ functionName: "previewFee",
+ args: [
+ shareAmount, // shareAmount (uint96)
+ address as Address, // to address
+ bridgeWildCard, // bridgeWildCard bytes
+ "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as Address, // feeToken (ETH address)
+ ],
+ });
+
+ console.log("Bridge fee calculation:", {
+ shareAmount: shareAmount.toString(),
+ to: address,
+ bridgeWildCard,
+ feeToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
+ calculatedFee: fee.toString(),
+ });
+
+ setBridgeFee(formatUnits(fee as bigint, 18));
+ } catch (error) {
+ console.error("Error previewing bridge fee:", error);
+ setBridgeFee("0");
+ } finally {
+ setIsLoadingFee(false);
+ }
+ };
+
+ // Helper function to get bridge wildcard
+ const getBridgeWildCard = (chain: string): `0x${string}` => {
+ switch (chain) {
+ case "arbitrum":
+ return "0x000000000000000000000000000000000000000000000000000000000000759e";
+ case "optimism":
+ return "0x000000000000000000000000000000000000000000000000000000000000759f";
+ case "ethereum":
+ return "0x000000000000000000000000000000000000000000000000000000000000759d";
+ default:
+ return "0x000000000000000000000000000000000000000000000000000000000000759e";
+ }
+ };
+
+ // Modify handleDeposit function
const handleDeposit = async () => {
console.log("Deposit clicked", {
address,
@@ -485,6 +654,8 @@ const DepositView: React.FC = ({
duration,
currentAllowance: allowance?.toString(),
isApproved,
+ isMultiChain,
+ targetChain,
});
if (!address || !amount || !approve || !deposit) {
@@ -504,8 +675,63 @@ const DepositView: React.FC = ({
throw new Error("Invalid amount");
}
- const roundedAmount = Math.round(amountFloat * Math.pow(10, depositTokenDecimals)) / Math.pow(10, depositTokenDecimals);
- const amountInWei = parseUnits(roundedAmount.toFixed(depositTokenDecimals), depositTokenDecimals);
+ const roundedAmount =
+ Math.round(amountFloat * Math.pow(10, depositTokenDecimals)) /
+ Math.pow(10, depositTokenDecimals);
+ const amountInWei = parseUnits(
+ roundedAmount.toFixed(depositTokenDecimals),
+ depositTokenDecimals
+ );
+
+ // Get rate from rate provider
+ const rateProviderAddress = strategyConfig.rateProvider;
+
+ const client = createPublicClient({
+ transport: http(strategyConfig.rpc || "https://base.llamarpc.com"),
+ chain: {
+ id: 8453,
+ name: "Base",
+ network: "base",
+ nativeCurrency: {
+ decimals: 18,
+ name: "Ethereum",
+ symbol: "ETH",
+ },
+ rpcUrls: {
+ default: {
+ http: [strategyConfig.rpc || "https://base.llamarpc.com"],
+ },
+ public: {
+ http: [strategyConfig.rpc || "https://base.llamarpc.com"],
+ },
+ },
+ },
+ });
+
+ // Get rate from rate provider using the deposit token address
+ const rate = await client.readContract({
+ address: rateProviderAddress as Address,
+ abi: RATE_PROVIDER_ABI,
+ functionName: "getRateInQuote",
+ args: [selectedAssetOption.contract as Address],
+ });
+
+ console.log("Raw rate from contract:", rate.toString());
+
+ // Calculate minimum mint amount in 6 decimals
+ // First multiply by rate, then divide by 1e18 to get 6 decimals
+ const minimumMint = (amountInWei * BigInt(rate)) / BigInt(1e18);
+
+ // Convert to exactly 6 decimals by multiplying by 1e6 and dividing by 1e18
+ const minimumMintIn6Decimals = (minimumMint * BigInt(1e6)) / BigInt(1e18);
+
+ console.log("Minimum mint calculation details:", {
+ amountInWei: amountInWei.toString(),
+ rate: rate.toString(),
+ minimumMint: minimumMint.toString(),
+ minimumMintIn6Decimals: minimumMintIn6Decimals.toString(),
+ minimumMintLength: minimumMintIn6Decimals.toString().length,
+ });
// First approve USDS for the boring vault
const boringVaultAddress = strategyConfig.boringVaultAddress;
@@ -514,36 +740,22 @@ const DepositView: React.FC = ({
}
// Check if we need approval
- const currentAllowance = allowance ? BigInt(allowance.toString()) : BigInt(0);
+ const currentAllowance = allowance
+ ? BigInt(allowance.toString())
+ : BigInt(0);
const needsApproval = currentAllowance < amountInWei;
- console.log("Approval check:", {
- currentAllowance: currentAllowance.toString(),
- amountInWei: amountInWei.toString(),
- needsApproval,
- isApproved,
- isApproving
- });
-
if (allowance === undefined) {
- setErrorMessage('Unable to fetch allowance. Please check your network and try again.');
+ setErrorMessage(
+ "Unable to fetch allowance. Please check your network and try again."
+ );
setIsWaitingForSignature(false);
return;
}
// Step 1: Approve USDS for boring vault if needed
if (needsApproval && !isApproved && !isApproving) {
- console.log('Calling approve function...');
- console.log("Sending approve transaction for boring vault...", {
- tokenContract: tokenContractAddress,
- spender: boringVaultAddress,
- amount: amountInWei.toString(),
- functionName: "approve",
- abi: ERC20_ABI,
- chainId: 8453,
- account: address
- });
-
+ console.log("Calling approve function...");
setIsApproving(true);
try {
const approveTx = await approve({
@@ -555,23 +767,11 @@ const DepositView: React.FC = ({
account: address as Address,
});
- console.log("Approval transaction sent:", {
- hash: approveTx,
- tokenContract: tokenContractAddress,
- spender: boringVaultAddress,
- amount: amountInWei.toString()
- });
-
if (typeof approveTx === "string" && approveTx.startsWith("0x")) {
setApprovalHash(approveTx as `0x${string}`);
}
} catch (error: any) {
- console.error("Approval transaction failed:", {
- error: error.message,
- tokenContract: tokenContractAddress,
- spender: boringVaultAddress,
- amount: amountInWei.toString()
- });
+ console.error("Approval transaction failed:", error);
setIsApproving(false);
setErrorMessage(error.message || "Approval failed");
}
@@ -587,34 +787,90 @@ const DepositView: React.FC = ({
// Only proceed with deposit if we have sufficient allowance
if (!needsApproval || isApproved) {
- // Step 2: Proceed with deposit to LayerZeroTeller
- console.log("Proceeding with deposit to LayerZeroTeller");
setIsDepositing(true);
- // Calculate minimum mint amount based on slippage
- const slippageAmount = amountInWei * BigInt(Math.floor(parseFloat(slippage) * 10000)) / BigInt(10000);
- const minimumMint = amountInWei - slippageAmount;
+ if (isMultiChain) {
+ // Preview bridge fee before proceeding
+ await previewBridgeFee(amountInWei);
- // Proceed with deposit using the LayerZeroTeller contract
- console.log("Sending deposit transaction to LayerZeroTeller:", {
- contract: vaultContractAddress,
- token: tokenContractAddress,
- amount: amountInWei.toString(),
- minimumMint: minimumMint.toString(),
- });
+ // Get bridge wildcard
+ const bridgeWildCard = getBridgeWildCard(targetChain);
- const tx = await deposit({
- address: vaultContractAddress as Address,
- abi: VAULT_ABI,
- functionName: "deposit",
- args: [tokenContractAddress as Address, amountInWei, minimumMint],
- chainId: 8453,
- account: address as Address,
- });
+ // Convert bridge fee to wei
+ const bridgeFeeWei = parseEther(bridgeFee);
+
+ // Proceed with multi-chain deposit
+ console.log("Sending multi-chain deposit transaction:", {
+ contract: vaultContractAddress,
+ token: tokenContractAddress,
+ amount: amountInWei.toString(),
+ minimumMint: "0", // Set minimumMint to 0 for multi-chain
+ bridgeWildCard,
+ bridgeFee: bridgeFeeWei.toString(),
+ });
+
+ try {
+ const tx = await deposit({
+ address: vaultContractAddress as Address,
+ abi: VAULT_ABI,
+ functionName: "depositAndBridge",
+ args: [
+ tokenContractAddress as Address,
+ amountInWei,
+ BigInt(0), // Set minimumMint to 0 for multi-chain
+ address as Address,
+ bridgeWildCard,
+ "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" as Address, // ETH address
+ bridgeFeeWei, // Use the calculated bridge fee
+ ],
+ chainId: 8453,
+ account: address as Address,
+ value: bridgeFeeWei, // Include the calculated bridge fee in ETH
+ });
+
+ if (tx && typeof tx === "string" && tx.startsWith("0x")) {
+ setTransactionHash(tx as `0x${string}`);
+ console.log("Multi-chain deposit transaction sent:", tx);
+ } else {
+ throw new Error("Invalid transaction response");
+ }
+ } catch (error: any) {
+ console.error("Multi-chain deposit failed:", error);
+ setErrorMessage(error.message || "Multi-chain deposit failed");
+ setIsDepositing(false);
+ return;
+ }
+ } else {
+ // Regular single-chain deposit
+ console.log("Sending deposit transaction:", {
+ contract: vaultContractAddress,
+ token: tokenContractAddress,
+ amount: amountInWei.toString(),
+ minimumMint: minimumMintIn6Decimals.toString(),
+ });
- if (tx && typeof tx === "string" && tx.startsWith("0x")) {
- setTransactionHash(tx as `0x${string}`);
- setIsDepositing(true);
+ try {
+ const tx = await deposit({
+ address: vaultContractAddress as Address,
+ abi: VAULT_ABI,
+ functionName: "deposit",
+ args: [tokenContractAddress as Address, amountInWei, minimumMintIn6Decimals],
+ chainId: 8453,
+ account: address as Address,
+ });
+
+ if (tx && typeof tx === "string" && tx.startsWith("0x")) {
+ setTransactionHash(tx as `0x${string}`);
+ console.log("Deposit transaction sent:", tx);
+ } else {
+ throw new Error("Invalid transaction response");
+ }
+ } catch (error: any) {
+ console.error("Deposit failed:", error);
+ setErrorMessage(error.message || "Deposit failed");
+ setIsDepositing(false);
+ return;
+ }
}
} else {
console.log("Insufficient allowance, approval needed first");
@@ -630,101 +886,90 @@ const DepositView: React.FC = ({
}
};
+ // Add effect to preview fee when amount changes
+ useEffect(() => {
+ if (isMultiChain && amount) {
+ const amountInWei = parseUnits(amount, depositTokenDecimals);
+ previewBridgeFee(amountInWei);
+ }
+ }, [amount, isMultiChain, targetChain]);
+
+ // Helper to get correct RPC and chain config for each chain
+ const getChainConfig = (chain: string) => {
+ switch (chain) {
+ case "arbitrum":
+ return {
+ rpcUrl: "https://arbitrum.drpc.org",
+ chain: {
+ id: 42161,
+ name: "Arbitrum",
+ network: "arbitrum",
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
+ rpcUrls: {
+ default: { http: ["https://arbitrum.drpc.org"] },
+ public: { http: ["https://arbitrum.drpc.org"] },
+ },
+ },
+ };
+ case "ethereum":
+ return {
+ rpcUrl: "https://eth.llamarpc.com",
+ chain: {
+ id: 1,
+ name: "Ethereum",
+ network: "ethereum",
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
+ rpcUrls: {
+ default: { http: ["https://eth.llamarpc.com"] },
+ public: { http: ["https://eth.llamarpc.com"] },
+ },
+ },
+ };
+ case "base":
+ default:
+ return {
+ rpcUrl: "https://base.llamarpc.com",
+ chain: {
+ id: 8453,
+ name: "Base",
+ network: "base",
+ nativeCurrency: { decimals: 18, name: "Ethereum", symbol: "ETH" },
+ rpcUrls: {
+ default: { http: ["https://base.llamarpc.com"] },
+ public: { http: ["https://base.llamarpc.com"] },
+ },
+ },
+ };
+ }
+ };
+
const fetchBalance = async () => {
- if (!address || selectedAsset !== "USD") return;
+ if (!address) return;
setIsLoadingBalance(true);
try {
- // Use correct USDS token contract address on Base
- const tokenContractAddress = "0x820C137fa70C8691f0e44Dc420a5e53c168921Dc" as Address;
- const rpcUrl = "https://base.llamarpc.com";
-
- console.log("Fetching balance for:", {
- tokenContract: tokenContractAddress,
- userAddress: address,
- network: "base",
- rpc: rpcUrl
- });
+ const tokenContractAddress = selectedAssetOption.contract as Address;
+ const decimals = Number(selectedAssetOption.decimal);
+ const { rpcUrl, chain } = getChainConfig(targetChain);
const client = createPublicClient({
transport: http(rpcUrl),
- chain: {
- id: 8453,
- name: "Base",
- network: "base",
- nativeCurrency: {
- decimals: 18,
- name: "Ethereum",
- symbol: "ETH",
- },
- rpcUrls: {
- default: { http: [rpcUrl] },
- public: { http: [rpcUrl] },
- },
- },
+ chain,
});
- // First try to get token info to verify contract
- try {
- const [name, symbol, decimals] = await Promise.all([
- client.readContract({
- address: tokenContractAddress,
- abi: ERC20_ABI,
- functionName: "name",
- }).catch(() => "Error reading name"),
- client.readContract({
- address: tokenContractAddress,
- abi: ERC20_ABI,
- functionName: "symbol",
- }).catch(() => "Error reading symbol"),
- client.readContract({
- address: tokenContractAddress,
- abi: ERC20_ABI,
- functionName: "decimals",
- }).catch(() => "Error reading decimals"),
- ]);
-
- console.log("Token contract info:", {
- address: tokenContractAddress,
- name,
- symbol,
- decimals,
- });
- } catch (error) {
- console.error("Error reading token info:", error);
- }
-
- // Then try to get balance
- const [balanceResult, decimalsResult] = await Promise.all([
- client.readContract({
- address: tokenContractAddress,
- abi: ERC20_ABI,
- functionName: "balanceOf",
- args: [address as Address],
- }).catch(error => {
- console.error("Error reading balance:", error);
- return BigInt(0);
- }),
- client.readContract({
- address: tokenContractAddress,
- abi: ERC20_ABI,
- functionName: "decimals",
- }).catch(error => {
- console.error("Error reading decimals:", error);
- return 6; // Default to 6 decimals for USDS
- }),
- ]);
-
- console.log("Balance results:", {
- rawBalance: balanceResult?.toString(),
- decimals: decimalsResult,
+ const balanceResult = await client.readContract({
+ address: tokenContractAddress,
+ abi: ERC20_ABI,
+ functionName: "balanceOf",
+ args: [address as Address],
});
- const formattedBalance = formatUnits(
- balanceResult as bigint,
- decimalsResult as number
- );
- console.log("Formatted balance:", formattedBalance);
+ const formattedBalance = Number(
+ formatUnits(balanceResult as bigint, decimals)
+ ).toLocaleString(undefined, {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ });
setBalance(formattedBalance);
} catch (error) {
console.error("Error fetching balance:", error);
@@ -734,12 +979,54 @@ const DepositView: React.FC = ({
}
};
- // Initial balance fetch
+ // Add effect to switch network when target chain changes
useEffect(() => {
- if (address && selectedAsset === "USD") {
+ if (switchChain) {
+ if (isMultiChain && targetChain) {
+ const chainId = getChainId(targetChain);
+ if (chainId) {
+ switchChain({ chainId });
+ }
+ } else if (!isMultiChain) {
+ // Always switch to Base when multi-chain is off
+ switchChain({ chainId: 8453 });
+ }
+ }
+ }, [targetChain, isMultiChain, switchChain]);
+
+ // Helper function to get chain ID
+ const getChainId = (chain: string): number | undefined => {
+ switch (chain) {
+ case "arbitrum":
+ return 42161;
+ case "optimism":
+ return 10;
+ case "ethereum":
+ return 1;
+ default:
+ return undefined;
+ }
+ };
+
+ useEffect(() => {
+ if (address) {
fetchBalance();
}
- }, [address, selectedAsset, duration, strategy]);
+ }, [
+ address,
+ selectedAssetIdx,
+ selectedAsset,
+ duration,
+ strategy,
+ targetChain,
+ ]);
+
+ // Update targetChain when connected chain changes
+ useEffect(() => {
+ if (chain) {
+ setTargetChain(chain.name.toLowerCase());
+ }
+ }, [chain]);
const handleMaxClick = () => {
setAmount(balance);
@@ -754,7 +1041,7 @@ const DepositView: React.FC = ({
};
return (
-
+
{depositSuccess ? (
@@ -829,7 +1116,7 @@ const DepositView: React.FC
= ({
Amount
- {amount} {depositToken}
+ {amount} {selectedAssetOption.name}
@@ -843,28 +1130,84 @@ const DepositView: React.FC
= ({
) : (
-
-
+
+
{/* Left Card - Deposit Input */}
-
+
- {depositTokenImage && (
+ {selectedAssetOption.image && (

)}
- Deposit {depositToken}
+ Deposit {selectedAssetOption.name}
+0.00 in 1 year
+ {/* --- Asset Dropdown & Multi-chain Toggle --- */}
+ {assetOptions.length > 1 && (
+
+
+
+
+ )}
+ {/* Multi-chain Toggle (always shown) */}
+
+
+ Multi-chain Deposit
+
+
+
+ {/* Target Chain Selection - Only shown when multi-chain is enabled */}
+ {isMultiChain && (
+
+
+
+
+ )}
+ {/* --- End Asset Dropdown & Multi-chain Toggle --- */}
= ({
)}
+ {/* Bridge Fee Display */}
+ {isMultiChain && (
+
+
+ Bridge Fee:{" "}
+ {isLoadingFee ? (
+
+
+ Loading...
+
+ ) : (
+ {bridgeFee} ETH
+ )}
+
+
+
+
+
+ You need to have enough ETH in your wallet to cover
+ the bridge fee. The fee will be paid in ETH along
+ with your deposit.
+
+
+
+
+ )}
{/* Right Card - Strategy Info */}
-
+
{/* Background gradient effect - top */}
@@ -979,7 +1382,7 @@ const DepositView: React.FC
= ({
{/* Deposit Cap Progress Bar - Only shown if show_cap is true */}
{showDepositCap && (
-
+
${remainingSpace} Remaining
@@ -1036,7 +1439,7 @@ const DepositView: React.FC = ({
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
index 3a96d3c..5f8bd0d 100644
--- a/src/components/ui/card.tsx
+++ b/src/components/ui/card.tsx
@@ -6,6 +6,7 @@ import {
TooltipTrigger,
TooltipProvider,
} from "@/components/ui/tooltip";
+import { InfoIcon } from "lucide-react";
type DurationType = "30_DAYS" | "60_DAYS" | "180_DAYS" | "PERPETUAL_DURATION";
@@ -26,26 +27,9 @@ interface CustomCardProps {
disableHover?: boolean;
onReset?: () => void;
isComingSoon?: boolean;
+ availableDurations?: DurationType[];
}
-const InfoIcon = () => (
-
-);
-
const formatDuration = (duration: string) => {
if (duration === "PERPETUAL_DURATION") return "Perpetual";
const [number, period] = duration.split("_");
@@ -66,6 +50,7 @@ const CustomCard: React.FC = ({
disableHover,
onReset,
isComingSoon,
+ availableDurations,
...props
}) => {
const handleDurationClick = (duration: DurationType) => {
@@ -78,6 +63,13 @@ const CustomCard: React.FC = ({
e.stopPropagation(); // Stop event from bubbling up to parent card
};
+ // Helper function to check if a duration is available
+ const isDurationAvailable = (duration: DurationType) => {
+ // If availableDurations is not provided, assume all are available
+ if (!availableDurations) return true;
+ return availableDurations.includes(duration);
+ };
+
return (
= ({
{...props}
>
{isStrategyCard ? (
-
+
+ {/* Coming Soon Overlay */}
+ {isComingSoon && (
+
+ Coming Soon
+
+ )}
{/* Image */}
![]()
= ({
Select Duration
-
-
-
-
+ {/* 30 Days Button */}
+
+
+
+
+
+ {!isDurationAvailable("30_DAYS") && (
+
+ Coming Soon
+
+ )}
+
+
+
+ {/* 60 Days Button */}
+
+
+
+
+
+ {!isDurationAvailable("60_DAYS") && (
+
+ Coming Soon
+
+ )}
+
+
+
+ {/* 180 Days Button */}
+
+
+
+
+
+ {!isDurationAvailable("180_DAYS") && (
+
+ Coming Soon
+
+ )}
+
+
+
+ {/* Perpetual Button */}
+
+
+
+
+
+ {!isDurationAvailable("PERPETUAL_DURATION") && (
+
+ Coming Soon
+
+ )}
+
+
)}
diff --git a/src/components/ui/header.tsx b/src/components/ui/header.tsx
index 064d2e3..6830b7d 100644
--- a/src/components/ui/header.tsx
+++ b/src/components/ui/header.tsx
@@ -42,7 +42,7 @@ export function Header({
{shouldShowBanner && (
@@ -112,7 +112,7 @@ export function Header({
)}
-
diff --git a/src/components/ui/markets-table.tsx b/src/components/ui/markets-table.tsx
index b3fc489..c34c69d 100644
--- a/src/components/ui/markets-table.tsx
+++ b/src/components/ui/markets-table.tsx
@@ -145,7 +145,7 @@ const MarketsTable: React.FC
= ({
return (
{/* Table Header */}
-
+
= ({
onRowClick && onRowClick(item)}
- className="cursor-pointer pl-[32px]"
+ className="cursor-pointer px-[16px] sm:pl-[32px]"
>
= ({
{/* Toggle buttons */}
-
+
@@ -250,16 +250,16 @@ const YieldDetailsView: React.FC
= ({
);
return (
-
+
-
-
+
@@ -267,30 +267,38 @@ const YieldDetailsView: React.FC = ({
{/* Stats */}
-
-
-
TVL
-
{tvl}
+
+ {/* TVL */}
+
-
-
Base APY
-
{baseApy}
+
+ {/* Base APY */}
+
-
-
Contract Address
-
+
+ {/* Contract Address */}
+
+
Contract Address
+
{contractAddress}
-
-
Network
-
{network}
+
+ {/* Network */}
+
+
{/* Tabs */}
{
const getAssetStrategies = (asset: AssetType) => {
- const strategies: StrategyAsset = {
- USD: USD_STRATEGIES,
- BTC: BTC_STRATEGIES,
- ETH: ETH_STRATEGIES,
- }[asset];
+ const strategies: Record>> = {
+ USD: USD_STRATEGIES as unknown as Partial>,
+ BTC: BTC_STRATEGIES as unknown as Partial>,
+ ETH: ETH_STRATEGIES as unknown as Partial>,
+ };
- // Use the duration as is since it matches the keys in env.ts
- const durationKey = duration;
- const strategy = strategies[durationKey];
+ const strategy = strategies[asset][duration];
if (!strategy) {
console.error(
- `No strategy found for ${asset} with duration ${durationKey}`
+ `No strategy found for ${asset} with duration ${duration}`
);
return {
stable: {
@@ -92,6 +123,7 @@ const getStrategyInfo = (duration: DurationType): StrategyData => {
value: "0%",
info: "-",
},
+ comingSoon: true,
},
};
}
@@ -110,6 +142,7 @@ const getStrategyInfo = (duration: DurationType): StrategyData => {
value: strategy.INCENTIVE.apy,
info: strategy.INCENTIVE.incentives,
},
+ comingSoon: strategy.INCENTIVE.comingSoon,
},
};
};
@@ -117,45 +150,41 @@ const getStrategyInfo = (duration: DurationType): StrategyData => {
return {
stable: {
USD: getAssetStrategies("USD").stable,
- BTC: getAssetStrategies("BTC").stable,
ETH: getAssetStrategies("ETH").stable,
+ BTC: getAssetStrategies("BTC").stable,
},
incentives: {
USD: getAssetStrategies("USD").incentives,
- BTC: getAssetStrategies("BTC").incentives,
ETH: getAssetStrategies("ETH").incentives,
+ BTC: getAssetStrategies("BTC").incentives,
},
};
};
const YieldSubpage: React.FC = ({ depositParams }) => {
const [selectedAsset, setSelectedAsset] = useState(null);
- const [selectedStrategy, setSelectedStrategy] = useState(null);
+ const [selectedStrategy, setSelectedStrategy] = useState(
+ null
+ );
- // Add effect to handle URL parameters or parent navigation
useEffect(() => {
- if (depositParams?.asset && depositParams?.duration) {
+ if (depositParams) {
+ const apy =
+ getStrategyInfo(depositParams.duration as DurationType)[
+ depositParams.strategy === "stable" ? "stable" : "incentives"
+ ][depositParams.asset as AssetType].apy.value;
+
setSelectedAsset({
asset: depositParams.asset,
duration: depositParams.duration as DurationType,
});
- if (depositParams.strategy) {
- const strategyInfo = getStrategyInfo(
- depositParams.duration as DurationType
- );
- const apy =
- strategyInfo[
- depositParams.strategy === "stable" ? "stable" : "incentives"
- ][depositParams.asset as AssetType].apy.value;
-
- setSelectedStrategy({
- type: depositParams.strategy as "stable" | "incentive",
- asset: depositParams.asset,
- duration: depositParams.duration as DurationType,
- apy,
- });
- }
+ setSelectedStrategy({
+ type: depositParams.strategy as "stable" | "incentive",
+ asset: depositParams.asset,
+ duration: depositParams.duration as DurationType,
+ apy,
+ });
}
}, [depositParams]);
@@ -187,7 +216,7 @@ const YieldSubpage: React.FC = ({ depositParams }) => {
// Always render the main content, assuming verification is handled by parent
return (
= ({ depositParams }) => {
/>
) : selectedAsset ? (
-
Select a Yield Source
-
+
Select a Yield Source
+
= ({ depositParams }) => {
selectedDuration={selectedAsset.duration}
onReset={handleReset}
disableHover={true}
- className="h-[311px]"
+ className="h-[311px] w-full"
/>
-
+
handleStrategySelect(
@@ -256,13 +285,25 @@ const YieldSubpage: React.FC = ({ depositParams }) => {
/>
- handleStrategySelect(
- "incentive",
+ {...(getStrategyInfo(selectedAsset.duration).incentives[
+ selectedAsset.asset as AssetType
+ ].comingSoon
+ ? {}
+ : {
+ onClick: () =>
+ handleStrategySelect(
+ "incentive",
+ selectedAsset.asset as AssetType
+ ),
+ })}
+ className={
+ "group " +
+ (getStrategyInfo(selectedAsset.duration).incentives[
selectedAsset.asset as AssetType
- )
+ ].comingSoon
+ ? "pointer-events-none opacity-60"
+ : "cursor-pointer")
}
- className="cursor-pointer"
>
= ({ depositParams }) => {
}
isStrategyCard={true}
disableHover={true}
+ isComingSoon={
+ getStrategyInfo(selectedAsset.duration).incentives[
+ selectedAsset.asset as AssetType
+ ].comingSoon === true
+ }
/>
@@ -288,10 +334,10 @@ const YieldSubpage: React.FC
= ({ depositParams }) => {
) : (
-
+
Select a asset you want yield on
-
+
= ({ depositParams }) => {
onDurationSelect={(duration: DurationType) =>
handleDurationSelect("USD", duration)
}
+ availableDurations={["PERPETUAL_DURATION"]}
/>
typeof window !== "undefined" && window.innerWidth < 640;
type AssetType = "ALL" | "USD" | "ETH" | "BTC";
@@ -108,6 +111,7 @@ const MarketsSubpage: React.FC = () => {
}
],
};
+ const router = useRouter();
// Fill the "ALL" category
marketData.ALL = [...marketData.ETH, ...marketData.BTC, ...marketData.USD];
@@ -124,7 +128,21 @@ const MarketsSubpage: React.FC = () => {
// Handler for row clicks
const handleRowClick = (item: MarketItem) => {
- setSelectedItem(item);
+ console.log("Row clicked:", item);
+ if (isMobile()) {
+ router.push({
+ pathname: `/yield/${item.id}`,
+ query: {
+ name: item.name,
+ tvl: item.tvl,
+ baseApy: item.baseYield,
+ contractAddress: item.contractAddress || "",
+ network: item.network || ""
+ },
+ });
+ } else {
+ setSelectedItem(item);
+ }
};
// Get sorted data
@@ -172,10 +190,10 @@ const MarketsSubpage: React.FC = () => {
};
return (
-
+
{/* Left side - 50% */}
-
-
+
+
{
{/* Asset Selection */}
-
-
-
-
-
-
+
@@ -251,7 +253,7 @@ const MarketsSubpage: React.FC = () => {
{/* Right side - 50% */}
-
+
{selectedItem ? (
{
- if (code === process.env.NEXT_PUBLIC_BETA_ACCESS_PUBLIC_CODE) {
- setIsVerified(true);
- setIsCodePopupOpen(false);
- setVerificationError("");
- } else {
- setVerificationError("Incorrect code. Please try again.");
+ // Function to check for an existing session (will need a backend API to read the cookie)
+ const checkSession = async () => {
+ try {
+ const response = await fetch('/api/check-session'); // We will need to create this API route
+ if (response.ok) {
+ const data = await response.json();
+ if (data.isValid) {
+ setIsVerified(true);
+ setIsCodePopupOpen(false);
+ return true;
+ }
+ }
+ } catch (error) {
+ console.error("Error checking session:", error);
+ }
+ setIsVerified(false);
+ setIsCodePopupOpen(true);
+ return false;
+ };
+
+ // useEffect to check session on component mount
+ useEffect(() => {
+ checkSession();
+ }, []); // Run only once on mount
+
+ const handleVerifyCode = async (code: string) => {
+ try {
+ const response = await fetch('/api/verify-code', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ accessCode: code }),
+ });
+
+ if (response.ok) {
+ setIsVerified(true);
+ setIsCodePopupOpen(false);
+ setVerificationError(""); // Clear any previous errors
+ } else {
+ const data = await response.json();
+ setVerificationError(data.message || "Incorrect code. Please try again.");
+ setIsVerified(false);
+ setIsCodePopupOpen(true);
+ }
+ } catch (error) {
+ console.error("Error verifying code:", error);
+ setVerificationError("An error occurred during verification.");
+ setIsVerified(false);
+ setIsCodePopupOpen(true);
}
};
@@ -73,79 +117,147 @@ export default function Page() {
return (
-
-
-
{
- setSelectedSubPage(SubPage.Yield);
- setDepositParams(null);
- }}
- >
-
-
-
-
-