WEB3DEV

Cover image for Como Usar o Pyth para Feeds de Preços na Solana
Adriano P. Araujo
Adriano P. Araujo

Posted on

Como Usar o Pyth para Feeds de Preços na Solana

Visão Geral

O Pyth é um Oráculo que traz dados de preços em tempo real para a cadeia de blocos de forma simples e fácil de usar. O Pyth trabalha diretamente com editores de dados de primeira parte para trazer dados de preços de ativos financeiros para a cadeia de blocos. O Pyth agrega dados para criptomoedas, ações, ativos forex e commodities. Ao colaborar com muitos provedores de dados, o Pyth pode oferecer alta integridade e segurança de dados. Neste guia, aprenderemos como obter dados de preços do Pyth em seus programas da Solana.

O Que Você Fará

Neste guia, você aprenderá a usar o SDK do Pyth para integrar dados de preços em tempo real ao seu programa. Você irá:

  1. Aprender o básico do protocolo Pyth

  2. Construir um programa simples da Solana que solicita e registra um feed de preços do Pyth

  3. Testar o programa na devnet da Solana

O Que Você Precisará

Dependências Usadas Neste Guia

Neste guia, estaremos usando o Solana Playground. Nossos testes foram realizados com as seguintes dependências (30 de maio de 2023):

Dependência Versão
anchor-lang 0.27.0 
solana-program 1.14.1
pyth-sdk-solana 0.7.0

Como Funciona o Pyth?

O Pyth é um oráculo de dados que traz feeds de preços para várias classes de ativos para a cadeia de blocos Solana. "As atualizações de preços do Pyth são criadas no Pythnet e transmitidas off-chain via Wormhole Network, um protocolo de mensagens entre cadeias. Essas atualizações são assinadas para que o programa Pyth on-chain possa verificar sua autenticidade." *(Fonte: Documentação do Pyth) O Pythnet é um cluster privado alimentado pela cadeia de blocos Solana. Como o Pyth usa o Wormhole, ele pode trazer dados para várias cadeias de blocos.

Para a Solana, os dados de ativos e preços são armazenados e transmitidos on-chain por meio de contas Solana. Três tipos principais de contas existem no Pyth:

  1. Contas de Produto (Metadados): Essas contas armazenam informações sobre o ativo (por exemplo, símbolo, tipo de ativo, descrição).

  2. Contas de Preço: Essas contas armazenam os dados de preço do ativo, um intervalo de confiança para o preço e o momento da última atualização.

  3. Contas de Mapeamento: Essas contas mapeiam a conta do produto à conta de preço.

O exercício de hoje será focado principalmente nas Contas de Preço. Para mais informações sobre outros tipos de contas, confira a Documentação do Pyth.

Iniciar um Novo Projeto Anchor

ℹ️ NOVO NO ANCHOR?

O Anchor é um framework de desenvolvimento popular para construir programas na Solana. Para começar, confira nosso Guia de Introdução ao Anchor.

Usaremos o Solana Playground para acelerar nosso desenvolvimento. O Solana Playground é um IDE baseado na web que permite escrever, implantar e testar programas da Solana. Se preferir usar seu próprio projeto Anchor local, certifique-se de adicionar pyth-sdk-solana = "0.7.1" ao seu arquivo Cargo.toml.

Vá para beta.solpg.io e clique em "➕" para criar um novo projeto:

  • Selecione "Anchor (Rust)"

  • Nomeie-o "Pyth Demo" e clique em "Criar"

Crie Seu Programa

Vamos criar um novo programa que buscará um feed de preços do Pyth e o registrará nos logs do programa Solana. Abra src>lib.rs, que deve estar preenchido com um programa simples. Vá em frente e exclua o conteúdo padrão.

Vamos começar importando as dependências de que precisaremos para este programa. Precisaremos das seguintes dependências:

use anchor_lang::prelude::*;
use pyth_sdk_solana::{load_price_feed_from_account_info};
use std::str::FromStr;

// Esta é a PublicKey do seu programa e será atualizada
// automaticamente ao construir o projeto.

declare_id!("11111111111111111111111111111111");

Enter fullscreen mode Exit fullscreen mode

Além das dependências do Anchor, precisaremos importar o load_price_feed_from_account_info do SDK do Pyth e o traço FromStr da biblioteca padrão do Rust (que nos permitirá converter um endereço de string em uma PublicKey).

O Solana Playground usará e declarará automaticamente o campo declare_id! para definir o ID do seu programa. Isso será atualizado automaticamente ao construir seu programa.

Também vamos definir duas constantes:

  1. um endereço de feed de preços (este será o endereço do feed de preços que queremos buscar). Uma lista de todos os feeds disponíveis pode ser encontrada aqui (certifique-se de selecionar "Solana Devnet" no menu suspenso). Para esta demonstração, usaremos o feed de preço BTC/USD na devnet da Solana, HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J.

  2. um "limiar de obsolescência" que será usado para determinar se o feed de preços está obsoleto (ou seja, se o feed de preços não foi atualizado nos últimos 60 segundos, consideraremos que está obsoleto)

Adicione as seguintes declarações abaixo de suas importações:


const BTC_USDC_FEED: &str = "HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J";
const STALENESS_THRESHOLD: u64 = 60; // limiar de obsolescência em segundos

Enter fullscreen mode Exit fullscreen mode

Sinta-se à vontade para experimentar com feeds de preços e limiares de obsolescência diferentes, mas para o nosso exemplo, estaremos procurando feeds de preços BTC/USD que tenham sido criados pelo menos nos últimos 60 segundos.

Crie uma Struct de Feed de Preços

Vamos definir uma struct para a nossa instrução de feed de preços. A struct definirá quais contas devemos passar para o nosso programa para buscar o feed de preços. Abaixo de suas constantes, adicione:


#[derive(Accounts)]

pub struct FetchBitcoinPrice<'info> {
    #[account(mut)]
    pub signer: Signer<'info>,
    #[account(address = Pubkey::from_str(BTC_USDC_FEED).unwrap() @ FeedError::InvalidPriceFeed)]
    pub price_feed: AccountInfo<'info>,

}

#[error_code]
pub enum FeedError {
    #[msg("Feed de Preço Inválido")]
    InvalidPriceFeed,
}

Enter fullscreen mode Exit fullscreen mode

Estamos definindo uma struct chamada FetchBitcoinPrice que terá duas contas:

  1. signer: Esta será a conta que assina a transação e paga as taxas de transação.

  2. price_feed: Esta será a conta do feed de preços que queremos buscar. Usamos a restrição de address(endereço) do Anchor (saiba mais sobre restrições do Anchor em nosso guia, aqui). Usamos a função Pubkey::from_str para converter nossa string BTC_USDC_FEED em uma chave pública. Também definimos um código de erro, InvalidPriceFeed, que será acionado se a conta do feed de preços for inválida.

Crie uma Instrução de Feed de Preços

Vamos definir nosso programa e criar uma função para buscar o feed de preços. Abaixo de sua struct, adicione:


#[program]

mod hello_pyth {
    use super::*;
    pub fn fetch_btc_price(ctx: Context<FetchBitcoinPrice>) -> Result<()> {
        // 1-Buscar o preço mais recente
        // 2-Formatar os valores de exibição arredondados para o dólar mais próximo
        // 3-Registrar o resultado
        Ok(())
    }

}

Enter fullscreen mode Exit fullscreen mode

Estamos definindo um programa chamado hello_pyth e uma função chamada fetch_btc_price que receberá um Contexto do tipo FetchBitcoinPrice e retornará um Result (Resultado) (ou Ok ou Err).

Busque o Preço Mais Recente

Para buscar o preço mais recente, passamos nosso endereço de feed de preços, &ctx.accounts.price_feed, para a função load_price_feed_from_account_info() do Pyth:

        // 1-Buscar o preço mais recente
        let price_account_info = &ctx.accounts.price_feed;
        let price_feed = load_price_feed_from_account_info( &price_account_info ).unwrap();
        let current_timestamp = Clock::get()?.unix_timestamp;
        let current_price = price_feed.get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).unwrap();
Enter fullscreen mode Exit fullscreen mode

Também usamos a struct Clock para obter o timestamp atual e definir uma variável current_price que busca um preço desde que ele não seja mais antigo que o limiar de obsolescência que especificamos anteriormente.

Formate os Valores de Exibição

Os dados de preços do Pyth são armazenados como um inteiro assinado de 64 bits com um expoente assinado de 32 bits.

Struct de Preço do Pyth

Aqui está a definição do Struct de Preço do Pyth (fonte):

pub struct Price {
    /// Price.
    #[serde(with = "utils::as_string")] // Para garantir a >precisão na conversão para JSON.
    #[schemars(with = "String")]
   pub price:        i64,
   /// Intervalo de confiança.
   #[serde(with = "utils::as_string")]
   #[schemars(with = "String")]

    pub conf:         u64,
  /// Expoente.
   pub expo:         i32,
   /// Hora de publicação.
   pub publish_time: UnixTimestamp,

}

Para registrar o preço em um formato legível por humanos (por exemplo, 27400 em vez de 27400000000000), precisaremos converter o price(preço) e o intervalo de confiança (conf) em um u64 e depois dividir pelo número de casas decimais (que também precisamos converter para um u64). Como o Pyth armazena isso como um número negativo, precisamos convertê-lo para um número positivo antes de dividir. Adicione o seguinte à sua função fetch_btc_price:

        // 2-Formatar os valores de exibição arredondados para o dólar mais próximo
        let display_price = u64::try_from(current_price.price).unwrap() / 10u64.pow(u32::try_from(-current_price.expo).unwrap());
        let display_confidence = u64::try_from(current_price.conf).unwrap() / 10u64.pow(u32::try_from(-current_price.expo).unwrap());
Enter fullscreen mode Exit fullscreen mode

Finalmente, registraremos o resultado nos logs do programa da Solana usando msg!. Sua instrução final deve parecer com isso:


#[program]

mod hello_pyth {
    use super::*;
    pub fn fetch_btc_price(ctx: Context<FetchBitcoinPrice>) -> Result<()> {
        // 1-Buscar o preço mais recente
        let price_account_info = &ctx.accounts.price_feed;
        let price_feed = load_price_feed_from_account_info( &price_account_info ).unwrap();
        let current_timestamp = Clock::get()?.unix_timestamp;
        let current_price = price_feed.get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).unwrap();

        // 2-Formatar os valores de exibição arredondados para o dólar mais próximo
        let display_price = u64::try_from(current_price.price).unwrap() / 10u64.pow(u32::try_from(-current_price.expo).unwrap());
        let display_confidence = u64::try_from(current_price.conf).unwrap() / 10u64.pow(u32::try_from(-current_price.expo).unwrap());
        
        // 3-Registrar o resultado
        msg!("Preço BTC/USD: ({} +- {})", display_price, display_confidence);
        Ok(())

    }

}

Enter fullscreen mode Exit fullscreen mode

Ótimo trabalho! Vamos testar.

Construa o Programa

Clique em 🔧 Build no menu à esquerda para compilar seu programa. Você deve ver uma mensagem de sucesso no console:


Building...
Build successful. Completed in 3.17s.
Enter fullscreen mode Exit fullscreen mode

Se você seguiu as instruções, não deverá receber nenhum erro, mas se receber, tente seguir as instruções para depurar. Se precisar de ajuda, sinta-se à vontade para perguntar no Discord. Estamos felizes em ajudar!

Implante o Programa

Como este projeto é apenas para fins de demonstração, podemos usar uma carteira "descartável". O Solana Playground facilita a criação de uma. Você deve ver um ponto vermelho "Não conectado" no canto inferior esquerdo da janela do navegador. Clique nele:

O Solana Playground gerará uma carteira para você (ou você pode importar a sua). Sinta-se à vontade para salvá-la para uso posterior e clique em continuar quando estiver pronto. Uma nova carteira será iniciada e conectada à devnet da Solana. Você precisará de cerca de 5 Devnet SOL para implantar o programa (Confira nosso Guia sobre Airdropping Devnet SOL).

Você deve clicar no ícone "⚙️" no canto inferior esquerdo para verificar se está na "devnet". Você também pode selecionar "custom" no menu suspenso se desejar se conectar ao seu Ponto de Extremidade QuickNode da Devnet.

💡 Criando um Ponto de extremidade QuickNode

Conecte-se a um Cluster Solana com seu Ponto de Extremidade QuickNode

Para desenvolver na Solana, você precisará de um ponto de extremidade API para se conectar à rede. Você pode usar nós públicos ou implantar e gerenciar sua própria infraestrutura; no entanto, se preferir tempos de resposta 8x mais rápidos, você pode deixar o trabalho pesado conosco. Veja por que mais de 50% dos projetos na Solana escolhem o QuickNode e inscreva-se para uma conta gratuita aqui. Vamos usar um ponto de extremidade Solana da Devnet.

Copie o link do Provedor HTTP:

Quando estiver pronto, clique no ícone de Ferramentas 🛠 no lado esquerdo da página e, em seguida, clique em "Deploy". Isso levará alguns minutos, mas você deverá ver uma mensagem de sucesso quando estiver completo.

Teste o Programa

Agora que seu programa está na cadeia, vamos testá-lo. Clique no ícone "🧪" no lado esquerdo da página. O Solana Playground possui uma interface fácil de usar para testar programas. Expanda a instrução fetchBtcPrice clicando na seta de alternância:

Selecione My address (Meu endereço) como o signatário e cole o mesmo feed de preço Pyth que você usou em seu programa (usamos HovQMDrbAgAYPCmHVSrezcSmkMtXSSUsLDFANExrZh2J).

Clique em "Testar" para enviar a transação para o cluster devnet. Você deverá ver uma mensagem de sucesso no console:


Testing 'fetchBtcPrice'...
✅  Test 'fetchBtcPrice' passed.

Enter fullscreen mode Exit fullscreen mode

Você também verá um pop-up de notificação com um link para o Solana Explorer. Abra-o. Se a notificação desapareceu antes de você clicar, você pode testar sua instrução novamente ou encontrar a transação no Solana Explorer procurando pelo ID do seu programa na devnet (por exemplo, https://explorer.solana.com/address/YOUR_PROGRAM_ID?cluster=devnet). O ID do seu programa está disponível no menu de Ferramentas 🛠️ em "Programa ID" e em sua declaração declare_id em lib.rs.

Ao abrir a transação no Solana Explorer, você deverá ver uma seção "Program Instructions Logs" que inclui o preço do BTC em USD:

Bom trabalho!

Você pode acessar nosso código completo no Solana Playground.

Conclusão

Você acabou de criar um programa que busca o preço do BTC no Pyth e o registra nos logs do programa Solana. Você pode usar esse mesmo padrão para criar programas que buscam qualquer dado no Pyth e usá-lo como ponto de partida para construir programas mais complexos.

Tem alguma pergunta ou ideia para compartilhar? Nos avise no Discord ou Twitter!

Nós ❤️ Feedback!

Deixe-nos saber se você tem algum feedback ou solicitações para novos tópicos. Adoraríamos ouvir de você.

Recursos


Este artigo foi escrito por QuickNode e traduzido por Adriano P. de Araujo. O original em inglês pode ser encontrado aqui.

Latest comments (0)