@pythnetwork/price-pusher 5.3.0 → 5.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +24 -21
  2. package/lib/aptos/aptos.d.ts +1 -0
  3. package/lib/aptos/aptos.d.ts.map +1 -0
  4. package/lib/aptos/command.d.ts +1 -0
  5. package/lib/aptos/command.d.ts.map +1 -0
  6. package/lib/common.d.ts +1 -0
  7. package/lib/common.d.ts.map +1 -0
  8. package/lib/controller.d.ts +2 -1
  9. package/lib/controller.d.ts.map +1 -0
  10. package/lib/controller.js +10 -2
  11. package/lib/evm/command.d.ts +1 -0
  12. package/lib/evm/command.d.ts.map +1 -0
  13. package/lib/evm/command.js +3 -0
  14. package/lib/evm/custom-gas-station.d.ts +1 -0
  15. package/lib/evm/custom-gas-station.d.ts.map +1 -0
  16. package/lib/evm/evm.d.ts +1 -0
  17. package/lib/evm/evm.d.ts.map +1 -0
  18. package/lib/evm/evm.js +1 -1
  19. package/lib/index.d.ts +1 -0
  20. package/lib/index.d.ts.map +1 -0
  21. package/lib/injective/command.d.ts +1 -0
  22. package/lib/injective/command.d.ts.map +1 -0
  23. package/lib/injective/injective.d.ts +2 -0
  24. package/lib/injective/injective.d.ts.map +1 -0
  25. package/lib/injective/injective.js +32 -31
  26. package/lib/interface.d.ts +1 -0
  27. package/lib/interface.d.ts.map +1 -0
  28. package/lib/options.d.ts +1 -0
  29. package/lib/options.d.ts.map +1 -0
  30. package/lib/price-config.d.ts +10 -1
  31. package/lib/price-config.d.ts.map +1 -0
  32. package/lib/price-config.js +39 -10
  33. package/lib/pyth-price-listener.d.ts +1 -0
  34. package/lib/pyth-price-listener.d.ts.map +1 -0
  35. package/lib/sui/command.d.ts +5 -1
  36. package/lib/sui/command.d.ts.map +1 -0
  37. package/lib/sui/command.js +28 -3
  38. package/lib/sui/sui.d.ts +21 -2
  39. package/lib/sui/sui.d.ts.map +1 -0
  40. package/lib/sui/sui.js +181 -12
  41. package/lib/utils.d.ts +1 -0
  42. package/lib/utils.d.ts.map +1 -0
  43. package/package.json +2 -2
package/README.md CHANGED
@@ -37,9 +37,20 @@ The parameters above are configured per price feed in a price configuration YAML
37
37
  time_difference: 60 # Time difference threshold (in seconds) to push a newer price feed.
38
38
  price_deviation: 0.5 # The price deviation (%) threshold to push a newer price feed.
39
39
  confidence_ratio: 1 # The confidence/price (%) threshold to push a newer price feed.
40
+ # Optional block to configure whether this feed can be early updated. If at least one feed meets the
41
+ # triggering conditions above, all other feeds who meet the early update conditions will be included in
42
+ # the submitted batch of prices. This logic takes advantage of the fact that adding a feed to a larger
43
+ # batch of updates incurs a minimal gas cost. All fields below are optional (and interpreted as infinity if omitted)
44
+ # and have the same semantics as the corresponding fields above.
45
+ early_update:
46
+ time_difference: 30
47
+ price_deviation: 0.1
48
+ confidence_ratio: 0.5
40
49
  - ...
41
50
  ```
42
51
 
52
+ Two sample YAML configuration files are available in the root of this repo.
53
+
43
54
  You can get the list of available price feeds from
44
55
  [here](https://pyth.network/developers/price-feed-ids/).
45
56
 
@@ -57,7 +68,7 @@ cd price_pusher
57
68
  npm run start -- evm --endpoint wss://example-rpc.com \
58
69
  --pyth-contract-address 0xff1a0f4744e8582DF...... \
59
70
  --price-service-endpoint https://example-pyth-price.com \
60
- --price-config-file "path/to/price-config-file.yaml.testnet.sample.yaml" \
71
+ --price-config-file "path/to/price-config.testnet.sample.yaml" \
61
72
  --mnemonic-file "path/to/mnemonic.txt" \
62
73
  [--pushing-frequency 10] \
63
74
  [--polling-frequency 5] \
@@ -66,7 +77,7 @@ npm run start -- evm --endpoint wss://example-rpc.com \
66
77
  # For Injective
67
78
  npm run start -- injective --grpc-endpoint https://grpc-endpoint.com \
68
79
  --pyth-contract-address inj1z60tg0... --price-service-endpoint "https://example-pyth-price.com" \
69
- --price-config-file "path/to/price-config-file.yaml.testnet.sample.yaml" \
80
+ --price-config-file "path/to/price-config.testnet.sample.yaml" \
70
81
  --mnemonic-file "path/to/mnemonic.txt" \
71
82
  [--pushing-frequency 10] \
72
83
  [--polling-frequency 5] \
@@ -80,27 +91,19 @@ npm run start -- aptos --endpoint https://fullnode.testnet.aptoslabs.com/v1 \
80
91
  [--polling-frequency 5] \
81
92
 
82
93
  # For Sui
83
- npm run start -- sui
84
- --endpoint https://sui-testnet-rpc.allthatnode.com,
85
- --pyth-package-id 0x975e063f398f720af4f33ec06a927f14ea76ca24f7f8dd544aa62ab9d5d15f44,
86
- --pyth-state-id 0xd8afde3a48b4ff7212bd6829a150f43f59043221200d63504d981f62bff2e27a,
87
- --wormhole-package-id 0xcc029e2810f17f9f43f52262f40026a71fbdca40ed3803ad2884994361910b7e,
88
- --wormhole-state-id 0xebba4cc4d614f7a7cdbe883acc76d1cc767922bc96778e7b68be0d15fce27c02,
89
- --price-feed-to-price-info-object-table-id 0xf8929174008c662266a1adde78e1e8e33016eb7ad37d379481e860b911e40ed5,
90
- --price-service-endpoint https://xc-testnet.pyth.network,
91
- --mnemonic-file ./mnemonic,
92
- --price-config-file ./price-config.testnet.sample.yaml
94
+ npm run start -- sui \
95
+ --endpoint https://sui-testnet-rpc.allthatnode.com \
96
+ --pyth-package-id 0x975e063f398f720af4f33ec06a927f14ea76ca24f7f8dd544aa62ab9d5d15f44 \
97
+ --pyth-state-id 0xd8afde3a48b4ff7212bd6829a150f43f59043221200d63504d981f62bff2e27a \
98
+ --wormhole-package-id 0xcc029e2810f17f9f43f52262f40026a71fbdca40ed3803ad2884994361910b7e \
99
+ --wormhole-state-id 0xebba4cc4d614f7a7cdbe883acc76d1cc767922bc96778e7b68be0d15fce27c02 \
100
+ --price-feed-to-price-info-object-table-id 0xf8929174008c662266a1adde78e1e8e33016eb7ad37d379481e860b911e40ed5 \
101
+ --price-service-endpoint https://xc-testnet.pyth.network \
102
+ --mnemonic-file ./mnemonic \
103
+ --price-config-file ./price-config.testnet.sample.yaml \
93
104
  [--pushing-frequency 10] \
94
105
  [--polling-frequency 5] \
95
-
96
-
97
-
98
- --endpoint https://fullnode.testnet.aptoslabs.com/v1 \
99
- --pyth-contract-address 0x7e783b349d3e89cf5931af376ebeadbfab855b3fa239b7ada8f5a92fbea6b387 --price-service-endpoint "https://xc-testnet.pyth.network" \
100
- --price-config-file "./price-config.testnet.sample.yaml" \
101
- --mnemonic-file "path/to/mnemonic.txt" \
102
- [--pushing-frequency 10] \
103
- [--polling-frequency 5] \
106
+ [--num-gas-objects 30]
104
107
 
105
108
 
106
109
  # Or, run the price pusher docker image instead of building from the source
@@ -28,3 +28,4 @@ export declare class AptosPricePusher implements IPricePusher {
28
28
  getPriceFeedsUpdateData(priceIds: string[]): Promise<number[][]>;
29
29
  updatePriceFeed(priceIds: string[], pubTimesToPush: number[]): Promise<void>;
30
30
  }
31
+ //# sourceMappingURL=aptos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aptos.d.ts","sourceRoot":"","sources":["../../src/aptos/aptos.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAG3E,qBAAa,kBAAmB,SAAQ,kBAAkB;IAEtD,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,QAAQ;gBADR,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EACxB,UAAU,EAAE,SAAS,EAAE,EACvB,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAKG,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CA6C3E;AAED,qBAAa,gBAAiB,YAAW,YAAY;IAKjD,OAAO,CAAC,sBAAsB;IAC9B,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,0BAA0B;IARpC,OAAO,CAAC,eAAe,CAA0B;IAEjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;gBAE7C,sBAAsB,EAAE,sBAAsB,EAC9C,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,0BAA0B,EAAE,MAAM;IAG5C;;;;;;OAMG;IACG,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAQhE,eAAe,CACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC;CAyFjB"}
@@ -15,3 +15,4 @@ declare const _default: {
15
15
  handler: (argv: any) => void;
16
16
  };
17
17
  export default _default;
18
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/aptos/command.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;;;;;;;;;;;;;;oBA6BL,GAAG;;AA1B9B,wBAsFE"}
package/lib/common.d.ts CHANGED
@@ -2,3 +2,4 @@ export type PushAttempt = {
2
2
  nonce: number;
3
3
  gasPrice: number;
4
4
  };
5
+ //# sourceMappingURL=common.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { DurationInSeconds } from "./utils";
2
- import { IPricePusher, IPriceListener } from "./interface";
2
+ import { IPriceListener, IPricePusher } from "./interface";
3
3
  import { PriceConfig } from "./price-config";
4
4
  export declare class Controller {
5
5
  private priceConfigs;
@@ -12,3 +12,4 @@ export declare class Controller {
12
12
  });
13
13
  start(): Promise<void>;
14
14
  }
15
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAS,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAiC,MAAM,gBAAgB,CAAC;AAE5E,qBAAa,UAAU;IAGnB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,sBAAsB;IALhC,OAAO,CAAC,gBAAgB,CAAoB;gBAElC,YAAY,EAAE,WAAW,EAAE,EAC3B,mBAAmB,EAAE,cAAc,EACnC,mBAAmB,EAAE,cAAc,EACnC,sBAAsB,EAAE,YAAY,EAC5C,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAKG,KAAK;CA2DZ"}
package/lib/controller.js CHANGED
@@ -25,18 +25,26 @@ class Controller {
25
25
  // their might be a message sent before.
26
26
  await (0, utils_1.sleep)(this.pushingFrequency * 1000);
27
27
  for (;;) {
28
+ // We will push all prices whose update condition is YES or EARLY as long as there is
29
+ // at least one YES.
30
+ let pushThresholdMet = false;
28
31
  const pricesToPush = [];
29
32
  const pubTimesToPush = [];
30
33
  for (const priceConfig of this.priceConfigs) {
31
34
  const priceId = priceConfig.id;
32
35
  const targetLatestPrice = this.targetPriceListener.getLatestPriceInfo(priceId);
33
36
  const sourceLatestPrice = this.sourcePriceListener.getLatestPriceInfo(priceId);
34
- if ((0, price_config_1.shouldUpdate)(priceConfig, sourceLatestPrice, targetLatestPrice)) {
37
+ const priceShouldUpdate = (0, price_config_1.shouldUpdate)(priceConfig, sourceLatestPrice, targetLatestPrice);
38
+ if (priceShouldUpdate == price_config_1.UpdateCondition.YES) {
39
+ pushThresholdMet = true;
40
+ }
41
+ if (priceShouldUpdate == price_config_1.UpdateCondition.YES ||
42
+ priceShouldUpdate == price_config_1.UpdateCondition.EARLY) {
35
43
  pricesToPush.push(priceConfig);
36
44
  pubTimesToPush.push((targetLatestPrice?.publishTime || 0) + 1);
37
45
  }
38
46
  }
39
- if (pricesToPush.length !== 0) {
47
+ if (pushThresholdMet) {
40
48
  console.log("Some of the above values passed the threshold. Will push the price.");
41
49
  // note that the priceIds are without leading "0x"
42
50
  const priceIds = pricesToPush.map((priceConfig) => priceConfig.id);
@@ -17,3 +17,4 @@ declare const _default: {
17
17
  handler: (argv: any) => void;
18
18
  };
19
19
  export default _default;
20
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/evm/command.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;oBA4CL,GAAG;;AAxC9B,wBA+GE"}
@@ -88,6 +88,9 @@ exports.default = {
88
88
  const priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
89
89
  const pythListener = new pyth_price_listener_1.PythPriceListener(priceServiceConnection, priceItems);
90
90
  const pythContractFactory = new evm_1.PythContractFactory(endpoint, mnemonic, pythContractAddress);
91
+ console.log(`Pushing updates from wallet address: ${pythContractFactory
92
+ .createWeb3PayerProvider()
93
+ .getAddress()}`);
91
94
  const evmListener = new evm_1.EvmPriceListener(pythContractFactory, priceItems, {
92
95
  pollingFrequency,
93
96
  });
@@ -7,3 +7,4 @@ export declare class CustomGasStation {
7
7
  private fetchMaticMainnetGasPrice;
8
8
  }
9
9
  export declare function getCustomGasStation(customGasStation?: number, txSpeed?: string): CustomGasStation | undefined;
10
+ //# sourceMappingURL=custom-gas-station.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-gas-station.d.ts","sourceRoot":"","sources":["../../src/evm/custom-gas-station.ts"],"names":[],"mappings":"AAWA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,YAAY,CAElB;gBACU,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAKlC,iBAAiB;YAIT,yBAAyB;CAexC;AAED,wBAAgB,mBAAmB,CACjC,gBAAgB,CAAC,EAAE,MAAM,EACzB,OAAO,CAAC,EAAE,MAAM,gCAKjB"}
package/lib/evm/evm.d.ts CHANGED
@@ -51,3 +51,4 @@ export declare class PythContractFactory {
51
51
  createWeb3Provider(): import("web3-core").HttpProvider | import("web3-core").WebsocketProvider;
52
52
  createWeb3PayerProvider(): HDWalletProvider;
53
53
  }
54
+ //# sourceMappingURL=evm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evm.d.ts","sourceRoot":"","sources":["../../src/evm/evm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAgB,iBAAiB,EAAmB,MAAM,UAAU,CAAC;AAE5E,OAAO,gBAAgB,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EACL,sBAAsB,EACtB,SAAS,EACT,aAAa,EACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAIxD,qBAAa,gBAAiB,SAAQ,kBAAkB;IACtD,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,YAAY,CAAW;gBAG7B,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,SAAS,EAAE,EACvB,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAUG,KAAK;YAeG,iBAAiB;IAc/B,OAAO,CAAC,iBAAiB;IAsBnB,mBAAmB,CACvB,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CAwBlC;AAED,qBAAa,cAAe,YAAW,YAAY;IAQ/C,OAAO,CAAC,UAAU;IAElB,OAAO,CAAC,0BAA0B;IATpC,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,YAAY,CAAW;IAC/B,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,eAAe,CAA0B;gBAGvC,UAAU,EAAE,sBAAsB,EAC1C,mBAAmB,EAAE,mBAAmB,EAChC,0BAA0B,EAAE,MAAM,EAC1C,gBAAgB,CAAC,EAAE,gBAAgB;IAa/B,eAAe,CACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,aAAa,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC;YA6IF,uBAAuB;CAQtC;AAED,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,mBAAmB;gBAFnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM;IAGrC;;;;;;OAMG;IACH,2BAA2B,IAAI,QAAQ;IAcvC;;;;;OAKG;IACH,kBAAkB,IAAI,QAAQ;IAS9B,oBAAoB,IAAI,OAAO;IAI/B,kBAAkB;IA0BlB,uBAAuB;CAQxB"}
package/lib/evm/evm.js CHANGED
@@ -151,7 +151,7 @@ class EvmPricePusher {
151
151
  // doesn't return any information why the call has reverted. Assuming that
152
152
  // the update data is valid there is no possible rejection cause other than
153
153
  // the target chain price being already updated.
154
- console.log("Execution reverted. With high probablity, the target chain price " +
154
+ console.log("Execution reverted. With high probability, the target chain price " +
155
155
  "has already updated, Skipping this push.");
156
156
  return;
157
157
  }
package/lib/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
@@ -15,3 +15,4 @@ declare const _default: {
15
15
  handler: (argv: any) => void;
16
16
  };
17
17
  export default _default;
18
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/injective/command.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;;;;;;;;;;;;;;oBA2BL,GAAG;;AAxB9B,wBA4FE"}
@@ -20,6 +20,7 @@ export declare class InjectivePricePusher implements IPricePusher {
20
20
  private grpcEndpoint;
21
21
  private wallet;
22
22
  private chainConfig;
23
+ private account;
23
24
  constructor(priceServiceConnection: PriceServiceConnection, pythContractAddress: string, grpcEndpoint: string, mnemonic: string, chainConfig?: Partial<InjectiveConfig>);
24
25
  private injectiveAddress;
25
26
  private signAndBroadcastMsg;
@@ -27,3 +28,4 @@ export declare class InjectivePricePusher implements IPricePusher {
27
28
  updatePriceFeed(priceIds: string[], pubTimesToPush: number[]): Promise<void>;
28
29
  }
29
30
  export {};
31
+ //# sourceMappingURL=injective.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"injective.d.ts","sourceRoot":"","sources":["../../src/injective/injective.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,sBAAsB,EACvB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAiC7C,qBAAa,sBAAuB,SAAQ,kBAAkB;IAE1D,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,YAAY;gBADZ,mBAAmB,EAAE,MAAM,EAC3B,YAAY,EAAE,MAAM,EAC5B,UAAU,EAAE,SAAS,EAAE,EACvB,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAKG,mBAAmB,CACvB,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CA6BlC;AAED,KAAK,eAAe,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AACF,qBAAa,oBAAqB,YAAW,YAAY;IAMrD,OAAO,CAAC,sBAAsB;IAC9B,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,YAAY;IAPtB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,OAAO,CAAwB;gBAG7B,sBAAsB,EAAE,sBAAsB,EAC9C,mBAAmB,EAAE,MAAM,EAC3B,YAAY,EAAE,MAAM,EAC5B,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;IAWxC,OAAO,CAAC,gBAAgB;YAIV,mBAAmB;IAkE3B,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;IAU1D,eAAe,CACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC;CAoDjB"}
@@ -41,6 +41,7 @@ class InjectivePricePusher {
41
41
  grpcEndpoint;
42
42
  wallet;
43
43
  chainConfig;
44
+ account = null;
44
45
  constructor(priceServiceConnection, pythContractAddress, grpcEndpoint, mnemonic, chainConfig) {
45
46
  this.priceServiceConnection = priceServiceConnection;
46
47
  this.pythContractAddress = pythContractAddress;
@@ -57,10 +58,11 @@ class InjectivePricePusher {
57
58
  }
58
59
  async signAndBroadcastMsg(msg) {
59
60
  const chainGrpcAuthApi = new sdk_ts_1.ChainGrpcAuthApi(this.grpcEndpoint);
60
- const account = await chainGrpcAuthApi.fetchAccount(this.injectiveAddress());
61
+ // Fetch the latest account details only if it's not stored.
62
+ this.account ??= await chainGrpcAuthApi.fetchAccount(this.injectiveAddress());
61
63
  const { txRaw: simulateTxRaw } = (0, sdk_ts_1.createTransactionFromMsg)({
62
- sequence: account.baseAccount.sequence,
63
- accountNumber: account.baseAccount.accountNumber,
64
+ sequence: this.account.baseAccount.sequence,
65
+ accountNumber: this.account.baseAccount.accountNumber,
64
66
  message: msg,
65
67
  chainId: this.chainConfig.chainId,
66
68
  pubKey: this.wallet.toPublicKey().toBase64(),
@@ -72,30 +74,41 @@ class InjectivePricePusher {
72
74
  // gas passed with the transaction should be more than that
73
75
  // in order for it to be successfully executed
74
76
  // this multiplier takes care of that
77
+ const gas = (gasUsed * this.chainConfig.gasMultiplier).toFixed();
75
78
  const fee = {
76
79
  amount: [
77
80
  {
78
81
  denom: "inj",
79
- amount: (gasUsed *
80
- this.chainConfig.gasPrice *
81
- this.chainConfig.gasMultiplier).toFixed(),
82
+ amount: (Number(gas) * this.chainConfig.gasPrice).toFixed(),
82
83
  },
83
84
  ],
84
- gas: (gasUsed * this.chainConfig.gasMultiplier).toFixed(),
85
+ gas,
85
86
  };
86
87
  const { signBytes, txRaw } = (0, sdk_ts_1.createTransactionFromMsg)({
87
- sequence: account.baseAccount.sequence,
88
- accountNumber: account.baseAccount.accountNumber,
88
+ sequence: this.account.baseAccount.sequence,
89
+ accountNumber: this.account.baseAccount.accountNumber,
89
90
  message: msg,
90
91
  chainId: this.chainConfig.chainId,
91
92
  fee,
92
93
  pubKey: this.wallet.toPublicKey().toBase64(),
93
94
  });
94
95
  const sig = await this.wallet.sign(Buffer.from(signBytes));
95
- /** Append Signatures */
96
- txRaw.signatures = [sig];
97
- const txResponse = await txService.broadcast(txRaw);
98
- return txResponse;
96
+ try {
97
+ this.account.baseAccount.sequence++;
98
+ /** Append Signatures */
99
+ txRaw.signatures = [sig];
100
+ // this takes approx 5 seconds
101
+ const txResponse = await txService.broadcast(txRaw);
102
+ return txResponse;
103
+ }
104
+ catch (e) {
105
+ // The sequence number was invalid and hence we will have to fetch it again.
106
+ if (JSON.stringify(e).match(/account sequence mismatch/) !== null) {
107
+ // We need to fetch the account details again.
108
+ this.account = null;
109
+ }
110
+ throw e;
111
+ }
99
112
  }
100
113
  async getPriceFeedUpdateObject(priceIds) {
101
114
  const vaas = await this.priceServiceConnection.getLatestVaas(priceIds);
@@ -121,22 +134,12 @@ class InjectivePricePusher {
121
134
  console.error(e);
122
135
  return;
123
136
  }
124
- let updateFeeQueryResponse;
125
- try {
126
- const api = new sdk_ts_1.ChainGrpcWasmApi(this.grpcEndpoint);
127
- const { data } = await api.fetchSmartContractState(this.pythContractAddress, Buffer.from(JSON.stringify({
128
- get_update_fee: {
129
- vaas: priceFeedUpdateObject.update_price_feeds.data,
130
- },
131
- })).toString("base64"));
132
- const json = Buffer.from(data).toString();
133
- updateFeeQueryResponse = JSON.parse(json);
134
- }
135
- catch (e) {
136
- console.error("Error fetching update fee");
137
- console.error(e);
138
- return;
139
- }
137
+ // In order to reduce the number of API calls
138
+ // We are calculating the fee using the same logic as in contract.
139
+ const updateFeeQueryResponse = {
140
+ denom: "inj",
141
+ amount: priceFeedUpdateObject.update_price_feeds.data.length.toFixed(),
142
+ };
140
143
  try {
141
144
  const executeMsg = sdk_ts_1.MsgExecuteContract.fromJSON({
142
145
  sender: this.injectiveAddress(),
@@ -145,8 +148,6 @@ class InjectivePricePusher {
145
148
  funds: [updateFeeQueryResponse],
146
149
  });
147
150
  const rs = await this.signAndBroadcastMsg(executeMsg);
148
- if (rs.code !== 0)
149
- throw new Error("Error: transaction failed");
150
151
  console.log("Succesfully broadcasted txHash:", rs.txHash);
151
152
  }
152
153
  catch (e) {
@@ -29,3 +29,4 @@ export declare abstract class ChainPriceListener implements IPriceListener {
29
29
  export interface IPricePusher {
30
30
  updatePriceFeed(priceIds: string[], pubTimesToPush: UnixTimestamp[]): Promise<void>;
31
31
  }
32
+ //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../src/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,SAAS,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,aAAa,CAAC;CAC5B,CAAC;AAEF,MAAM,WAAW,cAAc;IAE7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;CAC5D;AAED,8BAAsB,kBAAmB,YAAW,cAAc;IAK9D,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,gBAAgB;IACxB,SAAS,CAAC,UAAU,EAAE,SAAS,EAAE;IANnC,OAAO,CAAC,eAAe,CAA4B;IACnD,SAAS,CAAC,cAAc,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;gBAGvC,KAAK,EAAE,MAAM,EACb,gBAAgB,EAAE,iBAAiB,EACjC,UAAU,EAAE,SAAS,EAAE;IAQ7B,KAAK;YASG,UAAU;IASxB,SAAS,CAAC,qBAAqB,CAC7B,OAAO,EAAE,SAAS,EAClB,aAAa,EAAE,SAAS;IAkB1B,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI1D,QAAQ,CAAC,mBAAmB,CAC1B,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,eAAe,CACb,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,aAAa,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
package/lib/options.d.ts CHANGED
@@ -17,3 +17,4 @@ export declare const pushingFrequency: {
17
17
  export declare const mnemonicFile: {
18
18
  "mnemonic-file": Options;
19
19
  };
20
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,eAAO,MAAM,oBAAoB;;CAOhC,CAAC;AAEF,eAAO,MAAM,mBAAmB;;CAQ/B,CAAC;AAEF,eAAO,MAAM,eAAe;;CAM3B,CAAC;AAEF,eAAO,MAAM,gBAAgB;;CAQ5B,CAAC;AAEF,eAAO,MAAM,gBAAgB;;CAU5B,CAAC;AAEF,eAAO,MAAM,YAAY;;CAMxB,CAAC"}
@@ -7,12 +7,21 @@ export type PriceConfig = {
7
7
  timeDifference: DurationInSeconds;
8
8
  priceDeviation: PctNumber;
9
9
  confidenceRatio: PctNumber;
10
+ earlyUpdateTimeDifference: DurationInSeconds | undefined;
11
+ earlyUpdatePriceDeviation: PctNumber | undefined;
12
+ earlyUpdateConfidenceRatio: PctNumber | undefined;
10
13
  };
11
14
  export declare function readPriceConfigFile(path: string): PriceConfig[];
15
+ export declare enum UpdateCondition {
16
+ YES = 0,
17
+ EARLY = 1,
18
+ NO = 2
19
+ }
12
20
  /**
13
21
  * Checks whether on-chain price needs to be updated with the latest pyth price information.
14
22
  *
15
23
  * @param priceConfig Config of the price feed to check
16
24
  * @returns True if the on-chain price needs to be updated.
17
25
  */
18
- export declare function shouldUpdate(priceConfig: PriceConfig, sourceLatestPrice: PriceInfo | undefined, targetLatestPrice: PriceInfo | undefined): boolean;
26
+ export declare function shouldUpdate(priceConfig: PriceConfig, sourceLatestPrice: PriceInfo | undefined, targetLatestPrice: PriceInfo | undefined): UpdateCondition;
27
+ //# sourceMappingURL=price-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price-config.d.ts","sourceRoot":"","sources":["../src/price-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAI9D,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAmB,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAuBxC,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,SAAS,CAAC;IACd,cAAc,EAAE,iBAAiB,CAAC;IAClC,cAAc,EAAE,SAAS,CAAC;IAC1B,eAAe,EAAE,SAAS,CAAC;IAI3B,yBAAyB,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACzD,yBAAyB,EAAE,SAAS,GAAG,SAAS,CAAC;IACjD,0BAA0B,EAAE,SAAS,GAAG,SAAS,CAAC;CACnD,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAsB/D;AAED,oBAAY,eAAe;IAEzB,GAAG,IAAA;IAEH,KAAK,IAAA;IAEL,EAAE,IAAA;CACH;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,WAAW,EACxB,iBAAiB,EAAE,SAAS,GAAG,SAAS,EACxC,iBAAiB,EAAE,SAAS,GAAG,SAAS,GACvC,eAAe,CAmEjB"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.shouldUpdate = exports.readPriceConfigFile = void 0;
6
+ exports.shouldUpdate = exports.UpdateCondition = exports.readPriceConfigFile = void 0;
7
7
  const joi_1 = __importDefault(require("joi"));
8
8
  const yaml_1 = __importDefault(require("yaml"));
9
9
  const fs_1 = __importDefault(require("fs"));
@@ -17,6 +17,11 @@ const PriceConfigFileSchema = joi_1.default.array()
17
17
  time_difference: joi_1.default.number().required(),
18
18
  price_deviation: joi_1.default.number().required(),
19
19
  confidence_ratio: joi_1.default.number().required(),
20
+ early_update: joi_1.default.object({
21
+ time_difference: joi_1.default.number().optional(),
22
+ price_deviation: joi_1.default.number().optional(),
23
+ confidence_ratio: joi_1.default.number().optional(),
24
+ }).optional(),
20
25
  }))
21
26
  .unique("id")
22
27
  .unique("alias")
@@ -34,11 +39,23 @@ function readPriceConfigFile(path) {
34
39
  timeDifference: priceConfigRaw.time_difference,
35
40
  priceDeviation: priceConfigRaw.price_deviation,
36
41
  confidenceRatio: priceConfigRaw.confidence_ratio,
42
+ earlyUpdateTimeDifference: priceConfigRaw.early_update?.time_difference,
43
+ earlyUpdatePriceDeviation: priceConfigRaw.early_update?.price_deviation,
44
+ earlyUpdateConfidenceRatio: priceConfigRaw.early_update?.confidence_ratio,
37
45
  };
38
46
  return priceConfig;
39
47
  });
40
48
  }
41
49
  exports.readPriceConfigFile = readPriceConfigFile;
50
+ var UpdateCondition;
51
+ (function (UpdateCondition) {
52
+ // This price feed must be updated
53
+ UpdateCondition[UpdateCondition["YES"] = 0] = "YES";
54
+ // This price feed may be updated as part of a larger batch
55
+ UpdateCondition[UpdateCondition["EARLY"] = 1] = "EARLY";
56
+ // This price feed shouldn't be updated
57
+ UpdateCondition[UpdateCondition["NO"] = 2] = "NO";
58
+ })(UpdateCondition = exports.UpdateCondition || (exports.UpdateCondition = {}));
42
59
  /**
43
60
  * Checks whether on-chain price needs to be updated with the latest pyth price information.
44
61
  *
@@ -49,16 +66,16 @@ function shouldUpdate(priceConfig, sourceLatestPrice, targetLatestPrice) {
49
66
  const priceId = priceConfig.id;
50
67
  // There is no price to update the target with.
51
68
  if (sourceLatestPrice === undefined) {
52
- return false;
69
+ return UpdateCondition.YES;
53
70
  }
54
71
  // It means that price never existed there. So we should push the latest price feed.
55
72
  if (targetLatestPrice === undefined) {
56
73
  console.log(`${priceConfig.alias} (${priceId}) is not available on the target network. Pushing the price.`);
57
- return true;
74
+ return UpdateCondition.YES;
58
75
  }
59
76
  // The current price is not newer than the price onchain
60
77
  if (sourceLatestPrice.publishTime < targetLatestPrice.publishTime) {
61
- return false;
78
+ return UpdateCondition.NO;
62
79
  }
63
80
  const timeDifference = sourceLatestPrice.publishTime - targetLatestPrice.publishTime;
64
81
  const priceDeviationPct = (Math.abs(Number(sourceLatestPrice.price) - Number(targetLatestPrice.price)) /
@@ -68,12 +85,24 @@ function shouldUpdate(priceConfig, sourceLatestPrice, targetLatestPrice) {
68
85
  console.log(`Analyzing price ${priceConfig.alias} (${priceId})`);
69
86
  console.log("Source latest price: ", sourceLatestPrice);
70
87
  console.log("Target latest price: ", targetLatestPrice);
71
- console.log(`Time difference: ${timeDifference} (< ${priceConfig.timeDifference}?) OR ` +
72
- `Price deviation: ${priceDeviationPct.toFixed(5)}% (< ${priceConfig.priceDeviation}%?) OR ` +
73
- `Confidence ratio: ${confidenceRatioPct.toFixed(5)}% (< ${priceConfig.confidenceRatio}%?)`);
74
- const result = timeDifference >= priceConfig.timeDifference ||
88
+ console.log(`Time difference: ${timeDifference} (< ${priceConfig.timeDifference}? / early: < ${priceConfig.earlyUpdateTimeDifference}) OR ` +
89
+ `Price deviation: ${priceDeviationPct.toFixed(5)}% (< ${priceConfig.priceDeviation}%? / early: < ${priceConfig.earlyUpdatePriceDeviation}%?) OR ` +
90
+ `Confidence ratio: ${confidenceRatioPct.toFixed(5)}% (< ${priceConfig.confidenceRatio}%? / early: < ${priceConfig.earlyUpdatePriceDeviation}%?)`);
91
+ if (timeDifference >= priceConfig.timeDifference ||
75
92
  priceDeviationPct >= priceConfig.priceDeviation ||
76
- confidenceRatioPct >= priceConfig.confidenceRatio;
77
- return result;
93
+ confidenceRatioPct >= priceConfig.confidenceRatio) {
94
+ return UpdateCondition.YES;
95
+ }
96
+ else if ((priceConfig.earlyUpdateTimeDifference !== undefined &&
97
+ timeDifference >= priceConfig.earlyUpdateTimeDifference) ||
98
+ (priceConfig.earlyUpdatePriceDeviation !== undefined &&
99
+ priceDeviationPct >= priceConfig.earlyUpdatePriceDeviation) ||
100
+ (priceConfig.earlyUpdateConfidenceRatio !== undefined &&
101
+ confidenceRatioPct >= priceConfig.earlyUpdateConfidenceRatio)) {
102
+ return UpdateCondition.EARLY;
103
+ }
104
+ else {
105
+ return UpdateCondition.NO;
106
+ }
78
107
  }
79
108
  exports.shouldUpdate = shouldUpdate;
@@ -10,3 +10,4 @@ export declare class PythPriceListener implements IPriceListener {
10
10
  private onNewPriceFeed;
11
11
  getLatestPriceInfo(priceId: string): PriceInfo | undefined;
12
12
  }
13
+ //# sourceMappingURL=pyth-price-listener.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pyth-price-listener.d.ts","sourceRoot":"","sources":["../src/pyth-price-listener.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,sBAAsB,EACvB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEnE,qBAAa,iBAAkB,YAAW,cAAc;IACtD,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,eAAe,CAA4B;gBAEvC,UAAU,EAAE,sBAAsB,EAAE,UAAU,EAAE,SAAS,EAAE;IAWjE,KAAK;IAmBX,OAAO,CAAC,cAAc;IAsBtB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;CAG3D"}
@@ -14,7 +14,11 @@ declare const _default: {
14
14
  "wormhole-package-id": Options;
15
15
  "wormhole-state-id": Options;
16
16
  "price-feed-to-price-info-object-table-id": Options;
17
+ "max-vaas-per-ptb": Options;
18
+ "num-gas-objects": Options;
19
+ "gas-budget": Options;
17
20
  };
18
- handler: (argv: any) => void;
21
+ handler: (argv: any) => Promise<void>;
19
22
  };
20
23
  export default _default;
24
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/sui/command.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;oBAgFC,GAAG;;AA5EpC,wBA8JE"}
@@ -33,6 +33,7 @@ const fs_1 = __importDefault(require("fs"));
33
33
  const pyth_price_listener_1 = require("../pyth-price-listener");
34
34
  const controller_1 = require("../controller");
35
35
  const sui_1 = require("./sui");
36
+ const sui_js_1 = require("@mysten/sui.js");
36
37
  exports.default = {
37
38
  command: "sui",
38
39
  describe: "Run price pusher for sui. Most of the arguments below are" +
@@ -77,14 +78,32 @@ exports.default = {
77
78
  type: "string",
78
79
  required: true,
79
80
  },
81
+ "max-vaas-per-ptb": {
82
+ description: "Maximum number of VAAs that can be included in a single PTB.",
83
+ type: "number",
84
+ required: true,
85
+ default: 1,
86
+ },
87
+ "num-gas-objects": {
88
+ description: "Number of gas objects in the pool.",
89
+ type: "number",
90
+ required: true,
91
+ default: 30,
92
+ },
93
+ "gas-budget": {
94
+ description: "Gas budget for each price update",
95
+ type: "number",
96
+ required: true,
97
+ default: 500_000_000,
98
+ },
80
99
  ...options.priceConfigFile,
81
100
  ...options.priceServiceEndpoint,
82
101
  ...options.mnemonicFile,
83
102
  ...options.pollingFrequency,
84
103
  ...options.pushingFrequency,
85
104
  },
86
- handler: function (argv) {
87
- const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pushingFrequency, pollingFrequency, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, } = argv;
105
+ handler: async function (argv) {
106
+ const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pushingFrequency, pollingFrequency, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, numGasObjects, gasBudget, } = argv;
88
107
  const priceConfigs = (0, price_config_1.readPriceConfigFile)(priceConfigFile);
89
108
  const priceServiceConnection = new price_service_client_1.PriceServiceConnection(priceServiceEndpoint, {
90
109
  logger: {
@@ -95,12 +114,18 @@ exports.default = {
95
114
  debug: () => undefined,
96
115
  trace: () => undefined,
97
116
  },
117
+ priceFeedRequestConfig: {
118
+ binary: true,
119
+ },
98
120
  });
99
121
  const mnemonic = fs_1.default.readFileSync(mnemonicFile, "utf-8").trim();
122
+ console.log(`Pushing updates from wallet address: ${sui_js_1.Ed25519Keypair.deriveKeypair(mnemonic)
123
+ .getPublicKey()
124
+ .toSuiAddress()}`);
100
125
  const priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
101
126
  const pythListener = new pyth_price_listener_1.PythPriceListener(priceServiceConnection, priceItems);
102
127
  const suiListener = new sui_1.SuiPriceListener(pythPackageId, priceFeedToPriceInfoObjectTableId, endpoint, priceItems, { pollingFrequency });
103
- const suiPusher = new sui_1.SuiPricePusher(priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, endpoint, mnemonic);
128
+ const suiPusher = await sui_1.SuiPricePusher.createWithAutomaticGasPool(priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, endpoint, mnemonic, gasBudget, numGasObjects);
104
129
  const controller = new controller_1.Controller(priceConfigs, pythListener, suiListener, suiPusher, { pushingFrequency });
105
130
  controller.start();
106
131
  },
package/lib/sui/sui.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ChainPriceListener, IPricePusher, PriceInfo, PriceItem } from "../interface";
2
2
  import { DurationInSeconds } from "../utils";
3
3
  import { PriceServiceConnection } from "@pythnetwork/price-service-client";
4
+ import { RawSigner, SuiObjectRef } from "@mysten/sui.js";
4
5
  export declare class SuiPriceListener extends ChainPriceListener {
5
6
  private pythPackageId;
6
7
  private priceFeedToPriceInfoObjectTableId;
@@ -11,13 +12,31 @@ export declare class SuiPriceListener extends ChainPriceListener {
11
12
  getOnChainPriceInfo(priceId: string): Promise<PriceInfo | undefined>;
12
13
  }
13
14
  export declare class SuiPricePusher implements IPricePusher {
15
+ private readonly signer;
14
16
  private priceServiceConnection;
15
17
  private pythPackageId;
16
18
  private pythStateId;
17
19
  private wormholePackageId;
18
20
  private wormholeStateId;
19
21
  private priceFeedToPriceInfoObjectTableId;
20
- private readonly signer;
21
- constructor(priceServiceConnection: PriceServiceConnection, pythPackageId: string, pythStateId: string, wormholePackageId: string, wormholeStateId: string, priceFeedToPriceInfoObjectTableId: string, endpoint: string, mnemonic: string);
22
+ private maxVaasPerPtb;
23
+ private gasBudget;
24
+ private gasPool;
25
+ constructor(signer: RawSigner, priceServiceConnection: PriceServiceConnection, pythPackageId: string, pythStateId: string, wormholePackageId: string, wormholeStateId: string, priceFeedToPriceInfoObjectTableId: string, maxVaasPerPtb: number, endpoint: string, mnemonic: string, gasBudget: number, gasPool: SuiObjectRef[]);
26
+ /**
27
+ * Create a price pusher with a pool of `numGasObjects` gas coins that will be used to send transactions.
28
+ * The gas coins of the wallet for the provided mnemonic will be merged and then evenly split into `numGasObjects`.
29
+ */
30
+ static createWithAutomaticGasPool(priceServiceConnection: PriceServiceConnection, pythPackageId: string, pythStateId: string, wormholePackageId: string, wormholeStateId: string, priceFeedToPriceInfoObjectTableId: string, maxVaasPerPtb: number, endpoint: string, mnemonic: string, gasBudget: number, numGasObjects: number): Promise<SuiPricePusher>;
22
31
  updatePriceFeed(priceIds: string[], pubTimesToPush: number[]): Promise<void>;
32
+ private createPriceUpdateTransaction;
33
+ /** Send every transaction in txs in parallel, returning when all transactions have completed. */
34
+ private sendTransactionBlocks;
35
+ /** Send a single transaction block using a gas coin from the pool. */
36
+ private sendTransactionBlock;
37
+ private static initializeGasPool;
38
+ private static getAllGasCoins;
39
+ private static splitGasCoinEqually;
40
+ private static mergeGasCoinsIntoOne;
23
41
  }
42
+ //# sourceMappingURL=sui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sui.d.ts","sourceRoot":"","sources":["../../src/sui/sui.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAIL,SAAS,EAIT,YAAY,EAKb,MAAM,gBAAgB,CAAC;AAMxB,qBAAa,gBAAiB,SAAQ,kBAAkB;IAEpD,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,iCAAiC;IACzC,OAAO,CAAC,QAAQ;gBAFR,aAAa,EAAE,MAAM,EACrB,iCAAiC,EAAE,MAAM,EACzC,QAAQ,EAAE,MAAM,EACxB,UAAU,EAAE,SAAS,EAAE,EACvB,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAKG,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CAmD3E;AAED,qBAAa,cAAe,YAAW,YAAY;IAE/C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,sBAAsB;IAC9B,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,iBAAiB;IACzB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,iCAAiC;IACzC,OAAO,CAAC,aAAa;IAGrB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,OAAO;gBAXE,MAAM,EAAE,SAAS,EAC1B,sBAAsB,EAAE,sBAAsB,EAC9C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,EACvB,iCAAiC,EAAE,MAAM,EACzC,aAAa,EAAE,MAAM,EAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EACR,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EAAE;IAGjC;;;OAGG;WACU,0BAA0B,CACrC,sBAAsB,EAAE,sBAAsB,EAC9C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,EACvB,iCAAiC,EAAE,MAAM,EACzC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,cAAc,CAAC;IAiCpB,eAAe,CACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC;YAsDF,4BAA4B;IA2E1C,iGAAiG;YACnF,qBAAqB;IAMnC,sEAAsE;YACxD,oBAAoB;mBA6Cb,iBAAiB;mBA2BjB,cAAc;mBAiCd,mBAAmB;mBAsCnB,oBAAoB;CA2C1C"}
package/lib/sui/sui.js CHANGED
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SuiPricePusher = exports.SuiPriceListener = void 0;
4
4
  const interface_1 = require("../interface");
5
5
  const sui_js_1 = require("@mysten/sui.js");
6
+ const GAS_FEE_FOR_SPLIT = 2_000_000_000;
7
+ // TODO: read this from on chain config
8
+ const MAX_NUM_GAS_OBJECTS_IN_PTB = 256;
9
+ const MAX_NUM_OBJECTS_IN_ARGUMENT = 510;
6
10
  class SuiPriceListener extends interface_1.ChainPriceListener {
7
11
  pythPackageId;
8
12
  priceFeedToPriceInfoObjectTableId;
@@ -48,21 +52,39 @@ class SuiPriceListener extends interface_1.ChainPriceListener {
48
52
  }
49
53
  exports.SuiPriceListener = SuiPriceListener;
50
54
  class SuiPricePusher {
55
+ signer;
51
56
  priceServiceConnection;
52
57
  pythPackageId;
53
58
  pythStateId;
54
59
  wormholePackageId;
55
60
  wormholeStateId;
56
61
  priceFeedToPriceInfoObjectTableId;
57
- signer;
58
- constructor(priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, endpoint, mnemonic) {
62
+ maxVaasPerPtb;
63
+ gasBudget;
64
+ gasPool;
65
+ constructor(signer, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, endpoint, mnemonic, gasBudget, gasPool) {
66
+ this.signer = signer;
59
67
  this.priceServiceConnection = priceServiceConnection;
60
68
  this.pythPackageId = pythPackageId;
61
69
  this.pythStateId = pythStateId;
62
70
  this.wormholePackageId = wormholePackageId;
63
71
  this.wormholeStateId = wormholeStateId;
64
72
  this.priceFeedToPriceInfoObjectTableId = priceFeedToPriceInfoObjectTableId;
65
- this.signer = new sui_js_1.RawSigner(sui_js_1.Ed25519Keypair.deriveKeypair(mnemonic), new sui_js_1.JsonRpcProvider(new sui_js_1.Connection({ fullnode: endpoint })));
73
+ this.maxVaasPerPtb = maxVaasPerPtb;
74
+ this.gasBudget = gasBudget;
75
+ this.gasPool = gasPool;
76
+ }
77
+ /**
78
+ * Create a price pusher with a pool of `numGasObjects` gas coins that will be used to send transactions.
79
+ * The gas coins of the wallet for the provided mnemonic will be merged and then evenly split into `numGasObjects`.
80
+ */
81
+ static async createWithAutomaticGasPool(priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, endpoint, mnemonic, gasBudget, numGasObjects) {
82
+ if (numGasObjects > MAX_NUM_OBJECTS_IN_ARGUMENT) {
83
+ throw new Error(`numGasObjects cannot be greater than ${MAX_NUM_OBJECTS_IN_ARGUMENT} until we implement split chunking`);
84
+ }
85
+ const signer = new sui_js_1.RawSigner(sui_js_1.Ed25519Keypair.deriveKeypair(mnemonic), new sui_js_1.JsonRpcProvider(new sui_js_1.Connection({ fullnode: endpoint })));
86
+ const gasPool = await SuiPricePusher.initializeGasPool(signer, numGasObjects);
87
+ return new SuiPricePusher(signer, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, endpoint, mnemonic, gasBudget, gasPool);
66
88
  }
67
89
  async updatePriceFeed(priceIds, pubTimesToPush) {
68
90
  if (priceIds.length === 0) {
@@ -70,8 +92,43 @@ class SuiPricePusher {
70
92
  }
71
93
  if (priceIds.length !== pubTimesToPush.length)
72
94
  throw new Error("Invalid arguments");
95
+ if (this.gasPool.length === 0) {
96
+ console.warn("Skipping update: no available gas coin.");
97
+ return;
98
+ }
99
+ const priceFeeds = await this.priceServiceConnection.getLatestPriceFeeds(priceIds);
100
+ if (priceFeeds === undefined) {
101
+ console.warn("Failed to fetch price updates. Skipping push.");
102
+ return;
103
+ }
104
+ const vaaToPriceFeedIds = new Map();
105
+ for (const priceFeed of priceFeeds) {
106
+ // The ! will succeed as long as the priceServiceConnection is configured to return binary vaa data (which it is).
107
+ const vaa = priceFeed.getVAA();
108
+ if (!vaaToPriceFeedIds.has(vaa)) {
109
+ vaaToPriceFeedIds.set(vaa, []);
110
+ }
111
+ vaaToPriceFeedIds.get(vaa).push(priceFeed.id);
112
+ }
113
+ const txs = [];
114
+ let currentBatchVaas = [];
115
+ let currentBatchPriceFeedIds = [];
116
+ for (const [vaa, priceFeedIds] of vaaToPriceFeedIds.entries()) {
117
+ currentBatchVaas.push(vaa);
118
+ currentBatchPriceFeedIds.push(...priceFeedIds);
119
+ if (currentBatchVaas.length >= this.maxVaasPerPtb) {
120
+ const tx = await this.createPriceUpdateTransaction(currentBatchVaas, currentBatchPriceFeedIds);
121
+ if (tx !== undefined) {
122
+ txs.push(tx);
123
+ }
124
+ currentBatchVaas = [];
125
+ currentBatchPriceFeedIds = [];
126
+ }
127
+ }
128
+ await this.sendTransactionBlocks(txs);
129
+ }
130
+ async createPriceUpdateTransaction(vaas, priceIds) {
73
131
  const tx = new sui_js_1.TransactionBlock();
74
- const vaas = await this.priceServiceConnection.getLatestVaas(priceIds);
75
132
  // Parse our batch price attestation VAA bytes using Wormhole.
76
133
  // Check out the Wormhole cross-chain bridge and generic messaging protocol here:
77
134
  // https://github.com/wormhole-foundation/wormhole
@@ -110,7 +167,7 @@ class SuiPricePusher {
110
167
  catch (e) {
111
168
  console.log("Error fetching price info object id for ", priceId);
112
169
  console.error(e);
113
- return;
170
+ return undefined;
114
171
  }
115
172
  const coin = tx.splitCoins(tx.gas, [tx.pure(1)]);
116
173
  [price_updates_hot_potato] = tx.moveCall({
@@ -131,29 +188,132 @@ class SuiPricePusher {
131
188
  arguments: [price_updates_hot_potato],
132
189
  typeArguments: [`${this.pythPackageId}::price_info::PriceInfo`],
133
190
  });
191
+ return tx;
192
+ }
193
+ /** Send every transaction in txs in parallel, returning when all transactions have completed. */
194
+ async sendTransactionBlocks(txs) {
195
+ return Promise.all(txs.map((tx) => this.sendTransactionBlock(tx)));
196
+ }
197
+ /** Send a single transaction block using a gas coin from the pool. */
198
+ async sendTransactionBlock(tx) {
199
+ const gasObject = this.gasPool.shift();
200
+ if (gasObject === undefined) {
201
+ console.warn("No available gas coin. Skipping push.");
202
+ return;
203
+ }
204
+ let nextGasObject = undefined;
134
205
  try {
206
+ tx.setGasPayment([gasObject]);
207
+ tx.setGasBudget(this.gasBudget);
135
208
  const result = await this.signer.signAndExecuteTransactionBlock({
136
209
  transactionBlock: tx,
137
210
  options: {
138
- showInput: true,
139
211
  showEffects: true,
140
- showEvents: true,
141
- showObjectChanges: true,
142
- showBalanceChanges: true,
143
212
  },
144
213
  });
214
+ nextGasObject = (0, sui_js_1.getTransactionEffects)(result)
215
+ ?.mutated?.map((obj) => obj.reference)
216
+ .find((ref) => ref.objectId === gasObject.objectId);
145
217
  console.log("Successfully updated price with transaction digest ", result.digest);
146
218
  }
147
219
  catch (e) {
148
220
  console.log("Error when signAndExecuteTransactionBlock");
149
221
  if (String(e).includes("GasBalanceTooLow")) {
150
- console.log("Insufficient Gas Amount. Please top up your account");
151
- process.exit();
222
+ console.warn(`The balance of gas object ${gasObject.objectId} is too low. Removing from pool.`);
223
+ }
224
+ else {
225
+ nextGasObject = gasObject;
152
226
  }
153
227
  console.error(e);
154
- return;
228
+ }
229
+ if (nextGasObject !== undefined) {
230
+ this.gasPool.push(nextGasObject);
155
231
  }
156
232
  }
233
+ // This function will smash all coins owned by the signer into one, and then
234
+ // split them equally into numGasObjects.
235
+ static async initializeGasPool(signer, numGasObjects) {
236
+ const signerAddress = await signer.getAddress();
237
+ const { totalBalance: balance } = await signer.provider.getBalance({
238
+ owner: signerAddress,
239
+ });
240
+ const splitAmount = (BigInt(balance) - BigInt(GAS_FEE_FOR_SPLIT)) / BigInt(numGasObjects);
241
+ const consolidatedCoin = await SuiPricePusher.mergeGasCoinsIntoOne(signer, signerAddress);
242
+ const gasPool = await SuiPricePusher.splitGasCoinEqually(signer, signerAddress, Number(splitAmount), numGasObjects, consolidatedCoin);
243
+ console.log("Gas pool is filled with coins: ", gasPool);
244
+ return gasPool;
245
+ }
246
+ static async getAllGasCoins(provider, owner) {
247
+ let hasNextPage = true;
248
+ let cursor;
249
+ const coins = new Set([]);
250
+ let numCoins = 0;
251
+ while (hasNextPage) {
252
+ const paginatedCoins = await provider.getCoins({
253
+ owner,
254
+ cursor,
255
+ });
256
+ numCoins += paginatedCoins.data.length;
257
+ paginatedCoins.data.forEach((c) => coins.add(JSON.stringify({
258
+ objectId: c.coinObjectId,
259
+ version: c.version,
260
+ digest: c.digest,
261
+ })));
262
+ hasNextPage = paginatedCoins.hasNextPage;
263
+ cursor = paginatedCoins.nextCursor;
264
+ }
265
+ if (numCoins !== coins.size) {
266
+ throw new Error("Unexpected getCoins result: duplicate coins found");
267
+ }
268
+ return [...coins].map((item) => JSON.parse(item));
269
+ }
270
+ static async splitGasCoinEqually(signer, signerAddress, splitAmount, numGasObjects, gasCoin) {
271
+ // TODO: implement chunking if numGasObjects exceeds MAX_NUM_CREATED_OBJECTS
272
+ const tx = new sui_js_1.TransactionBlock();
273
+ const coins = tx.splitCoins(tx.gas, Array.from({ length: numGasObjects }, () => tx.pure(splitAmount)));
274
+ tx.transferObjects(Array.from({ length: numGasObjects }, (_, i) => coins[i]), tx.pure(signerAddress));
275
+ tx.setGasPayment([gasCoin]);
276
+ const result = await signer.signAndExecuteTransactionBlock({
277
+ transactionBlock: tx,
278
+ options: { showEffects: true },
279
+ });
280
+ const error = (0, sui_js_1.getExecutionStatusError)(result);
281
+ if (error) {
282
+ throw new Error(`Failed to initialize gas pool: ${error}. Try re-running the script`);
283
+ }
284
+ const newCoins = (0, sui_js_1.getCreatedObjects)(result).map((obj) => obj.reference);
285
+ if (newCoins.length !== numGasObjects) {
286
+ throw new Error(`Failed to initialize gas pool. Expected ${numGasObjects}, got: ${newCoins}`);
287
+ }
288
+ return newCoins;
289
+ }
290
+ static async mergeGasCoinsIntoOne(signer, owner) {
291
+ const gasCoins = await SuiPricePusher.getAllGasCoins(signer.provider, owner);
292
+ // skip merging if there is only one coin
293
+ if (gasCoins.length === 1) {
294
+ return gasCoins[0];
295
+ }
296
+ const gasCoinsChunks = chunkArray(gasCoins, MAX_NUM_GAS_OBJECTS_IN_PTB - 2);
297
+ let finalCoin;
298
+ for (let i = 0; i < gasCoinsChunks.length; i++) {
299
+ const mergeTx = new sui_js_1.TransactionBlock();
300
+ let coins = gasCoinsChunks[i];
301
+ if (finalCoin) {
302
+ coins = [finalCoin, ...coins];
303
+ }
304
+ mergeTx.setGasPayment(coins);
305
+ const mergeResult = await signer.signAndExecuteTransactionBlock({
306
+ transactionBlock: mergeTx,
307
+ options: { showEffects: true },
308
+ });
309
+ const error = (0, sui_js_1.getExecutionStatusError)(mergeResult);
310
+ if (error) {
311
+ throw new Error(`Failed to merge coins when initializing gas pool: ${error}. Try re-running the script`);
312
+ }
313
+ finalCoin = (0, sui_js_1.getTransactionEffects)(mergeResult).mutated.map((obj) => obj.reference)[0];
314
+ }
315
+ return finalCoin;
316
+ }
157
317
  }
158
318
  exports.SuiPricePusher = SuiPricePusher;
159
319
  // We are calculating stored price info object id for given price id
@@ -187,3 +347,12 @@ async function priceIdToPriceInfoObjectId(provider, pythPackageId, priceFeedToPr
187
347
  CACHE[priceId] = priceInfoObjectId;
188
348
  return priceInfoObjectId;
189
349
  }
350
+ function chunkArray(array, size) {
351
+ const chunked = [];
352
+ let index = 0;
353
+ while (index < array.length) {
354
+ chunked.push(array.slice(index, size + index));
355
+ index += size;
356
+ }
357
+ return chunked;
358
+ }
package/lib/utils.d.ts CHANGED
@@ -10,3 +10,4 @@ export declare function removeLeading0x(id: HexString): HexString;
10
10
  export declare function addLeading0x(id: HexString): HexString;
11
11
  export declare function isWsEndpoint(endpoint: string): boolean;
12
12
  export declare function verifyValidOption<options extends Readonly<Array<any>>, validOption extends options[number]>(option: any, validOptions: options): validOption;
13
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAE9D,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAC/B,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACvC,eAAO,MAAM,QAAQ,uCAAwC,CAAC;AAC9D,MAAM,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9C,eAAO,MAAM,iBAAiB,gBAAiB,CAAC;AAChD,MAAM,MAAM,gBAAgB,GAAG,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAEhE,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,SAAS,GAAG,SAAS,CAKxD;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,SAAS,GAAG,SAAS,CAKrD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAStD;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,SAAS,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EACpC,WAAW,SAAS,OAAO,CAAC,MAAM,CAAC,EACnC,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,eAOnC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pythnetwork/price-pusher",
3
- "version": "5.3.0",
3
+ "version": "5.4.2",
4
4
  "description": "Pyth Price Pusher",
5
5
  "homepage": "https://pyth.network",
6
6
  "main": "lib/index.js",
@@ -63,5 +63,5 @@
63
63
  "yaml": "^2.1.1",
64
64
  "yargs": "^17.5.1"
65
65
  },
66
- "gitHead": "6cdcf4dffd54017e19168be450df01acd68f07ab"
66
+ "gitHead": "5e44fa4c95ae7120ee8a05f145b27470d2c48e7b"
67
67
  }