Skip to content

satvikh/pact

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

91 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pact - 1st @ HackSMU Solana Track

Put money behind your word.

Pact is a social accountability app for commitments with real stakes. A user creates a pact, names trusted validators, submits proof when the work is done, and the group votes on whether the commitment was kept.

Stakes and escrow are done with Solana.

What It Does

  • Create commitments with a title, deadline, stake amount, and private validator group.
  • Organize commitments inside circles.
  • Authenticate with Supabase email/password.
  • Lock and track stake through the backend escrow layer.
  • Upload proof images for validator review.
  • Resolve commitments automatically when a voting majority is reached.
  • Show wallet, circle, validator, and commitment state in the mobile app.

Repository Layout

pact/
  backend/    Fastify API, Supabase service access, auth verification, escrow logic
  mobile/     Expo Router app for iOS, Android, and web
  supabase/   Local Supabase config and database migrations

Architecture

Pact uses Supabase for identity and data storage, with the backend enforcing all protected business actions.

  1. The mobile app signs in through Supabase Auth.
  2. Supabase returns a user session and access token.
  3. The mobile app sends that token to the Fastify backend.
  4. The backend verifies the token with Supabase Auth.
  5. The backend maps the Supabase identity to public.users.supabase_auth_user_id.
  6. The backend reads and writes business data with Supabase service-role access.

The client never supplies trusted userId, creatorId, or validatorId values for protected actions. Those are derived from the authenticated token on the backend.

Core Flow

  1. A signed-in user creates a pact and chooses 2 to 3 validators.
  2. The backend creates or checks the user wallet and records the escrow state.
  3. The pact enters awaiting_proof.
  4. The creator uploads proof and optional notes.
  5. The pact enters awaiting_votes.
  6. Assigned validators vote approve or reject.
  7. The backend resolves the pact as soon as approval or rejection has a majority.
  8. A won pact releases the stake; a lost pact forfeits it.

Resolved and cancelled commitments cannot receive more votes.

Local Setup

Install dependencies in each package:

cd backend
npm install

cd ../mobile
npm install

Copy the environment examples:

cp backend/.env.example backend/.env
cp mobile/.env.example mobile/.env

Set the Supabase values in both files. The backend needs the Supabase URL and service-role key. The mobile app needs the Supabase URL, anon key, and API base URL.

For local development on the same machine, the mobile API URL can be:

EXPO_PUBLIC_API_BASE_URL=http://127.0.0.1:5001

For Expo Go on a physical phone, use your computer's LAN IP instead:

EXPO_PUBLIC_API_BASE_URL=http://YOUR_COMPUTER_LAN_IP:5001

127.0.0.1 on a phone points at the phone itself, not your computer.

Running The App

Start the backend:

cd backend
npm run dev

Start the mobile app:

cd mobile
npm run start

Useful mobile targets:

npm run ios
npm run android
npm run web

If the Expo QR code does not open on a phone, make sure the phone and computer are on the same network, the Metro URL is reachable from the phone, and the mobile API base URL uses the computer's LAN IP.

Supabase

The supabase/ folder contains the local configuration and migrations for users, wallets, circles, invites, commitments, proofs, votes, and storage.

Backend scripts:

cd backend
npm run supabase:start
npm run supabase:reset
npm run supabase:stop

Use supabase db reset through the package script when you want to rebuild local schema from migrations.

Backend API

Public:

  • GET /health
  • GET /invites/:code

Authenticated:

  • GET /auth/me
  • GET /users
  • GET /wallets/me
  • POST /circles
  • GET /circles
  • GET /circles/:circleId
  • POST /circles/:circleId/invites
  • POST /invites/:code/accept
  • POST /commitments
  • GET /commitments
  • GET /commitments/:id
  • POST /commitments/:id/proof-image
  • POST /commitments/:id/submit-proof
  • POST /commitments/:id/votes
  • POST /commitments/:id/resolve

The mobile app should not need to call POST /commitments/:id/resolve for normal voting. Casting a vote already triggers automatic resolution when the result is mathematically decided.

Development Checks

Type-check the mobile app:

cd mobile
npm exec -- tsc --noEmit

Type-check the backend:

cd backend
npm exec -- tsc --noEmit

Run backend tests on macOS/Linux:

cd backend
SUPABASE_URL='http://localhost:54321' \
SUPABASE_SECRET_KEY='test-secret' \
ESCROW_MODE='mocked' \
SOLANA_DEMO_TOKEN_MINT='DEMO_USDC_MOCK_MINT' \
SOLANA_RPC_URL='https://api.devnet.solana.com' \
npx tsx --test test/*.test.ts

The npm test script currently uses PowerShell syntax.

Notes

  • Proof images are uploaded as base64 data URLs and stored through Supabase Storage.
  • The proof upload route has a larger request body limit to account for base64 expansion.
  • Escrow can run in mocked mode locally or be configured for SPL token movement.
  • Expiration-based resolution should be run by a backend scheduler or maintenance job; majority vote resolution happens when a vote is cast.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages