@@ -20,7 +20,7 @@ import { Connection, PublicKey } from "@solana/web3.js";
2020import * as buffer from "buffer" ;
2121import { AnchorProvider , BN , Program , Wallet } from "@coral-xyz/anchor" ;
2222import { SendUSDApp , IDL } from "./idl/send_usd_app" ;
23- import { PriceServiceConnection } from "@pythnetwork/price-service -client" ;
23+ import { HermesClient } from "@pythnetwork/hermes -client" ;
2424import { useState } from "react" ;
2525window . Buffer = buffer . Buffer ;
2626
@@ -43,17 +43,13 @@ async function postPriceUpdate(
4343 if ( ! ( wallet && destination && amount ) ) {
4444 return ;
4545 } else {
46- const priceServiceConnection = new PriceServiceConnection ( HERMES_URL , {
47- priceFeedRequestConfig : { binary : true } ,
48- } ) ;
46+ const hermesClient = new HermesClient ( HERMES_URL ) ;
4947 const pythSolanaReceiver = new PythSolanaReceiver ( {
5048 connection,
5149 wallet : wallet as Wallet ,
5250 } ) ;
5351
54- const priceUpdateData = await priceServiceConnection . getLatestVaas ( [
55- SOL_PRICE_FEED_ID ,
56- ] ) ;
52+ const priceUpdateData = await hermesClient . getLatestPriceUpdates ( [ SOL_PRICE_FEED_ID ] , { encoding : "base64" } ) ;
5753
5854 const sendUsdApp = new Program < SendUSDApp > (
5955 IDL as SendUSDApp ,
@@ -64,7 +60,7 @@ async function postPriceUpdate(
6460 const transactionBuilder = pythSolanaReceiver . newTransactionBuilder ( {
6561 closeUpdateAccounts : true ,
6662 } ) ;
67- await transactionBuilder . addPostPriceUpdates ( [ priceUpdateData [ 0 ] ] ) ;
63+ await transactionBuilder . addPostPriceUpdates ( priceUpdateData . binary . data ) ;
6864
6965 await transactionBuilder . addPriceConsumerInstructions (
7066 async (
@@ -94,26 +90,115 @@ async function postPriceUpdate(
9490 }
9591}
9692
97- function Button ( props : {
93+ async function postTwapPriceUpdate (
94+ connection : Connection ,
95+ wallet : AnchorWallet | undefined ,
96+ destination : PublicKey | undefined ,
97+ amount : number | undefined ,
98+ twapWindowSeconds : number
99+ ) {
100+ if ( ! ( wallet && destination && amount ) ) {
101+ return ;
102+ } else {
103+ const hermesClient = new HermesClient ( HERMES_URL ) ;
104+ const pythSolanaReceiver = new PythSolanaReceiver ( {
105+ connection,
106+ wallet : wallet as Wallet ,
107+ } ) ;
108+
109+ const twapUpdateData = await hermesClient . getLatestTwaps ( [ SOL_PRICE_FEED_ID ] , twapWindowSeconds , { encoding : "base64" } ) ;
110+
111+ const sendUsdApp = new Program < SendUSDApp > (
112+ IDL as SendUSDApp ,
113+ SEND_USD_PROGRAM_ID ,
114+ new AnchorProvider ( connection , wallet , AnchorProvider . defaultOptions ( ) )
115+ ) ;
116+
117+ const transactionBuilder = pythSolanaReceiver . newTransactionBuilder ( {
118+ closeUpdateAccounts : true ,
119+ } ) ;
120+ await transactionBuilder . addPostTwapUpdates ( twapUpdateData . binary . data ) ;
121+
122+ await transactionBuilder . addTwapConsumerInstructions (
123+ async (
124+ getTwapUpdateAccount : ( priceFeedId : string ) => PublicKey
125+ ) : Promise < InstructionWithEphemeralSigners [ ] > => {
126+ return [
127+ {
128+ instruction : await sendUsdApp . methods
129+ . sendUsingTwap ( new BN ( amount ) , new BN ( twapWindowSeconds ) )
130+ . accounts ( {
131+ destination,
132+ twapUpdate : getTwapUpdateAccount ( SOL_PRICE_FEED_ID ) ,
133+ } )
134+ . instruction ( ) ,
135+ signers : [ ] ,
136+ } ,
137+ ] ;
138+ }
139+ ) ;
140+
141+ await pythSolanaReceiver . provider . sendAll (
142+ await transactionBuilder . buildVersionedTransactions ( {
143+ computeUnitPriceMicroLamports : 50000 ,
144+ } ) ,
145+ { skipPreflight : true }
146+ ) ;
147+ }
148+ }
149+
150+ function Buttons ( props : {
98151 destination : PublicKey | undefined ;
99152 amount : number | undefined ;
100153} ) {
101154 const connectionContext = useConnection ( ) ;
102155 const wallet = useAnchorWallet ( ) ;
103-
156+ const [ twapWindowSeconds , setTwapWindowSeconds ] = useState < number > ( 300 ) ;
104157 return (
105- < button
106- onClick = { async ( ) => {
107- await postPriceUpdate (
108- connectionContext . connection ,
109- wallet ,
110- props . destination ,
111- props . amount
112- ) ;
113- } }
114- >
115- Send
116- </ button >
158+ < >
159+ < div style = { { display : "flex" , marginBottom : "20px" } } >
160+ < button
161+ onClick = { async ( ) => {
162+ await postPriceUpdate (
163+ connectionContext . connection ,
164+ wallet ,
165+ props . destination ,
166+ props . amount
167+ ) ;
168+ } }
169+ className = "wallet-adapter-button wallet-adapter-button-trigger"
170+ style = { { flex : "1" , marginRight : "20px" , height : "48px" , fontSize : "16px" } }
171+ >
172+ Send using Spot Price
173+ </ button >
174+ < div style = { { flex : "1" , display : "flex" , flexDirection : "column" } } >
175+ < button
176+ onClick = { async ( ) => {
177+ await postTwapPriceUpdate (
178+ connectionContext . connection ,
179+ wallet ,
180+ props . destination ,
181+ props . amount ,
182+ twapWindowSeconds
183+ ) ;
184+ } }
185+ className = "wallet-adapter-button wallet-adapter-button-trigger"
186+ style = { { height : "48px" , fontSize : "16px" , marginBottom : "10px" } }
187+ >
188+ Send using TWAP Price
189+ </ button >
190+ < p style = { { fontSize : "16px" , margin : "5px 0" } } > TWAP Window (seconds): { twapWindowSeconds } </ p >
191+ < input
192+ type = "range"
193+ min = "0"
194+ max = "599"
195+ value = { twapWindowSeconds }
196+ onChange = { ( e ) => setTwapWindowSeconds ( parseInt ( e . target . value ) ) }
197+ style = { { width : "100%" } }
198+ />
199+ </ div >
200+ </ div >
201+ </ >
117202 ) ;
118203}
119204
@@ -146,24 +231,26 @@ function App() {
146231 < WalletMultiButton />
147232 < WalletDisconnectButton />
148233 < p > Click to send the amount of USD in SOL</ p >
149- < p style = { { fontSize : "16px" } } >
150- Destination (paste a Solana public key)
151- </ p >
152- < input
153- type = "text"
154- value = { destination ? destination . toString ( ) : "" }
155- onChange = { handleSetDestination }
156- style = { { width : "100%" , height : "40px" , fontSize : "16px" } }
157- />
158- < p style = { { fontSize : "16px" } } > Amount (USD)</ p >
159- < input
160- type = "text"
161- value = { amount ? amount . toString ( ) : "" }
162- onChange = { handleSetAmount }
163- style = { { width : "100%" , height : "40px" , fontSize : "16px" } }
164- />
165-
166- < Button destination = { destination } amount = { amount } />
234+ < div style = { { width : "50%" , margin : "0 auto" } } >
235+ < p style = { { fontSize : "16px" } } >
236+ Destination (paste a Solana public key)
237+ </ p >
238+ < input
239+ type = "text"
240+ value = { destination ? destination . toString ( ) : "" }
241+ onChange = { handleSetDestination }
242+ style = { { width : "100%" , height : "40px" , fontSize : "16px" , marginBottom : "20px" } }
243+ />
244+ < p style = { { fontSize : "16px" } } > Amount (USD)</ p >
245+ < input
246+ type = "text"
247+ value = { amount ? amount . toString ( ) : "" }
248+ onChange = { handleSetAmount }
249+ style = { { width : "100%" , height : "40px" , fontSize : "16px" , marginBottom : "20px" } }
250+ />
251+
252+ < Buttons destination = { destination } amount = { amount } />
253+ </ div >
167254 </ header >
168255 </ div >
169256 </ WalletModalProvider >
0 commit comments