Skip to content

Commit

Permalink
web: add li.fi bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
dcposch committed Nov 12, 2023
1 parent 067bc66 commit 0a31a01
Show file tree
Hide file tree
Showing 31 changed files with 1,756 additions and 154 deletions.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Single stablecoin, single rollup, payments only. Cross-chain transfers coming soon.

No seed phrases. Keys are generated in your phone's secure enclave and never
leave. You can add multiple devices and create Passkey backups to secure your
leave. You can add multiple devices and create Passkey backups to secure your
account. Under the hood, it's a ERC-4337 contract wallet.

The mission is to make an excellent experience. Payments should be fast, secure, and permissionless.
Expand Down Expand Up @@ -55,12 +55,16 @@ The mission is to make an excellent experience. Payments should be fast, secure,
Yes, Daimo is and will always be open-source under GPLv3. We're here to collaborate. We want to make self-custody fast, safe, and easy. <a target="_blank" href="https://github.com/daimo-eth/daimo">See more on our Github.</a>
</details>

### Development
### Audits

- [Veridise audit 2023 Oct: Daimo](./audits/2023-10-veridise-daimo.pdf)
- [Veridise audit 2023 Oct: P-256 verifier](./audits-2023-10-veridise-p256.pdf)
- [Veridise audit 2023 Nov: WebAuthn verifier](./audits-2023-11-veridise-webauthn.pdf)

**Daimo is under active development.** Now on testnet, TestFlight, and in Play
Store open testing.
### Development

Veridise's audit report of our codebase is available [here](./audits/2023-10-veridise.pdf).
**Daimo is under active development.** Now in App Store and Play Store with an
invite code. Coming soon: desktop app, no invite code, cross-chain support.

<details>
<summary><strong>Architecture</strong></summary>
Expand Down
6 changes: 3 additions & 3 deletions apps/daimo-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
"@react-navigation/native": "^6.1.6",
"@react-navigation/native-stack": "^6.9.12",
"@scure/base": "^1.1.3",
"@tanstack/react-query": "^4.29.12",
"@trpc/client": "^10.40.0",
"@trpc/react-query": "^10.40.0",
"@tanstack/react-query": "^4.36.1",
"@trpc/client": "^10.43.2",
"@trpc/react-query": "^10.43.2",
"@types/jest": "^29.5.2",
"@types/react": "^18.2.15",
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
Expand Down
5 changes: 5 additions & 0 deletions apps/daimo-web/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ const nextConfig = {
source: "/.well-known/apple-app-site-association",
headers: [{ key: "content-type", value: "application/json" }],
},
{
source: "/(.*)",
headers: [{ key: "X-Frame-Options", value: "SAMEORIGIN" }],
},
];
},
webpack: (config) => {
Expand All @@ -26,6 +30,7 @@ const nextConfig = {
});
return config;
},
transpilePackages: ["@lifi/widget", "@lifi/wallet-management"],
};

module.exports = nextConfig;
14 changes: 11 additions & 3 deletions apps/daimo-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@
"@daimo/api": "*",
"@daimo/common": "*",
"@daimo/contract": "*",
"@lifi/widget": "^2.9.0",
"@rainbow-me/rainbowkit": "^1.1.1",
"@trpc/client": "^10.40.0",
"@tanstack/react-query": "^4.36.1",
"@trpc/client": "^10.43.2",
"@trpc/react-query": "^10.43.2",
"@trpc/server": "^10.43.2",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"autoprefixer": "^10.4.14",
"depcheck": "^1.4.3",
"encoding": "^0.1.13",
"eslint": "^8.44.0",
"eslint-config-next": "^13.5.4",
"lokijs": "^1.5.12",
"marked": "^9.0.3",
"next": "^13.5.4",
"pino-pretty": "^10.2.3",
"react": "18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.3.3",
"ts-loader": "^9.5.0",
"viem": "^1.16.0",
"wagmi": "^1.4.3"
"wagmi": "^1.4.3",
"zod": "^3.22.4"
},
"scripts": {
"dev": "next dev -p 3001",
Expand All @@ -29,4 +37,4 @@
"lint": "npx depcheck --ignores=@types/react,@types/react-dom,autoprefixer,tailwindcss && next lint",
"export": "next export"
}
}
}
4 changes: 2 additions & 2 deletions apps/daimo-web/src/app/SectionFAQ.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { Spacer } from "../components/layout";
import { SectionH3 } from "../components/typography";
import { TextH1 } from "../components/typography";
import { FAQ } from "../utils/parseFAQ";

export function SectionFAQ({ faq }: { faq: FAQ[] }) {
Expand All @@ -16,7 +16,7 @@ export function SectionFAQ({ faq }: { faq: FAQ[] }) {
}
`}</style>
<div className="m-auto max-w-screen-xl px-8 section-faq">
<SectionH3>Frequently Asked Questions</SectionH3>
<TextH1>Frequently Asked Questions</TextH1>
<Spacer h={48} />
<ul className="border-t border-grayLight">
{faq.map((qna, i) => (
Expand Down
6 changes: 3 additions & 3 deletions apps/daimo-web/src/app/SectionTeam.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Image from "next/image";

import { Spacer } from "../components/layout";
import { SectionH3 } from "../components/typography";
import { TextH1 } from "../components/typography";

export function SectionTeam() {
return (
<section className="bg-white py-24" id="team">
<center className="m-auto w-[32rem]">
<SectionH3>Meet the team</SectionH3>
<TextH1>Meet the team</TextH1>
<Spacer h={48} />
<div className="flex gap-12">
<Person
Expand Down Expand Up @@ -49,7 +49,7 @@ function Person({
<div className="flex flex-col items-center">
<Image src={img} width={270} height={270} alt={name} />
<Spacer h={24} />
<SectionH3>{name}</SectionH3>
<TextH1>{name}</TextH1>
<Spacer h={12} />
<div className="text-base text-grayMid">{title}</div>
<Spacer h={12} />
Expand Down
6 changes: 3 additions & 3 deletions apps/daimo-web/src/app/SectionWhyDaimo.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Image from "next/image";

import { Spacer } from "../components/layout";
import { SectionH3 } from "../components/typography";
import { TextH1 } from "../components/typography";

export function SectionWhyDaimo() {
return (
<section className="bg-white pt-24 md:bg-ivory md:py-24">
<div className="m-auto max-w-screen-xl px-8">
<SectionH3>Why Daimo</SectionH3>
<TextH1>Why Daimo</TextH1>
<Spacer h={48} />
<div className="flex gap-16 md:gap-8 xl:gap-16 flex-col items-stretch md:flex-row">
<WhyPellet
Expand Down Expand Up @@ -50,7 +50,7 @@ function WhyPellet({
className="w-20 h-20"
/>
<Spacer h={36} />
<SectionH3>{title}</SectionH3>
<TextH1>{title}</TextH1>
<Spacer h={36} />
<p className="text-grayMid text-2xl">{copy}</p>
</div>
Expand Down
37 changes: 37 additions & 0 deletions apps/daimo-web/src/app/bridge/BridgePageClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import { assert } from "@daimo/common";
import dynamic from "next/dynamic";

import { TextError, TextLight } from "../../components/typography";
import { chainConfig } from "../../env";
import { rpcHook } from "../../utils/rpcHook";

const LiFiWidgetNext = dynamic(
() => import("./Widget").then((module) => module.Widget),
{
ssr: false,
loading: () => <TextLight>Loading bridge...</TextLight>,
}
);

export function BridgePageClient({ name }: { name: string }) {
assert(name.length > 0);
const res = rpcHook.trpc.resolveName.useQuery({ name });

const isTestnet = chainConfig.chainL2.testnet;

return (
<main className="max-w-md mx-auto py-8 flex flex-col items-center">
{isTestnet && <TextError>Set DAIMO_CHAIN to use the bridge.</TextError>}
{res.isError && <TextError>{res.error.message}</TextError>}
{res.isLoading && <TextLight>Loading address...</TextLight>}
{res.isSuccess && !res.data && (
<TextError>Account {name} not found</TextError>
)}
{res.isSuccess && res.data && (
<LiFiWidgetNext toName={name} toAddress={res.data} />
)}
</main>
);
}
57 changes: 57 additions & 0 deletions apps/daimo-web/src/app/bridge/Widget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use client";

import { getChainConfig } from "@daimo/contract";
import { LiFiWidget, WidgetConfig } from "@lifi/widget";
import { useMemo } from "react";
import { Address } from "viem";

import { chainConfig } from "../../env";

const widgetConfigBase: WidgetConfig = {
integrator: "daimo",
variant: "default",
subvariant: "default",
appearance: "light",
buildUrl: false,
disabledUI: ["toAddress", "toToken"],
hiddenUI: ["appearance", "language"],
slippage: 0.005,
containerStyle: {
border: "1px solid rgb(234, 234, 234)",
borderRadius: "16px",
maxHeight: "1200px",
},
languages: {
default: "en",
allow: ["en"],
},
};

export function Widget({
toName,
toAddress,
}: {
toName: string;
toAddress: Address;
}) {
const widgetConfig = useMemo<WidgetConfig>(
() => ({
...widgetConfigBase,
languageResources: {
en: {
header: {
exchange: "Deposit to Daimo",
},
main: {
sendToWallet: `Send to ${toName} on Daimo`,
},
},
},
toAddress,
toChain: "bas",
toToken: chainConfig.tokenAddress,
}),
[toName, toAddress]
);
return <LiFiWidget integrator="daimo" config={widgetConfig} />;
}
26 changes: 26 additions & 0 deletions apps/daimo-web/src/app/bridge/[toAccountName]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Metadata } from "next";

import { RpcHookProvider } from "../../../utils/rpcHook";
import { BridgePageClient } from "../BridgePageClient";

type LinkProps = {
params: { toAccountName: string };
};

export async function generateMetadata(props: LinkProps): Promise<Metadata> {
return {
title: "Daimo Deposit",
description: "Deposit to Daimo account: " + props.params.toAccountName,
icons: {
icon: "/logo-web-favicon.png",
},
};
}

export default function BridgePage({ params }: LinkProps) {
return (
<RpcHookProvider>
<BridgePageClient name={params.toAccountName} />
</RpcHookProvider>
);
}
10 changes: 10 additions & 0 deletions apps/daimo-web/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@ body {
font-size: 16px;
}
}

/** Hack: avoid double scrollbar on the bridge page. */

.MuiScopedCssBaseline-root {
max-height: none !important;
}

.MuiBox-root > .MuiButtonBase-root:nth-child(2) {
display: none !important;
}
15 changes: 5 additions & 10 deletions apps/daimo-web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { Metadata } from "next";

import "./globals.css";
import "@rainbow-me/rainbowkit/styles.css";
import { Providers } from "../components/Providers";
import "./globals.css";

export const metadata: Metadata = {
title: "Daimo",
Expand All @@ -12,16 +11,12 @@ export const metadata: Metadata = {
},
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
<body>{children}</body>
</html>
);
}

export default RootLayout;
13 changes: 11 additions & 2 deletions apps/daimo-web/src/app/link/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import Image from "next/image";

import { PrimaryOpenInAppButton } from "../../../components/buttons";
import { PerformWalletAction } from "../../../components/PerformWalletAction";
import { trpc } from "../../../utils/trpc";
import { Providers, chainsDaimoL2 } from "../../../components/Providers";
import { rpc } from "../../../utils/rpc";

// Opt out of caching for all data requests in the route segment
export const dynamic = "force-dynamic";
Expand Down Expand Up @@ -49,6 +50,14 @@ export async function generateMetadata(props: LinkProps): Promise<Metadata> {
}

export default async function LinkPage(props: LinkProps) {
return (
<Providers chains={chainsDaimoL2}>
<LinkPageInner {...props} />
</Providers>
);
}

async function LinkPageInner(props: LinkProps) {
const { name, action, dollars, description, walletActionLinkStatus } =
(await loadTitleDesc(getUrl(props))) || {
title: "Daimo",
Expand Down Expand Up @@ -118,7 +127,7 @@ function metadata(title: string, description: string): Metadata {
async function loadTitleDesc(url: string): Promise<TitleDesc | null> {
let res: DaimoLinkStatus;
try {
res = await trpc.getLinkStatus.query({ url });
res = await rpc.getLinkStatus.query({ url });
} catch (err) {
console.warn(`Error loading link status for ${url}`, err);
const link = parseDaimoLink(url);
Expand Down
Loading

0 comments on commit 0a31a01

Please sign in to comment.