← LiquidityWise

O cartão de pool e o seu JSON

Rede principal do Ethereum

Duas coisas neste site foram feitas para outros sites: um cartão com a faixa sugerida de um pool, que qualquer página pode colocar num frame, e os mesmos números em JSON, que o código de qualquer site pode ler. Nenhuma das duas exige chave ou conta. As duas são descritas aqui como o código as serve, e testes prendem esta página a esse código: cada parâmetro listado é um com que o endereço é lido, e cada campo, um contra o qual a resposta é conferida antes de ser enviada.

O cartão

/embed/pool é uma página pequena escrita à mão, não pelo framework do site. Ela mostra o par com seu protocolo, taxa e rede; a faixa sugerida, com o horizonte e a largura para os quais foi traçada; o preço atual; uma linha dizendo que não é recomendação financeira; e um link de volta para a página do pool aqui, que abre numa nova aba. A faixa é sempre traçada com os padrões do site, 30 dias a 1σ, quaisquer que sejam os ajustes de cada um. Uma nota é acrescentada enquanto o preço atual está fora da faixa, e outra para um pool v4 cujo hook pode mudar quanto custa um swap.

Ela não tem script nem formulário e não carrega nada: sua própria Content-Security-Policy não permite nada além do seu estilo inline. As cores são as do site, claras ou escuras conforme o sistema do leitor está configurado. Ela não enxerga a página ao redor, e não há parâmetro para escolher.

Dê a ela um frame de 360 pixels de largura e 220 de altura, ou 260 de altura para um pool cujo hook pode mudar quanto custa um swap, cujo cartão traz essa linha a mais. É exatamente o que a página de cada pool oferece em "Incorporar este pool", já com a altura certa para aquele pool, e o frame oferecido nunca fica mais largo que a coluna onde é colado.

É a única página deste site que outro site pode colocar num frame. Todo outro endereço, este incluído, é servido com X-Frame-Options: DENY e frame-ancestors 'none'; o cartão é servido sem o primeiro e com frame-ancestors *.

O frame para o pool USDC / WETH de 0,3% na Ethereum, como a página desse pool o oferece:
<iframe src="https://liquiditywise.com/embed/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8" title="USDC / WETH on LiquidityWise" width="360" height="220" style="border:0;max-width:100%" loading="lazy"></iframe>
Com o que um cartão com números é servido, além dos cabeçalhos que toda página leva:
Content-Type: text/html; charset=utf-8
Cache-Control: public, max-age=300, s-maxage=300
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors *

Parâmetros

O cartão e o JSON recebem os mesmos parâmetros e nomeiam um pool como as próprias páginas do site: um pool v3 pelo seu address, como em /pool, e um pool v4 pelo seu id, como em /v4. É preciso exatamente um dos dois, e qual deles chegou diz qual é o protocolo. Um pool ou uma rede nomeados duas vezes são recusados, em vez de um dos valores ser escolhido, e qualquer parâmetro que não esteja listado aqui é ignorado.

O idioma vem só do endereço, nunca do navegador do leitor nem de um cookie: quem escolhe é o site que coloca o cartão, e o mesmo endereço é a mesma resposta para todos que o carregam, o que permite a um cache guardá-la. O cartão é escrito nos 10 idiomas do site, e em árabe ele corre da direita para a esquerda.

address
Aceita O endereço do contrato de um pool v3: 0x e 40 dígitos hexadecimais, em maiúsculas ou minúsculas.
Senão Qualquer outra coisa não nomeia nenhum pool, nem um pool v3 numa rede em que o v3 não é lido (Unichain): 400, e nada é lido.
id
Aceita O id de um pool v4, o hash da sua chave: 0x e 64 dígitos hexadecimais, em maiúsculas ou minúsculas.
Senão Qualquer outra coisa não nomeia nenhum pool: 400, e nada é lido. Nem um id ao lado de um address.
chain
Aceita O identificador curto de uma rede, da tabela abaixo. Sem ele, a rede é a Ethereum.
Senão Um identificador que o site não lê é recusado, nunca lido como Ethereum: 400.
lang
Aceita Um código de idioma da lista abaixo, para as palavras do cartão e o disclaimer do JSON; os números são os mesmos em todos os idiomas. Sem ele, inglês.
Senão Qualquer outro valor, ou lang duas vezes, dá inglês. Nunca é recusado.

As redes, pelo identificador que chain aceita:

chainRedepools v3pools v4
ethereumEthereumlidoslidos
baseBaselidoslidos
arbitrumArbitrum Onelidoslidos
unichainUnichainnão lidoslidos
optimismOP Mainnetlidoslidos
polygonPolygonlidoslidos

Os idiomas, pelo código que lang aceita:

  • enEnglish
  • trTürkçe
  • deDeutsch
  • esEspañol
  • arالعربية
  • hiहिन्दी
  • zh中文
  • ruРусский
  • ptPortuguês
  • zh-Hant繁體中文

O JSON

GET /api/embed/pool, com os mesmos parâmetros, responde com os números do cartão em JSON, para um site que prefere desenhá-los ele mesmo: a mesma leitura, guardada pelo mesmo tempo. Cada número é um número JSON, e cada preço vem no sentido em que a página do pool o escreve: quantos price.quote vale um price.base.

Antes de ser enviada, uma resposta é conferida contra o seu esquema, e uma que não o cumpre não é enviada: o pool é respondido como ilegível. Por isso uma resposta com status 200 sempre tem exatamente os campos abaixo, nem mais nem menos.

O script de qualquer site pode lê-la. Toda resposta, erros e recusas incluídos, é servida com Access-Control-Allow-Origin: *. Ela não grava cookie nem exige credencial. Um GET simples não precisa de preflight, e nenhum é respondido, então envie-o sem cabeçalhos próprios.

Com o que uma resposta com números é servida, além dos cabeçalhos que toda página leva:
Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=300, s-maxage=300
Access-Control-Allow-Origin: *

Cada campo de uma resposta 200, com seu tipo JSON:

protocol"v3" | "v4"
v3 para um pool nomeado por address, v4 para um nomeado por id.
chain.idinteger > 0
O chain id da rede.
chain.slugstring
O identificador curto da rede, como chain o aceita.
chain.namestring
O nome da rede.
poolstring
O endereço (v3) ou o id (v4) do pool, em minúsculas.
pair.token0string
O símbolo do primeiro token do pool, como o contrato dele o declara: é texto de um contrato que qualquer um pode implantar, então escape-o antes de colocá-lo numa página.
pair.token1string
O símbolo do segundo token, do mesmo jeito.
lpFeePpminteger ≥ 0 | null
A taxa que o pool paga aos provedores de liquidez num swap, em milionésimos: 3000 é 0,3%. null quando o hook de um pool v4 define a taxa swap a swap.
price.basestring
O token para uma unidade do qual cada preço é dado.
price.quotestring
O token em que cada preço é contado.
price.currentnumber > 0
O preço do pool quando foi lido.
range.lowernumber > 0
A borda inferior da faixa sugerida, como preço.
range.uppernumber > 0
A borda superior.
range.currentInRangeboolean
Se o preço atual está dentro da faixa. Quando não está, o cartão acrescenta uma nota.
range.lowerTruncatedboolean
true quando a grade de ticks do pool não alcança a borda inferior da banda, de modo que a faixa para antes dela.
range.upperTruncatedboolean
O mesmo, para a borda superior.
parameters.horizonDaysinteger > 0
O horizonte para o qual a faixa foi traçada, em dias: sempre o padrão, 30.
parameters.standardDeviationMultipliernumber > 0
A largura para a qual foi traçada, em desvios-padrão: sempre o padrão, 1σ.
hookMayAlterSwapsboolean
true para um pool v4 cujo hook pode mudar quanto custa um swap, lido do endereço do hook; false para o v3, para um pool v4 sem hook e para um hook cujas permissões não tocam nos swaps. Onde é true, o cartão traz uma nota.
analysedAtstring (ISO 8601)
Quando o preço foi lido, em UTC, ao milissegundo — não quando esta resposta foi enviada, o que pode ser minutos depois.
poolUrlstring
A página do pool neste site, com o mesmo horizonte e a mesma largura.
disclaimerstring
O que os números são e o que não são, no idioma que lang indica.
Uma resposta para o pool dos exemplos. Os números são um exemplo: calculados pelo próprio código do site a partir de um mês de preços de amostra, não lidos do pool, e um teste confere que o código ainda responde exatamente isto para esse mês:
{
  "protocol": "v3",
  "chain": {
    "id": 1,
    "slug": "ethereum",
    "name": "Ethereum"
  },
  "pool": "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
  "pair": {
    "token0": "USDC",
    "token1": "WETH"
  },
  "lpFeePpm": 3000,
  "price": {
    "base": "WETH",
    "quote": "USDC",
    "current": 3000
  },
  "range": {
    "lower": 2824.2704157479307,
    "upper": 3184.336896959513,
    "currentInRange": true,
    "lowerTruncated": false,
    "upperTruncated": false
  },
  "parameters": {
    "horizonDays": 30,
    "standardDeviationMultiplier": 1
  },
  "hookMayAlterSwaps": false,
  "analysedAt": "2026-08-21T09:15:00.000Z",
  "poolUrl": "https://liquiditywise.com/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8&days=30&sigma=1",
  "disclaimer": "For information only, not financial advice. The range is worked out from how far this pool's price moved over the last 30 days: it is not a forecast and not a recommendation."
}

Códigos de status e erros

O cartão e o JSON respondem com os mesmos códigos de status. Nenhum dos dois responde nunca com a página de erro do próprio site nem com o que deu errado por dentro: o cartão mostra uma frase e seu link de volta, e o JSON responde com um error que um script pode testar e com disclaimer — exceto uma recusa, que traz a espera no lugar dele.

A resposta do JSON, com por quanto tempo pode ser guardada:

200

Os números: um cartão com eles, ou o JSON acima.

Cache-Control: public, max-age=300, s-maxage=300

400 not-a-pool

O endereço não nomeia nenhum pool que o site lê: nem address nem id, ou os dois, ou um malformado ou repetido, uma rede que o site não lê, ou um pool v3 onde só o v4 é lido (Unichain). Nada foi lido, e perguntar de novo não muda a resposta.

Cache-Control: public, max-age=3600, s-maxage=3600

{
  "error": "not-a-pool",
  "disclaimer": "For information only, not financial advice. The range is worked out from how far this pool's price moved over the last 30 days: it is not a forecast and not a recommendation."
}

503 unreadable

Um pool bem nomeado que não pôde ser lido agora: uma fonte não respondeu a tempo, ou não existe esse pool nessa rede. poolUrl continua levando à página dele. Perguntar de novo em um minuto pode encontrá-lo.

Cache-Control: public, max-age=60, s-maxage=60

{
  "error": "unreadable",
  "poolUrl": "https://liquiditywise.com/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8&days=30&sigma=1",
  "disclaimer": "For information only, not financial advice. The range is worked out from how far this pool's price moved over the last 30 days: it is not a forecast and not a recommendation."
}

429 rate-limited

Requisições demais de um mesmo cliente: veja o limite abaixo. Retry-After e retryAfterSeconds dizem os dois quantos segundos esperar. O cartão responde com uma página curta que diz isso, no próprio idioma.

Cache-Control: no-store
Retry-After: 42

{
  "error": "rate-limited",
  "retryAfterSeconds": 42
}

Cache e limite de requisições

Toda resposta diz por quanto tempo pode ser guardada, por um navegador e por um cache compartilhado: 300 segundos para os números; 60 segundos para um pool que não pôde ser lido, para que um que volta não apareça como ilegível por muito tempo; 3.600 segundos para um endereço que não nomeia nenhum pool, o que nenhum momento posterior vai mudar. Uma recusa nunca é guardada. Cada resposta é a mesma para todos que perguntam com o mesmo endereço, e é isso que torna seguro guardá-la.

Por trás disso, o servidor guarda os números de cada pool por 300 segundos a partir do momento em que os leu, para o cartão e para o JSON, em todos os idiomas: cada cartão de um pool, em cada página onde estiver, é uma única leitura até ela expirar. Um pool que não pôde ser lido não é guardado, e é consultado de novo na próxima requisição. Por isso o preço numa resposta pode ser mais antigo que a resposta: analysedAt diz quando ele foi lido.

Uma requisição que nomeia um pool bem formado lê esse pool, então conta contra a mesma cota das páginas de pool do próprio site: 10 requisições por cliente numa janela de 60 segundos que começa na primeira requisição do cliente, sendo o cliente o endereço IP de onde a requisição chega ao site. Passado o limite, a resposta é 429, com Retry-After. Conta toda requisição assim que chega ao site, inclusive a respondida com o que o servidor guardou; uma requisição que não nomeia nenhum pool não conta.

Num navegador, um cartão ou um fetch conta contra o leitor que o carrega, não contra o site onde está — então, numa página com mais de 10 cartões, os que passam disso são recusados para cada leitor. Num servidor, todas as suas requisições dividem a única cota desse servidor: guarde cada resposta pelos 300 segundos que ela permite, o que custa pouco, pois na maior parte desse tempo o servidor responderia com a leitura que você já tem. O limite existe para manter baixo o custo de ler pools, e nada aqui promete que ele vá ficar como está.

Exemplos

Os números, de um terminal:
curl -s "https://liquiditywise.com/api/embed/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8"
Do script de uma página, ou de um servidor, com cada resposta tratada:
const response = await fetch(
  "https://liquiditywise.com/api/embed/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8",
);
const body = await response.json();

if (response.ok) {
  // The range, in body.price.quote per one body.price.base
  console.log(body.range.lower, body.range.upper, body.price.quote, body.price.base);
  console.log(body.disclaimer);
} else if (response.status === 429) {
  console.log(`Asked too often: wait ${body.retryAfterSeconds} seconds`);
} else {
  console.log(body.error); // "not-a-pool" or "unreadable"
}
O cartão, numa página:
<iframe src="https://liquiditywise.com/embed/pool?address=0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8" title="USDC / WETH on LiquidityWise" width="360" height="220" style="border:0;max-width:100%" loading="lazy"></iframe>

Um clique seleciona um bloco inteiro, pronto para copiar.

Condições, em palavras simples

Os números são medições, não recomendações. A faixa é calculada a partir de quanto o preço do pool se moveu nos últimos 30 dias: não é uma previsão nem uma recomendação de investimento, e toda resposta diz isso por si mesma, o cartão na própria face e o JSON em disclaimer. Como cada número é obtido, e o que cada um deixa de fora, está em "Como funciona".

O cartão traz seu próprio link de volta, "Analisado por LiquidityWise". O JSON não tem campo de atribuição, e nada no código pede uma; o que ele traz é poolUrl, a página do pool aqui, e disclaimer. Mostrados ao lado dos números, esses dois dizem ao leitor de onde eles vieram e o que são.

Não há número de versão nem chave, nem promessa de que a forma da resposta vá ficar como está ou de que o site esteja no ar num dado momento. O que vale é mais estreito: uma resposta é conferida contra o seu esquema antes de ser enviada, e testes prendem esta página a esse esquema e ao código que lê o endereço, então ela descreve o que é servido agora. Uma mudança em qualquer um dos dois aparece no histórico público do código.

O código é aberto, sob a licença MIT.

Como funcionaO código, no GitHubA licença MIT