Camada de contratos inteligentes de um prediction market brasileiro — mercados de previsão com liquidação em USDC na Polygon.
Produto no ar: pmarket.ai · Contratos em Polygon mainnet · 45 testes Foundry passando
Duas coisas verificáveis, cada uma por um caminho independente deste repositório: o produto responde em pmarket.ai, e os contratos abaixo respondem na Polygon. Este repositório é a camada on-chain — os contratos, os testes e a documentação de arquitetura do sistema.
Os contratos estão deployados e em operação em Polygon mainnet. Os endereços abaixo respondem hoje, e qualquer pessoa pode conferir sem depender deste repositório:
| Contrato | Endereço |
|---|---|
| ConditionalTokens | 0x77D04D23…78451 |
| CTFExchange | 0x2640Ca91…A3004 |
| UmaCtfAdapter | 0x467eCaeb…9E79d |
| FeeModule | 0x2F43Be22…5f3C6 |
cast code 0x2640Ca9114737f9384B4bc16928f066c92EA3004 --rpc-url https://polygon-rpc.comTransações de criação, blocos, datas e método completo de verificação em DEPLOYMENTS.md.
pmarket.ai está no ar: mercados de previsão sobre política, economia, esportes e cripto, com curadoria voltada ao público brasileiro. Depósito por PIX, precificação por LMSR, indicadores econômicos integrados a fontes oficiais.
Os mercados em produção hoje liquidam off-chain. Os contratos deste repositório são a camada seguinte — a migração para liquidação on-chain, com o colateral em USDC e a resolução por oráculo otimista em vez de por decisão da plataforma. Estão deployados e sob governança, ainda não recebendo o volume de produção.
Ser explícito sobre isso importa: o que está provado on-chain é o deploy, não o volume. A linha do tempo mostra como cada peça chegou aqui.
Um mercado de previsão precisa resolver três problemas: representar apostas como ativos negociáveis, casar compradores com vendedores, e decidir quem ganhou. Cada contrato resolve um.
Cada mercado emite dois tokens ERC-1155: YES e NO. Depositar 1 USDC cunha 1 YES
e 1 NO (splitPosition); devolver o par queima ambos e recupera 1 USDC
(mergePosition). Quando o mercado resolve, o token vencedor vale 1 USDC e o
perdedor vale zero.
Disso vem o invariante que sustenta todo o sistema:
1 YES + 1 NO = 1 USDC, sempre.
Enquanto ele valer, o contrato é solvente por construção — nunca há mais direito de resgate do que colateral depositado. É a primeira coisa que os testes verificam, por fuzzing.
Ordens são assinadas fora da cadeia via EIP-712 e casadas por um motor off-chain. Só a liquidação toca a blockchain. Ordem cancelada não custa gás.
O contrato reconhece três tipos de casamento:
- Direct — quem quer comprar YES encontra quem quer vender YES.
- Minting — um quer YES, outro quer NO. Nenhum dos dois precisa existir ainda: o contrato cunha o par a partir de USDC.
- Merging — um vende YES, outro vende NO. O par é queimado de volta em USDC.
Os dois últimos são o que dá liquidez a um mercado novo, sem book. Não é preciso haver contraparte para o mesmo lado — basta haver contraparte para o lado oposto.
Cancelamento usa nonce incremental: incrementNonce invalida de uma vez todas
as ordens assinadas anteriormente, fechando a janela de replay.
Quem decide o resultado é o UMA OptimisticOracleV2, não a plataforma. Um proponente posta caução afirmando o resultado. Abre-se janela de contestação. Sem contestação, o resultado é aceito; com contestação, escala para votação dos detentores de token UMA, e quem propôs errado perde a caução.
O adapter está sob Safe multisig 2-de-3, verificável on-chain:
cast call 0x467eCaebc56f352057b20cfb5eA5957cb629E79d "admin()(address)" \
--rpc-url https://polygon-rpc.comTaxa aplicada sobre o lado vencedor na liquidação, com divisão programática entre operação e fundo de reserva. O teste de fuzz garante que a taxa calculada nunca excede o valor negociado.
Para perguntas com mais de duas respostas (eleições, por exemplo), onde os resultados são mutuamente exclusivos e as probabilidades precisam somar 1.
45 testes Foundry, todos passando, incluindo fuzzing sobre as duas propriedades que não podem quebrar:
| Suíte | Testes |
|---|---|
ConditionalTokens.t.sol |
18 |
UmaCtfAdapter.t.sol |
10 |
CTFExchange.t.sol |
9 |
NegRiskAdapter.t.sol |
8 |
forge test -vv
forge test --match-test testFuzz -vvv # só os invariantesOs dois testes de propriedade:
testFuzz_invariant_yesNoPlusMeansOneUSDC— o invariante do colateral, sob entradas aleatórias.testFuzz_calculateFee_neverExceedsAmount— taxa nunca maior que o principal.
Requer Foundry.
git clone --recursive https://github.com/lfpossebon/pmarket-contracts.git
cd pmarket-contracts
forge build
forge testAs dependências são submódulos com commit fixado, então o build é
reprodutível. A do OpenZeppelin está travada em v5.0.2 de propósito — é a
usada no deploy de mainnet, e os caminhos de import mudaram entre as majors do
OZ. Se você já clonou sem --recursive:
git submodule update --init --recursivePara deploy, copie .env.example para .env e preencha. Os scripts em
script/ leem todas as chaves de variáveis de ambiente — nenhuma credencial
está embutida no código.
src/
ConditionalTokens.sol # ERC-1155 YES/NO, split/merge/redeem
CTFExchange.sol # casamento EIP-712, três tipos de match
UmaCtfAdapter.sol # resolução via UMA OOv2
NegRiskAdapter.sol # mercados de múltiplos resultados
FeeModule.sol # taxas e divisão
interfaces/ # IConditionalTokens, IOptimisticOracleV2
libraries/ # OrderStructs (EIP-712), CalculatorHelper
test/ # 45 testes Foundry
script/ # deploy, criação de mercado, E2E
docs/ # documentação de arquitetura do sistema
A pasta docs/ traz a arquitetura da plataforma inteira, não só dos contratos:
exchange, ciclo de ordens, oráculo, carteiras, modelos de mercado e o
dicionário de dados.
Esta implementação deriva de trabalho open-source de terceiros, e é importante ser explícito sobre o que é original e o que não é:
- ConditionalTokens deriva do Gnosis Conditional Tokens Framework — LGPL-3.0.
- CTFExchange, UmaCtfAdapter e NegRiskAdapter derivam dos contratos da Polymarket — MIT.
- FeeModule e as modificações dos demais são originais deste projeto.
A arquitetura de referência é a da Polymarket. O trabalho aqui foi adaptá-la ao contexto brasileiro, não inventá-la.
- Sem auditoria externa. Os contratos de origem são auditados; as modificações desta implementação não passaram por auditoria independente.
- Mainnet tem deploy, não volume. O ciclo completo de mercado — criação, negociação, resolução e resgate — foi exercitado em testnet Amoy. Os mercados em produção operam hoje no modelo off-chain da plataforma.
Linha do tempo da construção e decisões de arquitetura em STATUS.md.
MIT, com exceção de src/ConditionalTokens.sol, que permanece LGPL-3.0 por
derivação. Ver LICENSE.