Browse Source

Add Pyth examples

Valentin Madrid 2 years ago
parent
commit
103c60eb15

+ 7 - 0
oracles/pyth/anchor/.gitignore

@@ -0,0 +1,7 @@
+
+.anchor
+.DS_Store
+target
+**/*.rs.bk
+node_modules
+test-ledger

+ 8 - 0
oracles/pyth/anchor/.prettierignore

@@ -0,0 +1,8 @@
+
+.anchor
+.DS_Store
+target
+node_modules
+dist
+build
+test-ledger

+ 21 - 0
oracles/pyth/anchor/Anchor.toml

@@ -0,0 +1,21 @@
+[features]
+seeds = false
+skip-lint = false
+[programs.localnet]
+pythexample = "F6mNuN1xoPdRaZcUX3Xviq7x1EFtoBXygpFggCLd62eU"
+
+[registry]
+url = "https://api.apr.dev"
+
+[provider]
+cluster = "Localnet"
+wallet = "~/.config/solana/id.json"
+
+[scripts]
+test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
+
+[test.validator]
+url = "https://api.mainnet-beta.solana.com"
+
+[[test.validator.clone]]
+address = "H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG"

+ 13 - 0
oracles/pyth/anchor/Cargo.toml

@@ -0,0 +1,13 @@
+[workspace]
+members = [
+    "programs/*"
+]
+
+[profile.release]
+overflow-checks = true
+lto = "fat"
+codegen-units = 1
+[profile.release.build-override]
+opt-level = 3
+incremental = false
+codegen-units = 1

+ 12 - 0
oracles/pyth/anchor/migrations/deploy.ts

@@ -0,0 +1,12 @@
+// Migrations are an early feature. Currently, they're nothing more than this
+// single deploy script that's invoked from the CLI, injecting a provider
+// configured from the workspace's Anchor.toml.
+
+const anchor = require("@coral-xyz/anchor");
+
+module.exports = async function (provider) {
+  // Configure client to use the provider.
+  anchor.setProvider(provider);
+
+  // Add your deploy script here.
+};

+ 19 - 0
oracles/pyth/anchor/package.json

@@ -0,0 +1,19 @@
+{
+    "scripts": {
+        "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
+        "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
+    },
+    "dependencies": {
+        "@coral-xyz/anchor": "^0.27.0"
+    },
+    "devDependencies": {
+        "chai": "^4.3.4",
+        "mocha": "^9.0.3",
+        "ts-mocha": "^10.0.0",
+        "@types/bn.js": "^5.1.0",
+        "@types/chai": "^4.3.0",
+        "@types/mocha": "^9.0.0",
+        "typescript": "^4.3.5",
+        "prettier": "^2.6.2"
+    }
+}

+ 21 - 0
oracles/pyth/anchor/programs/pythexample/Cargo.toml

@@ -0,0 +1,21 @@
+[package]
+name = "pythexample"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "pythexample"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+anchor-lang = "0.27.0"
+pyth-sdk-solana = "0.7.1"
+pyth-sdk = "0.7.0"

+ 2 - 0
oracles/pyth/anchor/programs/pythexample/Xargo.toml

@@ -0,0 +1,2 @@
+[target.bpfel-unknown-unknown.dependencies.std]
+features = []

+ 11 - 0
oracles/pyth/anchor/programs/pythexample/src/error.rs

@@ -0,0 +1,11 @@
+use anchor_lang::prelude::*;
+
+#[error_code]
+pub enum ErrorCode {
+    #[msg("PythError")]
+    PythError,
+    #[msg("TryToSerializePriceAccount")]
+    TryToSerializePriceAccount,
+    #[msg("InvalidArgument")]
+    InvalidArgument,
+}

+ 43 - 0
oracles/pyth/anchor/programs/pythexample/src/lib.rs

@@ -0,0 +1,43 @@
+use anchor_lang::prelude::*;
+pub mod state;
+use state::PriceFeed;
+
+pub mod error;
+use error::ErrorCode;
+
+declare_id!("F6mNuN1xoPdRaZcUX3Xviq7x1EFtoBXygpFggCLd62eU");
+
+#[program]
+pub mod pythexample {
+    use super::*;
+    pub fn read_price(ctx: Context<Pyth>) -> Result<()> {
+        let price_feed = &ctx.accounts.price_feed;
+        let clock = &ctx.accounts.clock;
+        // Get the current timestamp
+        let timestamp: i64 = clock.unix_timestamp;
+        // Load the price from the price feed. Here, the price can be no older than 500 seconds.
+        let price: pyth_sdk::Price = price_feed
+            .get_price_no_older_than(timestamp, 30)
+            .ok_or(ErrorCode::PythError)?;
+
+        let confidence_interval: u64 = price.conf;
+
+        let asset_price_full: i64 = price.price;
+
+        let asset_exponent: i32 = price.expo;
+
+        let asset_price = asset_price_full as f64 * 10f64.powi(asset_exponent);
+
+        msg!("Price: {}", asset_price);
+        msg!("Confidence interval: {}", confidence_interval);
+
+        Ok(())
+    }
+}
+
+#[derive(Accounts)]
+pub struct Pyth<'info> {
+    pub price_feed: Account<'info, PriceFeed>,
+    pub system_program: Program<'info, System>,
+    pub clock: Sysvar<'info, Clock>,
+}

+ 43 - 0
oracles/pyth/anchor/programs/pythexample/src/state.rs

@@ -0,0 +1,43 @@
+// This file is the pyth default implementation for using PriceAccount in your Anchor context.
+use anchor_lang::prelude::*;
+use pyth_sdk_solana::state::load_price_account;
+use std::ops::Deref;
+use std::str::FromStr;
+
+// import the error code from the error.rs file
+use crate::error::ErrorCode;
+
+#[derive(Clone)]
+pub struct PriceFeed(pyth_sdk::PriceFeed);
+
+impl anchor_lang::Owner for PriceFeed {
+    fn owner() -> Pubkey {
+        // The mainnet Pyth program ID
+        let oracle_addr = "FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH";
+        return Pubkey::from_str(&oracle_addr).unwrap();
+    }
+}
+
+impl anchor_lang::AccountDeserialize for PriceFeed {
+    fn try_deserialize_unchecked(data: &mut &[u8]) -> Result<Self> {
+        let account = load_price_account(data).map_err(|_x| error!(ErrorCode::PythError))?;
+        let zeros: [u8; 32] = [0; 32];
+        let dummy_key = Pubkey::new_from_array(zeros);
+        let feed = account.to_price_feed(&dummy_key);
+        return Ok(PriceFeed(feed));
+    }
+}
+
+impl anchor_lang::AccountSerialize for PriceFeed {
+    fn try_serialize<W: std::io::Write>(&self, _writer: &mut W) -> std::result::Result<(), Error> {
+        Err(error!(ErrorCode::TryToSerializePriceAccount))
+    }
+}
+
+impl Deref for PriceFeed {
+    type Target = pyth_sdk::PriceFeed;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}

+ 30 - 0
oracles/pyth/anchor/tests/pythexample.ts

@@ -0,0 +1,30 @@
+import * as anchor from "@coral-xyz/anchor";
+import { Program } from "@coral-xyz/anchor";
+import { Pythexample } from "../target/types/pythexample";
+
+describe("pythexample", () => {
+  // Configure the client to use the local cluster.
+  anchor.setProvider(anchor.AnchorProvider.env());
+
+  const program = anchor.workspace.Pythexample as Program<Pythexample>;
+
+  const PYTH_FEED_ID = new anchor.web3.PublicKey(
+    "H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG"
+  );
+
+  it("Check SOL_USD Price", async () => {
+    const tx = await program.methods
+      .readPrice()
+      .accounts({
+        priceFeed: PYTH_FEED_ID,
+        systemProgram: anchor.web3.SystemProgram.programId,
+        clock: anchor.web3.SYSVAR_CLOCK_PUBKEY,
+      })
+      .rpc();
+
+    console.log(
+      "Your transaction signature, find the price in the program logs",
+      tx
+    );
+  });
+});

+ 11 - 0
oracles/pyth/anchor/tsconfig.json

@@ -0,0 +1,11 @@
+{
+            "compilerOptions": {
+              "types": ["mocha", "chai"],
+              "typeRoots": ["./node_modules/@types"],
+              "lib": ["es2015"],
+              "module": "commonjs",
+              "target": "es6",
+              "esModuleInterop": true
+            }
+          }
+