Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions contracts/pyth/PythAggregatorV3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,19 @@ contract PythAggregatorV3 {
bytes32 public priceId;
IPyth public pyth;

uint256 public constant HEART_BEAT = 1 days;

constructor(address _pyth, bytes32 _priceId) {
priceId = _priceId;
pyth = IPyth(_pyth);
}

function updateFeeds(bytes[] calldata priceUpdateData) public payable {
// Update the prices to the latest available values and pay the required fee for it. The `priceUpdateData` data
// should be retrieved from our off-chain Price Service API using the `pyth-evm-js` package.
// See section "How Pyth Works on EVM Chains" below for more information.
uint256 fee = pyth.getUpdateFee(priceUpdateData);
pyth.updatePriceFeeds{value: fee}(priceUpdateData);

// refund remaining eth
payable(msg.sender).call{value: address(this).balance}("");
pyth.updatePriceFeeds{value: msg.value}(priceUpdateData);
}

function decimals() public view virtual returns (uint8) {
PythStructs.Price memory price = pyth.getPriceNoOlderThan(priceId, 1 days);
PythStructs.Price memory price = pyth.getPriceNoOlderThan(priceId, HEART_BEAT);
return uint8(-1 * int8(price.expo));
}

Expand All @@ -55,12 +50,12 @@ contract PythAggregatorV3 {
}

function latestAnswer() public view virtual returns (int256) {
PythStructs.Price memory price = pyth.getPriceNoOlderThan(priceId, 1 days);
PythStructs.Price memory price = pyth.getPriceNoOlderThan(priceId, HEART_BEAT);
return int256(price.price);
}

function latestTimestamp() public view returns (uint256) {
PythStructs.Price memory price = pyth.getPriceNoOlderThan(priceId, 1 days);
PythStructs.Price memory price = pyth.getPriceNoOlderThan(priceId, HEART_BEAT);
return price.publishTime;
}

Expand Down Expand Up @@ -95,4 +90,8 @@ contract PythAggregatorV3 {
roundId = uint80(price.publishTime);
return (roundId, int256(price.price), price.publishTime, price.publishTime, roundId);
}

function getUpdateFee(bytes[] calldata priceUpdateData) public view returns (uint256) {
return pyth.getUpdateFee(priceUpdateData);
}
}
15 changes: 15 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import "./tasks/deploy-pyth";
import "./tasks/deploy-contract";
import "./tasks/deploy-double-agg";
import "./tasks/update-pyth";
import "./tasks/get-pyth-data";
import "./tasks/update-pyth-feeds";

export default {
solidity: "0.8.20",
Expand Down Expand Up @@ -57,6 +59,10 @@ export default {
url: `https://rpc.berachain.com`,
accounts: [process.env.WALLET_PRIVATE_KEY || ""],
},
mezo: {
url: `https://jsonrpc-mezo.boar.network`,
accounts: [process.env.WALLET_PRIVATE_KEY || ""],
},
},
etherscan: {
apiKey: {
Expand All @@ -69,6 +75,7 @@ export default {
berachain: process.env.BERASCAN_KEY || "",
manta: "",
era: process.env.ZKSYNC_KEY || "",
mezo:"TEST",
},
customChains: [
{
Expand Down Expand Up @@ -120,6 +127,14 @@ export default {
browserURL: "https://explorer.zircuit.com",
},
},
{
network: "mezo",
chainId: 31612,
urls: {
apiURL: "https://api.explorer.mezo.org/api",
browserURL: "https://explorer.mezo.org"
}
},
],
},
};
14 changes: 9 additions & 5 deletions tasks/deploy-pyth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,23 @@ task(`deploy-pyth`)
const [deployer] = await hre.ethers.getSigners();
console.log("i am", deployer.address);
console.log("args", args);
const contract = await hre.ethers.deployContract("PythAggregatorV3", args);

const contract = await hre.deployments.deploy("PythAggregatorV3", {
from: deployer.address,
args: args,
log: true,
});

await contract.waitForDeployment();
console.log(`deployed to`, contract.target);
console.log(`deployed to`, contract.address);

// verify contract for tesnet & mainnet
if (process.env.NODE_ENV != "test") {
// Verify contract programmatically
await hre.run("verify:verify", {
address: contract.target,
address: contract.address,
constructorArguments: args,
});
} else {
console.log(`Contract not verified, deployed locally.`);
}
});
});
26 changes: 26 additions & 0 deletions tasks/get-pyth-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { task } from "hardhat/config";
import { isAddress } from "ethers";
import { pythContracts } from "../utils/pyth";

task(`get-pyth-data`)
.addParam("priceid")
.setAction(async ({ priceid }, hre) => {
const pythContract = pythContracts[hre.network.name] as `0x${string}`;

if (!isAddress(pythContract)) {
throw new Error("Invalid Pyth contract address");
}

console.log(`\n🔗 Network: ${hre.network.name}`);
console.log(`📍 Pyth Contract: ${pythContract}`);

// Get contract instance
const pyth = await hre.ethers.getContractAt("IPyth", pythContract);

const getPriceUnsafe = await pyth.getPriceUnsafe(priceid);
console.log("Price unsafe:", getPriceUnsafe);

const getPrice = await pyth.getPriceNoOlderThan(priceid, 86400); // 1 day in seconds
console.log("Price no older than:", getPrice);
});

44 changes: 44 additions & 0 deletions tasks/update-pyth-feeds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { task } from "hardhat/config";
import { isAddress } from "ethers";
import { pythContracts } from "../utils/pyth";
import { EvmPriceServiceConnection } from "@pythnetwork/pyth-evm-js";

task("update-pyth-feeds")
.setAction(async (_, hre) => {
const pythContract = pythContracts[hre.network.name] as `0x${string}`;

if (!isAddress(pythContract)) {
throw new Error("Invalid Pyth contract address");
}

console.log(`🔗 Network: ${hre.network.name}`);
console.log(`📍 Pyth Contract: ${pythContract}`);

// Price IDs to update
const priceIds = [
"0xc9d8b075a5c69303365ae23633d4e085199bf5c520a3b90fed1322a0342ffc33", // wbtc/usd
"0x56a3121958b01f99fdc4e1fd01e81050602c7ace3a571918bb55c6a96657cca9", // tbtc/usd
];

// Get contract instance
const pyth = await hre.ethers.getContractAt("IPyth", pythContract);

// Get connection to the Pyth network
const connection = new EvmPriceServiceConnection(
"https://hermes.pyth.network"
); // See Hermes endpoints section below for other endpoints

// Get price update data in bytes format
const priceUpdateData = (await connection.getPriceFeedsUpdateData(
priceIds
)) as any;

// Get update fee. For one price id, the value of fee is 1
const fee = await pyth.getUpdateFee(priceUpdateData);
console.log("Update fee:", fee.toString());

// Update price feeds
// const tx = await pyth.updatePriceFeeds(priceUpdateData, { value: fee });
// await tx.wait();
// console.log("Tx hash:", tx.hash);
});
7 changes: 5 additions & 2 deletions tasks/update-pyth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ task(`update-pyth`)

const contract = await hre.ethers.getContractAt(
"PythAggregatorV3",
"0xd04a2e318e4557bb81344ea485b63d0d55732a37"
"0x94eae1d41036c23900DB6Af8Dd27841f4E3a5906"
);

const updateData: [`0x${string}`] = [priceid];
Expand All @@ -24,8 +24,11 @@ task(`update-pyth`)
updateData
)) as any;

const fee = await contract.getUpdateFee(priceUpdateData);
console.log("fee", fee);

const tx = await contract.updateFeeds(priceUpdateData, {
value: 1000000000n,
value: fee,
});

console.log(`tx`, tx);
Expand Down
5 changes: 4 additions & 1 deletion utils/pyth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ export const priceIdsUSD = {
usdc: "0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a",
usdt: "0x2b89b9dc8fdf9f34709a5b106b472f0f39bb6ca9ce04b0fd7f2e971688e2e53b",
zero: "0x9a11b5c6c8d6d266444459316c3aee7684aaa5a5434b189a173d8cddbb3deaae",
wbtc: "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43",
wbtc: "0xc9d8b075a5c69303365ae23633d4e085199bf5c520a3b90fed1322a0342ffc33",
wsteth: "0x6df640f3b8963d8f8358f791f352b8364513f6ab1cca5ed3f1f7b5448980e784",
bera: "0x962088abcfdbdb6e30db2e340c8cf887d9efb311b1f2f17b155a63dbb6d40265",
manta: "0xc3883bcf1101c111e9fcfe2465703c47f2b638e21fef2cce0502e6c8f416e0e2",
tbtc: "0x56a3121958b01f99fdc4e1fd01e81050602c7ace3a571918bb55c6a96657cca9",
musd: "0x0617a9b725011a126a2b9fd53563f4236501f32cf76d877644b943394606c6de",
};

// MATICX: "----",
Expand All @@ -31,4 +33,5 @@ export const pythContracts: { [key: string]: string } = {
linea: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729",
berachain: "0x2880ab155794e7179c9ee2e38200202908c17b43",
blast: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729",
mezo: "0x2880aB155794e7179c9eE2e38200202908C17B43",
};