This is a submission for Weekend Challenge: Passion Edition
Target categories: Best use of Snowflake + Best use of Solana.
The single most common mistake in cross-platform hackathon projects is using
one platform as a database and the other as a logo. A dashboard that reads
Solana data through an API and renders it in a UI is not a Solana project.
It's a dashboard.
The actual first question is: what can each platform do that the other
physically cannot?
Solana has behavioral data that exists nowhere else - every buy, sell, and
hold, public and permanent. But a blockchain cannot compute aggregates over
its own history; that's not what the runtime is for. Snowflake can chew
through millions of decoded transactions with a declarative SQL DAG - but it
has no way to make a statement the rest of the on-chain world can read and
build on.
So I built the loop that gives each side the other's missing half:
Solana (mainnet) --read--> Snowflake --compute--> Snowflake --write back--> Solana (devnet)
live swaps and streaming conviction an oracle account
transfers, decoded ingest + Dynamic scoring + Cortex any program can read
by Helius Tables DAG ML
FERVOR is a real-time conviction oracle for World Cup fandom on Solana.
It tracks sixteen national token communities from the 2026 field - Argentina
to Japan, Morocco to Canada - wired into eight derby rivalries (ARG-BRA,
FRA-ENG, POR-ESP, GER-NOR, USA-MEX, NED-BEL, CRO-MAR, JPN-CAN), and it does not measure hype, because following a crowd is
cheap. It measures conviction: the wallet that keeps buying its
country's token while the price bleeds, the holder who never sells through a
losing run. Passion, defined mechanically: holding when it hurts.
Every 5 minutes, a Snowflake task stages the freshly computed index and a
signing bridge writes it to Solana devnet as a confirmed transaction:
{
"oracle": "FERVOR",
"source": "Snowflake MARTS.TEAM_FERVOR_INDEX",
"team_id": 1,
"team": "ARGENTINA",
"fervor_index": 290,
"momentum": 4
}
That JSON is Snowflake output living on a block explorer.
And this is the league it produces - real numbers from the running system:
Argentina leads on conviction (109 average days held), Japan is the surprise
runner-up, England has the deepest bench of believers, and Norway folds under
pressure. The chain said so.
The architecture, actually explained
Six stages. Every stage does load-bearing work; there is no component whose
only job is to name-drop a technology.
| # | Stage | Tech | Latency | What it does |
|---|---|---|---|---|
| 1 | INGEST | Node worker + Helius enhanced transactions | 8 s batches | Polls decoded mainnet activity for the 16 tracked country tokens |
| 2 | LAND | micro-batch inserts | seconds | Raw VARIANT JSON into RAW.SOLANA_TX_LANDING
|
| 3 | TRANSFORM | Snowflake Dynamic Tables | 1 min lag | 7-table declarative DAG: decode, price, position, score |
| 4 | INTELLIGENCE | Snowflake Cortex | 5 min task |
ANOMALY_DETECTION, FORECAST, COMPLETE
|
| 5 | WRITE-BACK | Node signing bridge + Anchor | 5 min task | Queue staged in SQL, signed and confirmed on devnet |
| 6 | SERVE | Streamlit-in-Snowflake | live | 8-tab analytics app: predicted movers, a live World Cup panel, AI briefs, zero external hosting |
One database, six schemas, data flowing strictly left to right:
| Schema | Purpose | Key objects |
|---|---|---|
REF |
hand-loaded seed |
TEAM_TOKENS (mint, decimals, rival_team_id), PARAMS
|
RAW |
untouched VARIANT landing | SOLANA_TX_LANDING |
STAGING |
decoded events (Dynamic Tables) |
TOKEN_TRANSFERS, SWAP_EVENTS, PRICE_TICKS
|
MARTS |
conviction metrics (Dynamic Tables) |
WALLET_POSITIONS, WALLET_FERVOR, TEAM_FERVOR_INDEX, DEFECTION_FLOWS
|
ML |
Cortex outputs + time series |
TEAM_INDEX_HISTORY, FERVOR_ANOMALIES, FERVOR_FORECAST
|
ORACLE |
write-back queue + audit |
PUBLISH_QUEUE, PUBLISH_LOG
|
Stage 1: ingest - a cursor trick that makes polling feel like streaming
Helius decodes raw Solana transactions into clean JSON, so I ingest
structured events instead of byte soup. The worker polls each tracked mint
every 8 seconds, but the until cursor means each poll returns only
transactions newer than the last one seen. Functionally, it's a stream:
// ingest/poller.ts - the cursor is the whole trick
const cursor = new Map<string, string>(); // address -> newest seen signature
async function fetchNewTxs(address: string): Promise<EnhancedTx[]> {
const until = cursor.get(address);
const url =
`https://api.helius.xyz/v0/addresses/${address}/transactions` +
`?api-key=${HELIUS_API_KEY}&limit=50` +
(until ? `&until=${until}` : "");
const res = await fetch(url);
const txs = (await res.json()) as EnhancedTx[];
if (txs.length > 0) cursor.set(address, txs[0].signature); // newest first
return txs;
}
Landing is a multi-row INSERT ... SELECT PARSE_JSON(?) through the Node
connector. Honest note: true Snowpipe Streaming needs the Java ingest SDK.
8-second micro-batches demo identically (rows appear in Snowflake seconds
after they land on-chain) and were far more reliable to stand up in a
weekend.
Stage 3: the Dynamic Tables DAG - no cron, declared lag
I did not write a single scheduler for the transform layer. Each table
declares TARGET_LAG = '1 minute' and Snowflake works out the refresh order:
RAW.SOLANA_TX_LANDING
|- STAGING.TOKEN_TRANSFERS ----> MARTS.DEFECTION_FLOWS
|- STAGING.SWAP_EVENTS -------> STAGING.PRICE_TICKS
|- MARTS.WALLET_POSITIONS
|- MARTS.WALLET_FERVOR ----> MARTS.TEAM_FERVOR_INDEX
|- (1 min task) ML.TEAM_INDEX_HISTORY
|- (5 min task) Cortex models
|- (5 min task) ORACLE.PUBLISH_QUEUE
The result is a per-minute index series where each of the sixteen nations charts its own story:
Two pieces of the transform SQL earned their place the hard way.
Decoding BUY/SELL without per-DEX parsers. Helius does not emit a parsed
swap event for every venue, so I classify from token-balance movement: fee
payer receives the tracked token = BUY, sends it = SELL. Routed swaps get
their legs summed, and self-arbitrage transactions (fee payer both sends AND
receives the same token in one tx) are dropped entirely - an arb bot carries
zero conviction signal:
-- warehouse/02_staging_dt.sql (excerpt)
team_legs AS (
SELECT l.signature,
MIN(l.block_time) AS block_time,
l.raw_payload:feePayer::string AS wallet,
tt.value:mint::string AS mint,
r.team_id,
IFF(tt.value:toUserAccount::string = l.raw_payload:feePayer::string,
'BUY', 'SELL') AS side,
SUM(tt.value:tokenAmount::float) AS token_amount
FROM dedup l,
LATERAL FLATTEN(input => l.raw_payload:tokenTransfers) tt
JOIN REF.TEAM_TOKENS r ON r.mint = tt.value:mint::string
WHERE tt.value:toUserAccount::string = l.raw_payload:feePayer::string
OR tt.value:fromUserAccount::string = l.raw_payload:feePayer::string
GROUP BY l.signature, l.raw_payload:feePayer::string,
tt.value:mint::string, r.team_id, side
-- self-arb filter: a signature with BOTH a BUY and a SELL row for the
-- same token makes this window count 2, and the tx is excluded
QUALIFY COUNT(*) OVER (PARTITION BY l.signature, r.team_id) = 1
)
The 24-hour price change that survives trading gaps. The naive version is
LAG(price, 24) over hourly buckets. That counts ROWS, not HOURS - one
quiet hour and your "24h change" silently becomes a 25h change. ASOF JOIN
matches each tick to the nearest tick at or before 24 hours earlier, with a
warm-up fallback for series younger than a day:
-- warehouse/02_staging_dt.sql (excerpt)
SELECT cur.team_id, cur.window_start, cur.price_usd,
100.0 * (cur.price_usd - COALESCE(day_ago.price_usd, first_tick.price_usd))
/ NULLIF(COALESCE(day_ago.price_usd, first_tick.price_usd), 0)
AS price_change_24h_pct
FROM hourly cur
ASOF JOIN hourly day_ago
MATCH_CONDITION (DATEADD('hour', -24, cur.window_start) >= day_ago.window_start)
ON cur.team_id = day_ago.team_id
LEFT JOIN ( -- warm-up: earliest tick, for series younger than 24h
SELECT team_id, price_usd FROM hourly
QUALIFY ROW_NUMBER() OVER (PARTITION BY team_id ORDER BY window_start) = 1
) first_tick ON first_tick.team_id = cur.team_id;
Prices use the hourly median, not VWAP - SOL-leg attribution is
heuristic, and one mis-attributed leg destroys a volume-weighted mean.
The dips these series capture are the entire thesis:
The conviction score - the creative payload
Per wallet, per country, 0 to 100:
| Component | Max points | What it rewards |
|---|---|---|
25 x ln(1 + hold_days) / ln(366) |
25 | longevity of the relationship |
30 x min(1, dip_buys / 5) |
30 | buying while THEIR token bled 5%+ in 24h |
25 x min(1, underwater_buys / 5) |
25 | buying below their own average cost |
-20 x (sold within 3 days) |
-20 | punishes paper hands |
-- warehouse/03_marts_dt.sql (excerpt)
ROUND(GREATEST(0, LEAST(100,
25 * LN(1 + a.hold_duration_days) / LN(1 + 365)
+ 30 * LEAST(1, COALESCE(c.buy_against_decline, 0) / 5.0)
+ 25 * LEAST(1, COALESCE(c.buy_at_loss, 0) / 5.0)
- 20 * IFF(a.last_sell_time > DATEADD('day', -3, SYSDATE()), 1, 0)
)), 1) AS fervor_score
Two engineering decisions worth stealing:
-
Tenure in fractional days (
DATEDIFF('second', ...) / 86400.0). Whole-day granularity freezes longevity at zero for 24 hours and flat-lines the index on launch day. - Zero-signal wallets are excluded from the national average. The raw swap feed is dominated by one-shot MEV bots whose score is 0 by construction. Averaging them in measures bot traffic, not the fan base:
ROUND(( COALESCE(AVG(IFF(f.fervor_score > 0, f.fervor_score, NULL)), 0) * 0.6
+ LEAST(100, COUNT_IF(f.fervor_score >= 60) / 10.0) * 0.4 ) * 10, 0)
AS fervor_index -- 0 to 1000, this exact number goes on-chain
And the emotional centerpiece, MARTS.DEFECTION_FLOWS: the same wallet
selling one country and buying another within 24 hours. Selling ARGENTINA to
buy BRAZIL is not a portfolio rebalance. It is treason, and it gets its own
Sankey:
Stage 4: Cortex - ML without leaving SQL, plus the gotcha that cost an hour
Three models, retrained every 5 minutes by a task:
-- warehouse/04_ml_cortex.sql (excerpt)
CREATE OR REPLACE SNOWFLAKE.ML.ANOMALY_DETECTION ML.FERVOR_ANOMALY_MODEL(
INPUT_DATA => SYSTEM$QUERY_REFERENCE(
'SELECT team_id, ts, fervor_index FROM FERVOR.ML.TEAM_INDEX_HISTORY
WHERE NOT COALESCE(is_synthetic, FALSE) -- never train on demo spikes
AND ts < DATEADD(minute, -15, SYSDATE())'), -- strict train/detect split
SERIES_COLNAME => 'TEAM_ID', TIMESTAMP_COLNAME => 'TS',
TARGET_COLNAME => 'FERVOR_INDEX', LABEL_COLNAME => '');
The gotcha: my first version trained on ts < now and detected on the last
hour. Cortex rejected it with
All evaluation timestamps must be after the last timestamp in fitting data.
Training and detection must not overlap - split both at the same boundary,
strict < on one side, >= on the other.
The is_synthetic flag is the honesty mechanism. A demo needs a passion
spike on camera, so scripts/demo_spike.py injects labeled rows that
detection SEES but training NEVER fits. The detector flags the surge because
it genuinely is one.
The crowd-pleaser is one function call:
SNOWFLAKE.CORTEX.COMPLETE('mistral-large2',
'In two punchy sentences, hype up the fanbase of the ' || team_name ||
' token community. Fervor index ' || fervor_index || '/1000, ' ||
devoted_wallet_count || ' diamond-hand wallets, average hold ' ||
avg_hold_days || ' days. No preamble, no hashtags.')
And it goes further than a scheduled task: the dashboard's AI insights tab
calls Cortex on demand, from inside the app - no external API, the LLM runs
where the data lives. Pick a team and mistral-large2 writes an analyst brief
from the real numbers:
# warehouse/streamlit_app.py (excerpt) - LLM inference inside the warehouse
@st.cache_data(ttl=300)
def cortex_brief(prompt: str) -> str:
esc = prompt.replace("'", "''")
return str(session.sql(
f"SELECT SNOWFLAKE.CORTEX.COMPLETE('mistral-large2', '{esc}')"
).collect()[0][0])
prompt = (f"You are a crypto market analyst covering World Cup fan tokens. "
f"3 bullets: one strength, one risk, one outlook. {team} index "
f"{idx}/1000 ({chg:+.0f} over {window}), {bel} believers of {tot} "
f"wallets, buy pressure {bp:.0f}%, arch-rival is {rival}.")
The same tab shows a conviction-health table in percentages per nation:
share of wallets with a signal, share of believers, dip buys per wallet,
6-hour buy pressure, and Cortex's forecast for the next 12 minutes.
The FORECAST model does more than fill a column. The AI tab opens with a
Conviction outlook: every nation's index projected 12 minutes out, drawn as
a dumbbell (live index to forecast) so you read who is strengthening and who is
cracking in one glance, a durability score blending forecast trajectory, hold
time, momentum and buy pressure, and a league-wide brief Cortex writes from the
ranked board - naming the best-positioned fan base and the one most at risk,
with an explicit note that it forecasts holding behavior, not token price.
The market tabs render real 1-hour candlesticks built in SQL - no OHLC
API, just MIN_BY/MAX_BY over decoded swap executions:
SELECT TIME_SLICE(s.block_time, 1, 'HOUR') AS h,
MIN_BY(s.price_usd, s.block_time) AS open,
MAX(s.price_usd) AS high,
MIN(s.price_usd) AS low,
MAX_BY(s.price_usd, s.block_time) AS close,
SUM(s.sol_amount) AS volume
FROM STAGING.SWAP_EVENTS s
WHERE s.team_id = ? AND s.block_time >= DATEADD('hour', -72, CURRENT_TIMESTAMP())
GROUP BY 1 ORDER BY 1;
And the USD leg is genuinely live: the bridge pushes the real SOL/USD rate
into the warehouse every 60 seconds, so the whole DAG's pricing tracks the
market (SOL was $78.22 while I wrote this):
// bridge/server.ts (excerpt) - the warehouse gets a live price artery
async function updateSolPrice() {
const res = await fetch(
"https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd");
const px = (await res.json())?.solana?.usd;
if (!px) return;
const sf = await getConnection();
await exec(sf,
`UPDATE FERVOR.REF.PARAMS SET param_value=? WHERE param_key='SOL_USD'`, [px]);
}
setInterval(updateSolPrice, 60_000);
Stage 5: the write-back - where the trust boundary lives
The security property judges should check: the oracle keypair never
touches Snowflake. Snowflake stages numbers in a queue table; a small Node
bridge holds the key, signs, submits, and writes the audit trail back:
-- warehouse/05_oracle.sql
CREATE OR REPLACE TABLE ORACLE.PUBLISH_QUEUE (
publish_id NUMBER IDENTITY,
team_id NUMBER, team_name STRING,
fervor_index NUMBER,
momentum NUMBER DEFAULT 0, -- Cortex forecast minus current index
status STRING DEFAULT 'PENDING', -- PENDING -> SENT -> CONFIRMED | FAILED
queued_at TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP(),
tx_signature STRING, explorer_url STRING, last_error STRING
);
Until the Anchor program is deployed the bridge writes signed Memo
transactions - real, confirmed, explorer-visible. Once FERVOR_PROGRAM_ID
is set it flips to the PDA oracle automatically, encoding the Anchor
instruction by hand (no client library):
// bridge/server.ts - Anchor instruction without @coral-xyz/anchor:
// discriminator = sha256("global:update_index")[0..8], args are borsh LE
const [pda] = PublicKey.findProgramAddressSync(
[Buffer.from("fervor"),
Buffer.from(new Uint8Array(new Uint16Array([teamId]).buffer))],
programId);
const data = Buffer.alloc(8 + 2 + 4 + 4);
crypto.createHash("sha256").update("global:update_index")
.digest().copy(data, 0, 0, 8);
data.writeUInt16LE(teamId, 8);
data.writeUInt32LE(fervorIndex, 10);
data.writeInt32LE(momentum, 14);
The on-chain side is a deliberately minimal Anchor program - one PDA per
country, init_if_needed so the first write creates the account:
// oracle-program/programs/fervor_oracle/src/lib.rs (excerpt)
#[account]
pub struct FervorAccount {
pub authority: Pubkey, // first writer claims the PDA
pub team_id: u16,
pub fervor_index: u32,
pub momentum: i32,
pub updated_slot: u64,
}
Here is the oracle's actual output over one evening - every marker a
confirmed devnet transaction:
Two write-back designs, and which one to pick
| Inbound (External Access) | Outbound (queue poll) | |
|---|---|---|
| Flow | Snowflake proc calls the bridge over HTTPS | bridge polls PUBLISH_QUEUE
|
| Snowflake features | Secret + network rule + EAI + Snowpark proc | plain task + table |
| Needs public endpoint | yes (tunnel from a laptop) | no |
| Works on trial accounts | no | yes |
| Same on-chain result | yes | yes |
I implemented both. The honest part: Snowflake trial accounts reject the
inbound integration with error 509009: External access is not supported for trial accounts. I verified the rest of that path anyway - an authenticated POST /publish through a cloudflared tunnel produced a confirmed devnet transaction - and on a paid account warehouse/06_external_access.sql runs as-is. The submission runs
the queue design.
Stage 6: Streamlit-in-Snowflake - and the bugs that will bite you too
The dashboard runs inside the warehouse: eight tabs (Standings, World Cup,
Wallets and rivalries, Market, Token markets, Head to head, AI insights,
On-chain oracle), national colors with SVG emblems, a rotatable 3D conviction
scatter (days held x dip buys x score, sized by SOL traded), a Mesh3d conviction
skyline, a per-wallet drill-down, and Geist type throughout.
The war-stories table. If you build on Streamlit-in-Snowflake, these will
save you real hours:
| Symptom | Root cause | Fix |
|---|---|---|
AttributeError: module 'streamlit' has no attribute 'column_config' |
SiS pins an older Streamlit than you develop against |
hasattr guard; render tables as styled HTML |
| Every line chart is an identical straight diagonal | Snowpark to_pandas() returns numerics as object dtype (Decimal); Plotly plots them as CATEGORIES, so y = row index. Worse: pd.to_numeric RAISES TypeError on Decimals in the old pandas SiS pins, so a try/except "fix" silently does nothing |
convert element-wise with float() in the query helper; feed charts plain Python lists |
| Scatter axes scale correctly but zero points render | old Plotly silently drops traces whose customdata is a mixed-type numpy array |
prebuild hover strings; pass text= + hoverinfo="text"
|
ModuleNotFoundError: plotly in SiS only |
packages must be declared per app | ship environment.yml next to the app file in the stage |
Wallet tenure frozen at 0, LN(0) crash risk at day boundaries |
account timezone was America/Los_Angeles, block times are UTC |
ALTER ACCOUNT SET TIMEZONE = 'Etc/UTC' + SYSDATE() in score math |
Cortex MLUserError on detect |
train/detect windows overlapped | split both at the same -15 min boundary |
Anchor build fails on Windows: link.exe ... extra operand
|
Git Bash coreutils link.exe shadows MSVC's linker |
build in Solana Playground (5 min) or install VS Build Tools |
# the dtype fix that actually works on SiS's old pandas - every chart
# goes through this. float() handles Decimal on every pandas version.
@st.cache_data(ttl=20)
def q(sql: str) -> pd.DataFrame:
df = session.sql(sql).to_pandas()
for c in df.columns:
if df[c].dtype == object:
try:
df[c] = pd.Series(
[None if v is None else float(v) for v in df[c]],
index=df.index, dtype="float64")
except (ValueError, TypeError):
pass
return df
The live World Cup, joined to on-chain conviction
Here is the part that answers the only real objection - "but is any of this data
actually real?" - with something no other entry can fabricate. The dashboard's
World Cup tab reads the live 2026 FIFA World Cup: real fixtures, real scores
(with extra-time and penalty-shootout detail), the group tables, and the live
Golden Boot race, updated as matches finish, pulled from football-data.org and
set directly beside the on-chain conviction index.
Streamlit-in-Snowflake cannot reach the public internet - the same sandbox that
forces the oracle through a bridge - so a host-side sync writes the tournament
into Snowflake, exactly the way the bridge writes SOL/USD. The app only ever
reads a table:
# scripts/football_sync.py (excerpt) - three endpoints -> three REF tables
matches = get("/matches", KEY) # scores, half-time, extra-time, penalties
standings = get("/standings", KEY) # group tables: W/D/L, goals for/against, pts
scorers = get("/scorers", KEY) # Golden Boot race: goals + assists
# every row maps the API country name to our REF.TEAM_TOKENS team_id via
# our_team(), so the real tournament joins straight onto on-chain conviction
cur.executemany("INSERT INTO REF.WC_MATCHES (...) VALUES (...)", match_rows)
cur.executemany("INSERT INTO REF.WC_STANDINGS (...) VALUES (...)", group_rows)
cur.executemany("INSERT INTO REF.WC_SCORERS (...) VALUES (...)", scorer_rows)
The result is three tables checked against reality every few hours, and a tab
that puts each nation's real tournament result next to its live conviction:
tournament form, the group-stage table (where green means through to the
knockouts), and a Golden Boot bar chart with our nations highlighted (Mbappe and
Messi tied on eight). On the day I wrote this, France had just knocked Morocco
out 2-0 in the quarter-final and Argentina beat Switzerland 3-1 after extra time,
setting up an England vs Argentina semi-final; the tab asks the one question a
passion oracle exists to answer - did the losing fan base hold, or
capitulate? That is the entire thesis, now measurable against an actual
scoreline instead of a chart in a vacuum.
What's real vs. what's simplified
Judges reward honesty, so here is the exact line.
Real and running unattended: the full 7-table Dynamic Tables DAG at
1-minute lag; the conviction scoring above; three Cortex models retraining
on a 5-minute task; confirmed devnet transactions for all sixteen nations,
one publish cycle every 5 minutes, each with a clickable Solscan link in
ORACLE.PUBLISH_LOG; the live 2026 FIFA World Cup synced from
football-data.org into REF.WC_MATCHES, REF.WC_STANDINGS and REF.WC_SCORERS
(match scores with extra-time and penalty detail, the group tables, and the
Golden Boot race) and joined to conviction; the 8-tab dashboard inside Snowflake:
a Cortex-driven conviction outlook with predicted movers, candlestick token
markets, a head-to-head rivalry view, the World Cup panel, and an AI-insights tab
where Cortex writes briefs on demand.
Simplified, with reasons:
| Simplification | Why | Honest label |
|---|---|---|
| Country token mints are placeholders pending verification | unofficial "national" meme tokens need per-mint liquidity vetting before pointing mainnet ingest at them |
PLACEHOLDER_MINT_* in REF.TEAM_TOKENS; one UPDATE per row to go live, the poller skips placeholders |
| 8 s micro-batches, not Snowpipe Streaming SDK | SDK is Java-only | noted in code comments |
| BUY/SELL from balance deltas, not per-DEX decoding | Helius doesn't parse every venue | self-arb filter compensates |
| Token prices decoded from swap legs, not a DEX API | the pipeline must work from raw transactions alone | USD via a LIVE SOL/USD rate the bridge refreshes into REF.PARAMS every 60 s |
| Writes go to devnet; bridge is centralized | this is an oracle BRIDGE, not a decentralized oracle network | stated plainly |
| Demo data seeded | placeholder mints produce no organic feed yet | every seeded row labeled: _batch_id = 'DEMO_SEED', wallets end in demo, is_synthetic history is excluded from model training, sample oracle rows carry is_demo = TRUE and are replaced by real bridge writes |
The seeder deserves one sentence: it injects Helius-shaped transactions
UPSTREAM into the raw landing table and lets the real DAG compute every
downstream number - each nation with its own volatility, trend, and fan-base
depth, and in live mode a slowly drifting conviction sentiment so believers
capitulate or buy the dip and the index genuinely moves (which is what makes the
Cortex forecast diverge). Nothing in MARTS is hand-written.
These are unofficial Solana meme tokens named after national teams, not
licensed fan tokens (those live on Chiliz). FERVOR analyzes public on-chain
behavior only.
The bigger thing happening here
Strip away the World Cup framing and what's left is a pattern: a data
warehouse acting as a first-class blockchain participant. Read state that
only exists on-chain, compute something the chain cannot compute about its
own history, and commit the answer back where other programs can consume it
trustlessly - with the signing key held in a minimal, auditable bridge
process instead of the warehouse.
Proof-of-reserve attestations, risk scores for lending protocols,
activity-based airdrop eligibility - all of them are "aggregate off-chain,
attest on-chain" problems, and the plumbing is exactly what's in this repo.
Run it yourself
git clone https://github.com/SoumyaEXE/weekend-challenge
cp .env.example .env # Helius key, Snowflake creds, devnet keypair
npm install
python scripts/run_sql.py --all # entire warehouse: schemas, DAG, tasks
python scripts/demo_seed.py # labeled demo data through the real DAG
npm run bridge # queue -> signed devnet writes
python scripts/football_sync.py # live World Cup -> matches, standings, scorers
python scripts/deploy_streamlit.py
Within about two minutes the DAG refreshes, tasks snapshot history, and the
bridge confirms its first write. npm run publish:once forces a publish for
the impatient. python scripts/demo_spike.py makes the anomaly detector fire on
camera; --clean removes the evidence. To go live on real trades, fill
config/mints.json with verified mints and run python scripts/set_mints.py
(it auto-fetches each token's decimals and logo), then npm run ingest starts
pulling real mainnet activity.
Built with @dronzer2code within the challenge window with AI pair-programming. All simplifications and data labeling are documented above and in the README.













Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.