upstream

Reference

JavaScript engine

The same ES modules run in Node, in tests and in the browser.

The engine is three files under src/engine/. No dependencies; it uses WebCrypto (SHA-256 and Ed25519), which Node 20+ and current browsers provide.

platforms.mjs

parseTarget(input, hint?)        -> { platform, kind, key, url } | null
lookup(target, { fetch?, youtubeKey? })  -> Profile       // throws LookupError
PLATFORMS, ORDER                 // names, brand colours, where the proof goes

Profile = { platform, id, name, handle, url, avatar,
            proofText, live, viewers, followers, note }

fetch is injectable, which is how the tests feed real response shapes without a network.

proof.mjs

challengeCode({ mint, platform, id, wallet })   -> "up1-XXXX-XXXX-XXXX"
findCode(text, code)                             -> boolean
newKey(seed?)                                    -> { publicKey, seed, sign(msg) }
verifySig(publicKey, message, signature)         -> boolean
attest({ attester, mint, target, wallet, epoch, now, ctx })
                                                 -> { status, profile, code, attestation? }
checkAttestations(list, { trusted, threshold, expect, now, ttlMs })
                                                 -> { ok, valid, threshold, reasons }
b58encode(bytes), b58decode(string), isWallet(string), sha256(data)

escrow.mjs

new Vault(config, now)
  .accrue(amountLamports, now)            // BigInt, returns { streamer, launcher, burn }
  .claim({ attestations, wallet, now })
  .rotate({ attestations, wallet, now })
  .cancelRotation({ signature, now })
  .tick(now)                              // applies due rotations and expiries
  .snapshot(now)
  .code(wallet)                           // the code to paste for this wallet
  .conserved()                            // accrued == pending + paid + burned
cancelMessage({ mint, wallet, effectiveAt })
lamports(sol), toSol(lamports), DAY, DEFAULTS

Example

import { Vault, lamports } from './src/engine/escrow.mjs';
import { attest, newKey } from './src/engine/proof.mjs';

const vault = new Vault({ mint, target: { platform: 'twitch', id: '12826', name: 'twitch' },
                          launcher, trusted: [pkA, pkB, pkC], threshold: 2 });
vault.accrue(lamports(0.4));
const code = await vault.code(wallet);          // the owner pastes this in their bio
const a = await attest({ attester: keyA, mint, target: 'twitch.tv/twitch', wallet });
const b = await attest({ attester: keyB, mint, target: 'twitch.tv/twitch', wallet });
if (a.status === 'verified' && b.status === 'verified')
  await vault.claim({ attestations: [a.attestation, b.attestation], wallet });

attester/handler.mjs and client.mjs

createAttester({ key, name, fetch, now, limit, timeoutMs, youtubeKey })  -> { handle({method,path,body,ip}), publicKey }
toFetchHandler(attester)                                    -> async (Request) => Response    // Workers, Netlify, Deno, Bun
collect({ attesters, mint, target, wallet, epoch, expectId, threshold, fetch, timeoutMs })
                                                            -> { code, attestations, results, ok }

See Attester network.

chain/client.mjs (Node)

vaultAddress(programId, mint, launcher)  -> { address, bump }
decodeVault(bytes)                          -> the 512-byte account as an object
effectiveState(state, nowSecs)              -> what the next instruction will see (due rotation applied, epoch raised)
ixInitialize / ixCrank / ixClaim / ixRotate / ixCancelRotation
ed25519Ixs(attestations)                    -> the precompile instructions that must precede claim and rotate
programError(e)                             -> 'not_enough_proof', 'already_bound', ...

Needs @solana/web3.js. See On-chain program.

Time is an argument

Every method that depends on time takes now (milliseconds). Nothing reads the clock behind your back, which is why the tests can run 300 days in a few milliseconds and why the app's "wait 30 days" buttons work.

Money is BigInt

Amounts are lamports as BigInt. accrue refuses anything that is not a positive BigInt.