Skip to content

Commit faa4637

Browse files
committed
svm guide update
1 parent d4af9c8 commit faa4637

File tree

2 files changed

+159
-4
lines changed

2 files changed

+159
-4
lines changed
File renamed without changes.

pages/lazer/integrate-as-consumer/svm.mdx

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,176 @@ Integrating with Pyth Lazer in smart contracts as a consumer is a three-step pro
1414

1515
### Use Pyth Lazer SDK into smart contracts
1616

17-
Pyth Lazer provides [pyth-lazer-sdk]() crate, which allows consumers to parse the price updates.
17+
Pyth Lazer provides a [Rust SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/sdk/rust), which allows consumers to parse the price updates.
18+
19+
Add the following to your `Cargo.toml` file:
20+
21+
```toml copy
22+
[dependencies]
23+
pyth-lazer-sdk = 0.1.0
24+
```
25+
26+
Now you can create an instruction or multiple instructions that will receive Pyth Lazer messages.
27+
The instruction data sent to your program should include a byte array containing the Pyth Lazer message. The instruction data can also contain any other parameters your contracts may need.
28+
29+
In order to successfully validate the Pyth Lazer message, the instruction needs to receive the standard Solana sysvar account and Pyth Lazer storage account (`3rdJbqfnagQ4yx9HXJViD4zc4xpiSqmFsKpPuSCQVyQL`). You may also add any other accounts you need.
30+
31+
The following code can be used to set up a new instruction within a Solana contract:
32+
33+
```rust copy
34+
use num_derive::FromPrimitive;
35+
use num_traits::FromPrimitive;
36+
37+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromPrimitive)]
38+
pub enum Instruction {
39+
//...
40+
/// Update price.
41+
/// Data: `UpdateArgs` followed by a signed Pyth Lazer update.
42+
/// Accounts:
43+
/// 1. sysvar account [readonly] - required for Pyth Lazer
44+
/// 2. data account [writable] - needed by our example contract
45+
/// 3. pyth storage account [readonly] - required for Pyth Lazer
46+
Update = 1,
47+
}
48+
49+
/// Inputs to the `Update` instruction. `UpdateArgs` must be followed by a signed Pyth Lazer message.
50+
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
51+
#[repr(C, packed)]
52+
pub struct UpdateArgs {
53+
/// Example argument
54+
pub hello: u64,
55+
}
56+
57+
/// Program entrypoint's implementation.
58+
pub fn process_instruction(
59+
program_id: &Pubkey,
60+
accounts: &[AccountInfo],
61+
instruction_data: &[u8],
62+
) -> ProgramResult {
63+
// In our example contract, the first byte is the ID of the instruction.
64+
let instruction = *instruction_data
65+
.first()
66+
.ok_or(ProgramError::InvalidInstructionData)?;
67+
let instruction =
68+
Instruction::from_u8(instruction).ok_or(ProgramError::InvalidInstructionData)?;
69+
let instruction_args = &instruction_data[1..];
70+
71+
match instruction {
72+
Instruction::Initialize => {
73+
process_initialize_instruction(program_id, accounts, instruction_args)
74+
}
75+
Instruction::Update => process_update_instruction(program_id, accounts, instruction_args),
76+
}
77+
}
78+
79+
pub fn process_update_instruction(
80+
program_id: &Pubkey,
81+
accounts: &[AccountInfo],
82+
instruction_args: &[u8],
83+
) -> ProgramResult {
84+
// Verify accounts passed to the instruction.
85+
if accounts.len() != 3 {
86+
return Err(ProgramError::NotEnoughAccountKeys);
87+
}
88+
let sysvar_account = &accounts[0];
89+
let data_account = &accounts[1];
90+
let pyth_storage_account = &accounts[2];
91+
// See below for next steps...
92+
}
93+
```
94+
95+
Call `pyth_lazer_sdk::verify_message{:rust}` function with appropriate arguments to validate the Pyth Lazer signature of the message.
96+
97+
```rust copy
98+
// Offset of pyth message within the original instruction_data.
99+
// 1 byte is the instruction id.
100+
let pyth_message_total_offset = size_of::<UpdateArgs>() + 1;
101+
// We expect the instruction to the built-in ed25519 program to be
102+
// the first instruction within the transaction.
103+
let ed25519_instruction_index = 0;
104+
// We expect our signature to be the first (and only) signature to be checked
105+
// by the built-in ed25519 program within the transaction.
106+
let signature_index = 0;
107+
// Check signature verification.
108+
let verified = pyth_lazer_sdk::verify_message(
109+
pyth_storage_account,
110+
sysvar_account,
111+
pyth_message,
112+
ed25519_instruction_index,
113+
signature_index,
114+
pyth_message_total_offset.try_into().unwrap(),
115+
)?;
116+
```
117+
118+
<Callout type="info" icon="💡">
119+
Note: When using native ed25519 signatures on Solana, we must use the built-in
120+
ed25519 program provided by the Solana runtime. This program can't be invoked
121+
from another contract. Instead, it must be called in an explicit instruction
122+
within the submitted transaction. This means that the sender of the
123+
transaction must include that instruction in the transaction. Our SDK
124+
leverages Solana runtime capabilities to ensure the ed25519 program has been
125+
correctly called in the transaction.
126+
</Callout>
127+
128+
Now Parse the Pyth Lazer message.
129+
130+
```rust copy
131+
// Deserialize and use the payload.
132+
let data = PayloadData::deserialize_slice_le(verified.payload)
133+
.map_err(|_| ProgramError::InvalidInstructionData)?;
134+
135+
if data.feeds.is_empty() || data.feeds[0].properties.is_empty() {
136+
return Err(ProgramError::InvalidInstructionData);
137+
}
138+
```
139+
140+
Now you can update the state accourding to the contract's logic.
141+
142+
```rust copy
143+
// Read the data PDA of our example contract.
144+
let mut state_data = data_account.data.borrow_mut();
145+
let state =
146+
try_from_bytes_mut::<State>(*state_data).map_err(|_| ProgramError::InvalidAccountData)?;
147+
148+
if state.price_feed != data.feeds[0].feed_id.0 {
149+
return Err(ProgramError::InvalidInstructionData);
150+
}
151+
if data.channel_id != Channel::RealTime.id() {
152+
return Err(ProgramError::InvalidInstructionData);
153+
}
154+
if data.timestamp_us.0 <= state.latest_timestamp {
155+
return Err(ProgramError::AccountAlreadyInitialized);
156+
}
157+
let PayloadPropertyValue::Price(Some(price)) = data.feeds[0].properties[0] else {
158+
return Err(ProgramError::InvalidInstructionData);
159+
};
160+
state.latest_price = price.into_inner().into();
161+
state.latest_timestamp = data.timestamp_us.0;
162+
```
163+
164+
### Subscribe to Pyth Lazer to receive Price Updates
165+
166+
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.
167+
168+
Consult [How to fetch price updates from Pyth Lazer](../fetch-price-updates.mdx) for a complete step-by-step guide.
169+
170+
### Include the price updates into smart contract transactions
171+
172+
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 `update_price` method of your smart contract.
18173

19174
</Steps>
20175

21176
## Additional Resources
22177

23-
You may find these additional resources helpful for integrating Pyth Lazer into your EVM smart contracts.
178+
You may find these additional resources helpful for integrating Pyth Lazer into your Solana smart contracts.
24179

25180
### Price Feed IDs
26181

27182
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.
28183

29184
### Examples
30185

31-
[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.
186+
[pyth-lazer-example-solana](https://github.com/pyth-network/pyth-examples/tree/main/lazer/solana) is a simple example contract that parses and consumes price updates from Pyth Lazer.
32187

33188
[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.
34189

@@ -38,4 +193,4 @@ TODO:
38193

39194
### Error Codes
40195

41-
TODO: Add error codes for EVM.
196+
TODO: Add error codes for Solana.

0 commit comments

Comments
 (0)