Chainlink 2021 畢業黑客松將於十月22日開啟。快來註冊吧!
如果你想學習如何開發Solana智能合約和項目,那麼你來對地方了。
Solana 是一個新興公的閃光無許可的鏈,它了、廉價且可擴展的交易體驗,並且支持使用Rust,C++和C語言來寫智能合約。
在本技術教程中,我們將一起看一看如何在Solana Devnet(測試教程)中編寫、編寫合同並進行交互,如何在智能合約中使用Chainlink Price Feeds。
Solana 的架構和編程模型
Solana是一個網絡的錯誤鏈,可以處理多種筆交易,出塊時間達到了亞秒級。它通過拜占庭容(BFT)實現了這一點,該機制利用了之為歷史證明(PoH)的創新密碼學函數。
歷史證明
歷史證明(PoH)通過使用高頻可驗證延遲函數(V),隨著時間的模擬DF可通過密碼驗證的事件(在本例中為交易)。一個奇蹟的水滴,可以幫助網絡就時間和事件發生的過程順利,而無需等待其他節點的消息。希的連續輸出能夠給出可驗證的事件順序。
這允許並行處理事件來幫助提升網絡的性能,而在持續的區塊鏈場景中,單個進程驗證並打包要在下一個塊中的所有交易。
一個簡單的類比是突然一個100塊的大型拼圖。在情況下,完成拼圖需要一個或幾個特定的時間。的左上角到右下角,並按順序排列成一行。因為拼的具體順序和他們在拼的位置是具體的位置圖的,所以讓每個人專注於每一部分,可以解決拼圖。是相對時間來說具有可驗證性的事件序列對機制的影響;它可以將整個事件整體化為多個並行進程可能。
智能合約架構
Solana 模型提供了一種基礎傳統的基於EVM 的區塊鏈智能合約。在傳統的基於EVM 的鏈中,合約代碼/邏輯和狀態組合成一個在鏈上的合約。 Solana 中智能合約(程序)這是只讀程序或無狀態的,只有包含程序邏輯。部署後,智能操作就可以通過外部進行交互。 Solana 中與程序交互並且會與交互相關的數據。創建這是Solana和基於EVM的智能合約之間的區別。以太坊上的賬戶與Solana上的賬戶不同,Solana賬戶可以存儲數據(包括錢包)信息),而以太坊賬戶數據。
主題,Solana 還提供了CLI(命令行)和JSON RPC API,這些用於去中心化應用程序與Solana 使用區塊鏈進行交互。還可以現有的SDK,用於客戶端與區塊鍊和Solana程序譯文。
部署第一個Solana 智能合約
在本節中,你將創建並部署一個“hello world”的Solana 程序,使用Rust 進行寫信。
要求
在繼續之前,應安裝以下工具:
你好世界程序
該HelloWorld程序是一個智能合約,它可以輸出打印到模擬,並統計給定的賬戶調用該程序的次數,將存儲的數字存儲在鏈上。我們將代碼分解成部分來演示。
第一部分定義了一些Solana 程序並定義了程序的入口(“process_instruct”函數)。
use borsh::{BorshDeserialize, BorshSerialize}; use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, }; /// Define the type of state stored in accounts #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct GreetingAccount { /// number of greetings pub counter: u32, } // Declare and export the program's entrypoint entrypoint!(process_instruction);
流程指令函數接受程序編號,這是一個公鑰,也即程序部署後的地址;以及帳戶信息,這是用於和習慣交互的頁面。
pub fn process_instruction( program_id: &Pubkey, // Public key of the account the hello world program was loaded into accounts: &[AccountInfo], // The account to say hello to _instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
程序結果是寫程序的主要邏輯的地方。在本教程中,它只是打印一條消息,然後遍歷“帳戶”來選擇帳戶。但是,在我們的示例中,只有一個帳戶。
) -> ProgramResult { msg!("Hello World Rust program entrypoint"); // Iterating accounts is safer then indexing let accounts_iter = &mut accounts.iter(); // Get the account to say hello to let account = next_account_info(accounts_iter)?;
接下來,程序會檢查該帳戶是否修改了指定帳戶的數據。
// The account must be owned by the program in order to modify its data if account.owner != program_id { msg!("Greeted account does not have the correct program id"); return Err(ProgramError::IncorrectProgramId); }
之後,該系統啟動了賬戶存儲的數字加一記賬結果寫回,然後一條消息。
// Increment and store the number of times the account has been greeted 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(())
部署程序
是第一步複製代碼。
git clone https://github.com/solana-labs/example-helloworld cd example-helloworld
完成後,可以設置當前的環境為開發網。這是為Solana者們準備的寫和開發測試契約的網絡的測試網。
solana config set --url https://api.devnet.solana.com
接下來,需要為帳戶創建一個密碼。這對於在Solana 測試網上的操作說明是必要的。注意:這種訪問對的方法不安全,應該僅用於演示目的。為了安全,系統將提示你輸入密碼。
solana-keygen new --force
現在已經創建了一個賬戶,可以使用空投程序來獲取一些SOL通證。需要一些燈(部分SOL通證)來部分智能合約。該命令請求獲取SOL通證到你新生成的賬戶中:
solana airdrop 5
現在已經準備好構建hello world程序。可以通過運行以下命令來構建它:
npm run build:program-rust
程序構建完成,就可以將其部署到上。
solana program deploy dist/program/helloworld.so
最終結果是成功將hello world程序部署到devnet上,並且有一個指定的程序ID。這可以在Solana Devnet瀏覽器上進行檢查。
與部署的程序交互
為了與開發良好的程序交互,hello-world 提供了一個簡單的客戶端。這個客戶端是用Typescript 代碼庫的,使用了Solana 的web3.js 和Solana web3 API。
客戶端
客戶端入口是主文件文件,它按特定的順序執行任務,其中大部分任務都包含在hello_world.ts文件中。
首先,客戶端會建立一個和節點的連接,使用的函數是建立連接:
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); }
然後會調用建立付款人函數來確保有一個能夠支付的賬戶,如果沒有的話就創建一個新的。
export async function establishPayer(): Promise { let fees = 0; if (!payer) { const {feeCalculator} = await connection.getRecentBlockhash(); // Calculate the cost to fund the greeter account fees += await connection.getMinimumBalanceForRentExemption(GREETING_SIZE); // Calculate the cost of sending transactions fees += feeCalculator.lamportsPerSignature * 100; // wag payer = await getPayer(); }
客戶端然後會調用“checkProgram”函數,函數從./dist/program/helloworld-keypair.json中加載程序的密鑰對,並使用密鑰對的密鑰來獲取程序賬戶。如果程序不存在客戶端會報錯並停止。該程序確實存在,一個人創建了一個新的帳戶程序存儲狀態,指定程序目標,如果在這種情況下,它是指已執行的次數。
export async function checkProgram(): Promise { // Read program id from keypair file 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``, ); } // Check if the program has been deployed 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()}`); // Derive the address (public key) of a greeting account from the program so that it's easy to find later. const GREETING_SEED = 'hello'; greetedPubkey = await PublicKey.createWithSeed( payer.publicKey, GREETING_SEED, programId, );
然後客戶端調用“sayHello”函數服務程序發送“hello”交易事務。該交易包含一條消息,該指令要包含調用的helloworld程序賬戶的公鑰以及客戶端希望對方的賬戶。每次客戶端對一個帳戶執行此交易時,程序總數增加了一個帳戶數據存儲中的計數。
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), // All instructions are hellos }); await sendAndConfirmTransaction( connection, new Transaction().add(instruction), [payer], ); }
最後,客戶端調用“報告問候”查詢賬戶數據以檢索賬戶當前調用說Hello事務的次數。
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)', );
運行客戶端
在運行客戶端從部署的程序中讀取數據之前,還需要安裝客戶端的依賴項。
npm install
這步完成後,可以開啟客戶端。
npm run start
從輸出中查看程序可以成功執行,並且會顯示頁面已經打招呼的次數增加。
恭喜!你已經成功在Solana 測試網上部署了程序並交互了。現在我們進入了另一個Solana 的程序和客戶端案例,但我們來使用Chainlink 的價格信息。
Solana上使用Chainlink價格信息
Solana上的DeFi應用生態系統正在加速增長。為了支持基本的DeFi應用生態系統正在加速增長。為了支持基本的DeFi應用生態系統的增長。為了支持基本的DeFi應用生態系統的增長,例如以公平市場Fit市場價格引出,或者轉接頭不足的頭寸,這些dApp都需要達到高度可靠和價格的市場關鍵數據。
Solana 最近在他們的devnet 網絡上集成了Chainlink Price Feeds,為開發者提供高度去中心化、價格、亞秒級更新的價格參考數據,用於構建混合智能合約。
結合Solana 最高支持65,000 筆交易的能力及其極低的交易費用,Chainlink Price Feeds 有可能增強DeFi 協議基礎設施,能夠在交易執行和風險管理質量方面與傳統金融系統競爭。
在下一個的代碼案例中,我們將在Solana 測試網上約定並配合互動。
要求
Chainlink Solana Demo程序
Chainlink Solana Demo程序是一個智能合約,對連接到devnet上的Chainlink Price Feed賬戶,快速獲取並指定價格的最新價格。
Solana上的Chainlink Price Feeds都使用同一個程序,單獨的Price Feeds到一個單獨的賬戶,該賬戶使用程序提交價格更新。
第一部分包括使用borsh 對目標和傳出部署程序的參數進行序列化和反序列化的良好維護和聲明。
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, } /// Define the type of state stored in accounts #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct PriceFeedAccount { /// number of greetings pub answer: u128, } impl Decimal { pub fn new(value: u128, decimals: u32) -> Self { Decimal { value, decimals } } }
接下來,程序將入口聲明為“process_instructions”函數。
// Declare and export the program's entrypoint entrypoint!(process_instruction);
最後,“process_instruction”函數是包含智能合約主要邏輯的程序部分。它具有指定的價格饋送地址的交易,並處理在鏈上查詢和存儲價格數據的步驟。該函數還從鏈環索拉納包中調用獲取價格()函數,該包需要通過Cargo.toml文件從GitHub導入。該函數使用指定的Price Feed的地址獲取價格並存儲。
pub fn process_instruction( _program_id: &Pubkey, // Ignored accounts: &[AccountInfo], // Public key of the account to read price data from _instruction_data: &[u8], // Ignored ) -> 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)?; // This is the account of the price feed data 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"); } // Store the price ourselves 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()[..])?;
部署程序
是第一步複製代碼。
git clone https://github.com/smartcontractkit/chainlink-solana-demo cd chainlink-solana-demo
完成後,可以設置當前的環境為開發網。這是為Solana者們準備的寫和開發測試契約的網絡的測試網。
solana config set --url https://api.devnet.solana.com
接下來,需要為帳戶創建一個密碼。這對於在Solana 測試網上的操作說明是必要的。注意:這種訪問對的方法不安全,應該僅用於演示目的。為了安全,系統將提示你輸入密碼。
mkdir solana-wallet solana-keygen new --outfile solana-wallet/keypair.json
現在已經創建了一個賬戶,可以使用空投程序來獲取一些SOL通證。需要一些燈(部分SOL通證)來部分智能合約。該命令請求獲取SOL通證到你新生成的賬戶中。
solana airdrop 5 $(solana-keygen pubkey solana-wallet/keypair.json)
如果這步不行,可以通過下面的見證公鑰的話,並通過地址從solfaucet 證使用該水龍頭中請求一些SOL通。
solana-keygen pubkey ./solana-wallet/keypair.json
現在就可以使用Solana BPF 構建Chainlink Solana 演示程序了。
cargo build-bpf
程序構建完成,就可以將其部署到上面。– 密鑰對選項。
solana program deploy target/deploy/chainlink_solana_demo.so --keypair solana-wallet/keypair.json
最終結果是成功的事件程序部署到devnet 上,並且有一個指定的程序Id。這可以在Solana Devnet 瀏覽器上進行檢查。
與部署後的程序交互
為了與開發良好的程序交互,hello-world 提供了一個簡單的客戶端。這個客戶端是用Typescript 代碼庫的,使用了Solana 的web3.js 和Solana web3 API。
客戶端
客戶端入口是主文件文件,它按特定的順序執行任務,其中大部分任務都包含在hello_world.ts文件中。首先,客戶端會建立一個和節點的連接,使用的函數是建立連接。
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) }
然後會調用建立付款人函數來確保有一個能夠支付的賬戶,如果沒有的話就創建一個新的。
export async function establishPayer(): Promise { let fees = 0 if (!payer) { const { feeCalculator } = await connection.getRecentBlockhash() // Calculate the cost to fund the greeter account fees += await connection.getMinimumBalanceForRentExemption(AGGREGATOR_SIZE) // Calculate the cost of sending transactions fees += feeCalculator.lamportsPerSignature * 100 // wag try { // Get payer from cli config payer = await getPayer() } catch (err) { // Fund a new payer via airdrop payer = await newAccountWithLamports(connection, fees) } }
客戶端然後會調用“checkProgram”函數,函數從./dist/program/helloworld-keypair.json中加載程序的密鑰對,並使用密鑰對的密鑰來獲取程序賬戶。如果程序不存在,客戶端會報錯並停止。如果程序確實存在,本人創建了一個新的賬戶程序存儲狀態,則重新指定程序圖標。
export async function checkProgram(): Promise { // Read program id from keypair file 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``, ); } // Check if the program has been deployed 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()}`); // Derive the address (public key) of a greeting account from the program so that it's easy to find later. const GREETING_SEED = 'hello'; greetedPubkey = await PublicKey.createWithSeed( payer.publicKey, GREETING_SEED, programId, );
然後調用“getPrice”作為函數客戶端程序發送交易。包含交易一個Price Feed 帳戶。在本示例中,是Chainlink Solana 價格Feeds 頁面中開發的SOL/USD Feed 帳戶。完成後,則開始等待交易確認。
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], ) }
最後,客戶端會查詢賬戶數據以檢索出SOL/USD Price Feed存儲的價格數據。
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()) }
運行客戶端
在運行客戶端從部署的程序中讀取數據之前,還需要安裝客戶端的依賴項。
cd client yarn
這步完成後,可以開啟客戶端。
yarn start
可以從輸出中看到程序成功執行,並且它應該顯示存儲的當前SOL/USD 價格數據。
總結
Solana 為構建智能合約和去中心化應用提供了速度、數據、可擴展的區塊鏈。使用Solana 智能合約和Chainlink Price Feeds,開發者們能夠利用Chainlink Price Feeds 提供的數據數據和Solana 區塊鏈上可用的亞級更新等優勢,創建秒、可擴展的DeFi應用程序。
探索更多Chainlink技術教程,可以查看官方Chainlink YouTube上工程相關的視頻教程播放列表,並訪問docs.chain.link上的Chainlink文檔。如果要討論集成相關,可以點擊此處聯繫專家。
Chainlink 2021 黑客培訓將於2021110 月2 日開帷幕。無論你是開發者、創作者、藝術家、區塊鏈專家,還是該領域的入門,本次知識都是你的智能合約開發之旅之旅整個校園的學習的理想場所。
立即鎖定席位,遠遠超過30萬美元的獎品吧!