# Project export: Vero

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 12.0
- Tagline: Deploy on-chain strategies, automate risk triggers, and let Vero coordinate your cross-chain liquidity so you can focus on growth.
- Devpost: https://devpost.com/software/vero-uepxt8
- GitHub: https://github.com/pla-llc/vero-calhacks
- Video: https://www.youtube.com/embed/6nbphMhVoAA?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — LandonHarter (28 commits), PranTanTheMan (23 commits)

## Devpost submission (written by the team)

### Inspiration

Deploy on-chain strategies, automate risk triggers. All in one DeFi automation through building beautiful flowcharts to wire actions. Inspired by Sequence.io for their cash flow automation software that uses a flowchart UI, and other DeFi trading platforms where we wanted to combine the two together.

### What it does

Create comprehensive flowcharts that utilizes Programmable Transaction Blocks (PTBs) that work on the Sui network. Create a new account via Google which also created web3 credentials for you, including a Sui wallet address on the mainnet. In "Flows", while the main feature is creating those flowcharts, you can manually run them, OR you can run them offline because we use trigger.dev which act as cron jobs on the cloud that are running 24/7 so you can have triggers along with actions. For example, you can do swap action with PTB, using a trigger where the price goes above or below a target price you've set, or if you want you can send from a coin to another coin on the Sui network using a trigger where you can schedule those times.

### How we built it

We used Next.js for the frontend and Hono.js for the backend, all written with Typescript. We used a lot of technology from the Sui stack. We used the Mysten lab's Sui typescript SDK. We used Zklogin with Shinami as our mainnet prover to turn web2 credentials to create web3 credentials all while detaching both and creating proof signatures so we can use the powerful Programmable Transaction Blocks (PTB) that the Sui stack offers so we can enact multple-swaps or multiple-sends with lower gas fees. We also used external market data for the Sui network via Blockberry and Birdeye.

### Challenges we ran into

We ran into trouble trying to run the ZkLogin authentication on the mainnet, but we discovered that shinami was offering a prover service for mainnet applications for around 50 requests for the month.

### Accomplishments we're proud of

Successfully developing with the Sui stack for the first time. Especially making the PTB's actually work to send and swap coins. Implementing the ZkLogin on the mainnet, and having seamless connectivity and information to generate Zk proof signatures so we can validate the PTBs.

### What we learned

We learned various implementations on building with the Sui Stack, looking into their kits on building DeFi apps, using Programmable Transaction Blocks for executing numerous actions at the same time to reduce fees like no other technology.

### What's next

Add AI integration to also automate creating these flowcharts for you based on trends, social media, and various other technical analysis. Add bunch more triggers for more customizability.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 83 recognized source files, 247 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- PostgreSQL (technology) — claimed on Devpost, not found in the code
- AI coding agent: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (93 of 93)

```
.dockerignore
.github/workflows/docker-image.yml
.gitignore
.prettierrc
AGENTS.md
bun.lock
components.json
Dockerfile
next.config.ts
package.json
postcss.config.mjs
prisma/schema.prisma
src/app/(pages)/(app)/dashboard/flows/flows-client.tsx
src/app/(pages)/(app)/dashboard/flows/page.tsx
src/app/(pages)/(app)/dashboard/layout.tsx
src/app/(pages)/(app)/dashboard/page.tsx
src/app/(pages)/(app)/flows/[id]/page.tsx
src/app/(pages)/(auth)/layout.tsx
src/app/(pages)/(auth)/login/page.tsx
src/app/(pages)/(product)/(landing)/page.tsx
src/app/(pages)/(product)/layout.tsx
src/app/api/[...route]/hono.ts
src/app/api/[...route]/route.ts
src/app/api/[...route]/routers/auth.ts
src/app/api/[...route]/routers/coins.ts
src/app/api/[...route]/routers/flow.ts
src/app/api/[...route]/routers/marketData.ts
src/app/api/[...route]/routers/wallet.ts
src/app/api/[...route]/routers/zklogin.ts
src/app/globals.css
src/app/layout.tsx
src/app/zklogin/callback/page.tsx
src/components/auth/login.tsx
src/components/dashboard/sui-balance-card.tsx
src/components/flows/flow.tsx
src/components/nodes/base.tsx
src/components/nodes/draggable-node.tsx
src/components/nodes/nodes-context.tsx
src/components/nodes/types/price-target.tsx
src/components/nodes/types/schedule.tsx
src/components/nodes/types/send.tsx
src/components/nodes/types/swap.tsx
src/components/nodes/variables/coin.tsx
src/components/product/background.tsx
src/components/product/hero.tsx
src/components/product/navbar.tsx
src/components/ui/accordion.tsx
src/components/ui/avatar.tsx
src/components/ui/button.tsx
src/components/ui/calendar.tsx
src/components/ui/card.tsx
src/components/ui/command.tsx
src/components/ui/copy-button.tsx
src/components/ui/dialog.tsx
src/components/ui/dropdown-menu.tsx
src/components/ui/form.tsx
src/components/ui/input.tsx
src/components/ui/label.tsx
src/components/ui/navigation-menu.tsx
src/components/ui/popover.tsx
src/components/ui/separator.tsx
src/components/ui/sheet.tsx
src/components/ui/sidebar.tsx
src/components/ui/skeleton.tsx
src/components/ui/textarea.tsx
src/components/ui/tooltip.tsx
src/data/node-types.ts
src/hooks/use-auth-state.ts
src/hooks/use-mobile.ts
src/lib/auth/client.ts
src/lib/auth/server.ts
src/lib/hono/client.ts
src/lib/hono/server.ts
src/lib/prisma.ts
src/lib/sui.ts
src/lib/sui/ptb.ts
src/lib/utils.ts
src/lib/validator.ts
src/lib/zklogin/address.ts
src/lib/zklogin/client.ts
src/lib/zklogin/constants.ts
src/lib/zklogin/credential.ts
src/lib/zklogin/ephemeral.ts
src/lib/zklogin/google.ts
src/lib/zklogin/network.ts
src/lib/zklogin/nonce.ts
src/lib/zklogin/proof.ts
src/lib/zklogin/user-salt.ts
src/trigger/handlers.ts
src/trigger/triggers.ts
src/types/balance.ts
trigger.config.ts
tsconfig.json
```

### Dependencies

- package.json: @hono/zod-validator@^0.7.4, @mysten/deepbook-v3@^0.19.6, @mysten/sui@^1.43.1, @prisma/client@6.16.2, @radix-ui/react-accordion@^1.2.12, @radix-ui/react-avatar@^1.1.10, @radix-ui/react-dialog@^1.1.15, @radix-ui/react-dropdown-menu@^2.1.16, @radix-ui/react-label@^2.1.7, @radix-ui/react-navigation-menu@^1.2.14, @radix-ui/react-popover@^1.1.15, @radix-ui/react-separator@^1.1.7, @radix-ui/react-slot@^1.2.3, @radix-ui/react-tooltip@^1.2.8, @tailwindcss/postcss@^4, @trigger.dev/build@4.0.4, @trigger.dev/sdk@4.0.4, @types/node@^20, @types/react@^19, @types/react-dom@^19, @xyflow/react@^12.9.0, better-auth@^1.3.30, class-variance-authority@^0.7.1, clsx@^2.1.1, cmdk@^1.1.1, date-fns@^4.1.0, framer-motion@^12.23.24, hono@^4.10.3, lucide-react@^0.548.0, motion@^12.23.24, next@16.0.0, prisma@6.16.2, react@19.2.0, react-day-picker@^9.11.1, react-dom@19.2.0, tailwind-merge@^3.3.1, tailwindcss@^4, tw-animate-css@^1.4.0, typescript@^5, zod@^4.1.12

### Recent commits (newest first)

- forgot to add bun.lock
- forgot to change name of docker image
- docker + github action
- add refresh sui balance + copy wallet address util
- subtle restyle on the sidebar
- Restyling dashboard stuff
- schedule trigger working
- restyle callback and login stuff
- schedule node ui
- fix up bugs
- better flow ui
- better looking nodes
- fix up bug in trigger
- restyled landing page
- UI for price target trigger node
- Merge branch 'master' of https://github.com/pla-llc/vero-calhacks
- syntax changes
- mainnet zklogin
- trigger handling architecture
- added sending function

## Key source files (fetched from GitHub, selected and truncated for size)

### AGENTS.md

```markdown
# Repository Guidelines

## Project Structure & Module Organization
The Next.js App Router lives in `src/app`, with page-level logic in `page.tsx`, layout wrappers in `layout.tsx`, and API route handlers under `src/app/api`. Shared helpers should sit in `src/lib` so they can be imported from both server and client components. API requests flow through a Hono router defined in `src/app/api/[...route]/hono.ts`, with composable route modules under `src/app/api/[...route]/routers`; add new route groups there and register them when creating the Hono instance. Database models are defined in `prisma/schema.prisma`; update the file before running any migration. Static assets and icons belong in `public`, while project-level configuration is managed through `next.config.ts`, `tsconfig.json`, and `postcss.config.mjs`.

## Build, Test, and Development Commands
Install dependencies with `bun install` to stay aligned with the generated `bun.lock`. Use `bun run dev` for the local development server, `bun run build` to produce an optimized bundle, and `bun run start` to serve the build output. After editing the Prisma schema, regenerate the client with `bunx prisma generate` to keep types in sync.

## Coding Style & Naming Conventions
All source files are TypeScript with 2-space indentation. Components, hooks, and utilities should use PascalCase (e.g., `UserCard.tsx`), while route segments remain lowercase and hyphenated. Favor functional React components, server actions where possible, and colocate component-specific styles in module CSS or within the component file. Before submitting, ensure the code type-checks with `tsc --noEmit` (via `bunx tsc --noEmit` when needed).

## Testing Guidelines
A formal test harness is not yet configured. When adding critical logic, include lightweight assertions using tooling like `bun test` or `vitest` and document manual QA steps in the pull request. New modules should expose easily testable functions inside `src/lib` so automated testing can be adopted incrementally.

## Commit & Pull Request Guidelines
Follow concise, imperative commit messages (e.g., `Add onboarding flow`), mirroring the existing history. Each pull request should describe the change, call out schema updates, and list any manual verification performed. Attach screenshots or screen recordings for UI changes and link relevant issues or tickets when applicable.

## Environment & Security Notes
Load secrets and connection strings through `.env.local`; never commit `.env` files. If you introduce new environment variables, document them in the pull request and mirror them in deployment settings before merging. Rotate Prisma database credentials after sharing the repository externally.

## MCP SERVERS
ALWAYS USE CONTEXT7

```

### Dockerfile

```
FROM oven/bun:latest AS base

RUN apt-get update -y
RUN apt-get install -y openssl

FROM base AS deps
WORKDIR /app

COPY package.json bun.lock ./
RUN bun i

FROM base as BUILDER
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

RUN bunx prisma generate
RUN bun run build

FROM base as runner
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

RUN mkdir .next
RUN chown nextjs:nodejs .next

COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000
ENV PORT 3000

CMD node server.js
```

### package.json

```
{
  "name": "vero",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --webpack",
    "build": "next build --webpack",
    "start": "next start"
  },
  "dependencies": {
    "@hono/zod-validator": "^0.7.4",
    "@mysten/deepbook-v3": "^0.19.6",
    "@mysten/sui": "^1.43.1",
    "@prisma/client": "6.16.2",
    "@radix-ui/react-accordion": "^1.2.12",
    "@radix-ui/react-avatar": "^1.1.10",
    "@radix-ui/react-dialog": "^1.1.15",
    "@radix-ui/react-dropdown-menu": "^2.1.16",
    "@radix-ui/react-label": "^2.1.7",
    "@radix-ui/react-navigation-menu": "^1.2.14",
    "@radix-ui/react-popover": "^1.1.15",
    "@radix-ui/react-separator": "^1.1.7",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-tooltip": "^1.2.8",
    "@trigger.dev/sdk": "4.0.4",
    "@xyflow/react": "^12.9.0",
    "better-auth": "^1.3.30",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.1.1",
    "date-fns": "^4.1.0",
    "framer-motion": "^12.23.24",
    "hono": "^4.10.3",
    "lucide-react": "^0.548.0",
    "motion": "^12.23.24",
    "next": "16.0.0",
    "react": "19.2.0",
    "react-day-picker": "^9.11.1",
    "react-dom": "19.2.0",
    "tailwind-merge": "^3.3.1",
    "zod": "^4.1.12"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@trigger.dev/build": "4.0.4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "prisma": "6.16.2",
    "tailwindcss": "^4",
    "tw-animate-css": "^1.4.0",
    "typescript": "^5"
  }
}

```

### src/app/layout.tsx

```typescript
import { Geist } from "next/font/google";
import "./globals.css";

const font = Geist({
	variable: "--font-geist-sans",
	subsets: ["latin"],
});

export default function RootLayout({
	children,
}: Readonly<{
	children: React.ReactNode;
}>) {
	return (
		<html lang="en">
			<body
				className={`${font.className} antialiased dark overflow-x-hidden`}
			>
				{children}
			</body>
		</html>
	);
}

```

### src/lib/auth/server.ts

```typescript
import { createApi } from "@hono/server";
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import prisma from "../prisma";

export const auth = betterAuth({
	database: prismaAdapter(prisma, {
		provider: "postgresql",
	}),
	socialProviders: {
		google: {
			clientId: process.env.GOOGLE_CLIENT_ID!,
			clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
		},
	},
	databaseHooks: {
		user: {
			create: {
				after: async (user, context) => {
					const api = await createApi();
					await api.wallet.create.$post();
				},
			},
		},
	},
});

```

### src/lib/hono/server.ts

```typescript
"use server";

import { AppType } from "@/app/api/[...route]/route";
import { hc } from "hono/client";
import { headers } from "next/headers";

export async function createApi() {
	const baseUrl = process.env.NEXT_PUBLIC_BASE_URL!;
	return hc<AppType>(baseUrl, {
		fetch: async (url: URL | RequestInfo, options?: RequestInit) => {
			const h = await headers();

			const newHeaders = new Headers();
			for (const [key, value] of h.entries()) {
				newHeaders.append(key, value);
			}

			if (options?.headers) {
				for (const [key, value] of Object.entries(options.headers)) {
					if (!h.has(key)) {
						newHeaders.append(key, value);
					}
				}
			}

			return fetch(url, {
				...options,
				headers: newHeaders,
			});
		},
	}).api;
}

```

### src/app/(pages)/(auth)/layout.tsx

```typescript
import { useAuthState } from "@/hooks/use-auth-state";
import { redirect } from "next/navigation";
import { PropsWithChildren } from "react";

export default async function AuthLayout({ children }: PropsWithChildren) {
	const { signedIn } = await useAuthState();
	if (signedIn) {
		redirect("/dashboard");
	}

	return <>{children}</>;
}

```

### src/app/(pages)/(product)/layout.tsx

```typescript
import { Navbar } from "@/components/product/navbar";
import { useAuthState } from "@/hooks/use-auth-state";
import { PropsWithChildren } from "react";

export default async function ProductLayout({ children }: PropsWithChildren) {
	const authState = await useAuthState();

	return (
		<div className="flex flex-col">
			<Navbar authState={authState} />
			{children}
		</div>
	);
}

```

### src/app/api/[...route]/route.ts

```typescript
import { auth } from "@/lib/auth/server";
import { handle } from "hono/vercel";

import { createHono } from "./hono";
import authRouter from "./routers/auth";
import coinsRouter from "./routers/coins";
import flowRouter from "./routers/flow";
import marketDataRouter from "./routers/marketData";
import walletRouter from "./routers/wallet";
import zkloginRouter from "./routers/zklogin";

const app = createHono()
	.basePath("/api")
	.use("*", async (c, next) => {
		const session = await auth.api.getSession({
			headers: c.req.raw.headers,
		});

		if (!session) {
			c.set("user", null);
			c.set("session", null);
			return next();
		}

		c.set("user", session.user);
		c.set("session", session.session);
		return next();
	})
	.route("/auth", authRouter)
	.route("/market-data", marketDataRouter)
	.route("/wallet", walletRouter)
	.route("/zklogin", zkloginRouter)
	.route("/flows", flowRouter)
	.route("/coins", coinsRouter);

const appHandle = handle(app);
export {
	appHandle as DELETE,
	appHandle as GET,
	appHandle as OPTIONS,
	appHandle as PATCH,
	appHandle as POST,
	appHandle as PUT,
};

export type AppType = typeof app;

```

### src/app/(pages)/(app)/dashboard/layout.tsx

```typescript
import { Sidebar } from "@/components/ui/sidebar";
import { useAuthState } from "@/hooks/use-auth-state";
import { redirect } from "next/navigation";
import { PropsWithChildren } from "react";

export default async function DashboardLayout({ children }: PropsWithChildren) {
	const { signedIn } = await useAuthState();
	if (!signedIn) {
		redirect("/login");
	}

	const authState = await useAuthState();

	return (
		<div className="min-h-screen bg-background">
			<div className="min-h-screen grid grid-cols-[auto_1fr]">
				<Sidebar />
				<main className="">{children}</main>
			</div>
		</div>
	);
}

```

[75 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]