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
1 change: 1 addition & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ Top level: `correlationId`, `quote`, `quoteRequest`, `signature`, `timestamp`. `
- Intents withdrawals to a NEAR account **give no refund if the receiver isn't storage-registered** on the token → the factory must register the vault on USDC (K1.7) and owners must be registered on `wrap.near` before a NEAR plan (K8.2). Non-negotiable.
- `customRecipientMsg` (marked "highly experimental") delivers with `ft_transfer_call`; not needed by the vault design.
- Optional `insured: true` adds 0.02%; a LIMIT-order API exists in the SDK (relevant to the "buy the dip" stretch).
- **Quote signatures (K3.5):** SDK 0.1.26 `verifyQuoteSignature()` returns false for real, untampered unauthenticated dry quotes (the SDK hashes a fixed field list; the API now echoes `confidentiality`, `insured`, `depositMode`). Real quotes therefore **fail closed** until verification is confirmed with a partner JWT.
- Non-dry quotes without a partner JWT returned "Failed to get quote" → **K0.9 (deposit-address storage) is blocked on K0.8 (JWT)**.

### Global contracts (docs.near.org, NEP-591)
Expand Down
1 change: 1 addition & 0 deletions ARCHITECTURE_ESSENTIALS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ _None._
## 🔍 Still to verify
1. Does 1Click storage-register NEAR deposit addresses? (K-B1) — **blocked on the partner JWT (K0.8)**
2. Deposits arriving after the quote deadline
3. **K3.5** Quote signatures verify on real quotes — **blocked on the JWT**; until then real quotes fail closed, dry previews skip the check

## Stack
Rust near-sdk =5.30.0 + cargo-near 0.22 + near-workspaces =0.24.0 · Next.js 15.5 + TS strict · Tailwind + shadcn/ui · TanStack Query · zod · Drizzle (PGlite/Postgres) · 1Click SDK 0.1.26 · `@hot-labs/near-connect` 0.12.0 + `near-api-js` 7.4.0 · vitest + Playwright · pnpm 11 (worker/scripts run with `--conditions=react-server` so `server-only` works)
Expand Down
15 changes: 9 additions & 6 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
| 0. Setup & verification | Nov 4–5 | 80% | in progress (K0.9 blocked on K0.8; K0.8, K0.14 👤) |
| 1. Contracts | Nov 5–7 | 92% | in progress (K1.13 deploy is 👤) |
| 2. Core logic (`lib/core`) | Nov 6–7 | 100% | ✅ done |
| 3. Integrations (1Click, NEAR, DB) | Nov 7–8 | 0% | in progress (schema drafted) |
| 3. Integrations (1Click, NEAR, DB) | Nov 7–8 | 80% | in progress (K3.5 blocked on K0.8) |
| 4. API routes | Nov 8–9 | 0% | not started |
| 5. Worker (scheduler) | Nov 9–10 | 0% | not started |
| 6. UI | Nov 9–11 | 0% | not started |
Expand All @@ -27,7 +27,7 @@
| 10. README & public repo | Nov 12–13 | 14% | in progress |
| 11. Launch: video, post, submission | Nov 13–14 | 0% | not started |
| 12. After submission | Nov 14–25 | 0% | not started |
| **Overall (MVP)** | | **~27%** | |
| **Overall (MVP)** | | **~33%** | |

Phase % = done tasks ÷ non-dropped tasks in that phase. Overall = average of all 14 rows (D + phases 0–12). (Earlier entries over-estimated the overall %; recalculated 2026-11-04.)

Expand All @@ -47,6 +47,7 @@ Phase % = done tasks ÷ non-dropped tasks in that phase. Overall = average of al
## Blockers

- [K0.9] Real (non-dry) 1Click quotes need a partner JWT · 👤 human via K0.8 (partners.near-intents.org) · since 2026-11-04
- [K3.5] Confirming quote-signature verification needs a real quote → needs the partner JWT (K0.8) · 👤 human · since 2026-11-04

Format: `- [task id] what blocks it · who can unblock · since date`

Expand Down Expand Up @@ -110,10 +111,11 @@ Format: `- [task id] what blocks it · who can unblock · since date`

## Phase 3 — Integrations (Nov 7–8)

- [ ] **K3.1** [#39](https://github.com/MFrancis-dev/stack/issues/39) `lib/oneclick`: client, tokens cache, funding + buy quotes (dry/real), verify, status, submit
- [ ] **K3.2** [#40](https://github.com/MFrancis-dev/stack/issues/40) Fixtures for every status + valid/tampered signatures
- [ ] **K3.3** [#41](https://github.com/MFrancis-dev/stack/issues/41) `lib/near`: rpc (`get_state`, `ft_balance_of`), nep413 verify, factory + vault bindings, keeper signer (server only), event parsing
- [~] **K3.4** [#42](https://github.com/MFrancis-dev/stack/issues/42) (2026-11-04: all 3 tables written; migration not generated) `lib/db`: schema (users, funding_attempts, buys) + migration + queries
- [x] **K3.1** [#39](https://github.com/MFrancis-dev/stack/issues/39) `lib/oneclick`: client, tokens cache, funding + buy quotes (dry/real), verify, status, submit · 2026-11-04: `lib/oneclick`: configure, tokens (10-min cache, stale on outage), funding/buy EXACT_INPUT requests, per-chain deadlines, `requestQuote` (real quotes fail closed on bad signature), status + submit, readable errors
- [x] **K3.2** [#40](https://github.com/MFrancis-dev/stack/issues/40) Fixtures for every status + valid/tampered signatures · 2026-11-04: real dry-quote fixtures (BTC $100, ETH $10) + status fixtures for all 7 values; tampered/unknown cases tested
- [x] **K3.3** [#41](https://github.com/MFrancis-dev/stack/issues/41) `lib/near`: rpc (`get_state`, `ft_balance_of`), nep413 verify, factory + vault bindings, keeper signer (server only), event parsing · 2026-11-04: `lib/near`: rpc views, NEP-297 event parsing, vault/factory bindings, keeper signer (server-only), NEP-413 `verifySignIn` (tested with a real signature), `nearVaultClient` adapter
- [x] **K3.4** [#42](https://github.com/MFrancis-dev/stack/issues/42) (2026-11-04: all 3 tables written; migration not generated) `lib/db`: schema (users, funding_attempts, buys) + migration + queries · 2026-11-04: first migration (`drizzle/0000_…sql`), `db.migrate()` for PGlite + Postgres, queries for users/funding/buys, terminal-status sets; tested on in-memory PGlite
- [!] **K3.5** [#110](https://github.com/MFrancis-dev/stack/issues/110) 🔍 Verify 1Click quote signatures on real quotes · 2026-11-04: SDK 0.1.26 `verifyQuoteSignature` fails on real unauthenticated dry quotes; real quotes fail closed until verified with the partner JWT (K0.8)

## Phase 4 — API routes (Nov 8–9)

Expand Down Expand Up @@ -214,6 +216,7 @@ Format: `- [task id] what blocks it · who can unblock · since date`
## Update log
One line per contribution, newest first: `YYYY-MM-DD · who/agent · what changed · tasks`

- 2026-11-04 · agent · Phase 3 integrations: 1Click layer (fail-closed signatures), NEAR bindings + NEP-413 sign-in, first DB migration + queries; 66 TS tests; new K3.5 (signature verification) blocked on K0.8 · K3.1–K3.5
- 2026-11-04 · agent · Phase 2 core logic: money, cost, planMatch, address (checksums), assets, schemas; 33 TS tests · K2.1–K2.6
- 2026-11-04 · agent · Rewrote `main` history (approved force-push) to drop tool attribution lines from commit messages; every commit's files unchanged · —
- 2026-11-04 · agent · Phase 1 contracts: vault + factory implemented, mock-ft crate, 49 contract tests passing (unit + near-workspaces sandbox); wasm vault 137 KB / factory 146 KB · K1.1–K1.10, K1.12
Expand Down
66 changes: 66 additions & 0 deletions drizzle/0000_damp_silver_surfer.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
CREATE TABLE IF NOT EXISTS "buys" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"quote_ref" text NOT NULL,
"amount_in" numeric(78, 0) NOT NULL,
"target_asset" text NOT NULL,
"destination_address" text NOT NULL,
"cost_bps" integer,
"amount_in_usd" numeric,
"amount_out" numeric(78, 0),
"deposit_address" text,
"quote_signature" text,
"quote_raw" jsonb,
"near_tx_hash" text,
"oneclick_status" text,
"status" text NOT NULL,
"destination_tx_hash" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"completed_at" timestamp with time zone,
CONSTRAINT "buys_quote_ref_unique" UNIQUE("quote_ref"),
CONSTRAINT "buys_deposit_address_unique" UNIQUE("deposit_address")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "funding_attempts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"origin_asset" text NOT NULL,
"amount_in" numeric(78, 0) NOT NULL,
"expected_out" numeric(78, 0) NOT NULL,
"refund_to" text NOT NULL,
"deposit_address" text NOT NULL,
"quote_deadline" timestamp with time zone NOT NULL,
"quote_signature" text NOT NULL,
"quote_raw" jsonb NOT NULL,
"oneclick_status" text,
"status" text DEFAULT 'awaiting_deposit' NOT NULL,
"deposit_tx_hash" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "funding_attempts_deposit_address_unique" UNIQUE("deposit_address")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "users" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"near_account_id" text NOT NULL,
"vault_account_id" text,
"is_demo" boolean DEFAULT false NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "users_near_account_id_unique" UNIQUE("near_account_id"),
CONSTRAINT "users_vault_account_id_unique" UNIQUE("vault_account_id")
);
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "buys" ADD CONSTRAINT "buys_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
DO $$ BEGIN
ALTER TABLE "funding_attempts" ADD CONSTRAINT "funding_attempts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "buys_user_idx" ON "buys" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "funding_attempts_user_idx" ON "funding_attempts" USING btree ("user_id");
Loading
Loading