SIN Docs
Open the app

Docs

SIN is private, uncensored inference on open weight models. A wallet is the account, credits are USDG on Robinhood Chain, and nothing you say is logged.

How It Works

  • Sign in is one wallet signature. No email, no phone, no name.
  • Credits are bought with USDG in one transaction from the wallet. 1 USDG is 1 credit.
  • Every request in the app or through the API is paid from credits, by the token.
  • New visitors get a few free replies a day on one model, no wallet needed.
  • $SIN stakers earn USDG from every credit purchase.
PartWhat it is
AppThe chat at https://sin.run/app. Wallet sign in, model picker, streaming answers, agent mode with chain tools, history kept in the browser.
APIAn OpenAI compatible endpoint at https://api.sin.run. Same models, same credits, your own code.
CreditPurchaseThe contract that takes USDG and splits it in public. Credits are posted from its events.
SinStakingThe contract that pays $SIN stakers a share of every purchase, in USDG.
Robinhood ChainWhere the money moves. Gas is paid in ETH, well under a cent per transaction.

Privacy

What follows is what the code does. Enforced by code, not by policy.

  • No account. Sign in is a wallet signature. The service knows an address and nothing about who holds it.
  • No transcripts. Prompts and answers pass through in memory and are gone when the stream ends.
  • No keys. The service never holds a private key and never sends a transaction.
  • Conversations live in the browser. Synced history is ciphertext under a key derived from your wallet signature. The server sees sizes and dates.

Where Your Words Go

A prompt leaves your browser over TLS and reaches the API through Cloudflare. The origin only answers requests that carry a secret header the Cloudflare zone adds, so the edge cannot be stepped around. The API rebuilds each request from a fixed list of fields before anything is forwarded: messages, sampling settings, output limits, stop sequences, response format, tools and reasoning settings pass. Everything else is dropped, including the user and metadata fields OpenAI clients like to fill in. The rebuilt request goes to the model provider under one API key shared by every SIN user, with a random request id. The answer streams back the same way.

While this happens the API holds the request and the answer in memory. It writes neither to a log, a database, an error tracker or a cache. When the stream ends they are gone. The one exception you control is history sync, below, which stores what your browser already encrypted.

Cloudflare and the host carry the traffic and can see it in transit, as they can for most of the web. SIN hands neither of them your identity, and nothing SIN runs writes down what passes through them. The model provider is held to zero data retention.

What The Service Keeps

KeptWhyFor how long
Wallet addressIt is the accountWhile the account exists
Credit balance and ledgerEvery credit in and out, double entry, append onlyPermanent
PurchasesTransaction hash, amount and status of each purchasePermanent
Request metadataModel, token counts, cost, latency, status. Never the text30 days, then folded into daily totals. Deleted at once with history off
Encrypted history blobsSync between devicesUntil you delete them

Logs, Errors And Metrics

Each request produces one log line: a request id, the method, the route pattern, the status, the duration and the Cloudflare ray id. Inference lines add the model, token counts, the charge and upstream latency. A user id appears only on warnings and errors. A wallet address is never logged, and neither is an IP. Lines are kept for seven days.

The error tracker receives the exception, its stack, the process name and the request id. Request data, headers and user fields are stripped before an event leaves the process. Metrics are counts and timings by route and model, with no per user labels.

History

The app keeps conversations in the browser's own storage, per wallet, so two wallets on one machine never see each other's chats. Anyone who can open your browser profile can read them there, as with any local data.

Sync between devices is off until you turn it on for a wallet. When you do, the app asks your wallet to sign the fixed text SIN history key v1 and derives a 256 bit key from that signature with HKDF SHA 256, salted with sin-history-v1 and bound to your address. Each conversation is encrypted with AES 256 GCM under a fresh random 12 byte nonce, with the conversation id as authenticated data and a version byte in front. An index blob lists titles and ids so the sidebar can render without downloading everything. The server sees ciphertext, sizes, versions and timestamps.

The derived key is kept in the browser as a non extractable key, so you sign once per device, not on every reload. Turning sync off forgets it. There is no copy of the key on the server, so losing the wallet means losing the synced history. Smart contract wallets cannot sign deterministically, so for them the key is generated on the device and history stays per device. One call deletes every blob.

Agent Mode

A request runs no tool unless it names them. In the app that is the agent switch in the composer. With tools on, the model can ask the API to search the web, read a page, do arithmetic, read the clock, read balances and transactions on Robinhood Chain, look up a token, a Pons coin or a live price, or read your own credit balance. Each of those runs on SIN servers.

A web search goes to Brave from a SIN address under a SIN key. Brave sees the query and nothing about you. A page fetch leaves from a SIN address with a plain bot user agent, so the site sees SIN, not you, and the fetcher refuses private and internal networks. Tool calls and their arguments appear in your own stream so the app can show what the model is doing. They are not stored. The database keeps how many calls a request made. Nothing in agent mode signs, sends, stores or executes anything for you, and a fetched page is text the model reads, never an instruction the API follows.

The One Line

SIN does not moderate opinions, language or topics. It holds one line: sexual content involving minors, sexual images of real people without their consent, and working tools built to steal from people, such as wallet emptying contracts, phishing kits and malware. A request that crosses it is answered with refused before any credits are reserved, and costs nothing. The check reads the latest message in memory and keeps nothing.

Sign In

Sign In With Ethereum. The app fetches a nonce bound to the address, the wallet signs a message naming the app, the chain and the nonce, and the API opens a 30 day session. No refresh: a new signature after that.

sin.run wants you to sign in with your Ethereum account:
0xAbC0000000000000000000000000000000000001

Sign in to SIN

URI: https://sin.run/app
Version: 1
Chain ID: 4663
Nonce: 3f9c1a7d2b0e4c6a8d1f2e3b
Issued At: 2026-09-19T20:00:00.000Z

EOAs and smart contract wallets both work. Credits bought before the first sign in are posted on that sign in.

API Keys

Keys start with sin_, are shown once, never expire and survive sign out. A session token and a key are interchangeable as the bearer. 20 sessions per wallet, oldest evicted. 10 active keys.

Credits

USDG On Robinhood Chain

Credits are bought with USDG on Robinhood Chain. USDG gets there from Robinhood Wallet or over a bridge such as Across, Relay, Stargate, LiFi or the Arbitrum bridge. Gas is ETH, well under a cent per transaction.

Buying

A purchase is one transaction to the CreditPurchase contract. The app signs a permit, so approval and purchase land together. Approve then purchase works too. Minimum 5 USDG, no maximum.

Credits are posted after 64 confirmations, a few seconds. Above 500 USDG the watcher waits for a finalized block, which can take minutes. The purchase list shows each one move from seen to credited.

StatusMeaning
seenObserved on chain, waiting for confirmations
confirmedConfirmed, credit in progress
creditedThe credits are on the balance
below_minimumUnder 5 USDG, not credited
unmatchedNot credited automatically, for example USDG sent straight to the treasury. Handled by hand
orphanedThe block was reorganised away before confirmation. Nothing happened

Bonuses

Volume tiers come from the API and the highest tier at or below the purchase applies.

PurchaseBonus
50 USDG or more+5%
100 USDG or more+10%
250 USDG or more+15%
500 USDG or more+20%

A promo code credits a fixed amount at once, or parks a percentage bonus for the next purchase.

One Way

Credits belong to the wallet that bought them. No transfer, no withdrawal, no refund.

Free Replies

Visitors without a wallet get a free session: a small number of replies a day on one model, with a capped output length. The session is a token in the browser, not an account. Free replies stop for everyone once the day's free budget is spent, and start again the next day.

Staker Allowance

Wallets with $SIN staked can claim a weekly credit allowance sized to their stake. It is off until the allowance rate is published; /purchase/info and the staking page show it when it is on.

Billing

Prices are per million tokens, input and output separately, per model. The model list carries them.

Before a request runs the API reserves the estimated cost times 1.2. The estimate counts input characters plus an output budget. With max_tokens that is the budget; without it, the model ceiling shrunk to what the balance can pay, so an answer is only cut where the money runs out. On finish the API charges actual usage, never more than reserved, and releases the rest.

The balance shows spendable and reserved. Failures before the first token cost nothing. Stopping a stream charges what was delivered. An idle reservation is released after ten minutes. insufficient_balance means even 64 output tokens are not affordable, or the requested output budget exceeds the balance.

The ledger holds every credit movement. The request history holds model, tokens, cost and latency, never text. With history off those rows are deleted as each request settles.

Models

Open weight models, served as released, with the uncensored variants where they exist. The live list with context sizes and prices is at /v1/models.

The model list is published at launch.

Encrypted History

Conversations live in the browser. Sync between devices goes through the API as ciphertext.

The wallet signs a fixed message once per device and HKDF turns the signature into a 256 bit key the browser keeps as a non extractable key. Each conversation is a blob under AES GCM with a fresh nonce. An index blob lists the conversations so the sidebar renders without fetching everything. The server stores blobs by id and version and sees size, timestamp and count. There is no recovery key.

QuotaLimit
One blob512 KiB
All blobs per wallet64 MiB
Blobs per wallet2000

One call wipes everything. The history switch in settings controls request metadata only and does not touch the blobs.

API

OpenAI chat completions dialect. Any OpenAI client pointed at the base URL with an API key works.

Base URL  https://api.sin.run/v1

Conventions

  • Every response is JSON, except a streaming completion, which is an event stream.
  • Every error is an object with a stable code, a message and a request id: {"error":{"code":"...","message":"...","request_id":"..."}}. Branch on the code.
  • Money and token amounts are decimal strings with six decimals, never floats.
  • Every response carries an X-Request-Id header.
  • Auth is a bearer token: a session, an API key, or a free session token.

Endpoints

Auth

GET /auth/nonce · No auth Issues a nonce for the sign in message. Bound to the wallet, lives five minutes, used once. Query: wallet (address, required).

{ "nonce": "3f9c1a7d2b0e4c6a8d1f2e3b", "expires_at": "2026-09-19T20:05:00.123456Z", "chain_id": 4663 }

Errors: bad_wallet 400

POST /auth/verify · No auth Takes the signed message and opens a session for 30 days. A wallet that bought credits before its first sign in is credited inside this call. Body: message (the full message exactly as signed, newlines included), signature (65 byte hex).

{ "token": "e3b0c442…", "user": { "id": "8c2b1a4e-…", "wallet": "0xabc0…0001" } }

Errors: bad_message 400/401, bad_nonce 401, bad_signature 401, banned 403

POST /auth/logout · Session only Ends the session. API keys are untouched. 204.

Free Sessions

POST /free/session · No auth Opens a free session for the free model. Returns 404 when free replies are off.

{ "token": "free_…", "replies_left": 3, "model": "…", "expires_at": "2026-09-20T20:00:00Z" }

Errors: rate_limited 429, free_exhausted 429

Account

GET /me · Session or API key The account with available, reserved and spendable balance.

{
  "id": "8c2b1a4e-…",
  "wallet": "0xabc0…0001",
  "balance": { "available": "42.500000", "reserved": "0.120000", "spendable": "42.380000" },
  "tier": { "min_amount": "50", "bonus_pct": "5" },
  "no_history": false,
  "created_at": "2026-09-19T12:00:00.000000Z"
}

Errors: unauthorized 401, banned 403

GET /me/ledger · Session or API key Every credit movement, newest first. Positive amounts are credits, negative are charges. Keyset pagination on the entry id. Query: cursor (entries with an id below this), limit (1 to 200, default 50).

{
  "entries": [
    { "id": 1042, "txn_id": "2a7c…", "amount": "-0.012000", "type": "inference_charge",
      "ref_type": "inference", "ref_id": "5f0b…",
      "metadata": { "model": "…", "input_tokens": 12, "output_tokens": 240 },
      "created_at": "2026-09-19T20:01:12.410000Z" }
  ],
  "next_cursor": 1041
}

GET /me/deposits · Session or API key On chain purchases of the wallet, newest first, at most 200, including purchases made before the first sign in.

{ "deposits": [ { "id": "c1d2…", "tx_hash": "0x9d4f…", "block_number": 63180200, "source": "purchase",
  "amount": "10.000000", "status": "credited", "created_at": "…", "updated_at": "…" } ] }

GET /me/requests · Session or API key Inference metadata only, never text. Rows older than 30 days fold into daily totals. Query: cursor (timestamp from the previous next_cursor), limit (1 to 200, default 50).

{ "requests": [ { "id": "5f0b…", "model": "…", "status": "completed", "reserved": "0.015000",
  "input_tokens": 12, "output_tokens": 240, "charged": "0.012000", "latency_ms": 1840, "ttft_ms": 320,
  "tool_calls": 0, "error_code": null, "created_at": "…", "finished_at": "…" } ], "next_cursor": "" }

POST /me/settings · Session or API key Changes the fields present in the body. no_history: true deletes finished request rows at once. 204. Errors: bad_body 400

POST /me/allowance/claim · Session or API key Claims the weekly staker allowance for the signed in wallet.

{ "credited": "3.000000", "next_claim_at": "2026-09-26T20:00:00Z" }

Errors: allowance_off 404, allowance_not_yet 409

GET /me/api-keys · Session or API key Lists active keys, newest first, without the plaintext.

POST /me/api-keys · Session or API key Creates a key. The plaintext is returned once. Ten active keys per wallet. Body: name (a label).

{ "id": "7b1e…", "key": "sin_4c1f9e2a…", "name": "agent" }

Errors: bad_body 400, too_many_keys 409

DELETE /me/api-keys/{id} · Session or API key Revokes a key. Idempotent. 204.

Encrypted History

GET /me/history · Session only Lists the encrypted blobs, newest first, with sizes, versions and the quota.

{ "items": [ { "id": "0d9c…", "kind": "conversation", "size": 18432, "version": 3, "updated_at": "…" } ],
  "quota": { "used_bytes": 18432, "max_bytes": 67108864, "max_blobs": 2000, "max_blob_bytes": 524288 } }

GET /me/history/{id} · Session only Returns the raw ciphertext of one blob. Headers X-History-Version and Last-Modified.

PUT /me/history/{id} · Session only Creates or replaces a blob with raw ciphertext in the body. Query kind (conversation or index). Header If-Match with the expected current version makes the write conditional.

{ "id": "0d9c…", "version": 4 }

Errors: version_conflict 409, blob_too_large 413, quota_blobs 507, quota_bytes 507

DELETE /me/history/{id} · Session only. Deletes one blob.

DELETE /me/history · Session only. Deletes every blob of the wallet.

Purchase

GET /purchase/info · No auth Everything a purchase needs. Values change only on redeploy.

{
  "chain_id": 4663,
  "purchase_contract": "Published at launch",
  "token_address": "0x5fc5360d0400a0fd4f2af552add042d716f1d168",
  "token_decimals": 6,
  "staking_contract": "Published at launch",
  "staking_enabled": true,
  "sin_address": "Published at launch",
  "sin_decimals": 18,
  "min_amount": "5.00",
  "confirmations": 64,
  "discount_tiers": [
    { "min_amount": "50", "bonus_pct": "5" }, { "min_amount": "100", "bonus_pct": "10" },
    { "min_amount": "250", "bonus_pct": "15" }, { "min_amount": "500", "bonus_pct": "20" }
  ],
  "split": { "staking_bps": 1000, "buyback_bps": 1000 }
}

Staking

GET /staking/stats · No auth Public staking totals, cached for 30 seconds.

{
  "tvl_sin": "1250000",
  "total_distributed_usdg": "5120.5",
  "distributed_7d_usdg": "310.2",
  "distributed_30d_usdg": "1200",
  "total_paid_usdg": "4900.1",
  "stakers": 87,
  "total_burned_sin": "40000",
  "purchased_total_usdg": "51205",
  "purchased_24h_usdg": "1300",
  "sin_price_usdg": "0.0012",
  "updated_at": "2026-09-19T20:00:00Z"
}

GET /staking/events · No auth The staking activity feed from the chain watcher, newest first. Amounts are in SIN for staked, unstaked and burned, in USDG for claimed and reward_deposited. Query: address (only this wallet), cursor, limit (1 to 200, default 50).

{ "events": [ { "id": 12, "kind": "reward_deposited", "address": "0x…", "amount": "0.5",
  "tx_hash": "0x…", "block_number": 63180200, "at": "2026-09-19T10:00:00Z" } ], "next_cursor": "" }

Errors: bad_wallet 400, rate_limited 429

Promo

POST /promo/redeem · Session or API key Redeems a code. A fixed credit lands at once; a percentage bonus waits for the next credited purchase. One pending bonus at a time. Body: code (case does not matter).

{ "kind": "deposit_bonus_pct", "credited": "0", "bonus_pct": "20" }

Errors: promo_invalid 404, promo_exhausted 409, promo_used 409, promo_pending 409

Inference

GET /v1/models · No auth Enabled models in the OpenAI list shape, with context window and prices per million tokens in credits.

{ "object": "list", "data": [ { "id": "…", "object": "model", "owned_by": "sin", "max_context": 262144,
  "tee": false, "tools": true, "free": false,
  "pricing": { "input_per_1m": "…", "output_per_1m": "…" } } ] }

POST /v1/chat/completions · Session, API key or free session OpenAI chat completion, streaming or not. The upstream request is rebuilt from an allowlist of fields, one choice per request. Credits are reserved before the call and settled after it.

FieldType
modelstring, requiredA model id from the model list
messagesarray, requiredOpenAI chat messages with the roles system, user, assistant and tool
streambooleanEvent stream instead of one JSON response
max_tokensintegerOutput budget. Also caps the credit reservation
temperature, top_p, top_k, min_pnumberPassed through unchanged
repetition_penalty, presence_penalty, frequency_penaltynumberPassed through unchanged
stopstring or arrayPassed through unchanged
seedintegerPassed through unchanged
response_formatobjectPassed through unchanged
toolsarrayYour own function tools, passed through unchanged
reasoning_effortstringPassed through unchanged
sin_toolsarray of stringsSIN server tools for agent mode, see below
{ "model": "…", "messages": [ { "role": "system", "content": "You are terse." }, { "role": "user", "content": "Hello" } ],
  "stream": true, "max_tokens": 512 }

With stream the response is OpenAI chunks, a final usage chunk, then data: [DONE]. A stream without that marker was cut off. Every completion carries an X-Inference-Id header that matches the request history and the ledger.

Errors: invalid_request 400, context_too_long 400, refused 400, unauthorized 401, insufficient_balance 402, model_not_found 404, body_too_large 413, rate_limited 429, free_exhausted 429, upstream_unavailable 503, overloaded 503

Agent Mode Over The API

Name the server tools in sin_tools. The API runs them between model turns (at most six rounds) and adds two kinds of chunk to the stream so you can show the work. Plain OpenAI clients that do not set sin_tools never see them.

data: {"object":"sin.tool_call","id":"t1","name":"pons_token","arguments":{"query":"SIN"}}
data: {"object":"sin.tool_result","id":"t1","name":"pons_token","ok":true,"summary":"…","sources":["…"]}
ToolWhat it does
web_searchBrave web search
fetch_urlReads one public page as text
calcArithmetic
nowDate and time in a timezone
wallet_balanceETH and token balances of an address on Robinhood Chain
transactionA transaction and its receipt
token_infoName, symbol, decimals, supply and a holder balance of a token
pons_tokenA coin on the Pons launchpad by symbol or address
dex_priceLive price, FDV and liquidity from DexScreener
staking_statsThe SIN staking totals
account_balanceYour own credit balance

If the rounds run out the reply finishes with finish_reason: "tool_rounds" and answers from what it found.

Examples

curl https://api.sin.run/v1/chat/completions \
  -H "Authorization: Bearer $SIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID","messages":[{"role":"user","content":"hi"}],"stream":true}'
from openai import OpenAI
client = OpenAI(base_url="https://api.sin.run/v1", api_key=SIN_KEY)
stream = client.chat.completions.create(model="MODEL_ID", messages=[{"role": "user", "content": "hi"}], stream=True)
for chunk in stream:
    if chunk.choices:
        print(chunk.choices[0].delta.content or "", end="")
import OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'https://api.sin.run/v1', apiKey: process.env.SIN_KEY });
const stream = await client.chat.completions.create({ model: 'MODEL_ID', messages: [{ role: 'user', content: 'hi' }], stream: true });
for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta.content ?? '');

Limits

WhatLimit
Chat completions60 per minute, burst 20, per wallet across all sessions and keys
Sign in endpoints10 per minute per wallet
Promo codes5 per minute
Request body4 MiB
Sessions20 per wallet, oldest evicted
API keys10 active per wallet
Minimum purchase5 USDG

Error Codes

CodeStatusMeaning
unauthorized401Missing, expired or revoked token
banned403The account is disabled
bad_wallet400The wallet is not a hex address
bad_nonce401The nonce expired or was already used
bad_signature401The signature does not match the wallet
bad_message400, 401The sign in message is malformed or names the wrong domain or chain
bad_body, invalid_json400The body is not valid JSON or has the wrong shape
bad_id400The id is not a UUID
too_many_keys409Ten active keys already exist
not_found404No such blob
version_conflict409The blob moved on the server since the version in the request
blob_too_large413A blob over 512 KiB
quota_blobs, quota_bytes507The blob count or the byte quota of the wallet is full
promo_invalid404Unknown, not started or expired code
promo_exhausted409The code has reached its global limit
promo_used409The wallet already used this code
promo_pending409A purchase bonus is already waiting
allowance_off404The staker allowance is not on
allowance_not_yet409This week's allowance was already claimed
free_exhausted429No free replies left for this session or for today
refused400The request crosses the one line. Not charged
invalid_request400Missing model or messages, or more than one choice
context_too_long400The conversation exceeds the model context
model_not_found404Unknown or disabled model
insufficient_balance402Not enough credits for this request
body_too_large413Over 4 MiB
rate_limited429Too many requests
client_closed499The client went away before the model answered. Not charged
upstream_unavailable503The model host is failing. Not charged
overloaded503At capacity. Not charged
upstream_no_credits503The upstream account is empty. Not charged
upstream_unreachable502The model host did not answer. Not charged
upstream_unconfigured503No attestation upstream is configured
attestation_unsupported502The enclave report came in a shape the API does not project
upstream_rejected400The model host rejected the request; its message is passed through
internal500Server error. The request id finds it in the logs

Contracts

Two contracts handle all the money on Robinhood Chain. CreditPurchase takes USDG and turns it into credits. SinStaking pays $SIN stakers a share of every purchase. Both are open source, verified on the explorer, and cannot be upgraded or paused.

Where Each Purchase Goes

Every purchase is split the moment it lands. Nothing sits inside the contract between transactions. The treasury share pays for inference.

ShareGoes to
10%SinStaking, paid out to $SIN stakers in USDG
10%The buyback wallet, which buys $SIN and burns it
80%The treasury wallet, which pays for inference

A worked example. Someone buys 100 USDG of credits:

USDG
To SinStaking, shared by everyone staked at that moment10
To the buyback wallet, spent on $SIN that is then burned10
To the treasury, which pays the model provider80
Credits the buyer receives (100 plus the 10% tier bonus)110

The landing page numbers come straight from the staking stats: annualised buybacks are the last 24 hours of purchases times the buyback share times 365, and the staking APR is the last 24 hours of purchases times the staking share times 365, divided by the dollar value of all staked $SIN. So if 1,000 USDG of credits sell in a day and 50,000 dollars of $SIN is staked, stakers receive 100 USDG that day, about 73% a year, and 100 USDG of $SIN is bought and burned.

With nothing staked the staking share goes to the treasury, as does rounding dust. The owner can change the split; every change emits an event and applies from the next purchase. If any leg fails the whole purchase reverts. The contract never holds a balance. Tokens sent to it by mistake can be returned by the owner.

Staking $SIN

Staked $SIN earns USDG. Each distribution is divided among everyone staked at that moment, pro rata. USDG enters with every credit purchase, and anyone can call distribute with their own USDG.

Rewards accrue as they come in and stay claimable after a withdrawal. Stakers who join after a distribution do not share in it.

$SIN is locked for 24 hours after the most recent stake, and adding to a stake relocks the whole balance. After the lock, withdraw any amount, or exit to withdraw everything and claim in one transaction.

The lock is the flash loan defence. A stake cannot leave in the transaction that created it, and a full day makes just in time staking with borrowed money uneconomic.

SinStaking has no owner, no pause, no upgrade and no admin function. The lock and the tokens are fixed at deployment.

Addresses

NetworkRobinhood Chain
Explorerrobinhoodchain.blockscout.com
USDG0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
$SINPublished at launch
SinStakingPublished at launch
CreditPurchasePublished at launch

Source is verified on the explorer.

For Developers

The interfaces below are stable. Accounts are credited from the Purchased event and nothing else.

CreditPurchase

FunctionWhat it does
purchase(amount)Pulls the amount of USDG from the caller and splits it. Needs a prior approval
purchaseWithPermit(amount, deadline, v, r, s)Same with a permit signed by the caller, no approval step. A used permit with an existing allowance still goes through
MIN_PURCHASE()5000000, which is 5 USDG in six decimal units
STAKING_BPS(), BUYBACK_BPS()The current split in basis points. Live values, not constants
usdg(), staking(), treasury(), buyback()The token and the three destinations

Purchased(buyer, amount, toStaking, toBuyback, toTreasury) is emitted once per purchase with the amounts actually moved, and the three shares sum to the amount. The buyer is the sender; there is no purchase on behalf of another address. SplitChanged(stakingBps, buybackBps) and AddressChanged(key, oldAddress, newAddress) announce every owner change.

Permit Domain

USDG does not implement ERC 5267, so the permit domain is fixed by hand. The spender is CreditPurchase and the nonce comes from nonces(owner) on USDG.

nameGlobal Dollar
version1
chainId4663
verifyingContract0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168

SinStaking

FunctionWhat it does
stake(amount)Stakes $SIN and resets the lock to now plus 24 hours
withdraw(amount)Returns staked $SIN. Reverts with StakeLocked before the unlock time
claim()Sends the accrued USDG to the wallet
exit()Withdraws the full stake and claims in one call
distribute(amount)Anyone. Pulls USDG from the caller and shares it among current stakers. Reverts with NoStakers when nothing is staked
skim()Anyone. Allocates USDG that was sent to the contract directly instead of through distribute
earned(address)USDG the address can claim right now
unlockTime(address)Earliest timestamp the address can withdraw
totalStaked()All $SIN in the contract

Events: Staked, Withdrawn, RewardPaid and RewardAdded(from, amount). The sum of RewardAdded from CreditPurchase equals the sum of toStaking across all purchases.

What Cannot Change

  • Neither contract can be upgraded or paused.
  • SinStaking has no owner and no admin functions.
  • The CreditPurchase owner can change the split and the three destinations; every change emits an event. The owner cannot pause the contract or touch any wallet or stake.
  • Ownership moves in two steps; the new owner has to accept.
  • Reentrancy guards on every state changing function, OpenZeppelin SafeERC20 on every transfer. Reward accounting is the Synthetix StakingRewards pattern, with the accumulator advancing per distribution instead of per second.

For Agents

The API is OpenAI compatible, so any agent framework that takes a base URL and a key works. Buy credits once from a wallet, create a key in the app under Settings, API keys, and point the client at SIN.

export OPENAI_BASE_URL="https://api.sin.run/v1"
export OPENAI_API_KEY="sin_…"

A SIN agent skill that gives an agent its own wallet, buys credits and signs in without a human is coming soon.

FAQ

Is anything it says financial advice?

No. SIN is software and it can be wrong. It tells you what the chain shows and what it thinks; the decision is yours.

Is it really uncensored?

It runs open weight models without a refusal layer and it does not lecture. It holds one line, described under Privacy: sexual content involving minors, sexual images of real people without consent, and working tools built to steal from people.

What do you keep about me?

Your wallet address, your credit ledger and your purchases. Never the text of what you ask or what it answers. See What The Service Keeps.

How do $SIN stakers earn?

Every credit purchase sends 10% of it to SinStaking, shared by everyone staked at that moment and paid in USDG. Another 10% buys $SIN and burns it.

Do I need a wallet to try it?

No. The ask box on the home page gives a few free replies a day. A wallet signs you in for every model and your own credits.

Where is the contract address?

On the SIN X account, posted first at launch, and on this site. Anything that does not match both is not us.