Skip to content

Commit ecabaf8

Browse files
authored
fix(lazer): update svm integration guide (#565)
* fix(lazer): update svm integration guide * fix(lazer): replace sdk link with protocol crate link * fix(lazer): refer to crates.io for latest version
1 parent b310232 commit ecabaf8

File tree

1 file changed

+115
-23
lines changed
  • pages/lazer/integrate-as-consumer

1 file changed

+115
-23
lines changed

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

Lines changed: 115 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,34 @@ 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 a [Rust SDK](https://github.com/pyth-network/pyth-crosschain/tree/main/lazer/sdk/rust), which allows consumers to parse the price updates.
17+
Pyth Lazer provides a [Solana SDK](https://docs.rs/pyth-lazer-solana-contract/latest/pyth_lazer_solana_contract/), which allows consumers to parse and verify the price updates on Solana-compatible chains.
1818

19-
Add the following to your `Cargo.toml` file:
19+
To start, add the following to your `Cargo.toml` file (please check the current latest version at [crates.io](https://crates.io/crates/pyth-lazer-solana-contract)):
2020

2121
```toml copy
2222
[dependencies]
23-
pyth-lazer-sdk = 0.1.0
23+
pyth-lazer-solana-contract = { version = "x.y.z", features = ["no-entrypoint"] }
2424
```
2525

2626
Now you can create an instruction or multiple instructions that will receive Pyth Lazer messages.
2727
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.
2828

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.
29+
In order to successfully validate the Pyth Lazer message, the instruction needs to receive the following accounts:
30+
31+
- Fee payer account
32+
- Pyth Lazer program account
33+
- Pyth Lazer storage account
34+
- Pyth Lazer treasury account
35+
- The standard Solana system program account
36+
- The standard Solana instructions sysvar account
37+
38+
You may also add any other accounts your contract needs.
39+
40+
<Callout type="info" icon="💡">
41+
The code snippets below are part of the full consumer contract example
42+
[available on
43+
Github](https://github.com/pyth-network/pyth-examples/tree/main/lazer/solana).
44+
</Callout>
3045

3146
The following code can be used to set up a new instruction within a Solana contract:
3247

@@ -40,9 +55,13 @@ pub enum Instruction {
4055
/// Update price.
4156
/// Data: `UpdateArgs` followed by a signed Pyth Lazer update.
4257
/// 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
58+
/// 1. payer account
59+
/// 2. example data account [writable]
60+
/// 3. pyth program account [readonly]
61+
/// 4. pyth storage account [readonly]
62+
/// 5. pyth treasury account [writable]
63+
/// 6. system program [readonly]
64+
/// 7. instructions sysvar sysvar account [readonly]
4665
Update = 1,
4766
}
4867

@@ -82,36 +101,54 @@ pub fn process_update_instruction(
82101
instruction_args: &[u8],
83102
) -> ProgramResult {
84103
// Verify accounts passed to the instruction.
85-
if accounts.len() != 3 {
104+
if accounts.len() != 7 {
86105
return Err(ProgramError::NotEnoughAccountKeys);
87106
}
88-
let sysvar_account = &accounts[0];
107+
let payer_account = &accounts[0];
89108
let data_account = &accounts[1];
90-
let pyth_storage_account = &accounts[2];
109+
let _pyth_program_account = &accounts[2];
110+
let pyth_storage_account = &accounts[3];
111+
let pyth_treasury_account = &accounts[4];
112+
let system_program_account = &accounts[5];
113+
let instructions_sysvar_account = &accounts[6];
91114
// See below for next steps...
92115
}
93116
```
94117

95-
Call `pyth_lazer_sdk::verify_message{:rust}` function with appropriate arguments to validate the Pyth Lazer signature of the message.
118+
Invoke the Pyth Lazer on-chain program with appropriate arguments to validate the Pyth Lazer signature of the message.
96119

97120
```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;
101121
// We expect the instruction to the built-in ed25519 program to be
102122
// the first instruction within the transaction.
103123
let ed25519_instruction_index = 0;
104124
// We expect our signature to be the first (and only) signature to be checked
105125
// by the built-in ed25519 program within the transaction.
106126
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(),
127+
// Verify Lazer signature.
128+
invoke(
129+
&ProgramInstruction::new_with_bytes(
130+
pyth_lazer_solana_contract::ID,
131+
&VerifyMessage {
132+
message_data: pyth_message.to_vec(),
133+
ed25519_instruction_index,
134+
signature_index,
135+
}
136+
.data(),
137+
vec![
138+
AccountMeta::new(*payer_account.key, true),
139+
AccountMeta::new_readonly(*pyth_storage_account.key, false),
140+
AccountMeta::new(*pyth_treasury_account.key, false),
141+
AccountMeta::new_readonly(*system_program_account.key, false),
142+
AccountMeta::new_readonly(*instructions_sysvar_account.key, false),
143+
],
144+
),
145+
&[
146+
payer_account.clone(),
147+
pyth_storage_account.clone(),
148+
pyth_treasury_account.clone(),
149+
system_program_account.clone(),
150+
instructions_sysvar_account.clone(),
151+
],
115152
)?;
116153
```
117154

@@ -125,7 +162,7 @@ let verified = pyth_lazer_sdk::verify_message(
125162
correctly called in the transaction.
126163
</Callout>
127164

128-
Now Parse the Pyth Lazer message.
165+
Now parse the Pyth Lazer message.
129166

130167
```rust copy
131168
// Deserialize and use the payload.
@@ -161,6 +198,12 @@ state.latest_price = price.into_inner().into();
161198
state.latest_timestamp = data.timestamp_us.0;
162199
```
163200

201+
<Callout type="info" icon="💡">
202+
Pyth Lazer also provides
203+
[pyth_lazer_protocol](https://docs.rs/pyth-lazer-protocol/latest/pyth_lazer_protocol/)
204+
Rust crate, which allows consumers to parse the price updates off-chain.
205+
</Callout>
206+
164207
### Subscribe to Pyth Lazer to receive Price Updates
165208

166209
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.
@@ -171,6 +214,55 @@ Consult [How to fetch price updates from Pyth Lazer](../fetch-price-updates.mdx)
171214

172215
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.
173216

217+
In order to execute signature verification, you need to include an instruction for the built-in Solana ed25519 program in your transaction.
218+
219+
<Tabs items={['Rust', 'JS']}>
220+
<Tabs.Tab>
221+
In Rust, you can leverage helpers provided in the `pyth_lazer_solana_contract` crate:
222+
223+
```rust copy
224+
// Instruction #0 will be ed25519 instruction;
225+
// Instruction #1 will be our contract instruction.
226+
let instruction_index = 1;
227+
// Total offset of Pyth Lazer update within the instruction data;
228+
// 1 byte is the instruction type.
229+
let message_offset = (size_of::<UpdateArgs>() + 1).try_into().unwrap();
230+
let ed25519_args = pyth_lazer_solana_contract::Ed25519SignatureOffsets::new(
231+
&message,
232+
instruction_index,
233+
message_offset,
234+
);
235+
let mut tx = Transaction::new_with_payer(
236+
&[
237+
Instruction::new_with_bytes(
238+
solana_program::ed25519_program::ID,
239+
&pyth_lazer_solana_contract::ed25519_program_args(&[ed25519_args]),
240+
vec![],
241+
),
242+
Instruction::new_with_bytes(
243+
pyth_lazer_solana_example::ID,
244+
&update_data,
245+
vec![
246+
AccountMeta::new(payer.pubkey(), true),
247+
AccountMeta::new(data_pda_key, false),
248+
AccountMeta::new(pyth_lazer_solana_contract::ID, false),
249+
AccountMeta::new_readonly(pyth_lazer_solana_contract::STORAGE_ID, false),
250+
AccountMeta::new(treasury, false),
251+
AccountMeta::new_readonly(system_program::ID, false),
252+
AccountMeta::new_readonly(sysvar::instructions::ID, false),
253+
],
254+
),
255+
],
256+
Some(&payer.pubkey()),
257+
);
258+
```
259+
260+
</Tabs.Tab>
261+
<Tabs.Tab>
262+
In TypeScript and JavaScript, you can leverage helpers provided in the `@pythnetwork/pyth-lazer-sdk` NPM package.
263+
{/* TODO: add example code */}
264+
</Tabs.Tab>
265+
</Tabs>
174266
</Steps>
175267

176268
## Additional Resources

0 commit comments

Comments
 (0)