|
| 1 | +import { Callout, Tabs, Steps } from "nextra/components"; |
| 2 | + |
| 3 | +# Integrate Pyth Lazer as a Consumer on Solana |
| 4 | + |
| 5 | +This guide is intended to serve users who wants to consume prices from the Pyth Lazer on **Solana**. |
| 6 | + |
| 7 | +Integrating with Pyth Lazer in smart contracts as a consumer is a three-step process: |
| 8 | + |
| 9 | +1. **Use** Pyth Lazer SDK into Solana smart contracts to parse the price updates. |
| 10 | +2. **Subscribe** to Pyth Lazer websocket to receive price updates on backend or frontend. |
| 11 | +3. **Include** the price updates into the smart contract transactions. |
| 12 | + |
| 13 | +<Steps> |
| 14 | + |
| 15 | +### Use Pyth Lazer SDK into smart contracts |
| 16 | + |
| 17 | +Pyth Lazer provides a [solidity SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/contracts/evm), which allows consumers to parse the price updates. |
| 18 | + |
| 19 | +```bash copy |
| 20 | +forge install pythnet/pyth-crosschain |
| 21 | +``` |
| 22 | + |
| 23 | +Add the following to `requirements.txt{:js}` file: |
| 24 | + |
| 25 | +```bash copy |
| 26 | +pyth-lazer-sdk/=lib/pythnet/pyth-crosschain/lazer/contracts/evm |
| 27 | +``` |
| 28 | + |
| 29 | +Once the SDK is installed, one can import the sdk into smart contracts: |
| 30 | + |
| 31 | +```solidity copy |
| 32 | +import { PythLazer } from "pyth-lazer/PythLazer.sol"; |
| 33 | +import { PythLazerLib } from "pyth-lazer/PythLazerLib.sol"; |
| 34 | +
|
| 35 | +``` |
| 36 | + |
| 37 | +After importing the SDK, initialize the [`PythLazer`](https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/contracts/evm/src/PythLazer.sol#L7) contract and set up state varables to store prices and timestamps: |
| 38 | + |
| 39 | +```solidity copy |
| 40 | +contract ExampleConsumer { |
| 41 | + // Example state. |
| 42 | + PythLazer pythLazer; |
| 43 | + uint64 public price; |
| 44 | + uint64 public timestamp; |
| 45 | +
|
| 46 | + //... |
| 47 | +
|
| 48 | + constructor(address pythLazerAddress) { |
| 49 | + pythLazer = PythLazer(pythLazerAddress); |
| 50 | + } |
| 51 | +} |
| 52 | +
|
| 53 | +``` |
| 54 | + |
| 55 | +Add an argument of type `bytes calldata{:solidity}` to the method which will receive the Pyth Lazer price udpate: |
| 56 | + |
| 57 | +```solidity copy |
| 58 | +function updatePrice(bytes calldata priceUpdate) public payable { |
| 59 | + uint256 verification_fee = pythLazer.verification_fee(); |
| 60 | + (bytes calldata payload, ) = verifyUpdate{ value: verification_fee }(update); |
| 61 | + //... |
| 62 | +} |
| 63 | +
|
| 64 | +``` |
| 65 | + |
| 66 | +The `verifyUpdate` function will verify the price update and return the payload and the verification fee. This call takes a fee which can be queried from [`verification_fee(){:solidity}`](https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/contracts/evm/src/PythLazer.sol#L9) function and passed to the `verifyUpdate` call. This fee is used to cover the cost of verifying the price update. |
| 67 | + |
| 68 | +This SDK provides [`parsePayloadHeader`](https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/contracts/evm/src/PythLazerLib.sol#L21) method to retrieve the values from the payload header. |
| 69 | + |
| 70 | +```solidity copy |
| 71 | +(uint64 _timestamp, Channel channel, uint8 feedsLen, uint16 pos) = parsePayloadHeader(payload); |
| 72 | +``` |
| 73 | + |
| 74 | +This method returns: |
| 75 | + |
| 76 | +- `_timestamp`: The timestamp of the price update. |
| 77 | +- `channel`: The channel of the price update. |
| 78 | +- `feedsLen`: The number of feeds in the price update. |
| 79 | +- `pos`: The cursor position of the payload. |
| 80 | + |
| 81 | +One can iterate over all the feeds and properties present within the price update, modifying the state variables as necessary. |
| 82 | + |
| 83 | +Here is an example of how to iterate over the feeds and properties: |
| 84 | + |
| 85 | +```solidity copy |
| 86 | +for (uint8 i = 0; i < feedsLen; i++) { |
| 87 | + uint32 feedId; |
| 88 | + uint8 num_properties; |
| 89 | + (feedId, num_properties, pos) = parseFeedHeader(payload, pos); |
| 90 | + for (uint8 j = 0; j < num_properties; j++) { |
| 91 | + PriceFeedProperty property; |
| 92 | + (property, pos) = parseFeedProperty(payload, pos); |
| 93 | + if (property == PriceFeedProperty.Price) { |
| 94 | + uint64 _price; |
| 95 | + (_price, pos) = parseFeedValueUint64(payload, pos); |
| 96 | + if (feedId == 2 && _timestamp > timestamp) { |
| 97 | + price = _price; |
| 98 | + timestamp = _timestamp; |
| 99 | + } |
| 100 | + } else if (property == PriceFeedProperty.BestBidPrice) { |
| 101 | + uint64 _price; |
| 102 | + (_price, pos) = parseFeedValueUint64(payload, pos); |
| 103 | + } else if (property == PriceFeedProperty.BestAskPrice) { |
| 104 | + uint64 _price; |
| 105 | + (_price, pos) = parseFeedValueUint64(payload, pos); |
| 106 | + } else { |
| 107 | + revert("unknown property"); |
| 108 | + } |
| 109 | + } |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +<Callout type="warning" emoji="⚠️"> |
| 114 | + Make sure to pass the `pos` variable to every parsing call and assign the |
| 115 | + returned `pos` value to the same variable. Failure to do so will cause |
| 116 | + incorrect parsing results. |
| 117 | +</Callout> |
| 118 | + |
| 119 | +<Callout type="warning" emoji="⚠️"> |
| 120 | + When calling these parse functions, you must not skip price feeds or |
| 121 | + properties. Every parsing function will modify your `pos` variable, so |
| 122 | + skipping a call of `parseFeedHeader`, `parseFeedProperty`, or |
| 123 | + `parseFeedValueUint64` will lead to incorrect parsing results. Keep in mind |
| 124 | + that you can customize the set of price feeds and properties when requesting |
| 125 | + price updates via WebSocket. This will be explained in the next step. |
| 126 | +</Callout> |
| 127 | + |
| 128 | +### Subscribe to Pyth Lazer to receive Price Updates |
| 129 | + |
| 130 | +Pyth Lazer provides a websocket endpoint to receive price updates. Moreover, Pyth Lazer also provides a [typescript SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/sdk/js) to subscribe to the websocket endpoint. |
| 131 | + |
| 132 | +Consult [How to fetch price updates from Pyth Lazer](../fetch-price-updates.mdx) for a complete step-by-step guide. |
| 133 | + |
| 134 | +### Include the price updates into smart contract transactions |
| 135 | + |
| 136 | +Now that you have the price updates, and your smart contract is able to parse the price updates, you can include the price updates into the smart contract transactions by passing the price updates received from the previous step as an argument to the `updatePrice` method of your smart contract. |
| 137 | + |
| 138 | +</Steps> |
| 139 | + |
| 140 | +## Additional Resources |
| 141 | + |
| 142 | +You may find these additional resources helpful for integrating Pyth Lazer into your EVM smart contracts. |
| 143 | + |
| 144 | +### Price Feed IDs |
| 145 | + |
| 146 | +Pyth Lazer supports a wide range of price feeds. Consult the [Price Feed IDs](../price-feeds.mdx) page for a complete list of supported price feeds. |
| 147 | + |
| 148 | +### Examples |
| 149 | + |
| 150 | +[Pyth-lazer-example-evm](https://github.com/pyth-network/pyth-examples/tree/main/lazer/evm) is a simple example contract that parses and consumes price updates from Pyth Lazer. |
| 151 | + |
| 152 | +[pyth-lazer-example-js](https://github.com/pyth-network/pyth-examples/tree/main/lazer/js) is a simple example for subscribing to the Pyth Lazer websocket. |
| 153 | + |
| 154 | +### API Reference |
| 155 | + |
| 156 | +TODO: |
| 157 | + |
| 158 | +### Error Codes |
| 159 | + |
| 160 | +TODO: Add error codes for EVM. |
0 commit comments