A Prediction Market on the AT Protocol
at main 247 lines 8.4 kB view raw
1import 'dotenv/config'; 2import { ZaCoCiaranCumulusBet, ZaCoCiaranCumulusMarket, ZaCoCiaranCumulusResolution } from '../../generated/typescript'; 3import { is, type Did } from '@atcute/lexicons'; 4import type { CreateCommit, DeleteCommit } from '@atcute/jetstream'; 5import { marketsTable, betsTable, resolutionsTable } from '@/db' 6import { eq } from 'drizzle-orm'; 7import { db } from '@/db'; 8import { createUri, isBetCollection, isMarketCollection, isResolutionCollection } from './utils'; 9 10export async function tryListMarkets() { 11 return await db.query.marketsTable.findMany({ 12 columns: { 13 uri: true, 14 did: true, 15 cid: true, 16 question: true, 17 liquidity: true, 18 closesAt: true, 19 createdAt: true, 20 }, 21 orderBy: (markets, { desc }) => [desc(markets.createdAt)], 22 with: { 23 bets: { 24 columns: { 25 uri: true, 26 did: true, 27 cid: true, 28 position: true, 29 createdAt: true, 30 } 31 }, 32 resolution: { 33 columns: { 34 uri: true, 35 did: true, 36 cid: true, 37 answer: true, 38 createdAt: true, 39 } 40 } 41 }, 42 }) 43} 44 45export async function tryFindMarket(uri: string) { 46 return await db.query.marketsTable.findFirst({ 47 columns: { 48 uri: true, 49 did: true, 50 cid: true, 51 question: true, 52 liquidity: true, 53 closesAt: true, 54 createdAt: true, 55 }, 56 where: eq(marketsTable.uri, uri), 57 with: { 58 bets: { 59 columns: { 60 uri: true, 61 did: true, 62 cid: true, 63 position: true, 64 createdAt: true, 65 } 66 }, 67 resolution: { 68 columns: { 69 uri: true, 70 did: true, 71 cid: true, 72 answer: true, 73 createdAt: true, 74 } 75 } 76 } 77 }) 78} 79 80export async function tryFindMarketBets(uri: string) { 81 return await db.query.betsTable.findMany({ 82 columns: { 83 uri: true, 84 did: true, 85 cid: true, 86 position: true, 87 createdAt: true, 88 }, 89 where: eq(betsTable.marketUri, uri), 90 orderBy: (bets, { desc }) => [desc(bets.createdAt)], 91 with: { 92 market: { 93 columns: { 94 uri: true, 95 did: true, 96 cid: true, 97 question: true, 98 liquidity: true, 99 closesAt: true, 100 createdAt: true, 101 }, 102 with: { 103 resolution: { 104 columns: { 105 uri: true, 106 did: true, 107 cid: true, 108 answer: true, 109 createdAt: true, 110 } 111 } 112 } 113 } 114 } 115 }) 116} 117 118export async function tryFindMarketResolutions(uri: string) { 119 return await db.query.resolutionsTable.findFirst({ 120 columns: { 121 uri: true, 122 did: true, 123 cid: true, 124 answer: true, 125 createdAt: true, 126 }, 127 where: eq(resolutionsTable.marketUri, uri), 128 orderBy: (resolutions, { desc }) => [desc(resolutions.createdAt)], 129 with: { 130 market: { 131 columns: { 132 uri: true, 133 did: true, 134 cid: true, 135 question: true, 136 liquidity: true, 137 closesAt: true, 138 createdAt: true, 139 }, 140 with: { 141 bets: { 142 columns: { 143 uri: true, 144 did: true, 145 cid: true, 146 position: true, 147 createdAt: true, 148 } 149 } 150 } 151 } 152 } 153 }) 154} 155 156export async function tryCreateMarket(did: Did, { record, rev, rkey, cid }: CreateCommit) { 157 if (is(ZaCoCiaranCumulusMarket.mainSchema, record)) { 158 const uri = createUri(did, record.$type, rkey); 159 console.log("> Creating Market:", uri); 160 161 const { question, liquidity } = record; 162 const [closesAt, createdAt] = [new Date(record.closesAt), new Date(record.createdAt)]; 163 164 const existing = await db.query.marketsTable.findFirst({ where: eq(marketsTable.uri, uri) }); 165 if (existing) return; 166 167 const marketData: typeof marketsTable.$inferInsert = { 168 uri, did, rev, rkey, cid, question, liquidity, closesAt, record, createdAt, 169 }; 170 171 await db.insert(marketsTable).values(marketData); 172 console.log("> Created Market!"); 173 } 174} 175 176export async function tryDeleteMarket(did: Did, commit: DeleteCommit) { 177 if (!isMarketCollection(commit.collection)) return; 178 const uri = createUri(did, commit.collection, commit.rkey); 179 console.log("> Deleting Market:", uri); 180 await db.delete(marketsTable).where(eq(marketsTable.uri, uri)) 181} 182 183export async function tryCreateBet(did: Did, { record, rev, rkey, cid }: CreateCommit) { 184 if (is(ZaCoCiaranCumulusBet.mainSchema, record)) { 185 const uri = createUri(did, record.$type, rkey); 186 console.log("> Creating Bet:", uri); 187 188 const { position, market } = record; 189 const createdAt = new Date(record.createdAt); 190 191 const existing = await db.query.betsTable.findFirst({ where: eq(betsTable.uri, uri) }); 192 if (existing) return; 193 194 const resolution = await db.query.resolutionsTable.findFirst({ where: eq(resolutionsTable.uri, market.uri) }); 195 if (resolution?.uri) return; 196 197 const indexedMarket = await db.query.marketsTable.findFirst({ where: eq(marketsTable.uri, market.uri), columns: { closesAt: true } }); 198 if (!indexedMarket || (new Date() > indexedMarket.closesAt)) return; 199 200 const betData: typeof betsTable.$inferInsert = { 201 uri, did, rev, rkey, cid, position, marketUri: market.uri, record, createdAt 202 } 203 204 await db.insert(betsTable).values(betData); 205 console.log("> Created Bet!"); 206 } 207} 208 209export async function tryDeleteBet(did: Did, commit: DeleteCommit) { 210 if (!isBetCollection(commit.collection)) return; 211 const uri = createUri(did, commit.collection, commit.rkey); 212 console.log("> Deleting Bet:", uri); 213 await db.delete(betsTable).where(eq(betsTable.uri, uri)) 214} 215 216export async function tryCreateResolution(did: Did, { record, rev, rkey, cid }: CreateCommit) { 217 if (is(ZaCoCiaranCumulusResolution.mainSchema, record)) { 218 const uri = createUri(did, record.$type, rkey); 219 console.log("> Creating Resolution:", uri); 220 221 const { answer, market } = record; 222 const createdAt = new Date(record.createdAt); 223 224 const existing = await db.query.resolutionsTable.findFirst({ where: eq(resolutionsTable.uri, uri) }); 225 if (existing) return; 226 227 const indexedMarket = await db.query.marketsTable.findFirst({ where: eq(marketsTable.uri, market.uri), columns: { did: true } }); 228 if (!indexedMarket) return; 229 230 const canUserEdit = did === indexedMarket.did; 231 if (!canUserEdit) return; 232 233 const resolutionData: typeof resolutionsTable.$inferInsert = { 234 uri, did, rev, rkey, cid, answer, marketUri: market.uri, record, createdAt 235 } 236 237 await db.insert(resolutionsTable).values(resolutionData); 238 console.log("> Created Resolution!"); 239 } 240} 241 242export async function tryDeleteResolution(did: Did, commit: DeleteCommit) { 243 if (!isResolutionCollection(commit.collection)) return; 244 const uri = createUri(did, commit.collection, commit.rkey); 245 console.log("> Deleting Resolution:", uri); 246 await db.delete(resolutionsTable).where(eq(resolutionsTable.uri, uri)) 247}