diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..f0041cb2d --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,73 @@ +name: Deploy Dapp to GitHub Pages + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + env: + CI: true + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' # keep your choice + + - name: Enable Corepack & pnpm + run: | + corepack enable + corepack prepare pnpm@9 --activate + pnpm --version + pnpm config set store-dir ~/.pnpm-store + + - name: Cache pnpm store + uses: actions/cache@v4 + with: + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Install dependencies (workspace root) + run: pnpm install --frozen-lockfile + + - name: Build SDK + run: pnpm -w --filter "@trustswap/sdk" run build + + - name: Build with Vite (web) + run: pnpm -w --filter "./apps/web" run build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: apps/web/dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 69cee5ae9..d716255f4 100644 --- a/README.md +++ b/README.md @@ -1 +1,270 @@ -# TrustSwap \ No newline at end of file +# TrustSwap (Uniswap V2 fork) + +Uniswap V2–style DEX with **native tTRUST** + **wrapped WTTRUST**, Router V2, StakingRewards, and a Vite + React + wagmi + viem frontend. + +- Native coin: `tTRUST` (gas) + +- Wrapped ERC-20: `WTTRUST` (used in pairs) + +- Rewards token: `TSWP` + + +> ⚠️ **No `.env` files** in the frontend anymore. All config lives in plain TypeScript. + +---------- + +## Monorepo layout + +``` +. +├─ contracts/ # Hardhat (core V2, router, scripts) +└─ front/ # Vite + React + wagmi + viem + +``` + +---------- + +## Configuration (no `.env`) + +All frontend config lives in `front/src/config/protocol.ts`. + +```ts +// front/src/config/protocol.ts +import type { Address } from 'viem' + +export const RPC_URL = 'https://testnet.rpc.intuition.systems/http' +export const CHAIN_ID = 13579 + +export const FACTORY_ADDRESS = '0xd103E057242881214793d5A1A7c2A5B84731c75c' as Address +export const ROUTER_ADDRESS = '0xA90f2DC77650941a53F5e4f2F345D84f5c0dc2dd' as Address +export const WNATIVE_ADDRESS = '0xc82d6A5e0Da8Ce7B37330C4D44E9f069269546E6' as Address +export const TSWP_ADDRESS = '0x7da120065e104C085fAc6f800d257a6296549cF3' as Address + +export const NATIVE_SYMBOL = 'tTRUST' +export const WRAPPED_SYMBOL = 'WTTRUST' +export const SHOW_WRAPPED_SYMBOL = false + +// Gas (optional) +export const FORCE_LEGACY_GAS = true +export const GAS_PRICE_GWEI = 0.2 +export const GAS_LIMIT = 1_200_000n +export const GAS_LIMIT_CREATE_PAIR = 3_000_000n + +// Hide token addresses in UI (e.g., old tests) +export const HIDE_TOKENS: Address[] = [ + // '0xDeadBeef...', +] + +``` + +> If you redeploy Factory/Router/WNATIVE/TSWP, update addresses here. +> The frontend no longer reads `import.meta.env.*`. + +---------- + +## Install & run + +### Prereqs + +- Node 18+ (or 20+) + +- pnpm 8+ + + +### Install deps + +```bash +pnpm i -w + +``` + +### Run frontend (dev) + +```bash +cd front +pnpm dev + +``` + +### Build contracts + +```bash +cd contracts +pnpm compile + +``` + +> Hardhat scripts (deploy, feeTo toggle, etc.) live in `contracts/scripts/*`. + +---------- + +## Add a token via Pull Request (PR) + +We accept token additions via PR if they meet simple safety/compat criteria. +Listing a token is just adding an entry to `front/src/tokens/intuit.ts`. + +### 1) Fork & branch + +```bash +git checkout -b feat/list- + +``` + +### 2) Edit `front/src/tokens/intuit.ts` + +```ts +import type { Address } from 'viem' +import { WNATIVE_ADDRESS, TSWP_ADDRESS } from '../config/protocol' + +export type Currency = { + symbol: string + name: string + decimals: number + isNative?: boolean + address?: Address + wrapped?: Address + logoURI?: string +} + +export const TOKENS: Currency[] = [ + { + symbol: 'tTRUST', + name: 'Intuition Native', + decimals: 18, + isNative: true, + wrapped: WNATIVE_ADDRESS, + logoURI: '', + }, + { + symbol: 'WTTRUST', + name: 'Wrapped tTRUST', + decimals: 18, + address: WNATIVE_ADDRESS, + logoURI: '', + }, + { + symbol: 'TSWP', + name: 'TrustSwap Token', + decimals: 18, + address: TSWP_ADDRESS, + logoURI: '', + }, + + // 👉 Add your token here: + { + symbol: 'MYT', + name: 'MyToken', + decimals: 18, + address: '0xYourErc20AddressHere' as Address, + logoURI: 'https://…/mytoken.png', // optional + }, +] + +``` + +**Rules:** + +- Use a **checksummed** address (EIP-55). + +- **Correct `decimals`** (verify on-chain). + +- Short `symbol` (≤ 10 chars), clear `name`. + +- `logoURI` optional (HTTPS). You can also submit a logo to `front/public/logos/` and reference it. + + +### 3) ⚠️ Important: Listing in UI does **not** create liquidity + +Adding a token to `TOKENS` only makes it **selectable** in the app. +To **swap** this token with another, there **must be a Uniswap V2 pair with liquidity**. + +- If a **direct pair** exists (e.g., `MYT/WTTRUST`) **and has liquidity**, swaps will work. + +- If not, you must **create the pair and seed liquidity**: + + 1. Go to **Add Liquidity** in the app (or call Factory + Router directly). + + 2. Select your token and the counter-token (e.g., `WTTRUST`). + + 3. Provide both assets at the current price ratio and **Supply**. + + 4. Once liquidity exists, quotes (`getAmountsOut`) and swaps will work. + +- You can also allow routing via a common intermediate (e.g., `MYT → WTTRUST → TSWP`) by ensuring those pairs have liquidity. + + +### 4) Local checks + +```bash +pnpm -w -C front typecheck +pnpm -w -C front build + +``` + +### 5) Open the PR + +Include: + +- Contract address (explorer link) + +- `decimals`, `symbol`, `name` + +- If possible: verified contract on explorer, and notes about special behaviors (fee-on-transfer, blacklist, pausable, etc.) + + +### 6) Review criteria (summary) + +We approve if: + +- ERC-20 standard (at least `decimals`, `symbol`, `balanceOf`, `allowance`, `transferFrom`). + +- No obvious malicious patterns (honeypot, arbitrary blacklists, unlimited mint without governance, etc.). + +- No known incompatibilities with Uniswap V2 (fee-on-transfer tokens may need “SupportingFeeOnTransfer” swap routes; listing is still fine). + + +---------- + +## UI notes + +- **TokenSelector** also supports **import by address** (stored in localStorage). + Tokens in `intuit.ts` are “referenced” and shown by default. + +- **PoolsList** auto-discovers pairs from the **Factory**. + Use `HIDE_TOKENS` in `config/protocol.ts` to hide old test pairs by token address. + + +---------- + +## Admin (protocol fees) + +- Uniswap V2 “protocol fees” (LP fee mint 0.05% when `feeTo` is set) are controlled on **Factory** by **`feeToSetter`**: + + - `feeToSetter` may call `factory.setFeeTo()` (enable) or `factory.setFeeTo(0x0)` (disable). + + - Scripts: `contracts/scripts/20_set_fee_to.ts` & `21_unset_fee_to.ts`. + + +---------- + +## Deployments + +Active deployment addresses are centralized in `front/src/config/protocol.ts`. +Update them there if you redeploy Factory/Router/WNATIVE/TSWP. + +---------- + +## Troubleshooting + +- **No quote (getAmountsOut revert)**: pair missing / zero liquidity / wrong path. Check `Factory.getPair()` and addresses. + +- **“Invalid opcode” on `readContract`**: ensure the address is an **ERC-20 contract**, not an EOA or unrelated contract. + +- **Swap blocked by “approve first”**: approve the input token with the **Approve** button. + + +---------- + +## License + +MIT (or your preferred license). diff --git a/apps/web/package.json b/apps/web/package.json index 83290f8c8..d61001e18 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -5,7 +5,8 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc -b && vite build", + "build": "vite build", + "typecheck": "tsc -b", "lint": "eslint .", "preview": "vite preview" }, diff --git a/apps/web/src/lib/dynamic.tsx b/apps/web/src/lib/dynamic.tsx index 076aed7ec..b424a5154 100644 --- a/apps/web/src/lib/dynamic.tsx +++ b/apps/web/src/lib/dynamic.tsx @@ -20,25 +20,31 @@ function toDynamicEvmNetwork() { rpcUrls: INTUITION.rpcUrls.default.http.slice(), blockExplorerUrls: [INTUITION.blockExplorers?.default?.url].filter(Boolean) as string[], nativeCurrency: INTUITION.nativeCurrency, - testnet: true, - iconUrls: [], + testnet: true, + iconUrls: [], } } export function RootProviders({ children }: PropsWithChildren) { + // Prefer env var, fallback to hard-coded id + const envId = (import.meta.env.VITE_DYNAMIC_ENV_ID as string | undefined) ?? "78601171-b1f9-42d1-b651-b76f97becab7" + + if (!envId) { + console.error("Missing Dynamic environmentId") + return
Wallet connect disabled: missing Dynamic environmentId.
+ } + return ( - - {children} - + {children}