From c0bec95e1fe43968d1cc96f2a6f4123ecfcc15b5 Mon Sep 17 00:00:00 2001 From: Redchar1992 Date: Tue, 15 Sep 2026 14:47:32 +0800 Subject: [PATCH 1/2] feat(demo): add wallet-free RWA flows and deploy the testnet vault - Separate simulation from Base Sepolia and configure GitHub Pages\n- Add liquidity-aware ERC-4626 exits and wallet transaction safeguards\n- Cover protocol, SDK and browser behavior with regression checks\n- Document mock-asset boundaries and public deployment receipts --- .github/workflows/pages.yml | 56 ++ .gitignore | 3 + README.md | 35 +- apps/web/index.html | 4 +- apps/web/src/App.tsx | 4 + apps/web/src/Testnet.tsx | 22 + apps/web/src/components/Actions.tsx | 44 +- apps/web/src/components/TxFeedback.tsx | 9 + apps/web/src/components/VaultPanel.tsx | 77 +++ apps/web/src/components/Widgets.tsx | 9 +- apps/web/src/demo/Demo.tsx | 53 ++ apps/web/src/demo/model.ts | 118 ++++ apps/web/src/deployments.ts | 7 +- apps/web/src/main.tsx | 33 +- apps/web/src/styles.css | 42 ++ apps/web/src/tx.ts | 43 +- apps/web/src/wagmi.ts | 9 +- apps/web/vite.config.ts | 2 + contracts/foundry.toml | 1 + contracts/script/Deploy.s.sol | 15 +- contracts/script/DeployVault.s.sol | 47 ++ contracts/src/vault/LienVault.sol | 33 +- contracts/test/Subscription.t.sol | 18 + contracts/test/Vault.t.sol | 85 +++ docs/architecture-and-risks.md | 41 ++ docs/demo-runbook.md | 49 ++ docs/vault-deployment.json | 76 +++ docs/verification.md | 22 + packages/sdk/src/abis.ts | 15 + packages/sdk/src/index.ts | 48 +- tools/tests/demo.spec.ts | 64 ++ tools/tests/model.test.ts | 47 ++ tools/tests/package-lock.json | 795 +++++++++++++++++++++++++ tools/tests/package.json | 14 + tools/tests/playwright.config.ts | 6 + tools/tests/sdk.test.ts | 67 +++ tools/tests/testnet-vault-smoke.ts | 86 +++ 37 files changed, 2013 insertions(+), 86 deletions(-) create mode 100644 .github/workflows/pages.yml create mode 100644 apps/web/src/Testnet.tsx create mode 100644 apps/web/src/components/TxFeedback.tsx create mode 100644 apps/web/src/components/VaultPanel.tsx create mode 100644 apps/web/src/demo/Demo.tsx create mode 100644 apps/web/src/demo/model.ts create mode 100644 contracts/script/DeployVault.s.sol create mode 100644 docs/architecture-and-risks.md create mode 100644 docs/demo-runbook.md create mode 100644 docs/vault-deployment.json create mode 100644 docs/verification.md create mode 100644 tools/tests/demo.spec.ts create mode 100644 tools/tests/model.test.ts create mode 100644 tools/tests/package-lock.json create mode 100644 tools/tests/package.json create mode 100644 tools/tests/playwright.config.ts create mode 100644 tools/tests/sdk.test.ts create mode 100644 tools/tests/testnet-vault-smoke.ts diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..b41dfb7 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,56 @@ +name: Verify and deploy Pages +on: + push: + branches: [main] + pull_request: + workflow_dispatch: +permissions: + contents: read +concurrency: + group: pages-${{ github.ref }} + cancel-in-progress: false +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - uses: actions/setup-node@v4 + with: + node-version: 22 + - run: corepack enable && corepack prepare pnpm@9.15.0 --activate + - run: pnpm install --frozen-lockfile + - run: pnpm --filter @lien/web build + env: + GITHUB_PAGES: 'true' + - run: npm ci --prefix tools/tests + - run: npm test --prefix tools/tests + - run: cd tools/tests && npx playwright install --with-deps chromium + - run: | + pnpm --filter @lien/web exec vite preview --host 127.0.0.1 --port 4173 --base /lien/ & + for i in $(seq 1 30); do curl -fsS http://127.0.0.1:4173/lien/ && break || sleep 1; done + npm run test:browser --prefix tools/tests + - uses: foundry-rs/foundry-toolchain@v1 + with: + version: v1.5.1 + - run: forge test --summary + working-directory: contracts + - uses: actions/upload-pages-artifact@v3 + if: github.ref == 'refs/heads/main' + with: + path: apps/web/dist + deploy: + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + needs: verify + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 419a6c9..34cd98f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ contracts/broadcast/ # ponder .ponder/ apps/indexer/ponder-env.d.ts +# browser verification artifacts +tools/tests/test-results/ +tools/tests/playwright-report/ diff --git a/README.md b/README.md index 91df7d0..a50ae8c 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,19 @@ redemption flows, and usable as **collateral in an isolated lending market** so holders can borrow stablecoins **without redeeming** — the protocol takes a *lien* on the tokenized asset. -> Portfolio project (EVM / RWA). Built EVM-native (Foundry + viem/wagmi) as the -> transferable counterpart to my TRON lending work — the full-stack pattern moves -> from TronWeb to viem as an interface swap, not a rewrite. +**技术实践 / technical exploration — mock assets only, not a live investment product.** + +- **[Open the wallet-free demo](https://redchar1992.github.io/lien/)** — simulation is the default; Base Sepolia is a separate explicit mode. +- [Five-minute walkthrough and deployment guide](docs/demo-runbook.md) +- [Architecture, reused components and trust boundaries](docs/architecture-and-risks.md) +- [Verification evidence](docs/verification.md) ## Why it's interesting (the hard parts) - **Compliance as a first-class concern.** The RWA token is *permissioned* (KYC allowlist + transfer restrictions + agent powers: freeze / forceTransfer / - recovery — required by regulation, with the centralization trade-off discussed - honestly). A faithful, focused subset of the ERC-3643 model. + recovery — a technical permission model, **not proof of legal compliance**; + centralization trade-offs are documented). A faithful, focused subset of the ERC-3643 model. - **NAV oracle = the asset's truth.** Off-chain valuation pushed on-chain with staleness + circuit-breaker guards; yield accrues via NAV appreciation. An RWA oracle carries more responsibility than a DeFi price feed. @@ -43,9 +46,9 @@ The depositor journey, and the mechanism behind each step: 4. **Borrow against it.** Post `tBILL` as collateral in the isolated lending market (a vendored Morpho Blue engine) and **borrow USDC without selling** — the protocol takes a *lien* on your tokenized asset. The **Health Factor** = LTV-weighted collateral ÷ debt; - above 1.0 is safe, below 1.0 is liquidatable. The NAV→Morpho oracle adapter is - **fail-safe**: a stale/circuit-broken NAV freezes the market rather than pricing off a - dead feed. + above 1.0 meets the configured threshold, below 1.0 is liquidatable. The NAV→Morpho oracle adapter is + **fail-safe**: a stale/circuit-broken NAV blocks price-dependent actions rather than pricing off a + dead feed (repayment and supplying liquidity remain possible). 5. **Liquidation of permissioned collateral.** If a position goes underwater, anyone can liquidate it through the `LiquidationRouter` — a KYC'd contract with a USDC buffer that receives the seized `tBILL`, fronts the repayment, and pays the keeper the incentive in @@ -57,9 +60,9 @@ The depositor journey, and the mechanism behind each step: The UI surfaces your pending redemptions with a live countdown and a Claim button that unlocks once settled — and `withdrawProceeds` can never dip into the reserve backing those queued claims. -7. **Or deposit into the curated vault.** Prefer hands-off, diversified exposure? +7. **Or deposit into the curated vault.** Prefer a curated lending route? `LienVault` (ERC-4626, MetaMorpho-style) takes your USDC and a curator allocates it - across multiple isolated RWA markets under per-market caps; yield accrues to depositors. + across configured isolated RWA markets under per-market caps; borrower interest accrues to depositors. The demo configures only one market, not diversified exposure. Every privileged/agent action emits an auditable event; the trust boundary is documented honestly rather than hidden. @@ -67,7 +70,7 @@ honestly rather than hidden. ## Architecture ``` -Frontend (React + Vite + wagmi/viem) → Indexer (ponder/viem + Postgres) +Frontend (React + Vite + wagmi/viem) Indexer scaffold (not hosted) │ │ └────────────── Contracts (Foundry) ───┘ compliance/ IdentityRegistry + transfer-restriction hook + roles @@ -84,19 +87,20 @@ Postgres · pnpm + turbo monorepo · Base Sepolia. ## Status — roadmap +Original milestone test counts below are historical; [verification](docs/verification.md) records the current regression results. + - [x] **M0** — monorepo scaffold + vendored Morpho engine - [x] **M1** — compliance core (IdentityRegistry, permissioned RWA token, agent roles) — 14 tests - [x] **M2** — NAV oracle (staleness + circuit breaker) + subscription/redemption (USDC ↔ RWA @ NAV, T+N queue) — 11 tests - [x] **M3** — isolated lending market over Morpho + NAV→1e36 oracle adapter + permissioned-collateral liquidation (router pattern) — 8 tests · [ADRs](contracts/docs/合规设计.md) - [x] **M4** — full-stack frontend (React + viem/wagmi + RainbowKit) + viem tx-lifecycle SDK + ponder indexer scaffold — typechecks - [x] **M5** — deployed + seeded live on **Base Sepolia** ([addresses](#live-on-base-sepolia)); `pnpm --filter @lien/web dev` connects to it -- [x] **M6** — curated ERC-4626 vault (MetaMorpho-style): depositors lend USDC across isolated RWA markets under per-market caps; yield accrues to depositors — 4 tests +- [x] **M6** — curated ERC-4626 vault with cap/queues, liquidity-aware exit limits and deposit/withdraw/redeem UI; see current verification evidence below. +- [x] **M7** — wallet-free simulation, risk scenarios, static Pages workflow and browser/SDK/contract regression checks. ## Live on Base Sepolia -Deployed + seeded 2026-06-08 (market liquidity, a healthy borrow position, router -buffer + redemption liquidity). NAV $1.00. Run `pnpm --filter @lien/web dev` and -connect a Base Sepolia wallet. +Core contracts were deployed and seeded on 2026-06-08. That is historical state, not a promise of current NAV freshness, liquidity or keeper uptime. Open the demo and explicitly switch to Base Sepolia, or run `pnpm --filter @lien/web dev`. See [verification](docs/verification.md) for dated evidence. No mainnet or real assets are used. | Contract | Address | | --- | --- | @@ -107,6 +111,7 @@ connect a Base Sepolia wallet. | SubscriptionManager | `0x8Fe81a819c6280678b607fDCCC09AB54e526E48b` | | Morpho (engine) | `0x62bd467F599153e8E3C46c6629CA2b774AF405B4` | | MorphoNavOracleAdapter | `0x86e9000956B488192F3e572d2C73c0C0DfCB7b0b` | +| LienVault (2026-09-15) | `0xc4ca6BbC70C429F96d19577B641fbe126a0CA93B` | | LiquidationRouter | `0xdBc5Fe8F7Bc3cd34F5fBdBb670F1Aa7690d25375` | To interact (subscribe / borrow) a wallet must be KYC-verified by the agent — the diff --git a/apps/web/index.html b/apps/web/index.html index 5a093b3..e416dd7 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -1,10 +1,10 @@ - + - + lien — RWA credit diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 6ec23ea..dabed3c 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -1,3 +1,4 @@ +import { VaultPanel } from './components/VaultPanel' import { ConnectButton } from '@rainbow-me/rainbowkit' import { KycBadge, NavCard, PortfolioCard, StatsBar } from './components/Widgets' import { SubscribeRedeem, BorrowPanel } from './components/Actions' @@ -27,6 +28,7 @@ export function App() { +
Base Sepolia · 真实测试网调用 / Testnet
使用 mock USDC / tBILL,无真实资产背书。钱包需测试币;RWA 申购需管理员白名单。NAV 可能已过期,读取失败不代表有效。不会自动切换网络或发送交易。
@@ -57,6 +59,8 @@ export function App() { + +