Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
169 commits
Select commit Hold shift + click to select a range
2a38587
feat(contracts): add Ethereum and Sepolia rows to the deploy chain table
codeForEatingEverything Oct 24, 2026
45e8c7d
feat(contracts): deploy script reads the seven-row table and GRAD row…
codeForEatingEverything Oct 24, 2026
d0e7463
chore(contracts): Sepolia profile and Ethereum / Sepolia RPC endpoints
codeForEatingEverything Oct 24, 2026
69a6868
test(contracts): script checks for the Ethereum and Sepolia rows
codeForEatingEverything Oct 24, 2026
4b649fb
docs(contracts): list the Sepolia pairs in the CreatePools header
codeForEatingEverything Oct 24, 2026
62e868c
feat(contracts): PositionMarket and SeatLP scripts honour DEPLOYMENTS…
codeForEatingEverything Oct 24, 2026
cfd9742
feat(scripts): Sepolia token addresses and the seven-row testnet salt
codeForEatingEverything Oct 24, 2026
83ae6c5
feat(scripts): Sepolia testnet parameters
codeForEatingEverything Oct 24, 2026
f271c2c
feat(scripts): GRAD_11155111 graduation and order-minimum row for Sep…
codeForEatingEverything Oct 24, 2026
e60ae70
feat(scripts): verify and keep PositionMarket and SeatLP records acro…
codeForEatingEverything Oct 24, 2026
9c1f672
feat(scripts): FUND=1 stakes the missing wstETH on Sepolia
codeForEatingEverything Oct 24, 2026
dc1d5a4
feat(scripts): one CRE config per chain when syncing off-chain configs
codeForEatingEverything Oct 24, 2026
c15aa8c
feat(scripts): deploy-testnet accepts sepolia and deploys PositionMar…
codeForEatingEverything Oct 24, 2026
136c565
feat(scripts): roll-testnet accepts sepolia
codeForEatingEverything Oct 24, 2026
d4ca5ad
test(scripts): one Sepolia seed clears its graduation row
codeForEatingEverything Oct 24, 2026
b9c51d8
test(scripts): Sepolia preflight, FUND=1 staking, salts and the marke…
codeForEatingEverything Oct 24, 2026
cf049ea
feat(sdk): Sepolia and Ethereum mainnet in the supported chains
codeForEatingEverything Oct 24, 2026
c87474b
feat(sdk): 1,000-block log range on Ethereum chains
codeForEatingEverything Oct 24, 2026
3e50a97
refactor(sdk): deployments codegen keyed by chainId, chains without a…
codeForEatingEverything Oct 24, 2026
b9e9c3e
test(sdk): Sepolia chain table, log range and deployments codegen
codeForEatingEverything Oct 24, 2026
fb209d7
feat(keeper): run on Sepolia (CHAIN_ID 11155111, RPC env)
codeForEatingEverything Oct 24, 2026
4f0e5b0
test(keeper): Sepolia config
codeForEatingEverything Oct 24, 2026
38c7e92
feat(bootstrap-mm): classify a pool's band from its pair
codeForEatingEverything Oct 24, 2026
19806a5
feat(bootstrap-mm): POOLS entries may say poolId:auto, clearer errors…
codeForEatingEverything Oct 24, 2026
09f7c96
test(bootstrap-mm): band classification for Sepolia and the existing …
codeForEatingEverything Oct 24, 2026
f7bf767
feat(keeper-cre): sepolia-settings target on ethereum-testnet-sepolia
codeForEatingEverything Oct 24, 2026
71ede1e
feat(keeper-cre): simulate:sepolia and sync-config:sepolia scripts
codeForEatingEverything Oct 24, 2026
0234ed9
feat(keeper-cre): sync-config per chain with forwarder, gas limit and…
codeForEatingEverything Oct 24, 2026
7494174
test(keeper-cre): Sepolia sync-config rows and the undeployed case
codeForEatingEverything Oct 24, 2026
40fe3b1
feat(ops): CCTP USDC transfers to and from Sepolia (domain 0)
codeForEatingEverything Oct 24, 2026
77c4c75
feat(ops): Sepolia in status, balances and block-time calibration
codeForEatingEverything Oct 24, 2026
3781a91
chore(deploy): Sepolia RPC variables for the self-hosted web image
codeForEatingEverything Oct 24, 2026
25fcddd
feat(web): Sepolia as a selectable network
codeForEatingEverything Oct 24, 2026
1c4a4ce
feat(web): pay with native ETH on Sepolia, wrapped to WETH
codeForEatingEverything Oct 24, 2026
fca83ad
feat(web): Sepolia chain and token logos (ETH, WETH, wstETH, USDC)
codeForEatingEverything Oct 24, 2026
40dc8eb
feat(web): Sepolia token rules and vault names
codeForEatingEverything Oct 24, 2026
2410910
feat(web): /api/ref/spot, a display-only ETH/USD reference from Coinb…
codeForEatingEverything Oct 24, 2026
93c3eb5
feat(web): Sepolia reference prices
codeForEatingEverything Oct 24, 2026
5f30ac5
feat(web): Loop staking rate falls back to wstETH getWstETHByStETH
codeForEatingEverything Oct 24, 2026
bae2f76
fix(web): Loop card names the LST provider per token instead of alway…
codeForEatingEverything Oct 24, 2026
a7803dc
feat(web): server chain index scans Sepolia logs through an archive R…
codeForEatingEverything Oct 24, 2026
8a15cd5
feat(web): API routes answer 404 notDeployed for a served network wit…
codeForEatingEverything Oct 24, 2026
34eb0cf
feat(web): calm 'not deployed yet' page state for a served network wi…
codeForEatingEverything Oct 24, 2026
7fad500
docs(web): How it works lists deployments per network from the deploy…
codeForEatingEverything Oct 24, 2026
b58a5d0
feat(web): Sepolia demo network
codeForEatingEverything Oct 24, 2026
2603a69
test(web): Sepolia chain table, active-chain following and network names
codeForEatingEverything Oct 24, 2026
7398418
test(web): Sepolia native wrap, token rules and logo files
codeForEatingEverything Oct 24, 2026
3172f9e
test(web): Sepolia reference prices and the spot parser
codeForEatingEverything Oct 24, 2026
81f3f74
test(web): Sepolia log sources and the 404 notDeployed answer
codeForEatingEverything Oct 24, 2026
fa5810a
test(web): undeployed network renders calmly without reads, network m…
codeForEatingEverything Oct 24, 2026
80fb1df
test(web): Sepolia demo network pages and counts
codeForEatingEverything Oct 24, 2026
b135309
chore(deployments): record the Sepolia testnet deployment
codeForEatingEverything Oct 24, 2026
8c99ea3
chore(sdk): regenerate deployments with Sepolia
codeForEatingEverything Oct 24, 2026
c872046
chore(keeper-cre): Sepolia workflow config (receiver, 18 pools, 3 vau…
codeForEatingEverything Oct 24, 2026
81664d3
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (cont…
codeForEatingEverything Oct 24, 2026
d05dd97
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (Conf…
codeForEatingEverything Oct 24, 2026
445f8d8
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (ICon…
codeForEatingEverything Oct 24, 2026
20b4870
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (Rout…
codeForEatingEverything Oct 24, 2026
080c4cf
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (Seat…
codeForEatingEverything Oct 24, 2026
9af795e
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (Sett…
codeForEatingEverything Oct 24, 2026
eb2933c
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (Stat…
codeForEatingEverything Oct 24, 2026
6d79677
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (libr…
codeForEatingEverything Oct 24, 2026
9a409a7
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (test…
codeForEatingEverything Oct 24, 2026
51bec8b
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (test…
codeForEatingEverything Oct 24, 2026
82d8cbb
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (test…
codeForEatingEverything Oct 24, 2026
4d8b17b
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (test…
codeForEatingEverything Oct 24, 2026
b9e53b9
feat(core): v0.8 fees, fee-based graduation, NC buys in pricing (test…
codeForEatingEverything Oct 24, 2026
5720e8c
chore(contracts): gas snapshot for v0.8 fees and NC pricing
codeForEatingEverything Oct 24, 2026
d1983b2
refactor(core): move mature, pause and graduate bodies into WindowLogic
codeForEatingEverything Oct 24, 2026
36c55f6
feat(core): cap cumulative guardian pause per pool (Confluere.sol)
codeForEatingEverything Oct 24, 2026
5be1462
feat(core): cap cumulative guardian pause per pool (IConfluere.sol)
codeForEatingEverything Oct 24, 2026
cd2e7a5
feat(core): cap cumulative guardian pause per pool (State.sol)
codeForEatingEverything Oct 24, 2026
b00b803
feat(core): cap cumulative guardian pause per pool (libraries/logic)
codeForEatingEverything Oct 24, 2026
9e400c2
feat(core): cap cumulative guardian pause per pool (tests invariant)
codeForEatingEverything Oct 24, 2026
5ee90d4
feat(core): cap cumulative guardian pause per pool (tests unit)
codeForEatingEverything Oct 24, 2026
def2172
feat(core): put band and cadence into PoolKey (contracts/script)
codeForEatingEverything Oct 24, 2026
883e6f0
feat(core): put band and cadence into PoolKey (Confluere.sol)
codeForEatingEverything Oct 24, 2026
8aeafce
feat(core): put band and cadence into PoolKey (IConfluere.sol)
codeForEatingEverything Oct 24, 2026
a079aa9
feat(core): put band and cadence into PoolKey (PoolKey.sol)
codeForEatingEverything Oct 24, 2026
e3f09b4
feat(core): put band and cadence into PoolKey (SeatRegistry.sol)
codeForEatingEverything Oct 24, 2026
607fb0b
feat(core): put band and cadence into PoolKey (State.sol)
codeForEatingEverything Oct 24, 2026
c969f74
feat(core): put band and cadence into PoolKey (Vault.sol)
codeForEatingEverything Oct 24, 2026
a5783eb
feat(core): put band and cadence into PoolKey (libraries/logic)
codeForEatingEverything Oct 24, 2026
05de149
feat(core): put band and cadence into PoolKey (tests audit)
codeForEatingEverything Oct 24, 2026
077f038
feat(core): put band and cadence into PoolKey (tests invariant)
codeForEatingEverything Oct 24, 2026
bf4bce7
feat(core): put band and cadence into PoolKey (tests medusa)
codeForEatingEverything Oct 24, 2026
f37dbdf
feat(core): put band and cadence into PoolKey (tests unit)
codeForEatingEverything Oct 24, 2026
3d491ba
feat(core): put band and cadence into PoolKey (tests utils)
codeForEatingEverything Oct 24, 2026
dc8ed4e
feat(sdk): v0.8 fee math (trade fee, spread share, NC units, escrow s…
codeForEatingEverything Oct 24, 2026
a50d431
feat(core): one-percent log strike grid with canonical index (contrac…
codeForEatingEverything Oct 24, 2026
0e48e18
feat(core): one-percent log strike grid with canonical index (Conflue…
codeForEatingEverything Oct 24, 2026
493a9b2
feat(core): one-percent log strike grid with canonical index (Grid.sol)
codeForEatingEverything Oct 24, 2026
6745246
feat(core): one-percent log strike grid with canonical index (IConflu…
codeForEatingEverything Oct 24, 2026
1e800ba
feat(core): one-percent log strike grid with canonical index (State.sol)
codeForEatingEverything Oct 24, 2026
c7921b8
feat(core): one-percent log strike grid with canonical index (librari…
codeForEatingEverything Oct 24, 2026
68cae7b
feat(core): one-percent log strike grid with canonical index (tests f…
codeForEatingEverything Oct 24, 2026
90934e1
feat(core): one-percent log strike grid with canonical index (tests i…
codeForEatingEverything Oct 24, 2026
691851e
feat(core): one-percent log strike grid with canonical index (tests m…
codeForEatingEverything Oct 24, 2026
e3f8940
feat(core): one-percent log strike grid with canonical index (tests u…
codeForEatingEverything Oct 24, 2026
a955878
feat(core): one-percent log strike grid with canonical index (tests u…
codeForEatingEverything Oct 24, 2026
7d1bb48
feat(core): track tier volume and the main tier per market and expiry…
codeForEatingEverything Oct 24, 2026
0ee43fe
feat(core): track tier volume and the main tier per market and expiry…
codeForEatingEverything Oct 24, 2026
8d02c45
feat(core): track tier volume and the main tier per market and expiry…
codeForEatingEverything Oct 24, 2026
1ffb18a
feat(core): track tier volume and the main tier per market and expiry…
codeForEatingEverything Oct 24, 2026
58a6a15
feat(core): track tier volume and the main tier per market and expiry…
codeForEatingEverything Oct 24, 2026
82ab892
feat(core): track tier volume and the main tier per market and expiry…
codeForEatingEverything Oct 24, 2026
f07972e
feat(core): track tier volume and the main tier per market and expiry…
codeForEatingEverything Oct 24, 2026
4ea2b4f
feat(core): let rollovers fill in the last batch before expiry (contr…
codeForEatingEverything Oct 24, 2026
634ed74
feat(core): let rollovers fill in the last batch before expiry (Confl…
codeForEatingEverything Oct 24, 2026
d936884
feat(core): let rollovers fill in the last batch before expiry (IConf…
codeForEatingEverything Oct 24, 2026
eddb49e
feat(core): let rollovers fill in the last batch before expiry (State…
codeForEatingEverything Oct 24, 2026
846fe7e
feat(core): let rollovers fill in the last batch before expiry (libra…
codeForEatingEverything Oct 24, 2026
cf7338d
feat(core): let rollovers fill in the last batch before expiry (tests…
codeForEatingEverything Oct 24, 2026
40950af
feat(core): let rollovers fill in the last batch before expiry (tests…
codeForEatingEverything Oct 24, 2026
246eca7
feat(core): let rollovers fill in the last batch before expiry (tests…
codeForEatingEverything Oct 24, 2026
d240c37
feat(core): let rollovers fill in the last batch before expiry (tests…
codeForEatingEverything Oct 24, 2026
96637ab
feat(core): let rollovers fill in the last batch before expiry (tests…
codeForEatingEverything Oct 24, 2026
f4c9bd3
feat(web): v0.8 fee math in quotes, pool rows and the demo chain (web…
codeForEatingEverything Oct 24, 2026
89070fe
feat(web): v0.8 fee math in quotes, pool rows and the demo chain (web…
codeForEatingEverything Oct 24, 2026
0085ed8
feat(web): show v0.8 fees and single-token vault withdrawals (web src…
codeForEatingEverything Oct 24, 2026
294587a
feat(web): show v0.8 fees and single-token vault withdrawals (web src…
codeForEatingEverything Oct 24, 2026
72f1cf2
feat(web): show v0.8 fees and single-token vault withdrawals (web test)
codeForEatingEverything Oct 24, 2026
f13102c
feat(periphery): Specials physical lending and margin-aware Router repay
codeForEatingEverything Oct 24, 2026
3be33ff
feat(sdk): v0.8 PoolKey, 1% log grid, Specials and autoSell views (sd…
codeForEatingEverything Oct 24, 2026
cf4ea8d
feat(sdk): v0.8 PoolKey, 1% log grid, Specials and autoSell views (sd…
codeForEatingEverything Oct 24, 2026
8b6529c
feat(periphery): Looper intent accounts for multi-batch looping
codeForEatingEverything Oct 24, 2026
d1aab43
feat(vault): main or high tier by volume, two traded expiries, autoSe…
codeForEatingEverything Oct 24, 2026
51e4845
feat(vault): main or high tier by volume, two traded expiries, autoSe…
codeForEatingEverything Oct 24, 2026
166a626
feat(vault): main or high tier by volume, two traded expiries, autoSe…
codeForEatingEverything Oct 24, 2026
7afa5b8
feat(keeper): v0.8 PoolCreated, Selling close, autoSell-aware purge (…
codeForEatingEverything Oct 24, 2026
16b835b
feat(keeper): v0.8 PoolCreated, Selling close, autoSell-aware purge (…
codeForEatingEverything Oct 24, 2026
9879449
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
e8bd960
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
0ef14c6
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
ed782a2
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
3fbcb56
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
f9d7804
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
c9c8e96
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
55bb26d
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
e6d8c41
feat(core): add the Specials lending interface with lazy short accoun…
codeForEatingEverything Oct 24, 2026
2077cb1
feat(seats): 70% seat share before graduation, 35% after; keeper fina…
codeForEatingEverything Oct 24, 2026
1f6d590
feat(keeper-cre): sync-config reads the v0.8 PoolKey and keeps Sellin…
codeForEatingEverything Oct 24, 2026
aa1ad61
feat(web): v0.8 PoolKey, 1% log grid and auto-sell in create market (…
codeForEatingEverything Oct 24, 2026
fdf922b
feat(web): v0.8 PoolKey, 1% log grid and auto-sell in create market (…
codeForEatingEverything Oct 24, 2026
e13f6b6
feat(web): v0.8 PoolKey, 1% log grid and auto-sell in create market (…
codeForEatingEverything Oct 24, 2026
e6f1de2
feat(web): Specials page, selling status and quote-only vault copy (w…
codeForEatingEverything Oct 24, 2026
2e9c170
feat(web): Specials page, selling status and quote-only vault copy (w…
codeForEatingEverything Oct 24, 2026
79d3016
feat(web): Specials page, selling status and quote-only vault copy (w…
codeForEatingEverything Oct 24, 2026
7ba66f2
feat(web): Specials page, selling status and quote-only vault copy (w…
codeForEatingEverything Oct 24, 2026
37ff156
feat(bootstrap-mm): per-market exit by seat or weekly volume, buyer f…
codeForEatingEverything Oct 24, 2026
933e3b8
feat(bootstrap-mm): per-market exit by seat or weekly volume, buyer f…
codeForEatingEverything Oct 24, 2026
65995e1
fix(web): follow the v0.8 Status enum (Selling = 4, Closed = 5) (web …
codeForEatingEverything Oct 24, 2026
42d5f99
fix(web): follow the v0.8 Status enum (Selling = 4, Closed = 5) (web …
codeForEatingEverything Oct 24, 2026
25114d9
feat(scripts): testnet strikes on the 1% log grid, fee GRAD rows, fee…
codeForEatingEverything Oct 24, 2026
a475937
fix(scripts): keep Deploy.run within the stack limit
codeForEatingEverything Oct 24, 2026
cfd9e75
feat(core): sell defaulted collateral into the next expiry (autoSell)…
codeForEatingEverything Oct 24, 2026
e790fd0
feat(core): sell defaulted collateral into the next expiry (autoSell)…
codeForEatingEverything Oct 24, 2026
b4f1826
feat(core): sell defaulted collateral into the next expiry (autoSell)…
codeForEatingEverything Oct 24, 2026
06e3f01
feat(core): sell defaulted collateral into the next expiry (autoSell)…
codeForEatingEverything Oct 24, 2026
8e90582
feat(core): sell defaulted collateral into the next expiry (autoSell)…
codeForEatingEverything Oct 24, 2026
086a918
feat(core): sell defaulted collateral into the next expiry (autoSell)…
codeForEatingEverything Oct 24, 2026
d5152d4
feat(core): sell defaulted collateral into the next expiry (autoSell)…
codeForEatingEverything Oct 24, 2026
65ff381
fix(vault): keep VaultFactory under EIP-170 with a hash-checked Vault…
codeForEatingEverything Oct 24, 2026
41de24f
fix(vault): keep VaultFactory under EIP-170 with a hash-checked Vault…
codeForEatingEverything Oct 24, 2026
fc1bef9
fix(vault): keep VaultFactory under EIP-170 with a hash-checked Vault…
codeForEatingEverything Oct 24, 2026
9c24212
test(halmos): symbolic check of the v0.8 NC pricing rule
codeForEatingEverything Oct 24, 2026
3192a7b
refactor(core): split closeWindow's shared-pot close from the fee-esc…
codeForEatingEverything Oct 24, 2026
f8631b9
test(halmos): pack the NC pricing harness and mark the two slow check…
codeForEatingEverything Oct 24, 2026
f12922e
fix(scripts): treat a zombie anvil as dead in the e2e wait loop
codeForEatingEverything Oct 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added apps/web/public/chains/11155111.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions apps/web/src/app/api/ref/spot/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Display-only mainnet spot price, for networks without a market worth quoting (Sepolia: ETH / USD for WETH / USDC and,
* times wstETH's own rate, wstETH / USDC). GET /api/ref/spot?symbol=ETH_USD -> { price, source }. Asks Coinbase, then
* Kraken; each answer is cached for 30 s, and when both fail the route answers `{ price: null }` (status 502) so the
* screens simply show no reference. Never used on-chain.
*/
import { SPOT_PAIRS, coinbaseSpot, krakenLast } from "@/lib/spot";

const TTL_S = 30;

async function ask(url: string, parse: (j: unknown) => number | null): Promise<number | null> {
try {
const r = await fetch(url, { next: { revalidate: TTL_S }, signal: AbortSignal.timeout(4_000) });
return r.ok ? parse(await r.json()) : null;
} catch {
return null;
}
}

export async function GET(req: Request) {
const symbol = new URL(req.url).searchParams.get("symbol") ?? "";
const pair = Object.hasOwn(SPOT_PAIRS, symbol) ? SPOT_PAIRS[symbol] : undefined; // not toString / __proto__
if (!pair) return Response.json({ price: null }, { status: 400 });
const cache = { "cache-control": `public, max-age=${TTL_S}` };
const cb = await ask(`https://api.coinbase.com/v2/prices/${pair.coinbase}/spot`, coinbaseSpot);
if (cb !== null) return Response.json({ price: cb, source: "coinbase" }, { headers: cache });
const kr = await ask(`https://api.kraken.com/0/public/Ticker?pair=${pair.kraken}`, krakenLast);
if (kr !== null) return Response.json({ price: kr, source: "kraken" }, { headers: cache });
return Response.json({ price: null }, { status: 502 });
}
13 changes: 13 additions & 0 deletions apps/web/src/app/specials/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Metadata } from "next";
import { Suspense } from "react";
import { SpecialsScreen } from "@/components/specials/SpecialsScreen";

export const metadata: Metadata = { title: "Specials | Confluere" };

export default function Page() {
return (
<Suspense>
<SpecialsScreen />
</Suspense>
);
}
10 changes: 5 additions & 5 deletions apps/web/src/components/borrow/LendForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Link from "next/link";
import { useState } from "react";
import { useConnection } from "wagmi";
import type { Hex } from "viem";
import { quoteDown, scaleOf } from "@confluere/sdk";
import { quoteDown, quoteUp, scaleOf, tradeFee } from "@confluere/sdk";
import { useActiveChain } from "@/lib/activeChain";
import { launchTokensOf } from "@/lib/chains";
import { useAccountOrders, useMinNotional, usePools, useTokens, tokenOf, type PoolRow } from "@/lib/data";
Expand Down Expand Up @@ -134,10 +134,10 @@ function LendFormFor({ pool, onClose, embedded }: { pool: PoolRow; onClose?: ()

// facts per 1 unit of collateral until an amount is typed (Pendle PT panel: rate, maturity, payout before input)
const units = amt > 0n && q.units > 0n ? q.units : 10n ** BigInt(A.decimals);
const paid = amt > 0n ? amt : quoteDown(units, price, scale);
const fee = tradeFee(quoteUp(units, price, scale)); // v0.8: 0.015% on the lender's side, included in the amount
const paid = amt > 0n ? amt : quoteUp(units, price, scale) + fee;
const payout = quoteDown(units, f.netPerUnit || bbPrice, scale);
const per = amt > 0n ? "" : ` per 1 ${A.symbol}`;
const fee = (quoteDown(units, bbPrice, scale) - payout);
const ft = positionName("ft", pool, A, Q, { all: allPools ?? [] });

return (
Expand All @@ -164,12 +164,12 @@ function LendFormFor({ pool, onClose, embedded }: { pool: PoolRow; onClose?: ()
<DetailRows
rows={[
amt > 0n && q.units > 0n && { k: "You receive", v: <span title={ft.symbol}>≈ {fmtToken(q.units, { symbol: ft.short, decimals: A.decimals })}</span>, hint: `1 C = 1 ${A.symbol} of backing`, testId: "lend-receive" },
{ k: "Fixed rate", v: price > 0n ? rateText(f, pool.expiry - now) : "—", hint: "net of the protocol's share" },
{ k: "Fixed rate", v: price > 0n ? rateText(f, pool.expiry - now) : "—", hint: "net of the 0.015% trade fee" },
{ k: `You pay${per}`, v: <>≈ {fmtToken(paid, Q)}</>, hint: price > 0n ? `${fmtPer(price, Q)} ${Q.symbol} per ${A.symbol}` : undefined },
{ k: `Paid back${per}`, v: fmtToken(payout, Q), hint: `on ${fmtChip(pool.expiry)} UTC, if repaid` },
!own && { k: "Worst case", v: worst.priced ? rateText(worst, pool.expiry - now) : "—", hint: `you pay at most ${fmtPer(cap, Q)} per ${A.symbol}` },
{ k: "Collateral", v: `${A.symbol}, bought back at ${fmtPer(bbPrice, Q)}`, hint: refUnits ? `${pct(Number(bbPrice - refUnits) / Number(refUnits), 1)} vs market (reference)` : undefined },
{ k: "Fee", v: fee > 0n ? fmtToken(fee, Q) : "None", hint: "share of the interest, only if repaid" },
{ k: "Trade fee", v: fee > 0n ? fmtToken(fee, Q) : "None", hint: "0.015%, included in what you pay; the 5% share of the interest comes from the borrower's side", testId: "lend-fee" },
{ k: "Repayment", v: repaysAnyTime(pool) ? "Any time" : "At maturity", hint: repaysAnyTime(pool) ? "you may be paid back early" : undefined },
]}
/>
Expand Down
8 changes: 4 additions & 4 deletions apps/web/src/components/borrow/LoopCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ export function LoopCard({ pool, A, Q, embedded = false, top }: { pool: PoolRow;
onMax={address && spendable ? () => setStart(toInput(spendable, A.decimals)) : undefined}
/>
{short && stake && (
<p className="flex flex-wrap items-center gap-2 rounded-[var(--radius-control)] bg-warn-soft px-3 py-2 text-caption text-warn">
You need {A.symbol}. Stake {deployment ? Q.symbol.replace(/^W/, "") : ""} at FastLane (opens their site).
<p className="flex flex-wrap items-center gap-2 rounded-[var(--radius-control)] bg-warn-soft px-3 py-2 text-caption text-warn [overflow-wrap:anywhere]">
You need {A.symbol}. {stake.href ? <>Stake {deployment ? Q.symbol.replace(/^W/, "") : ""} at {stake.provider} (opens their site).</> : stake.note}
{stake.href && (
<a href={stake.href} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 font-medium underline underline-offset-2">
Open <ArrowUpRight size={14} strokeWidth={1.75} aria-hidden />
Expand Down Expand Up @@ -189,7 +189,7 @@ export function LoopCard({ pool, A, Q, embedded = false, top }: { pool: PoolRow;
<ol className="mt-4 flex flex-wrap gap-x-4 gap-y-1 text-caption text-ink-3" aria-label="Each round">
<li>1 Borrow {Q.symbol}</li>
<li>2 Withdraw it from Portfolio</li>
<li>{staking ? "3 Stake at FastLane (their site)" : `3 Turn it into ${P.symbol} where you usually trade`}</li>
<li>{staking ? (stake ? `3 Stake at ${stake.provider}${stake.href ? " (their site)" : `: send ${Q.symbol.replace(/^W/, "")} to the ${A.symbol} contract`}` : "3 Stake it (outside Confluere)") : `3 Turn it into ${P.symbol} where you usually trade`}</li>
</ol>
</SummaryDetails>
{plan && <p className="mt-3 text-caption text-ink-3">If you don&apos;t repay: you keep the {Q.symbol}, lenders keep the {A.symbol} you sold. No liquidation.</p>}
Expand Down Expand Up @@ -258,7 +258,7 @@ export function LoopCard({ pool, A, Q, embedded = false, top }: { pool: PoolRow;
<ol className="mt-4 flex flex-wrap gap-x-4 gap-y-1 text-caption text-ink-3" aria-label="Each round">
<li>1 Borrow {Q.symbol}</li>
<li>2 Withdraw it from Portfolio</li>
<li>{staking ? "3 Stake at FastLane (their site)" : `3 Turn it into ${P.symbol} where you usually trade`}</li>
<li>{staking ? (stake ? `3 Stake at ${stake.provider}${stake.href ? " (their site)" : `: send ${Q.symbol.replace(/^W/, "")} to the ${A.symbol} contract`}` : "3 Stake it (outside Confluere)") : `3 Turn it into ${P.symbol} where you usually trade`}</li>
</ol>
<div className="mt-3">
{!open ? (
Expand Down
11 changes: 6 additions & 5 deletions apps/web/src/components/borrow/borrowFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { launchTokensOf } from "@/lib/chains";
import { useAccountOrders, useBlockTime, useMinNotional, type PoolRow, type Token } from "@/lib/data";
import { useBalances, usePending, usePositions } from "@/lib/position";
import { useActionCtx, useChainClock } from "@/lib/useProduct";
import { borrowQuote, defaultBorrowLimit, defaultMoveBps, minUnits, refToUnits, type BorrowQuote } from "@/lib/quote";
import { borrowQuote, defaultBorrowLimit, defaultMoveBps, minUnits, netAtPrice, refToUnits, type BorrowQuote } from "@/lib/quote";
import { repayDeadline, type Deadline } from "@/lib/deadline";
import { borrowWrites } from "@/lib/actions";
import { defaultTtl } from "@/lib/order";
Expand Down Expand Up @@ -115,7 +115,8 @@ export function useBorrowOrder({ pool, A, Q, amount, ref, payNative = false, rec
const limitTooHigh = ownPrice && !!pool && limit >= bbPrice;
const limitLow = ownPrice && !!anchor && limit > 0n && limit < (anchor * 9n) / 10n;
const scale = pool ? scaleOf(pool.assetDecimals) : 1n;
const floor = q && pool && !ownPrice ? quoteDown(show, q.guard, scale) : undefined;
// net of the v0.8 fees at the guard price: 0.015% trade fee and 5% of (buy-back − price)
const floor = q && pool && !ownPrice ? netAtPrice(pool, show, q.guard) : undefined;

const sym = payToken?.symbol ?? "";
let label: string;
Expand Down Expand Up @@ -256,9 +257,9 @@ export function BorrowTerms({ o, testId = "borrow-terms", collapsible = false, t
{ k: `You receive${per}`, v: <>{o.ownPrice ? "at least " : "≈ "}{fmtToken(qs.receive, Q)}</>, hint: o.floor !== undefined && o.units > 0n ? `at least ${fmtToken(o.floor, Q)} (price may move ${o.move / 100}%)` : undefined },
{ k: `Repay${per}`, v: fmtToken(qs.repay, Q), testId: "row-repay" },
{ k: "Repay by", v: <DeadlineText o={o} />, hint: left > 0 ? `in ${fmtWait(left)}${repaysAnyTime(pool) ? ", any time before" : ", only after maturity"}` : undefined },
{ k: "Interest", v: fmtToken(qs.repay - qs.receive > 0n ? qs.repay - qs.receive : 0n, Q) },
{ k: "Interest", v: fmtToken(qs.repay - qs.receive > 0n ? qs.repay - qs.receive : 0n, Q), hint: "fees included" },
vsMarket !== undefined && { k: "Buy-back price", v: `${fmtPer(o.bbPrice, Q)} ${Q.symbol}`, hint: `${vsMarket >= 0 ? "+" : ""}${pct(vsMarket, 1)} vs market (reference)` },
{ k: "Fee", v: "None" },
{ k: "Fees", v: fmtToken(qs.tradeFee + qs.spreadShare, Q), hint: `0.015% trade fee ${fmtToken(qs.tradeFee, Q)} + 5% of the interest ${fmtToken(qs.spreadShare, Q)}; nothing on repay`, testId: "row-fees" },
]}
/>
);
Expand Down Expand Up @@ -333,7 +334,7 @@ export function BorrowReview({ o, onPlaced }: { o: BorrowOrder; onPlaced?: () =>
{ k: "Repay by", v: <DeadlineText o={o} />, hint: anyTime ? "any time before" : "only after maturity" },
o.floor !== undefined && { k: "You receive at least", v: fmtToken(o.floor, Q), hint: "or the order waits for the next round" },
{ k: "Valid for", v: `${o.tries} round${o.tries > 1 ? "s" : ""}`, hint: `≈ ${fmtWait(o.tries * roundSeconds(pool))}` },
{ k: "Fee", v: "None" },
{ k: "Fees", v: fmtToken(q.tradeFee + q.spreadShare, Q), hint: "0.015% trade fee + 5% of the interest, taken from what you receive; nothing on repay" },
]}
/>
<OutcomeStrip
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/earn/EarnScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/**
* Earn (IA v3.1 item 4): vaults only. Lending into one maturity yourself means getting C on the Borrow market page.
* A vault opens its drawer (?vault=, shareable). Rates are net of the protocol's share of the interest.
* A vault opens its drawer (?vault=, shareable). Rates are net of the 0.015% trade fee.
*/
import Link from "next/link";
import { useMemo } from "react";
Expand Down Expand Up @@ -60,7 +60,7 @@ export function EarnScreen() {
<SectionTitle id="vaults-title">Vaults</SectionTitle>
<VaultRows vaults={vaults.data} loading={(vaults.isLoading || metaMissing) && !vaults.data} failed={vaults.isError && !vaults.data} onRetry={retry} tokens={tokens} open={open} now={now} onOpen={(a, pay) => setVault(a, { pay })} />
<p className="mt-3 text-caption text-ink-3">
Rates are what the vault would earn right now, after the protocol&apos;s share of the interest. Prefer to choose a maturity yourself?{" "}
Rates are what the vault would earn right now, after the 0.015% trade fee. Withdrawals are paid in the loan token: vaults lend only into auto-sell markets, so defaulted collateral is sold in the next maturity's rounds first. Prefer to choose a maturity yourself?{" "}
<Link href="/borrow" className="link">
Lend in one market
</Link>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/earn/VaultDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ function VaultBody({ v, quote, asset, role, native, wrapped = false }: { v: Vaul
}
/>
{rw.picker}
<p className="text-caption text-ink-3">Withdrawals are requested now and paid at the end of this period ({periodEnd}), first come first served. If the vault&apos;s funds are out on loan, they are paid when those loans reach maturity.</p>
<p className="text-caption text-ink-3">Withdrawals are requested now and paid at the end of this period ({periodEnd}), first come first served. If the vault&apos;s funds are out on loan, they are paid when those loans reach maturity. You are paid in the loan token: the vault lends only into markets that auto-sell defaulted collateral in the next maturity's rounds; any small part that does not sell is claimable by share.</p>
{me && me.pendingRedeem > 0n && <StatusPill tone="neutral">Withdrawal requested · payable after {ended ? "the next roll" : <DateTime ts={v.targetExpiry} />}.</StatusPill>}
{me && me.claimAssets > 0n && (
<Button variant="secondary" size="md" block loading={tx.busy} onClick={() => void collect("assets")}>
Expand Down
10 changes: 9 additions & 1 deletion apps/web/src/components/layout/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { NetworkStatus } from "../ui/NetworkStatus";
import { ToastRegion } from "../ui/Toast";
import { MoreMenu } from "./MoreMenu";
import { NetworkMenu } from "./NetworkMenu";
import { NotDeployed, worksUndeployed } from "./NotDeployed";
import { WalletMenu, WalletPicker } from "./WalletMenu";
import { GITHUB_URL, PRIMARY, TABS, inMore, needsBack } from "./nav";

Expand Down Expand Up @@ -144,6 +145,13 @@ export function Footer() {
);
}

/** The page, or the calm "not deployed yet" state on a served network without contracts (components/layout/NotDeployed). */
function Page({ children }: { children: ReactNode }) {
const { deployment } = useActiveChain();
const path = usePathname() ?? "/";
return !deployment && !worksUndeployed(path) ? <NotDeployed /> : <>{children}</>;
}

export function AppShell({ children }: { children: ReactNode }) {
return (
<>
Expand All @@ -154,7 +162,7 @@ export function AppShell({ children }: { children: ReactNode }) {
<TestnetStrip />
<main id="main" tabIndex={-1} className="mx-auto max-w-[1200px] px-4 pt-6 outline-none lg:px-6 lg:pt-10">
<BackHeader />
{children}
<Page>{children}</Page>
</main>
<Footer />
<TabBar />
Expand Down
17 changes: 12 additions & 5 deletions apps/web/src/components/layout/NetworkMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ const KNOWN: Record<number, string> = { 1: "Ethereum", 10: "OP Mainnet", 56: "BN
/** Name of a common EVM chain id, for "Your wallet is on Ethereum" (no full chain list in the bundle). */
const anyChainName = (id: number | undefined) => (id !== undefined && KNOWN[id]) || "another network";

/** The menu's second line for a network: its open markets, or that it has no deployment yet. */
export function marketLine(deployed: boolean, open: number | undefined): string {
if (!deployed) return "Not deployed yet";
return open === undefined ? "Checking markets…" : open > 0 ? `${open} market${open > 1 ? "s" : ""} open` : "No open markets";
}

/** Markets with a maturity still ahead, per chain, from the server index (no chain reads, so the menu opens instantly). */
export function openMarketCount(idx: PoolIndex | undefined, nowS: number): number | undefined {
if (!idx) return undefined;
Expand All @@ -45,7 +51,7 @@ function useMarketCounts(chains: ChainMeta[]) {
})),
});
const now = Date.now() / 1000;
return chains.map((c, i) => ({ chain: c, open: openMarketCount(qs[i]?.data, now) }));
return chains.map((c, i) => ({ chain: c, deployed: !!deploymentOf(c.id), open: openMarketCount(qs[i]?.data, now) }));
}

/** Chain health in one line, for the bottom of the menu. */
Expand All @@ -64,13 +70,14 @@ function HealthLine() {
}

/**
* Network switch: works without a wallet; with one, asks the wallet to follow. The pill itself carries the wallet's
* Network switch: works without a wallet; with one, asks the wallet to follow. Every served network is listed, one
* without a deployment yet (Sepolia before its deploy) too: picking it shows the "not deployed yet" page. The pill itself carries the wallet's
* wrong-network state ("Wrong network", warn colour) and a spinner while the wallet is asked.
*/
export function NetworkMenu({ compact }: { compact?: boolean }) {
const { chainId, meta, walletOnOtherChain, walletChainId } = useActiveChain();
const { switchNetwork, switching } = useSwitchNetwork();
const rows = useMarketCounts(ENABLED.filter((c) => !!deploymentOf(c.id)));
const rows = useMarketCounts(ENABLED);
return (
<Menu
label="Network"
Expand Down Expand Up @@ -99,7 +106,7 @@ export function NetworkMenu({ compact }: { compact?: boolean }) {
>
{(close) => (
<div className="w-[272px]">
{rows.map(({ chain: c, open }) => {
{rows.map(({ chain: c, deployed, open }) => {
const on = c.id === chainId;
return (
<button
Expand All @@ -120,7 +127,7 @@ export function NetworkMenu({ compact }: { compact?: boolean }) {
{c.name}
{c.testnet && <span className="ml-1.5 text-caption font-normal text-ink-3">Testnet</span>}
</span>
<span className="block text-caption text-ink-3">{open === undefined ? "Checking markets…" : open > 0 ? `${open} market${open > 1 ? "s" : ""} open` : "No open markets"}</span>
<span className="block text-caption text-ink-3">{marketLine(deployed, open)}</span>
</span>
{on && <Check size={16} strokeWidth={1.75} className="text-brand" aria-hidden />}
</button>
Expand Down
Loading
Loading