Esse artigo é uma tradução do blog Chainlink feita por Fatima Lima e você pode encontrar a versão original aqui.
Se você deseja aprender como desenvolver contratos inteligentes e programas na plataforma Solana, então está no lugar certo.
Solana é uma rede de blockchain emergente de alto desempenho e sem permissão que oferece transações rápidas, escaláveis e de baixo custo, além de suportar contratos inteligentes criados com linguagens de programação Rust, C++, e C.
Neste artigo técnico iremos trazer exemplos de redação, implantação e interação com contratos inteligentes no conjunto Solana Devnet, assim como explicar como usar os Feeds de Preço do Chainlink nos seus contratos inteligentes da Solana.
Modelo de Programação e Estrutura da Solana
Solana é uma rede de blockchain de alto desempenho com capacidade para milhares de transações por segundo e um tempo de confirmação de cada bloco de frações de segundo. Isso acontece por meio de um mecanismo de consenso, o Byzantine Fault Tolerant (BFT) que faz uso de uma inovadora função criptográfica chamada Prova de Histórico.
Prova de Histórico
Prova de Histórico (PoH) cria uma ordem de eventos verificáveis criptograficamente (neste caso, transações), ao longo do tempo, por meio do uso de uma função de delay verificável de alta frequência ou VDF. Basicamente isso significa que a PoH é como um relógio criptográfico que ajuda a rede a chegar a tempo e ordenar os eventos sem precisar esperar para ouvir dos outros nós. Assim como um relógio de água antigo pode gravar a passagem do tempo observando a elevação dos níveis de água, as saídas sequenciais da Prova de Histórico do constante estado de blockchain em hash fornecem uma ordem verificável dos eventos ao longo do tempo.
Isso melhora o desempenho da rede, permitindo que os eventos ordenados sejam processados em paralelo, que é o oposto do cenário da blockchain tradicional em que um processo simples verifica e agrupa todas as transações para serem incluídas no próximo bloco.
Fazendo uma simples analogia, imagine um quebra-cabeça com 100 peças. Num cenário normal, uma ou mais pessoas levariam um certo tempo para montar o quebra-cabeça. Mas, imagine que previamente as peças tivessem sido numeradas de acordo com suas posições, do canto superior esquerdo até o canto inferior direito e dispostas em uma linha em ordem sequencial. Graças à ordem exata das peças e a suas posições serem conhecidas de antemão, o quebra-cabeça será resolvido mais rapidamente, tendo pessoas focando em cada seção. Esse é o efeito de uma sequência verificável de eventos ao longo do tempo no mecanismo de consenso; permite que o processamento seja quebrado em processos paralelos múltiplos.
Estrutura de Contrato Inteligente
A Solana oferece um modelo de contrato inteligente diferente do modelo dos blockchains tradicionais baseados em EVM (Ethereum Virtual Machine). Nas redes baseadas em EVM, o código/lógica do contrato e o estado são reunidos num contrato único implantado na rede. Com a Solana, um contrato inteligente (ou programa) é somente leitura ou sem estado e apenas contém a lógica do programa. Depois de implantados, os contratos inteligentes podem interagir por meio de contas externas. As contas que interagem com os programas armazenam dados relativos à interação do programa. Isso cria uma separação lógica entre o estado (contas) e lógica de contrato (programas). Essa é a diferença crucial entre a Solana e os contratos inteligentes baseados em EVM. As contas em Ethereum não são como as contas em Solana. Contas Solana podem armazenar dados (incluindo informação da carteira) ao contrário das contas Ethereum, que são referências das carteiras de pessoas.
Além disso, a Solana oferece um CLI e JSON RPC API que podem ser usados por aplicações descentralizadas para interagir com a blockchain da Solana. Elas também podem usar um dos SDKs existentes, que permitem que os clientes conversem com o blockchain e os programas da Solana.
Representação de alto nível do fluxo de trabalho do desenvolvimento da Solana Source: Documentação da Solana
Implantando o seu Primeiro Contrato Inteligente da Solana
Nesta seção você vai criar e implementar seu primeiro “Olá mundo” do programa da Solana, escrito em Rust.
Requisitos
Antes de continuar, é necessário instalar:
- NodeJS v14 or greater & NPM
- A última compilação estável do Rust
- Solana CLI v1.7.11 ou posterior
- Git
O Programa OláMundo
O programa OláMundo é um contrato inteligente que imprime saídas para o console e conta o número de vezes que o programa foi chamado para uma dada conta, armazenando o número na rede. Vamos quebrar o código em seções separadas.
A primeira seção define alguns parâmetros padrões do programa da Solana e define um ponto de entrada para o programa (a função ‘process_instruction’). Além disso, ela usa borsh para serializar e desserializar parâmetros que estão sendo passados de e para o programa implantado.
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};
/// Definir o tipo de estado armazenado nas contas
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
/// número de saudações
pub counter: u32,
}
// Declarar e exportar o ponto de entrada do programa
entrypoint!(process_instruction);
A função process_instruction aceita o program_id, que é a chave pública na qual o programa é implantado e a accountInfo, que é a conta que dá um olá.
pub fn process_instruction(
program_id: &Pubkey, // Chave pública da conta na qual o programa olá mundo foi carregado
accounts: &[AccountInfo], // A conta para dizer Olá
_instruction_data: &[u8], // Ignoradas
O ProgramResult é onde a lógica principal do programa reside. Nesse caso, ele simplesmente imprime uma mensagem e então seleciona as contas fazendo um looping pelas ‘accounts’. Mas, em nosso exemplo, teremos apenas uma conta.
) -> ProgramResult {
msg!("Hello World Rust program entrypoint");
// Interar contas é mais seguro que indexar
let accounts_iter = &mut accounts.iter();
// Pegue a conta para dizer Olá para
let account = next_account_info(accounts_iter)?;
Em seguida, o programa checa se a conta tem permissão para modificar os dados de uma conta específica.
// A conta precisa pertencer ao programa para modificar seus dados
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
Finalmente, a função pega o número armazenado da conta existente, acrescenta um ao valor, retorna o resultado e exibe uma mensagem.
// Incrementar e armazenar o número de vezes qua a conta foi saudada
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter += 1;
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Greeted {} time(s)!", greeting_account.counter);
Ok(())
Implantando o Programa
A primeira etapa é clonar o repositório.
git clone https://github.com/solana-labs/example-helloworld
cd example-helloworld
Uma vez feito isso, você pode ajustar seu ambiente atual para devnet. Esta é a rede de teste para desenvolvedores da Solana escreverem e testarem contratos inteligentes.
solana config set --url https://api.devnet.solana.com
Em seguida, você precisa criar um novo keypair para a sua conta. Isso é necessário para interagir com programas implantados (contratos inteligentes) no devnet da Solana. Tome nota: esse é um método inseguro para guardar chaves e só deve ser usado para demonstração. Será solicitado que você insira uma senha por motivos de segurança.
solana-keygen new --force
Agora que você criou uma conta, você pode usar o programa airdrop para obter alguns tokens SOL. Você vai precisar de alguns lamports (frações de SOL) para implementar seu contrato inteligente. Este comando solicita tokens SOL em sua conta recém-gerada:
solana airdrop 5
Agora você está pronto para criar o programa olá mundo. Você pode criá-lo executando o seguinte comando
npm run build:program-rust
Uma vez que o programa foi criado, você pode implantá-lo no devnet. A saída do comando anterior vai fornecer o comando que você vai precisar para executar, mas ele deve ser algo semelhante a:
solana program deploy dist/program/helloworld.so
O resultado final é que você tenha implementado com sucesso o programa Olá mundo para o devnet com o Id do programa atribuído. Isso poderá ser verificado no Solana Devnet explorer.
Interagindo Com o Programa Implantado
Para interagir com o programa implantado, o repositório olá-mundo contém um cliente simples. Esse cliente é escrito em Typescript usando a Solana web3.js SDK e a Solana web3 API.
O Cliente
O ponto de entrada do cliente é o arquivo main.ts, que executa uma série de tarefas em uma ordem específica, a maioria das quais estão contidas no arquivo hello_world.ts.
Primeiramente, o cliente estabelece uma conexão com o grupo chamando a função ‘establishConnection’
export async function establishConnection(): Promise {
const rpcUrl = await getRpcUrl();
connection = new Connection(rpcUrl, 'confirmed');
const version = await connection.getVersion();
console.log('Connection to cluster established:', rpcUrl, version);
}
Depois ele chama a função ‘establishPayer’ para se assegurar de que exista uma conta disponível para pagar as transações e cria uma, se necessário.
export async function establishPayer(): Promise {
let fees = 0;
if (!payer) {
const {feeCalculator} = await connection.getRecentBlockhash();
d
// Calcular o custo para financiar a conta de saudação
fees += await connection.getMinimumBalanceForRentExemption(GREETING_SIZE);
// Calcular o custo para enviar transações
fees += feeCalculator.lamportsPerSignature * 100; // wag
payer = await getPayer();
}
O cliente, então, chama a função ‘checkProgram’, que carrega o keypair do programa implantado de ./dist/program/helloworld-keypair.json e usa a chave pública do keypair para buscar a conta do programa. Se o programa não existe, o cliente vai parar, com um erro. Se o programa existe, ele cria uma nova conta com o programa atribuído como seu proprietário, para armazenar o estado do programa, que neste caso, é o número de vezes que o programa foi executado.
export async function checkProgram(): Promise {
// Ler o id do programa do arquivo keypair
try {
const programKeypair = await createKeypairFromFile(PROGRAM_KEYPAIR_PATH);
programId = programKeypair.publicKey;
} catch (err) {
const errMsg = (err as Error).message;
throw new Error(
`Failed to read program keypair at '${PROGRAM_KEYPAIR_PATH}' due to error: ${errMsg}. Program may need to be deployed with \`solana program deploy dist/program/helloworld.so\``,
);
}
// Verificar se o programa foi implantado
const programInfo = await connection.getAccountInfo(programId);
if (programInfo === null) {
if (fs.existsSync(PROGRAM_SO_PATH)) {
throw new Error(
'Program needs to be deployed with `solana program deploy dist/program/helloworld.so`',
);
} else {
throw new Error('Program needs to be built and deployed');
}
} else if (!programInfo.executable) {
throw new Error(`Program is not executable`);
}
console.log(`Using program ${programId.toBase58()}`);
// Obter o endereço (chave pública) da conta de saudação do programa para que seja fácil encontrar mais tarde.
const GREETING_SEED = 'hello';
greetedPubkey = await PublicKey.createWithSeed(
payer.publicKey,
GREETING_SEED,
programId,
);
O cliente então, cria e envia uma transação ‘Olá’ para o programa chamando a função ‘sayHello’. A transação contém uma instrução que contém a chave pública do programa olámundo para chamar e a conta para a qual o cliente deseja dizer olá. Cada vez que o cliente realiza essa transação para uma conta, o programa incrementa uma contagem no armazenamento de dados da conta de destino.
export async function sayHello(): Promise {
console.log('Saying hello to', greetedPubkey.toBase58());
const instruction = new TransactionInstruction({
keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}],
programId,
data: Buffer.alloc(0), // Todas as instruções são olás
});
await sendAndConfirmTransaction(
connection,
new Transaction().add(instruction),
[payer],
);
}
Finalmente, o cliente consulta os dados da conta com ‘reportGreetings’, para recuperar o número atual de vezes que a transação sayHello foi chamada na conta.
export async function reportGreetings(): Promise {
const accountInfo = await connection.getAccountInfo(greetedPubkey);
if (accountInfo === null) {
throw 'Error: cannot find the greeted account';
}
const greeting = borsh.deserialize(
GreetingSchema,
GreetingAccount,
accountInfo.data,
);
console.log(
greetedPubkey.toBase58(),
'has been greeted',
greeting.counter,
'time(s)',
);
Executando o Cliente
Antes de executar o cliente para ler os dados do seu programa, você vai precisar instalar as dependências do cliente.
npm install
Uma vez feito isso, você pode começar o cliente.
npm run start
Você deve ver a saída mostrando o seu programa sendo executado com sucesso e ele deve exibir o número de vezes que a sua conta foi saudada. As execuções subsequentes devem aumentar esse número.
Parabéns, você implantou e interagiu com o contrato inteligente da Solana na rede devnet! Agora vamos mergulhar em outro exemplo de programa e cliente, da Solana, só que dessa vez vamos usar os Feeds de Preço do Chainlink.
Feeds de Preço do Chainlink
O ecossistema do apps do DeFi na Solana está crescendo em taxa acelerada. Para alimentar os mecanismos básicos de DeFi (finanças descentralizadas) e executar funções key on-chain, tais como emissão de empréstimos nos preços de mercado ou liquidação de posições subcolateralizadas, esses dApps (aplicativos descentralizados) precisam de acesso a dados do mercado, altamente confiáveis e de alta qualidade.
Solana recentemente integrou Feeds de Preço do Chainlink em sua rede devnet, oferecendo desenvolvedores altamente descentralizados, de alta qualidade e dados de referência de preços em frações de segundo para criar contratos inteligentes híbridos.
Quando combinado com a habilidade que a Solana possui de suportar até 65,000 transações por segundo e suas taxas de transação extremamente baixas, os Feeds de Preço do Chainlink tem o potencial de capacitar a infraestrutura do protocolo DeFi que pode competir com os sistemas financeiros tradicionais em termos de execução comercial e qualidade de gestão de risco.
Neste próximo exemplo de código, vamos implantar e interagir com um programa da Solana que usa os Feeds de Preço do Chainlink na rede devnet da Solana.
Requisitos
- NodeJS v14 or greater & NPM
- A última compilação RUST estável
- Solana CLI v1.7.11 ou posterior
- Git
O Programa de Demonstração da Chainlink Solana
O programa da Chainlink Solana Demo é um contrato inteligente que simplesmente conecta a uma conta de Feed do Preço do Chainlink na devnet e recupera e armazena o último preço do par de preço específico.
Todos os Feeds de Preço do Chainlink na Solana utilizam um programa único, em que cada preço individual alimenta uma única conta que envia atualizações de preços usando o programa.
A primeira seção consiste em normal e declarando usar borsh para serializar e desserializar parâmetros sendo passados de e para o programa implantado. Além disso, algumas structs são configuradas para armazenar respostas de feed de preço e dados decimais.
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
pubkey::Pubkey,
};
struct Decimal {
pub value: u128,
pub decimals: u32,
}
/// Definir o tipo de estado armazenado nas contas
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct PriceFeedAccount {
/// número de saudações
pub answer: u128,
}
impl Decimal {
pub fn new(value: u128, decimals: u32) -> Self {
Decimal { value, decimals }
}
}
Em seguida, o programa declara o ponto de entrada para o programa ser a função ‘process_instructions’.
// Declarar e exportar o ponto de entrada do programa entrypoint
entrypoint!(process_instruction);
Finalmente, a função ‘process_instruction’ é a parte do programa que contém a lógica principal para o contrato inteligente. Ele recebe a transação com o feed address especificado e lida com as etapas para recuperar e armazenar os dados de preço na rede. A função também chama get_price() do pacote chainlink-solana, que é importado do GitHub por meio do arquivo Cargo.toml. A função termina armazenando o preço na conta especificada.
pub fn process_instruction(
_program_id: &Pubkey, // Ignorado
accounts: &[AccountInfo], // Chave pública da conta para ler dados de preço de
_instruction_data: &[u8], // Ignorado
) -> ProgramResult {
msg!("Chainlink Solana Demo program entrypoint");
let accounts_iter = &mut accounts.iter();
// This is the account of our our account
let my_account = next_account_info(accounts_iter)?;
// Esta é a conta dos dados do feed de preço
let feed_account = next_account_info(accounts_iter)?;
const DECIMALS: u32 = 9;
let price = chainlink::get_price(&chainlink::id(), feed_account)?;
if let Some(price) = price {
let decimal = Decimal::new(price, DECIMALS);
msg!("Price is {}", decimal);
} else {
msg!("No current price");
}
// Armazenar os preços nós mesmos
let mut price_data_account = PriceFeedAccount::try_from_slice(&my_account.data.borrow())?;
price_data_account.answer = price.unwrap_or(0);
price_data_account.serialize(&mut &mut my_account.data.borrow_mut()[..])?;
Implantando o Programa
O primeiro passo é clonar o repositório.
git clone https://github.com/smartcontractkit/chainlink-solana-demo
cd chainlink-solana-demo
Uma vez feito isso, você pode configurar seu ambiente atual para o devnet. Esta é a rede de testes para os desenvolvedores da Solana escreverem e testarem os contratos inteligentes.
solana config set --url https://api.devnet.solana.com
Em seguida você precisa criar um novo keypair para a sua conta. Isso é necessário para interagir com os programas implantados (contratos inteligentes) no devnet da Solana. De novo, esse é um método inseguro para guardar chaves e só deve ser usado para fins de demonstração. Será solicitado que você envie uma senha por razões de segurança.
mkdir solana-wallet
solana-keygen new --outfile solana-wallet/keypair.json
Agora que você criou uma conta, pode usar o programa airdrop para obter alguns tokens SOL. Você vai precisar de alguns lamports (frações de SOL) para implantar seu programa. Este comando requer tokens SOL para sua conta recém-gerada.
solana airdrop 5 $(solana-keygen pubkey solana-wallet/keypair.json)
Se não funcionar, você pode ver sua chave pública com o seguinte comando, e depois, usá-la para solicitar tokens SOL da solfaucet.
solana-keygen pubkey ./solana-wallet/keypair.json
Agora você está pronto para criar o programa de Chainlink Solana Demo usando Solana BPF.
cargo build-bpf
Uma vez que o programa tenha sido criado, você pode implantá-lo no devnet. A saída de comando anterior vai te dar o comando de que você precisa para executar, mas deve ser algo semelhante ao seguinte. Tome nota do -keypair anexado no final.
solana program deploy target/deploy/chainlink_solana_demo.so --keypair solana-wallet/keypair.json
O resultado final é que você implantou com sucesso o programa olá mundo para o devnet com um Id do programa atribuído. Isso, então, pode ser checado no Solana Devnet explorer.
Interagindo Com o Programa Implantado
Para interagir com o programa implantado, o repositório da Chainlink Solana Demo contém um cliente escrito em Typescript usando o Solana web3.js SDK e o Solana web3 API.
O cliente
O ponto de entrada do cliente é o arquivo main.ts, que executa um número de tarefas em uma ordem específica, a maioria das quais está contida no arquivo hello_world.ts.
Primeiramente, estabelece uma conexão com o grupo chamando a função ‘establishConnection’.
export async function establishConnection(): Promise {
const rpcUrl = await getRpcUrl()
connection = new Connection(rpcUrl, 'confirmed')
const version = await connection.getVersion()
console.log('Connection to cluster established:', rpcUrl, version)
}
Então ele chama a função ‘establishPayer’ para assegurar que exista uma conta disponível para pagar as transações e cria uma, se necessário.
export async function establishPayer(): Promise {
let fees = 0
if (!payer) {
const { feeCalculator } = await connection.getRecentBlockhash()
// Calcular o custo para financiar a conta de saudação
fees += await connection.getMinimumBalanceForRentExemption(AGGREGATOR_SIZE)
// Calcular o custo para enviar transações
fees += feeCalculator.lamportsPerSignature * 100 // wag
try {
// Obter pagador de cli config
payer = await getPayer()
} catch (err) {
// Financiar um novo pagador via airdrop
payer = await newAccountWithLamports(connection, fees)
}
}
O cliente, então, chama a função ‘checkProgram’ , que carrega keypair do programa implantado de ./deploy/chainlink-solana-demo-keypair.json e usa a chave pública para o keypair para obter a conta do programa. Se o programa não existe, o cliente interrompe com um erro. Se o programa existe, vai criar uma nova conta com o programa atribuído como seu proprietário para armazenar o estado do programa.
export async function checkProgram(): Promise {
// Ler o id do programa do arquivo keypair
try {
const programKeypair = await createKeypairFromFile(PROGRAM_KEYPAIR_PATH);
programId = programKeypair.publicKey;
} catch (err) {
const errMsg = (err as Error).message;
throw new Error(
`Failed to read program keypair at '${PROGRAM_KEYPAIR_PATH}' due to error: ${errMsg}. Program may need to be deployed with \`solana program deploy dist/program/helloworld.so\``,
);
}
// Checar se o programa foi implantado
const programInfo = await connection.getAccountInfo(programId);
if (programInfo === null) {
if (fs.existsSync(PROGRAM_SO_PATH)) {
throw new Error(
'Program needs to be deployed with `solana program deploy dist/program/chainlink_solana_demo.so`',
);
} else {
throw new Error('Program needs to be built and deployed');
}
} else if (!programInfo.executable) {
throw new Error(`Program is not executable`);
}
console.log(`Using program ${programId.toBase58()}`);
// Derivar o endereço (chave pública) da conta de saudação do programa para que mais tarde seja fácil de encontrar
const GREETING_SEED = 'hello';
greetedPubkey = await PublicKey.createWithSeed(
payer.publicKey,
GREETING_SEED,
programId,
);
O cliente cria e envia uma transação para o programa com a função ‘getPrice’. A transação contém uma conta de feed de preços como parâmetro. Nesse caso, você está enviando a conta de feed do SOL/USD listada na Chainlink Solana Devnet feeds page. Uma vez que a instrução é enviada, ela aguarda a confirmação da transação.
export async function getPrice(): Promise {
console.log('Getting data from ', readingPubkey.toBase58())
const priceFeedAccount = "FmAmfoyPXiA8Vhhe6MZTr3U6rZfEZ1ctEHay1ysqCqcf"
const AggregatorPublicKey = new PublicKey(priceFeedAccount)
const instruction = new TransactionInstruction({
keys: [{ pubkey: readingPubkey, isSigner: false, isWritable: true },
{ pubkey: AggregatorPublicKey, isSigner: false, isWritable: false }],
programId,
data: Buffer.alloc(0), // All instructions are hellos
})
await sendAndConfirmTransaction(
connection,
new Transaction().add(instruction),
[payer],
)
}
Finalmente, o cliente consulta os dados da conta para recuperar o preço armazenado do feed de preço SOL/USD.
export async function reportPrice(): Promise {
const accountInfo = await connection.getAccountInfo(readingPubkey)
if (accountInfo === null) {
throw new Error('Error: cannot find the aggregator account')
}
const latestPrice = borsh.deserialize(
AggregatorSchema,
AggregatorAccount,
accountInfo.data,
)
console.log("Current price of SOL/USD is: ", latestPrice.answer.toString())
}
Executando o Cliente
Antes de poder executar o cliente para ler dados do programa implantado, você precisa instalar as dependências do cliente.
cd client
yarn
Uma vez feito isso, você pode começar o cliente.
yarn start
Você deverá ver a saída mostrando seu programa sendo executado com sucesso e ele deve exibir o valor atual de SOL/USD armazenado.
Resumo
A Solana oferece uma blockchain escalável, de alta velocidade e de baixo custo para criar contratos inteligentes e aplicativos descentralizados. Aproveitando os contratos inteligentes da Solana e os Feeds de Preço de Chainlink, desenvolvedores podem criar mais rápido, aplicativos escaláveis de DeFi, aproveitando os dados de alta qualidade oferecidos pelos Feeds de Preço da Chainlink e atualizações em frações de segundo na blockchain da Solana.
Para explorar mais tutoriais técnicos do Chainlink, consulte a engineering video tutorials playlist no canal oficial do Chainlink no YouTube, e visite a documentação do Chainlink em docs.chain.link. Para uma possível integração, fale com um especialista.
O pontapé inicial do Chainlink Fall 2021 Hackathon aconteceu em 22 de outubro de 2021. Se você for um desenvolvedor, criador, artista, especialista em blockchain ou totalmente novo nesse espaço, esse hackathon é o lugar perfeito para impulsionar sua jornada de desenvolvimento do seu contrato inteligente e para aprender com mentores líderes do ramo
Top comments (0)