Torne-se um árbitro OpenFiat
Os árbitros decidem disputas com o próprio OPEN em risco, não a reputação. Isto percorre o bond, encontrar um caso e emitir o voto de comprometer-então-revelar que de fato paga.
O que você precisa
- Uma carteira Solana com pelo menos 10.000 OPEN — o min_stake_arbitrator da configuração de staking implantada, que a governança pode mudar — faça bond dela pela página Stake do openfiat-app, ou pelas instruções abaixo se você estiver construindo seu próprio cliente
- Uma carteira que consiga assinar mensagens, o que toda carteira Solana relevante faz — a página Arbitrate do openfiat-app roda o caso inteiro no navegador, e os SDKs estão aí se você preferir programar
- Acesso de rede ao endpoint JSON-RPC de pelo menos um nó OpenFiat, o seu ou um público
Trabalhando um caso
Trabalhe nestes passos em ordem, um caso de cada vez. Cada passo traz o comando ou código de que precisa.
- 01
Faça bond de OPEN para desbloquear o pool de arbitragem
Os árbitros precisam fazer stake antes de poderem ver as provas de um único caso — é o que torna subornar um deles inútil (você não sabe qual caso mirar) e dá à rede algo para dar slash se você votar contra o consenso revelado. A configuração de devnet implantada define o mínimo de árbitro em 10.000 OPEN, dez vezes os 1.000 que todo outro papel aporta.
stake.tsimport { PublicKey, Transaction } from "@solana/web3.js"; import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; import { onchain } from "@openfiat/sdk"; // Role.Arbitrator = 1 — same instruction pair openfiat-app's Stake page // submits when you connect a wallet and bond there directly. const from = getAssociatedTokenAddressSync(mint, owner, false, TOKEN_2022_PROGRAM_ID); const amount = 10_000n * 1_000_000_000n; // OPEN has 9 decimals (OFS-4100 §1) const instructions = [ onchain.staking.initializeStakeAccountIx(owner, onchain.Role.Arbitrator), onchain.staking.stakeIx(owner, mint, onchain.Role.Arbitrator, from, amount), ]; const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); const tx = new Transaction({ feePayer: owner, blockhash, lastValidBlockHeight }).add(...instructions); const { signature } = await wallet.signAndSendTransaction(tx); - 02
Encontre um caso aberto
Os árbitros escolhem quais disputas trabalhar — ninguém lhe designa uma. Consulte qualquer nó por casos que ainda não atingiram a contagem exigida de árbitros.
shell# Any node will do — arbitrators pick a case, nobody assigns one. curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"getDisputes","params":{}}' # Filter the result for status: "Open" and arbitrators.length < required_arbitrators - 03
Ingresse antes de poder ver as provas
Ingressar é o que desbloqueia um caso para você: os próprios envios do comprador e do vendedor, as confirmações de pagamento e o log de mensagens da negociação deles. Uma vez que um caso tem seu complemento completo de árbitros, ele trava e a fase de compromisso começa.
join.rsuse openfiat_disputes::events::ArbitratorJoin; use openfiat_sdk::wallet::Keypair; use openfiat_sdk::{Client, ClientConfig}; // Joining is what unlocks the case's evidence for you — buyer/seller // submissions and the trade's own message log become visible once you're // in `dispute.arbitrators`. let client = Client::new(ClientConfig { endpoint: node_url, ..Default::default() }); client .send_arbitrator_join( ArbitratorJoin { dispute_id: dispute.id.clone(), arbitrator: peer_id(&keypair), arbitrator_public_key: keypair.public_key(), timestamp: Timestamp::now(), }, &keypair, ) .await?; - 04
Comprometa seu voto — duas vezes
Dois votos de comprometer-revelar rodam lado a lado: um off-chain que registra na própria trilha de auditoria e reputação do caso, e um on-chain contra a conta DisputeCase do openfiat-escrow que de fato decide o resultado ponderado por stake. Use a mesma decisão e o mesmo salt para ambos — mas o enum próprio de cada lado, não o mesmo número: Invalid off-chain é 2, InvalidDispute on-chain é 3, porque MutualSettlement fica em 2 on-chain. Faça o hash do byte errado e você se compromete a algo que nunca poderá revelar, que é exatamente o que faz um árbitro levar slash.
commit.rs + commit.ts// Off-chain (marketplace record + reputation) — Rust SDK let secret: [u8; 32] = rand::random(); let commitment = openfiat_disputes::commitment::compute(vote, &secret); // sha256(vote || secret) client.send_vote_commit(VoteCommit { dispute_id, arbitrator: peer_id(&keypair), commitment, timestamp: Timestamp::now() }, &keypair).await?; // keep `secret` — you need it again at reveal, and it must never be guessable before then // On-chain (the stake-weighted vote that actually pays out) — TypeScript SDK import { onchain } from "@openfiat/sdk"; import { createHash } from "node:crypto"; // onchain.DisputeOutcome, NOT the off-chain Vote byte: the two enums // diverge from Invalid onwards (see this file's header). Hash the wrong // one and the commitment can never be opened. const commitment = createHash("sha256").update(Buffer.from([onchain.DisputeOutcome.BuyerWins])).update(salt).digest(); const ix = onchain.escrow.commitDisputeVoteIx(arbitrator, reservationId, commitment); - 05
Revele assim que a janela abrir
Revele seu resultado e salt em ambos os lugares assim que a janela de compromisso fechar. On-chain, é também aqui que seu voto ganha seu peso real: a instrução de reveal lê sua conta de stake do papel Arbitrator diretamente, então uma carteira sem stake de árbitro simplesmente não consegue fornecer um válido.
reveal.rs + reveal.ts// Off-chain — Rust SDK client.send_vote_reveal(VoteReveal { dispute_id, arbitrator: peer_id(&keypair), vote, secret, timestamp: Timestamp::now() }, &keypair).await?; // On-chain — reads your Arbitrator StakeAccount directly (no CPI) to weight this vote import { onchain } from "@openfiat/sdk"; const [arbitratorStake] = onchain.staking.stakeAccountPda(arbitrator, onchain.Role.Arbitrator); const ix = onchain.escrow.revealDisputeVoteIx( arbitrator, reservationId, onchain.DisputeOutcome.BuyerWins, salt, arbitratorStake, ); - 06
O resultado se executa sozinho
Uma vez que todo árbitro revelou, ou a janela de reveal fecha, qualquer um — você, o comprador, o vendedor ou um bot não relacionado — pode chamar execute_dispute_outcome. Ele só conta os votos já registrados on-chain. O design então paga à maioria uma parcela das taxas do caso e retira parte do stake de quem revelou contra ela — mas nem a recompensa nem a penalidade estão implementadas, então hoje a contagem move os fundos dos traders e nada mais.
resolve.tsimport { onchain } from "@openfiat/sdk"; // Permissionless — it only tallies votes the reveal step above already // recorded on-chain, so anyone (the buyer, the seller, either arbitrator, // or an unrelated crank bot) can call it once the reveal window closes. const ix = onchain.escrow.executeDisputeOutcomeIx(mint, seller, reservationId, destinations);