Skip to content

Commit

Permalink
Merge pull request #31 from hirosystems/feat/oracle-update
Browse files Browse the repository at this point in the history
feat: add decode-price-feeds to oracle
  • Loading branch information
ali-bahjati authored Jan 9, 2024
2 parents 987326d + 4a6ae2e commit 3afb7a8
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 28 deletions.
9 changes: 2 additions & 7 deletions Clarinet.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,13 @@ contract_id = 'SP2J933XB2CP2JQ1A4FGN8JA968BBG3NK3EKZ7Q9F.hk-ecc-v1'
[[project.requirements]]
contract_id = 'SP2J933XB2CP2JQ1A4FGN8JA968BBG3NK3EKZ7Q9F.hk-merkle-tree-keccak160-v1'

[contracts.pyth-helper-v1]
path = 'contracts/pyth-helper-v1.clar'
clarity_version = 2
epoch = 2.4

[contracts.pyth-governance-v1]
path = 'contracts/pyth-governance-v1.clar'
clarity_version = 2
epoch = 2.4

[contracts.pyth-oracle-v1]
path = 'contracts/pyth-oracle-v1.clar'
[contracts.pyth-oracle-v2]
path = 'contracts/pyth-oracle-v2.clar'
clarity_version = 2
epoch = 2.4

Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,14 @@ That can be consumed with the following invocation:

```clarity
(contract-call?
'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-helper-v1 ;; Address of the helper contract
'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-oracle-v2 ;; Address of the helper contract
read-price
0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) ;; BTC-USD price identifier
{
pyth-storage-contract: 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-storage-v1,
pyth-decoder-contract: 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-pnau-decoder-v1,
wormhole-core-contract: 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.wormhole-core-v1
}
```

The authenticity of the price feeds is verified during their ingestion, making the cost of queries as light as possible.
Expand Down Expand Up @@ -121,9 +126,14 @@ This VAA can be encoded as a Clarity buffer, and submitted to the Pyth contract

```clarity
(contract-call?
'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-helper-v1 ;; Address of the helper contract
'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-oracle-v2 ;; Address of the helper contract
verify-and-update-price
0x504e41550100000003b8...a7b10321ad7c2404a910) ;; BTC-USD price update
0x504e41550100000003b8...a7b10321ad7c2404a910 ;; BTC-USD price update
{
pyth-storage-contract: 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-storage-v1,
pyth-decoder-contract: 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.pyth-pnau-decoder-v1,
wormhole-core-contract: 'SP2T5JKWWP3FYYX4YRK8GK5BG2YCNGEAEY2P2PKN0.wormhole-core-v1
})
```

If the VAA is valid, the contract call will return a payload with the following signature:
Expand Down
2 changes: 1 addition & 1 deletion contracts/pyth-governance-v1.clar
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
pyth-storage-contract: principal,
wormhole-core-contract: principal
} {
pyth-oracle-contract: .pyth-oracle-v1,
pyth-oracle-contract: .pyth-oracle-v2,
pyth-decoder-contract: .pyth-pnau-decoder-v1,
pyth-storage-contract: .pyth-store-v1,
wormhole-core-contract: .wormhole-core-v1
Expand Down
12 changes: 0 additions & 12 deletions contracts/pyth-helper-v1.clar

This file was deleted.

20 changes: 20 additions & 0 deletions contracts/pyth-oracle-v1.clar → contracts/pyth-oracle-v2.clar
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,23 @@
;; Charge fee
(unwrap! (stx-transfer? fee-amount tx-sender (get address fee-info)) ERR_BALANCE_INSUFFICIENT)
(ok updated-prices))))

(define-public (decode-price-feeds
(price-feed-bytes (buff 8192))
(execution-plan {
pyth-storage-contract: <pyth-storage-trait>,
pyth-decoder-contract: <pyth-decoder-trait>,
wormhole-core-contract: <wormhole-core-trait>
}))
(begin
;; Check execution flow
(try! (contract-call? .pyth-governance-v1 check-execution-flow contract-caller (some execution-plan)))
;; Perform contract-call
(let ((pyth-decoder-contract (get pyth-decoder-contract execution-plan))
(wormhole-core-contract (get wormhole-core-contract execution-plan))
(decoded-prices (try! (contract-call? pyth-decoder-contract decode-and-verify-price-feeds price-feed-bytes wormhole-core-contract)))
(fee-info (contract-call? .pyth-governance-v1 get-fee-info))
(fee-amount (* (len decoded-prices) (* (get mantissa fee-info) (pow u10 (get exponent fee-info))))))
;; Charge fee
(unwrap! (stx-transfer? fee-amount tx-sender (get address fee-info)) ERR_BALANCE_INSUFFICIENT)
(ok decoded-prices))))
4 changes: 2 additions & 2 deletions unit-tests/pyth/oracle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { ParsedTransactionResult } from "@hirosystems/clarinet-sdk";
import { pnauMainnetVaas } from "./fixtures";
import { wormhole } from "../wormhole/helpers";

const pythOracleContractName = "pyth-oracle-v1";
const pythOracleContractName = "pyth-oracle-v2";
const pythDecoderPnauContractName = "pyth-pnau-decoder-v1";
const pythStorageContractName = "pyth-store-v1";
const wormholeCoreContractName = "wormhole-core-v1";

describe("pyth-oracle-v1::decode-and-verify-price-feeds mainnet VAAs", () => {
describe("pyth-oracle-v2::decode-and-verify-price-feeds mainnet VAAs", () => {
const accounts = simnet.getAccounts();
const sender = accounts.get("wallet_1")!;

Expand Down
50 changes: 49 additions & 1 deletion unit-tests/pyth/pnau.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it } from "vitest";
import { wormhole } from "../wormhole/helpers";
import { pyth } from "./helpers";

const pythOracleContractName = "pyth-oracle-v1";
const pythOracleContractName = "pyth-oracle-v2";
const pythDecoderPnauContractName = "pyth-pnau-decoder-v1";
const pythGovernanceContractName = "pyth-governance-v1";
const pythStorageContractName = "pyth-store-v1";
Expand Down Expand Up @@ -788,5 +788,53 @@ describe("pyth-pnau-decoder-v1::decode-and-verify-price-feeds failures", () => {
}),
]),
);

// decode-price-feeds should not be filtering the outdated results
res = simnet.callPublicFn(
pythOracleContractName,
"decode-price-feeds",
[Cl.buffer(pnau), executionPlan],
sender,
);
expect(res.result).toBeOk(
Cl.list([
Cl.tuple({
"price-identifier": Cl.buffer(pyth.BtcPriceIdentifier),
price: Cl.int(actualPricesUpdates.decoded[0].price),
conf: Cl.uint(actualPricesUpdates.decoded[0].conf),
"ema-conf": Cl.uint(actualPricesUpdates.decoded[0].emaConf),
"ema-price": Cl.int(actualPricesUpdates.decoded[0].emaPrice),
expo: Cl.int(actualPricesUpdates.decoded[0].expo),
"prev-publish-time": Cl.uint(
actualPricesUpdates.decoded[0].prevPublishTime,
),
"publish-time": Cl.uint(actualPricesUpdates.decoded[0].publishTime),
}),
Cl.tuple({
"price-identifier": Cl.buffer(pyth.StxPriceIdentifier),
price: Cl.int(actualPricesUpdates.decoded[1].price),
conf: Cl.uint(actualPricesUpdates.decoded[1].conf),
"ema-conf": Cl.uint(actualPricesUpdates.decoded[1].emaConf),
"ema-price": Cl.int(actualPricesUpdates.decoded[1].emaPrice),
expo: Cl.int(actualPricesUpdates.decoded[1].expo),
"prev-publish-time": Cl.uint(
actualPricesUpdates.decoded[1].prevPublishTime,
),
"publish-time": Cl.uint(actualPricesUpdates.decoded[1].publishTime),
}),
Cl.tuple({
"price-identifier": Cl.buffer(pyth.UsdcPriceIdentifier),
price: Cl.int(actualPricesUpdates.decoded[2].price),
conf: Cl.uint(actualPricesUpdates.decoded[2].conf),
"ema-conf": Cl.uint(actualPricesUpdates.decoded[2].emaConf),
"ema-price": Cl.int(actualPricesUpdates.decoded[2].emaPrice),
expo: Cl.int(actualPricesUpdates.decoded[2].expo),
"prev-publish-time": Cl.uint(
actualPricesUpdates.decoded[2].prevPublishTime,
),
"publish-time": Cl.uint(actualPricesUpdates.decoded[2].publishTime),
}),
]),
);
});
});
4 changes: 2 additions & 2 deletions unit-tests/pyth/ptgm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { hexToBytes } from "@noble/hashes/utils";
import { ParsedTransactionResult } from "@hirosystems/clarinet-sdk";
import { ptgmTestnetVaas } from "./fixtures";

const pythOracleContractName = "pyth-oracle-v1";
const pythOracleContractName = "pyth-oracle-v2";
const pythStorageContractName = "pyth-store-v1";
const pythDecoderPnauContractName = "pyth-pnau-decoder-v1";
const pythGovernanceContractName = "pyth-governance-v1";
Expand Down Expand Up @@ -616,7 +616,7 @@ describe("pyth-governance-v1::update-pyth-oracle-contract", () => {
const guardianSet = wormhole.generateGuardianSetKeychain(19);
let updateOracleContract = {
address: "ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG",
contractName: "pyth-oracle-v2",
contractName: "pyth-oracle-new-version",
};
let ptgmVaaPayload = pyth.buildPtgmVaaPayload({ updateOracleContract });

Expand Down

0 comments on commit 3afb7a8

Please sign in to comment.