The receivables back office for Brazilian companies and freelancers who sell services abroad. Each stablecoin payment arrives tied to its invoice, valued in BRL at the Central Bank's official PTAX rat
翻譯準備中
金融TypeScript · JavaScriptGitHub @yasminqjs上線中營運者未確認
試用過了嗎? 點一下就好,不需要登入。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證The receivables back office for Brazilian companies and freelancers who sell services abroad. Each stablecoin payment arrives tied to its invoice, valued in BRL at the Central Bank's official PTAX rate, and backed by a receipt anyone can verify onchain.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Stablecoin receivables for Brazilian companies, with proof.
Brazilian software houses, agencies and freelancers increasingly sell services abroad and get paid in stablecoins. The money arrives in seconds, but a wallet only shows anonymous transfers: no client, no invoice, no value in reais. And since 2026 the paperwork is theirs: whoever converts those stablecoins to reais has to be told who paid, for what, and why.
Yvex Chain is the receivables back office for that. A company invoices its client, the client pays in stablecoins on Solana with the invoice's hash inside the payment, and every payment becomes a record the company's team, its accountant and its exchange can use: tied to its invoice, valued in BRL at the Central Bank's official PTAX rate, and backed by a receipt anyone can verify onchain.
Yvex Chain does not convert currency, custody funds or calculate taxes.
Built for Colosseum's Crypto World's Fair hackathon. Devnet only.
httpOnly cookie. No passwords.service, prize, grant, own_transfer, other) and name
the payer once. Later payments from that wallet arrive already classified.Rate: R$ 5,4321 · PTAX buy · 2026-10-07. A Saturday payment uses Friday's rate./verify/<id>, or pastes the receipt JSON at /verify, and sees
Verified or Mismatch. The page has a Tamper test: edit any value and verify again./pay/<id> by QR code (Solana Pay) or
from a browser wallet. The page flips to Paid within seconds and the payment arrives
already classified.Tax rules may require a different rate date. Yvex Chain stores the rate and the date used; your accountant decides the rule.
An account is always one wallet: the one that receives the payments. A company account is that same account with a name, a tax ID and a team. Nobody shares keys. Each person signs in with their own wallet and picks the company under "Switch account".
| Role | Who | Can |
|---|---|---|
| Owner | The company wallet itself | Everything, including what needs its signature: sealing receipts and cashing out. Manages the team |
| Finance | Colleagues | Import and classify payments, create invoices, change the BRL rule, prepare reports and payout quotes |
| Read-only | Accountants, auditors | See payments, invoices and reports and download them |
flowchart TB
ana["Company team<br/>dashboard"] --> app
client["Client<br/>/pay"] --> app
auditor["Accountant / exchange<br/>/verify"] --> app
subgraph app["Next.js app (pages + API routes)"]
services["Services: import, classify, PTAX,<br/>receipts, invoices, reports, payouts"]
end
services --> db[("Supabase (Postgres)<br/>a cache, not the source of truth")]
services --> rpc["Solana devnet RPC"]
services --> bcb["Central Bank PTAX API"]
auditor -. "verification reads the chain directly,<br/>in the browser" .-> rpc
subgraph solana["Solana devnet"]
spl["SPL Token transfers"]
memo["Memo program:<br/>seals and invoice hashes"]
ref["Solana Pay reference keys"]
end
rpc --- solana
Everything that matters for trust lives on Solana. The database stores what the user typed (category, contact names) and caches what the chain says; verification never reads it.
Code layout:
app/ pages and API routes (Next.js App Router)
components/ React components
lib/solana/ transaction parsing, import, memo, Solana Pay, sending
lib/receipts/ canonical JSON, hashing, verification
lib/fx/ PTAX lookup
lib/services/ the use cases; they receive their dependencies as an argument
lib/db/ the database interface, with Supabase and local-file implementations
lib/mock/ the mock Solana node, demo keys and demo data
scripts/ dev launcher, demo seeding, live checks
supabase/ database schema
test/ unit tests and real devnet fixtures
| Primitive | Use | Where |
|---|---|---|
| SPL Token | Stablecoin payments received and invoice payments | lib/solana/pay.ts |
| Reading history | getSignaturesForAddress on the wallet's token accounts, then getParsedTransactions. The amount received is the change between preTokenBalances and postTokenBalances; the payer is the wallet whose balance dropped the most | lib/solana/import.ts, lib/solana/parse.ts |
| Solana Pay | Transfer request URL and QR code for each invoice, built from the spec | lib/solana/pay.ts |
| Reference key | A random public key per invoice, attached to the transfer as a read-only account. getSignaturesForAddress(reference) finds the payment without any cooperation from the payer | lib/services/invoices.ts |
| Memo program | Two attestations: the invoice hash inside the payment, and the receipt seal signed by the recipient | lib/solana/memo.ts |
| Verification | The browser recomputes the hash and reads both transactions from an RPC node | lib/receipts/verify.ts |
client-acme key printed by npm run seed:demo -- --show-key./pay/<id> page on a computer and scan the QR code with Phantom.The format is called recebi ("I received" in Portuguese), the project's working name. The
code's identifiers and environment variables use it too.
A sealed receipt is a memo transaction signed by the recipient, whose memo is exactly:
recebi:v1:<receipt_hash>:<payment_tx>
receipt_hash: lowercase hex SHA-256 of the receipt's canonical JSON: UTF-8, object keys
sorted recursively, no whitespace, money as strings (never numbers).payment_tx: the signature of the payment the receipt is about.The receipt itself:
{
"amount": "1200.00",
"amount_brl": "6518.52",
"block_time": "2026-10-07T14:03:11Z",
"category": "service",
"counterparty": "Acme Labs",
"description": "Web app development, September",
"fx": { "date": "2026-10-07", "rate": "5.4321", "rule": "ptax_buy", "source": "BCB PTAX" },
"invoice_hash": null,
"issued_at": "2026-10-08T15:20:00Z",
"mint": "<mint address>",
"network": "solana:devnet",
"payer_wallet": "<payer wallet>",
"payment_tx": "<payment signature>",
"recipient_wallet": "<recipient wallet>",
"symbol": "USDC",
"type": "recebi.receipt",
"v": 1
}
Invoice payments carry recebi:inv:v1:<invoice_hash> in their memo, where invoice_hash is the
SHA-256 of the invoice's canonical JSON. For a company account that JSON includes issuer_name
and issuer_tax_id, so the payer's own transaction commits to who issued the invoice.
Given a receipt and the signature of its seal, any tool can check it with an RPC node:
recebi:v1:<hash>:<payment_tx> with that hash, and be signed by recipient_wallet.payment_tx). It must have succeeded and have credited
exactly amount of mint to token accounts owned by recipient_wallet.payer_wallet is set, its balance of mint must have decreased in that transaction.invoice_hash is set, the payment's memo must contain recebi:inv:v1:<invoice_hash>.What this proves: the payment happened onchain, and this receipt was sealed by the recipient
and not altered since. What it does not prove: who the payer is. counterparty is declared
by the recipient.
The Memo program already gives us what a receipt needs: a public, immutable record that is provably signed by the recipient's wallet, for a fraction of a cent. A program of our own would add attack surface and audit cost without giving users anything they do not already have. If a real need appears (for example, revocation or counter-signing by the payer), that is the moment to write one.
| Status | |
|---|---|
| Wallet sign-in, history import, PTAX conversion, sealing, public verification, Solana Pay invoices, CSV and PDF exports | Real, on devnet |
| Pix cash-out | Simulated. It sits behind a PayoutProvider interface; in production a partner authorised by the Central Bank executes it. No tokens move |
| Stablecoin in the demo | A devnet test token ("Test USD"), because Circle's devnet faucet limits how much USDC it gives. Devnet USDC works too |
| Identity | No KYC. The payer's name is whatever the recipient declares, and so are a company's name and tax ID. Team members are identified by their wallets only |
Yvex Chain also runs in a mock mode that needs no wallet extension, no database and no network. It replaces the three external systems, and nothing else:
lib/mock/) that accepts real signed
transactions, executes the Token, Associated Token Account and Memo programs, and answers
JSON-RPC in the same shape as a real node;On a serverless host there is no shared memory or disk between two requests, so there the mock world (ledger and database) travels in the visitor's own cookies, compressed. Each visitor gets a private sandbox, and a link to a receipt or invoice opens only in the browser that created it.
Import, hashing, sealing and verification run the same code as in live mode. Mock mode is for
development and rehearsal: every page shows a "Mock mode" banner, and its receipts say
"network": "recebi:mock", which the verifier refuses outside mock mode. A mock "Verified"
proves nothing to a third party.
Requirements: Node.js 20 or newer. A step-by-step guide in Portuguese, with a click-by-click demo script and troubleshooting, is in COMO-USAR.md.
npm install
cp .env.example .env.local
npm run dev:mock
Open http://localhost:3000, click Use demo wallet, sign in, and click Import from wallet. "Reset demo data" in the top banner starts over.
On Windows PowerShell, if npm is blocked by the script execution policy, use npm.cmd
instead (npm.cmd run dev:mock).
Create a free Supabase project. In its SQL Editor, run
supabase/schema.sql. If you set the project up before company
accounts existed, run the file again: it is idempotent and only adds the three new tables.
Fill in .env.local:
| Variable | Value |
|---|---|
NEXT_PUBLIC_RECEBI_MODE | live |
SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY | From Supabase > Project Settings > API. Server only |
SESSION_SECRET | 32+ random characters (the command is in .env.example) |
NEXT_PUBLIC_RPC_URL | A devnet RPC URL with a key, e.g. Helius. The public RPC works but rate-limits hard |
NEXT_PUBLIC_STABLE_MINTS | Accepted tokens. npm run seed:demo prints the value to use |
Create the demo data on devnet:
npm run seed:demo
It creates the wallets ana, ana-savings, client-acme and bounty-platform in
.demo-keys/ (git-ignored), a test token, and four payments to ana. If the devnet airdrop
fails, it prints an address to fund at https://faucet.solana.com.
Install Phantom, turn on Settings > Developer Settings > Testnet Mode
with Solana Devnet, and import ana's key (npm run seed:demo -- --show-key).
npm run dev:live, connect Phantom, sign in, import.
Row Level Security is enabled on every table with no policies: the browser can read nothing
from the database, only the server can, with the service_role key.
| Command | What it does |
|---|---|
npm test | Unit tests: transfer extraction on real devnet transactions, canonical JSON and hashing, PTAX rules, company roles and the audit trail, and the whole product flow on the mock node |
npm run smoke:mock | Walks every API route over HTTP against a running mock-mode server, then signs in as a Finance member, a Read-only member and an outsider and checks what each one is refused |
npm run e2e:live | The whole live flow through the API of a running live-mode server, after seed:demo: imports real devnet payments into Supabase, pays an invoice and seals a receipt on devnet, verifies it, then deletes what it wrote |
npm run smoke:live | Walks the API of a running live-mode server with a throwaway wallet: signed-message login, Supabase persistence, real PTAX, invoices, CSV and PDF. Deletes what it created |
npm run check:supabase | Tests a real Supabase project through the app's own database code: connection, tables and every operation, using throwaway rows that it deletes afterwards |
npm run check:live | Reads the real PTAX API and scans a real devnet wallet. After seed:demo, it also seals a receipt and pays an invoice on devnet and verifies both |
npm run lint, npm run typecheck, npm run build | ESLint, TypeScript, production build |
The same repository deploys two ways. For both at once, create two Vercel projects from it.
Sandbox, no configuration. Import the repository and click Deploy, with no environment
variables. On Vercel, an unset NEXT_PUBLIC_RECEBI_MODE means mock mode: anyone can open the
site, click Use demo wallet and walk through the product without a wallet extension or a
database. The "Mock mode" banner is always on screen and nothing there is onchain.
The product, on devnet. Set these before deploying:
| Variable | Value |
|---|---|
NEXT_PUBLIC_RECEBI_MODE | live |
SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY | From Supabase > Project Settings > API |
SESSION_SECRET | 32+ random characters |
NEXT_PUBLIC_STABLE_MINTS | The JSON from .env.local, without the surrounding single quotes |
NEXT_PUBLIC_RPC_URL | A devnet RPC URL with a key. Optional but recommended |
NEXT_PUBLIC_* values are compiled into the site, so redeploy after changing one.
PayoutProvider interface, receiving
the purpose and counterparty of each conversion from the invoice instead of from a formNext.js, React, Tailwind CSS, @solana/web3.js, @solana/spl-token, Solana Wallet Adapter,
@supabase/supabase-js, jose, tweetnacl, bs58, decimal.js, qrcode,
@react-pdf/renderer, Vitest, tsx, ESLint and TypeScript, each under its own license.
All code in this repository was written during the hackathon, between 14 September and 12 October 2026. Technical decisions and their reasons are logged in DECISIONS.md.