1
- import { HexString } from "@pythnetwork/pyth-common-js" ;
1
+ import { HexString , PriceServiceConnection } from "@pythnetwork/pyth-common-js" ;
2
2
import { ChainPricePusher , PriceInfo , PriceListener } from "./interface" ;
3
3
import { DurationInSeconds } from "./utils" ;
4
4
import { PriceConfig } from "./price-config" ;
5
- import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts" ;
5
+ import {
6
+ ChainGrpcAuthApi ,
7
+ ChainGrpcWasmApi ,
8
+ DEFAULT_STD_FEE ,
9
+ MsgExecuteContract ,
10
+ Msgs ,
11
+ PrivateKey ,
12
+ TxGrpcClient ,
13
+ TxResponse ,
14
+ createTransactionFromMsg ,
15
+ } from "@injectivelabs/sdk-ts" ;
6
16
7
17
type PriceQueryResponse = {
8
18
price_feed : {
@@ -16,6 +26,11 @@ type PriceQueryResponse = {
16
26
} ;
17
27
} ;
18
28
29
+ type UpdateFeeResponse = {
30
+ denom : string ;
31
+ amount : string ;
32
+ } ;
33
+
19
34
// this use price without leading 0x
20
35
// FIXME: implement common methods in the parent class
21
36
export class InjectivePriceListener implements PriceListener {
@@ -103,10 +118,118 @@ export class InjectivePriceListener implements PriceListener {
103
118
}
104
119
105
120
export class InjectivePricePusher implements ChainPricePusher {
121
+ private wallet : PrivateKey ;
122
+ constructor (
123
+ private priceServiceConnection : PriceServiceConnection ,
124
+ private pythContract : string ,
125
+ private grpcEndpoint : string ,
126
+ mnemonic : string
127
+ ) {
128
+ this . wallet = PrivateKey . fromMnemonic ( mnemonic ) ;
129
+ }
130
+
131
+ private injectiveAddress ( ) : string {
132
+ return this . wallet . toBech32 ( ) ;
133
+ }
134
+
135
+ private async signAndBroadcastMsg (
136
+ msg : Msgs ,
137
+ fee = DEFAULT_STD_FEE
138
+ ) : Promise < TxResponse > {
139
+ const chainGrpcAuthApi = new ChainGrpcAuthApi ( this . grpcEndpoint ) ;
140
+ const account = await chainGrpcAuthApi . fetchAccount (
141
+ this . injectiveAddress ( )
142
+ ) ;
143
+ const { signBytes, txRaw } = createTransactionFromMsg ( {
144
+ sequence : account . baseAccount . sequence ,
145
+ accountNumber : account . baseAccount . accountNumber ,
146
+ message : msg ,
147
+ chainId : "injective-888" ,
148
+ fee,
149
+ pubKey : this . wallet . toPublicKey ( ) . toBase64 ( ) ,
150
+ } ) ;
151
+
152
+ const sig = await this . wallet . sign ( Buffer . from ( signBytes ) ) ;
153
+
154
+ /** Append Signatures */
155
+ txRaw . setSignaturesList ( [ sig ] ) ;
156
+
157
+ const txService = new TxGrpcClient ( this . grpcEndpoint ) ;
158
+ const txResponse = await txService . broadcast ( txRaw ) ;
159
+
160
+ return txResponse ;
161
+ }
162
+
163
+ async getPriceFeedUpdateObject ( priceIds : string [ ] ) : Promise < any > {
164
+ const vaas = await this . priceServiceConnection . getLatestVaas ( priceIds ) ;
165
+
166
+ return {
167
+ update_price_feeds : {
168
+ data : vaas ,
169
+ } ,
170
+ } ;
171
+ }
172
+
106
173
async updatePriceFeed (
107
174
priceIds : string [ ] ,
108
175
pubTimesToPush : number [ ]
109
176
) : Promise < void > {
110
- console . log ( "dummy pushed" ) ;
177
+ if ( priceIds . length === 0 ) {
178
+ return ;
179
+ }
180
+
181
+ if ( priceIds . length !== pubTimesToPush . length )
182
+ throw new Error ( "Invalid arguments" ) ;
183
+
184
+ let priceFeedUpdateObject ;
185
+ try {
186
+ // get the latest VAAs for updatePriceFeed and then push them
187
+ priceFeedUpdateObject = await this . getPriceFeedUpdateObject ( priceIds ) ;
188
+ } catch ( e ) {
189
+ console . error ( "Error fetching the latest vaas to push" ) ;
190
+ console . error ( e ) ;
191
+ return ;
192
+ }
193
+
194
+ let updateFeeQueryResponse : UpdateFeeResponse ;
195
+ try {
196
+ const api = new ChainGrpcWasmApi ( this . grpcEndpoint ) ;
197
+ const { data } = await api . fetchSmartContractState (
198
+ this . pythContract ,
199
+ Buffer . from (
200
+ JSON . stringify ( {
201
+ get_update_fee : {
202
+ vaas : priceFeedUpdateObject . update_price_feeds . data ,
203
+ } ,
204
+ } )
205
+ ) . toString ( "base64" )
206
+ ) ;
207
+
208
+ const json = Buffer . from ( data as string , "base64" ) . toString ( ) ;
209
+ updateFeeQueryResponse = JSON . parse ( json ) ;
210
+ } catch ( e ) {
211
+ console . error ( "Error fetching update fee" ) ;
212
+ console . error ( e ) ;
213
+ return ;
214
+ }
215
+
216
+ // TODO: add specific error messages
217
+ try {
218
+ const executeMsg = MsgExecuteContract . fromJSON ( {
219
+ sender : this . injectiveAddress ( ) ,
220
+ contractAddress : this . pythContract ,
221
+ msg : priceFeedUpdateObject ,
222
+ funds : [ updateFeeQueryResponse ] ,
223
+ } ) ;
224
+
225
+ const rs = await this . signAndBroadcastMsg ( executeMsg ) ;
226
+
227
+ if ( rs . code !== 0 ) throw new Error ( "Error: transaction failed" ) ;
228
+
229
+ console . log ( "Succesfully broadcasted txHash:" , rs . txHash ) ;
230
+ } catch ( e ) {
231
+ console . error ( "Error executing messages" ) ;
232
+ console . log ( e ) ;
233
+ }
111
234
}
112
235
}
0 commit comments