feed.move 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. module pyth_lazer::feed;
  2. use pyth_lazer::i16::{Self, I16};
  3. use pyth_lazer::i64::{Self, I64};
  4. use sui::bcs;
  5. // Error codes for feed parsing
  6. const EInvalidProperty: u64 = 2;
  7. /// The feed struct is based on the Lazer rust protocol definition defined here:
  8. /// https://github.com/pyth-network/pyth-crosschain/blob/main/lazer/sdk/rust/protocol/src/payload.rs
  9. ///
  10. /// Some fields in Lazer are optional, as in Lazer might return None for them due to some conditions (for example,
  11. /// not having enough publishers to calculate the price) and that is why they are represented as Option<Option<T>>.
  12. /// The first Option<T> is for the existence of the field within the update data and the second Option<T> is for the
  13. /// value of the field.
  14. public struct Feed has copy, drop {
  15. /// Unique identifier for the price feed (e.g., 1 for BTC/USD, 2 for ETH/USD)
  16. feed_id: u32,
  17. /// Current aggregate price from all publishers
  18. price: Option<Option<I64>>,
  19. /// Best bid price available across all publishers
  20. best_bid_price: Option<Option<I64>>,
  21. /// Best ask price available across all publishers
  22. best_ask_price: Option<Option<I64>>,
  23. /// Number of publishers contributing to this price feed
  24. publisher_count: Option<u16>,
  25. /// Price exponent (typically negative, e.g., -8 means divide price by 10^8)
  26. exponent: Option<I16>,
  27. /// Confidence interval representing price uncertainty
  28. confidence: Option<Option<I64>>,
  29. /// Funding rate for derivative products (e.g., perpetual futures)
  30. funding_rate: Option<Option<I64>>,
  31. /// Timestamp when the funding rate was last updated
  32. funding_timestamp: Option<Option<u64>>,
  33. /// How often the funding rate and funding payments are calculated, in microseconds
  34. funding_rate_interval: Option<Option<u64>>,
  35. }
  36. /// Create a new Feed with the specified parameters
  37. public(package) fun new(
  38. feed_id: u32,
  39. price: Option<Option<I64>>,
  40. best_bid_price: Option<Option<I64>>,
  41. best_ask_price: Option<Option<I64>>,
  42. publisher_count: Option<u16>,
  43. exponent: Option<I16>,
  44. confidence: Option<Option<I64>>,
  45. funding_rate: Option<Option<I64>>,
  46. funding_timestamp: Option<Option<u64>>,
  47. funding_rate_interval: Option<Option<u64>>,
  48. ): Feed {
  49. Feed {
  50. feed_id,
  51. price,
  52. best_bid_price,
  53. best_ask_price,
  54. publisher_count,
  55. exponent,
  56. confidence,
  57. funding_rate,
  58. funding_timestamp,
  59. funding_rate_interval,
  60. }
  61. }
  62. /// Get the feed ID
  63. public fun feed_id(feed: &Feed): u32 {
  64. feed.feed_id
  65. }
  66. /// Get the price
  67. public fun price(feed: &Feed): Option<Option<I64>> {
  68. feed.price
  69. }
  70. /// Get the best bid price
  71. public fun best_bid_price(feed: &Feed): Option<Option<I64>> {
  72. feed.best_bid_price
  73. }
  74. /// Get the best ask price
  75. public fun best_ask_price(feed: &Feed): Option<Option<I64>> {
  76. feed.best_ask_price
  77. }
  78. /// Get the publisher count
  79. public fun publisher_count(feed: &Feed): Option<u16> {
  80. feed.publisher_count
  81. }
  82. /// Get the exponent
  83. public fun exponent(feed: &Feed): Option<I16> {
  84. feed.exponent
  85. }
  86. /// Get the confidence interval
  87. public fun confidence(feed: &Feed): Option<Option<I64>> {
  88. feed.confidence
  89. }
  90. /// Get the funding rate
  91. public fun funding_rate(feed: &Feed): Option<Option<I64>> {
  92. feed.funding_rate
  93. }
  94. /// Get the funding timestamp
  95. public fun funding_timestamp(feed: &Feed): Option<Option<u64>> {
  96. feed.funding_timestamp
  97. }
  98. /// Get the funding rate interval
  99. public fun funding_rate_interval(feed: &Feed): Option<Option<u64>> {
  100. feed.funding_rate_interval
  101. }
  102. /// Set the feed ID
  103. public(package) fun set_feed_id(feed: &mut Feed, feed_id: u32) {
  104. feed.feed_id = feed_id;
  105. }
  106. /// Set the price
  107. public(package) fun set_price(feed: &mut Feed, price: Option<Option<I64>>) {
  108. feed.price = price;
  109. }
  110. /// Set the best bid price
  111. public(package) fun set_best_bid_price(feed: &mut Feed, best_bid_price: Option<Option<I64>>) {
  112. feed.best_bid_price = best_bid_price;
  113. }
  114. /// Set the best ask price
  115. public(package) fun set_best_ask_price(feed: &mut Feed, best_ask_price: Option<Option<I64>>) {
  116. feed.best_ask_price = best_ask_price;
  117. }
  118. /// Set the publisher count
  119. public(package) fun set_publisher_count(feed: &mut Feed, publisher_count: Option<u16>) {
  120. feed.publisher_count = publisher_count;
  121. }
  122. /// Set the exponent
  123. public(package) fun set_exponent(feed: &mut Feed, exponent: Option<I16>) {
  124. feed.exponent = exponent;
  125. }
  126. /// Set the confidence interval
  127. public(package) fun set_confidence(feed: &mut Feed, confidence: Option<Option<I64>>) {
  128. feed.confidence = confidence;
  129. }
  130. /// Set the funding rate
  131. public(package) fun set_funding_rate(feed: &mut Feed, funding_rate: Option<Option<I64>>) {
  132. feed.funding_rate = funding_rate;
  133. }
  134. /// Set the funding timestamp
  135. public(package) fun set_funding_timestamp(feed: &mut Feed, funding_timestamp: Option<Option<u64>>) {
  136. feed.funding_timestamp = funding_timestamp;
  137. }
  138. /// Set the funding rate interval
  139. public(package) fun set_funding_rate_interval(
  140. feed: &mut Feed,
  141. funding_rate_interval: Option<Option<u64>>,
  142. ) {
  143. feed.funding_rate_interval = funding_rate_interval;
  144. }
  145. /// Parse a feed from a BCS cursor
  146. public(package) fun parse_from_cursor(cursor: &mut bcs::BCS): Feed {
  147. let feed_id = cursor.peel_u32();
  148. let mut feed = new(
  149. feed_id,
  150. option::none(),
  151. option::none(),
  152. option::none(),
  153. option::none(),
  154. option::none(),
  155. option::none(),
  156. option::none(),
  157. option::none(),
  158. option::none(),
  159. );
  160. let properties_count = cursor.peel_u8();
  161. let mut properties_i = 0;
  162. while (properties_i < properties_count) {
  163. let property_id = cursor.peel_u8();
  164. if (property_id == 0) {
  165. // Price property
  166. let price = cursor.peel_u64();
  167. if (price != 0) {
  168. feed.set_price(option::some(option::some(i64::from_u64(price))));
  169. } else {
  170. feed.set_price(option::some(option::none()));
  171. }
  172. } else if (property_id == 1) {
  173. // Best bid price property
  174. let best_bid_price = cursor.peel_u64();
  175. if (best_bid_price != 0) {
  176. feed.set_best_bid_price(
  177. option::some(option::some(i64::from_u64(best_bid_price))),
  178. );
  179. } else {
  180. feed.set_best_bid_price(option::some(option::none()));
  181. }
  182. } else if (property_id == 2) {
  183. // Best ask price property
  184. let best_ask_price = cursor.peel_u64();
  185. if (best_ask_price != 0) {
  186. feed.set_best_ask_price(
  187. option::some(option::some(i64::from_u64(best_ask_price))),
  188. );
  189. } else {
  190. feed.set_best_ask_price(option::some(option::none()));
  191. }
  192. } else if (property_id == 3) {
  193. // Publisher count property
  194. let publisher_count = cursor.peel_u16();
  195. feed.set_publisher_count(option::some(publisher_count));
  196. } else if (property_id == 4) {
  197. // Exponent property
  198. let exponent = cursor.peel_u16();
  199. feed.set_exponent(option::some(i16::from_u16(exponent)));
  200. } else if (property_id == 5) {
  201. // Confidence property
  202. let confidence = cursor.peel_u64();
  203. if (confidence != 0) {
  204. feed.set_confidence(option::some(option::some(i64::from_u64(confidence))));
  205. } else {
  206. feed.set_confidence(option::some(option::none()));
  207. }
  208. } else if (property_id == 6) {
  209. // Funding rate property
  210. let exists = cursor.peel_bool();
  211. if (exists) {
  212. let funding_rate = cursor.peel_u64();
  213. feed.set_funding_rate(option::some(option::some(i64::from_u64(funding_rate))));
  214. } else {
  215. feed.set_funding_rate(option::some(option::none()));
  216. }
  217. } else if (property_id == 7) {
  218. // Funding timestamp property
  219. let exists = cursor.peel_bool();
  220. if (exists) {
  221. let funding_timestamp = cursor.peel_u64();
  222. feed.set_funding_timestamp(option::some(option::some(funding_timestamp)));
  223. } else {
  224. feed.set_funding_timestamp(option::some(option::none()));
  225. }
  226. } else if (property_id == 8) {
  227. // Funding rate interval property
  228. let exists = cursor.peel_bool();
  229. if (exists) {
  230. let funding_rate_interval = cursor.peel_u64();
  231. feed.set_funding_rate_interval(
  232. option::some(option::some(funding_rate_interval)),
  233. );
  234. } else {
  235. feed.set_funding_rate_interval(option::some(option::none()));
  236. }
  237. } else {
  238. // Unknown property - we cannot safely skip it without knowing its length
  239. abort EInvalidProperty
  240. };
  241. properties_i = properties_i + 1;
  242. };
  243. feed
  244. }