Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 15 additions & 4 deletions packages/app/src/app/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ function CreateLockInner() {
usdcDecimals,
treasury,
explorerUrl,
explorerName,
usdcNote,
} = useMemo(
() =>
getChainConfig(
Expand Down Expand Up @@ -474,7 +476,7 @@ function CreateLockInner() {
lockInFlightRef.current = false;
setStep("success");
toast("Lock created!", "success", {
label: "View on BaseScan",
label: `View on ${explorerName}`,
href: `${explorerUrl}/tx/${lockTxHash}`,
});
trackLockCreated({
Expand Down Expand Up @@ -511,6 +513,7 @@ function CreateLockInner() {
chainId,
sablierLockup,
explorerUrl,
explorerName,
usdcDecimals,
]);

Expand Down Expand Up @@ -616,6 +619,7 @@ function CreateLockInner() {
usdcDecimals={usdcDecimals}
sablierAddress={sablierLockup}
explorerUrl={explorerUrl}
explorerName={explorerName}
onCreateAnother={resetForm}
/>
) : (
Expand Down Expand Up @@ -708,6 +712,11 @@ function CreateLockInner() {
{formatUnits(totalAmount, usdcDecimals)} USDC (incl. {brokerFeePct} fee).
</p>
)}
{usdcNote && (
<p className="text-[11px] text-faint leading-relaxed">
{usdcNote}
</p>
)}
{IS_TESTNET && isConnected && (
<button
onClick={() =>
Expand Down Expand Up @@ -978,7 +987,7 @@ function CreateLockInner() {
rel="noopener noreferrer"
className="inline-flex items-center min-h-[2.75rem] px-2 text-sm text-faint hover:text-cyan underline transition-colors"
>
View on BaseScan
View on {explorerName}
</a>
)}
</div>
Expand Down Expand Up @@ -1023,7 +1032,7 @@ function CreateLockInner() {
rel="noopener noreferrer"
className="inline-flex items-center min-h-[2.75rem] px-2 text-sm text-faint hover:text-cyan underline transition-colors"
>
View on BaseScan
View on {explorerName}
</a>
)}
</div>
Expand Down Expand Up @@ -1389,6 +1398,7 @@ function SuccessView({
usdcDecimals,
sablierAddress,
explorerUrl,
explorerName,
onCreateAnother,
}: {
txHash: `0x${string}`;
Expand All @@ -1398,6 +1408,7 @@ function SuccessView({
usdcDecimals: number;
sablierAddress: Address;
explorerUrl: string;
explorerName: string;
onCreateAnother: () => void;
}) {
const now = Math.floor(Date.now() / 1000);
Expand Down Expand Up @@ -1492,7 +1503,7 @@ function SuccessView({
rel="noopener noreferrer"
className="block text-xs text-faint hover:text-cyan underline transition-colors text-center"
>
View transaction on BaseScan
View transaction on {explorerName}
</a>
</div>
</div>
Expand Down
14 changes: 7 additions & 7 deletions packages/app/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const TRUST_POINTS = [
{
eyebrow: "04",
title: "Site goes down. You're fine.",
body: "Your lock lives in Sablier, not on our servers. Interact directly via BaseScan anytime. RipGuard disappearing tomorrow doesn't touch your funds.",
body: "Your lock lives in Sablier, not on our servers. Interact directly via your chain's block explorer anytime. RipGuard disappearing tomorrow doesn't touch your funds.",
},
];

Expand Down Expand Up @@ -122,7 +122,7 @@ export default function Home() {
const chainId = useChainId();
// Unconnected users + users on unsupported chains see the default chain's
// contract link so the landing always has somewhere real to point at.
const { sablierLockup, explorerUrl } = getChainConfig(
const { sablierLockup, explorerUrl, explorerName } = getChainConfig(
isSupportedDeploymentChain(chainId, IS_TESTNET) ? chainId : DEFAULT_CHAIN_ID,
);
const sablierExplorerUrl = `${explorerUrl}/address/${sablierLockup}`;
Expand Down Expand Up @@ -457,7 +457,7 @@ export default function Home() {
>
Sablier Lockup contract
</a>{" "}
on BaseScan. Verified, immutable, no proxy.
on the chain&apos;s block explorer. Verified, immutable, no proxy.
</span>
</li>
<li className="flex gap-4">
Expand Down Expand Up @@ -622,7 +622,7 @@ export default function Home() {
</p>
<p className="text-faint">
All smart contracts carry risk. Start with $5. Verify the
Sablier contract yourself on BaseScan.
Sablier contract yourself on the chain&apos;s block explorer.
</p>
</FAQItem>

Expand All @@ -640,7 +640,7 @@ export default function Home() {
<p>
Your lock lives in Sablier, not on our servers. If this site
goes down tomorrow, your vault keeps counting down and you can
claim directly through BaseScan or the Sablier app.
claim directly through the chain&apos;s block explorer or the Sablier app.
</p>
<p className="text-foreground">
RipGuard is the UI. Sablier is the bank.
Expand Down Expand Up @@ -745,13 +745,13 @@ export default function Home() {
rel="noopener noreferrer"
className="inline-flex items-center min-h-[2.75rem] px-3 hover:text-cyan transition-colors"
>
BaseScan
{explorerName}
</a>
</nav>
</div>
<p className="mt-10 text-xs text-faint max-w-2xl mx-auto text-center leading-relaxed">
RipGuard does not custody funds. All locks are created directly in
Sablier&apos;s audited protocol on Base. Non-custodial. Immutable. Not
Sablier&apos;s audited protocol. Non-custodial. Immutable. Not
financial advice. DYOR. 0.5% broker fee collected via Sablier&apos;s
native mechanism.
</p>
Expand Down
11 changes: 8 additions & 3 deletions packages/app/src/app/vaults/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ function VaultDashboard() {
const chainConfig = getChainConfig(
isSupportedDeploymentChain(chainId, IS_TESTNET) ? chainId : DEFAULT_CHAIN_ID,
);
const { sablierLockup, usdcDecimals, explorerUrl } = chainConfig;
const { sablierLockup, usdcDecimals, explorerUrl, explorerName } = chainConfig;
const publicClient = usePublicClient();
const { toast } = useToast();

Expand Down Expand Up @@ -589,11 +589,11 @@ function VaultDashboard() {
setClaimingId(null);
refetchStreams();
toast("Claim successful!", "success", {
label: "View on BaseScan",
label: `View on ${explorerName}`,
href: `${explorerUrl}/tx/${withdrawTxHash}`,
});
}
}, [isWithdrawConfirmed, withdrawTxHash, claimingId, refetchStreams, toast, explorerUrl]);
}, [isWithdrawConfirmed, withdrawTxHash, claimingId, refetchStreams, toast, explorerUrl, explorerName]);

// Toast + reset claiming state if tx rejected or failed
useEffect(() => {
Expand Down Expand Up @@ -709,6 +709,11 @@ function VaultDashboard() {

return (
<div className="flex-1 w-full max-w-4xl mx-auto px-5 sm:px-8 pb-24 space-y-10">
{chainConfig.usdcNote && (
<p className="text-[11px] text-faint leading-relaxed pt-1">
{chainConfig.usdcNote}
</p>
)}
{totals && (
<div className="border-y border-line py-7">
<ul className="flex flex-wrap items-baseline gap-x-10 gap-y-5">
Expand Down
35 changes: 33 additions & 2 deletions packages/app/src/config/chains.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe("chain registry", () => {
});

it("throws on unsupported chainId", () => {
expect(() => getChainConfig(1)).toThrow(/Unsupported chainId: 1/);
expect(() => getChainConfig(999_999)).toThrow(/Unsupported chainId: 999999/);
});

it("DEFAULT_CHAIN_ID resolves to a registered chain", () => {
Expand Down Expand Up @@ -55,7 +55,8 @@ describe("chain registry", () => {
it("isSupportedChain recognizes registered chains", () => {
expect(isSupportedChain(8453)).toBe(true);
expect(isSupportedChain(84532)).toBe(true);
expect(isSupportedChain(1)).toBe(false);
expect(isSupportedChain(1)).toBe(true);
expect(isSupportedChain(999_999)).toBe(false);
expect(isSupportedChain(undefined)).toBe(false);
});

Expand All @@ -71,4 +72,34 @@ describe("chain registry", () => {
expect(isSupportedDeploymentChain(999_999, true)).toBe(false);
expect(isSupportedDeploymentChain(undefined, false)).toBe(false);
});

it("BNB Chain uses 18-decimal Binance-Peg USDC", () => {
const bsc = getChainConfig(56);
expect(bsc.usdcDecimals, "BNB Chain USDC is 18 decimals, not 6").toBe(18);
expect(bsc.usdc).toBe("0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d");
expect(bsc.usdcNote, "BNB Chain entry must disclose Binance-Peg variant").toMatch(
/Binance-Peg/i
);
});

it("every other registered chain uses 6-decimal USDC", () => {
for (const chain of Object.values(CHAINS)) {
if (chain.chainId === 56) continue; // BNB is the documented exception
expect(chain.usdcDecimals, `${chain.name} expected 6 decimals`).toBe(6);
}
});

it("all mainnet chains share the same treasury EOA", () => {
const mainnetChains = Object.values(CHAINS).filter((c) => !c.isTestnet);
const treasuries = new Set(mainnetChains.map((c) => c.treasury.toLowerCase()));
expect(treasuries.size, "treasury must be the same address on every mainnet chain").toBe(1);
});

it("all 7 target EVM mainnet chains are present", () => {
// Base, Ethereum, Arbitrum, Optimism, Polygon, Avalanche, BNB Chain
const expected = [1, 10, 56, 137, 8453, 42161, 43114];
for (const id of expected) {
expect(isSupportedChain(id), `chainId ${id} must be in the registry`).toBe(true);
}
});
});
Loading