mirror of
https://github.com/labrinyang/lp-terminal.git
synced 2026-07-27 13:17:43 +00:00
chore: require a fee receiver at build time, ignore internal docs
The build now fails fast when KYBERSWAP_FEE_RECEIVER is unset instead of shipping a bundle that sweeps output fees nowhere. Documents the key in .env.example alongside what it actually costs: 0 bps on direct swaps, 9 bps on ZAP's embedded swap leg. Docs are written for the private tree and get copied in by mistake during a sync, so *.md is ignored by default and publishing one is opt-in. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-12
@@ -1,7 +1,6 @@
|
|||||||
# LP TERMINAL — copy to `.env` in the REPO ROOT (next to package.json).
|
# LP TERMINAL — copy to `.env` in the REPO ROOT (next to package.json).
|
||||||
# Vite reads it via envDir/envPrefix (see vite.config.ts); the indexer reads
|
# Vite reads it via envDir/envPrefix (see vite.config.ts); the indexer reads
|
||||||
# `RPC` from the same file. Every key here is OPTIONAL — with no .env at all
|
# `RPC` from the same file.
|
||||||
# the app runs against the chain's public RPC and the public Kyber aggregator.
|
|
||||||
|
|
||||||
# Private JSON-RPC URL for Robinhood Chain (chainId 4663), e.g. a provider URL
|
# Private JSON-RPC URL for Robinhood Chain (chainId 4663), e.g. a provider URL
|
||||||
# with its key path. SECRET: Vite BAKES this into the JS bundle, so set it only
|
# with its key path. SECRET: Vite BAKES this into the JS bundle, so set it only
|
||||||
@@ -10,19 +9,21 @@
|
|||||||
# Unset -> https://rpc.mainnet.chain.robinhood.com (keyless, slower).
|
# Unset -> https://rpc.mainnet.chain.robinhood.com (keyless, slower).
|
||||||
RPC=
|
RPC=
|
||||||
|
|
||||||
# KyberSwap aggregator base. Absolute URL = browser calls Kyber directly.
|
# REQUIRED (the build fails without it): address that receives the terminal
|
||||||
# A path like /kyber = route through your own same-origin reverse proxy.
|
# output fee — currently 0 bps on direct swaps, 9 bps on ZAP's embedded swap
|
||||||
|
# leg. Use an address you control. (Legacy env name.)
|
||||||
|
KYBERSWAP_FEE_RECEIVER=
|
||||||
|
|
||||||
|
# KyberSwap base for READ-ONLY USD valuation (UP rewards, APR, simulations).
|
||||||
|
# MARKET and ZAP never call it, and the app has no Kyber transaction builder.
|
||||||
|
# Absolute URL = browser calls Kyber directly; a path like /kyber = route
|
||||||
|
# through your own same-origin reverse proxy.
|
||||||
KYBERSWAP_AGGREGATOR_API_BASE_URL=https://aggregator-api.kyberswap.com
|
KYBERSWAP_AGGREGATOR_API_BASE_URL=https://aggregator-api.kyberswap.com
|
||||||
KYBERSWAP_CHAIN=robinhood
|
KYBERSWAP_CHAIN=robinhood
|
||||||
|
|
||||||
# WHITELIST: the only address swap calldata is ever sent to. The app hard-fails
|
# Swap-solver quote API (cross-venue split routing, ready-to-sign Settler tx).
|
||||||
# a quote whose routerAddress differs from this. Change only if Kyber migrates.
|
# Defaults to the hosted public instance; point it at your own if you run one.
|
||||||
KYBERSWAP_ROUTER_ADDRESS=0x6131B5fae19EA4f9D964eAc0408E4408b66337b5
|
# VITE_SOLVER_URL=
|
||||||
|
|
||||||
# Optional platform fee on Kyber swaps, charged on the output token and paid by
|
|
||||||
# the Kyber router. BOTH must be set to activate; leave blank for no fee.
|
|
||||||
# KYBERSWAP_FEE_BPS=10
|
|
||||||
# KYBERSWAP_FEE_RECEIVER=0x...
|
|
||||||
|
|
||||||
# Optional: only WalletConnect QR pairing needs this. Injected wallets
|
# Optional: only WalletConnect QR pairing needs this. Injected wallets
|
||||||
# (MetaMask / Rabby / OKX) work without it. Get one at cloud.walletconnect.com
|
# (MetaMask / Rabby / OKX) work without it. Get one at cloud.walletconnect.com
|
||||||
|
|||||||
+15
@@ -10,3 +10,18 @@ indexer-dist
|
|||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
*.log
|
*.log
|
||||||
.vite
|
.vite
|
||||||
|
|
||||||
|
# local tool caches/artifacts
|
||||||
|
.codegraph/
|
||||||
|
graphify-out/
|
||||||
|
# Claude Code worktrees — nested checkouts of THIS repo; committing one would
|
||||||
|
# bury a duplicate copy of the tree. Project config under .claude/ stays tracked.
|
||||||
|
.claude/worktrees/
|
||||||
|
|
||||||
|
# Internal notes stay internal. Docs are written for the private tree and get
|
||||||
|
# copied in by mistake during a sync; ignoring them means `git add -A` can never
|
||||||
|
# publish one. Publishing a doc is opt-in: add it below, deliberately.
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
# The contract map is on-chain fact, and the source comments cite it by path.
|
||||||
|
!docs/up33-contract-map.md
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "lp-terminal",
|
"name": "up33-terminal",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "lp-terminal",
|
"name": "up33-terminal",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rainbow-me/rainbowkit": "2.2.11",
|
"@rainbow-me/rainbowkit": "2.2.11",
|
||||||
|
|||||||
+5
-2
@@ -1,15 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "lp-terminal",
|
"name": "up33-terminal",
|
||||||
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "MIT",
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "node --experimental-test-module-mocks --import=tsx --test indexer/*.test.ts src/lib/*.test.ts src/lib/bridge/*.test.ts",
|
||||||
"smoke": "tsx scripts/smoke.ts",
|
"smoke": "tsx scripts/smoke.ts",
|
||||||
|
"smoke:bridge": "tsx scripts/bridge-smoke.ts",
|
||||||
"smoke:uni": "tsx scripts/uni-browse-smoke.ts",
|
"smoke:uni": "tsx scripts/uni-browse-smoke.ts",
|
||||||
|
"smoke:logtail": "tsx scripts/logtail-smoke.ts",
|
||||||
"indexer": "tsx indexer/main.ts"
|
"indexer": "tsx indexer/main.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+7
-4
@@ -6,16 +6,20 @@ import { fileURLToPath } from 'node:url'
|
|||||||
// envPrefix exposes exactly those keys to the client bundle.
|
// envPrefix exposes exactly those keys to the client bundle.
|
||||||
//
|
//
|
||||||
// SECRET RULE: `RPC` (private key-bearing URL) is for personal/local builds only —
|
// SECRET RULE: `RPC` (private key-bearing URL) is for personal/local builds only —
|
||||||
// a build meant for public serving must NOT have it set (see README "Chain reads").
|
// a build meant for public serving must NOT have it set (see README "Deploy").
|
||||||
// A public build reads the chain through same-origin `/rpc` instead; the dev and
|
// A public build reads the chain through same-origin `/rpc` instead; the dev and
|
||||||
// preview servers below emulate that reverse proxy so the mode is testable locally.
|
// preview servers below emulate that reverse proxy so the mode is testable locally.
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const envDir = fileURLToPath(new URL('.', import.meta.url))
|
const envDir = fileURLToPath(new URL('.', import.meta.url))
|
||||||
const env = loadEnv(mode, envDir, ['RPC'])
|
const env = loadEnv(mode, envDir, ['RPC', 'KYBERSWAP_'])
|
||||||
|
const feeReceiver = (process.env.KYBERSWAP_FEE_RECEIVER ?? env.KYBERSWAP_FEE_RECEIVER ?? '').trim()
|
||||||
|
if (!/^0x[0-9a-fA-F]{40}$/.test(feeReceiver)) {
|
||||||
|
throw new Error('KYBERSWAP_FEE_RECEIVER must be a valid address; the swap/zap fee sweep requires a configured receiver (see .env.example)')
|
||||||
|
}
|
||||||
// local emulation of the production reverse proxies, so the server deployment
|
// local emulation of the production reverse proxies, so the server deployment
|
||||||
// mode is fully testable via `RPC="" npm run build && npm run preview`:
|
// mode is fully testable via `RPC="" npm run build && npm run preview`:
|
||||||
// - /rpc -> the .env RPC (or RPC_PROXY_TARGET override); key stays in node
|
// - /rpc -> the .env RPC (or RPC_PROXY_TARGET override); key stays in node
|
||||||
// - /kyber -> the public kyber aggregator
|
// - /kyber -> read-only Kyber valuation endpoint
|
||||||
const upstream = (process.env.RPC_PROXY_TARGET ?? env.RPC ?? '').trim()
|
const upstream = (process.env.RPC_PROXY_TARGET ?? env.RPC ?? '').trim()
|
||||||
const passthru = (prefix: string, target: string) => ({
|
const passthru = (prefix: string, target: string) => ({
|
||||||
target,
|
target,
|
||||||
@@ -23,7 +27,6 @@ export default defineConfig(({ mode }) => {
|
|||||||
rewrite: (p: string) => p.replace(new RegExp(`^${prefix}`), ''),
|
rewrite: (p: string) => p.replace(new RegExp(`^${prefix}`), ''),
|
||||||
})
|
})
|
||||||
const proxy: Record<string, object> = {
|
const proxy: Record<string, object> = {
|
||||||
'/kyber-setting': passthru('/kyber-setting', 'https://ks-setting.kyberswap.com'),
|
|
||||||
'/kyber': passthru('/kyber', 'https://aggregator-api.kyberswap.com'),
|
'/kyber': passthru('/kyber', 'https://aggregator-api.kyberswap.com'),
|
||||||
'/dexscreener': passthru('/dexscreener', 'https://api.dexscreener.com'),
|
'/dexscreener': passthru('/dexscreener', 'https://api.dexscreener.com'),
|
||||||
'/goldsky': passthru('/goldsky', 'https://api.goldsky.com'),
|
'/goldsky': passthru('/goldsky', 'https://api.goldsky.com'),
|
||||||
|
|||||||
Reference in New Issue
Block a user