BaseWrapper.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import {
  2. Address,
  3. beginCell,
  4. Cell,
  5. Contract,
  6. ContractProvider,
  7. Dictionary,
  8. Sender,
  9. SendMode,
  10. toNano,
  11. } from "@ton/core";
  12. import { createCellChain } from "@pythnetwork/pyth-ton-js";
  13. import { createGuardianSetsDict } from "../tests/utils/wormhole";
  14. import { HexString, Price } from "@pythnetwork/price-service-sdk";
  15. import { DataSource } from "@pythnetwork/xc-admin-common";
  16. export class BaseWrapper implements Contract {
  17. constructor(
  18. readonly address: Address,
  19. readonly init?: { code: Cell; data: Cell },
  20. ) {}
  21. static createFromAddress(address: Address) {
  22. return new this(address);
  23. }
  24. static createInitData(config: {
  25. priceFeedId?: HexString;
  26. price?: Price;
  27. emaPrice?: Price;
  28. singleUpdateFee?: number;
  29. dataSources?: DataSource[];
  30. guardianSetIndex: number;
  31. guardianSet: string[];
  32. chainId: number;
  33. governanceChainId: number;
  34. governanceContract: string;
  35. governanceDataSource?: DataSource;
  36. }): Cell {
  37. const priceDict = Dictionary.empty(
  38. Dictionary.Keys.BigUint(256),
  39. Dictionary.Values.Cell(),
  40. );
  41. if (config.priceFeedId && config.price && config.emaPrice) {
  42. const priceCell = beginCell()
  43. .storeInt(
  44. config.price.getPriceAsNumberUnchecked() * 10 ** -config.price.expo,
  45. 64,
  46. )
  47. .storeUint(
  48. config.price.getConfAsNumberUnchecked() * 10 ** -config.price.expo,
  49. 64,
  50. )
  51. .storeInt(config.price.expo, 32)
  52. .storeUint(config.price.publishTime, 64)
  53. .endCell();
  54. const emaPriceCell = beginCell()
  55. .storeInt(
  56. config.emaPrice.getPriceAsNumberUnchecked() *
  57. 10 ** -config.emaPrice.expo,
  58. 64,
  59. )
  60. .storeUint(
  61. config.emaPrice.getConfAsNumberUnchecked() *
  62. 10 ** -config.emaPrice.expo,
  63. 64,
  64. )
  65. .storeInt(config.emaPrice.expo, 32)
  66. .storeUint(config.emaPrice.publishTime, 64)
  67. .endCell();
  68. const priceFeedCell = beginCell()
  69. .storeRef(priceCell)
  70. .storeRef(emaPriceCell)
  71. .endCell();
  72. priceDict.set(BigInt(config.priceFeedId), priceFeedCell);
  73. }
  74. // Create a dictionary for valid data sources
  75. const isValidDataSourceDict = Dictionary.empty(
  76. Dictionary.Keys.BigUint(256),
  77. Dictionary.Values.Bool(),
  78. );
  79. if (config.dataSources) {
  80. config.dataSources.forEach((source) => {
  81. const sourceCell = beginCell()
  82. .storeUint(source.emitterChain, 16)
  83. .storeBuffer(Buffer.from(source.emitterAddress, "hex"))
  84. .endCell();
  85. const cellHash = BigInt("0x" + sourceCell.hash().toString("hex"));
  86. isValidDataSourceDict.set(cellHash, true);
  87. });
  88. }
  89. // Group price feeds and update fee
  90. const priceFeedsCell = beginCell()
  91. .storeDict(priceDict)
  92. .storeUint(config.singleUpdateFee || 0, 256)
  93. .endCell();
  94. // Group data sources information
  95. const dataSourcesCell = beginCell()
  96. .storeDict(isValidDataSourceDict)
  97. .endCell();
  98. // Group guardian set information
  99. const guardianSetCell = beginCell()
  100. .storeUint(config.guardianSetIndex, 32)
  101. .storeDict(
  102. createGuardianSetsDict(config.guardianSet, config.guardianSetIndex),
  103. )
  104. .endCell();
  105. // Group chain and governance information
  106. const governanceCell = beginCell()
  107. .storeUint(config.chainId, 16)
  108. .storeUint(config.governanceChainId, 16)
  109. .storeBuffer(Buffer.from(config.governanceContract, "hex"))
  110. .storeDict(Dictionary.empty()) // consumed_governance_actions
  111. .storeRef(
  112. config.governanceDataSource
  113. ? beginCell()
  114. .storeUint(config.governanceDataSource.emitterChain, 16)
  115. .storeBuffer(
  116. Buffer.from(config.governanceDataSource.emitterAddress, "hex"),
  117. )
  118. .endCell()
  119. : beginCell().endCell(),
  120. ) // governance_data_source
  121. .storeUint(0, 64) // last_executed_governance_sequence, set to 0 for initial state
  122. .storeUint(0, 32) // governance_data_source_index, set to 0 for initial state
  123. .storeUint(0, 256) // upgrade_code_hash, set to 0 for initial state
  124. .endCell();
  125. // Create the main cell with references to grouped data
  126. return beginCell()
  127. .storeRef(priceFeedsCell)
  128. .storeRef(dataSourcesCell)
  129. .storeRef(guardianSetCell)
  130. .storeRef(governanceCell)
  131. .endCell();
  132. }
  133. async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {
  134. await provider.internal(via, {
  135. value,
  136. sendMode: SendMode.PAY_GAS_SEPARATELY,
  137. body: beginCell().endCell(),
  138. });
  139. }
  140. async getCurrentGuardianSetIndex(
  141. provider: ContractProvider,
  142. methodName: string,
  143. ) {
  144. const result = await provider.get(methodName, []);
  145. return result.stack.readNumber();
  146. }
  147. async sendUpdateGuardianSet(
  148. provider: ContractProvider,
  149. via: Sender,
  150. vm: Buffer,
  151. ) {
  152. const messageBody = beginCell()
  153. .storeUint(1, 32) // OP_UPDATE_GUARDIAN_SET
  154. .storeRef(createCellChain(vm))
  155. .endCell();
  156. await provider.internal(via, {
  157. value: toNano("0.1"),
  158. sendMode: SendMode.PAY_GAS_SEPARATELY,
  159. body: messageBody,
  160. });
  161. }
  162. async sendUpdatePriceFeeds(
  163. provider: ContractProvider,
  164. via: Sender,
  165. updateData: Buffer,
  166. updateFee: bigint,
  167. ) {
  168. const messageBody = beginCell()
  169. .storeUint(2, 32) // OP_UPDATE_PRICE_FEEDS
  170. .storeRef(createCellChain(updateData))
  171. .endCell();
  172. await provider.internal(via, {
  173. value: updateFee,
  174. sendMode: SendMode.PAY_GAS_SEPARATELY,
  175. body: messageBody,
  176. });
  177. }
  178. async getChainId(provider: ContractProvider, methodName: string) {
  179. const result = await provider.get(methodName, []);
  180. return result.stack.readNumber();
  181. }
  182. async getPriceUnsafe(
  183. provider: ContractProvider,
  184. priceFeedId: HexString,
  185. methodName: string,
  186. ) {
  187. const result = await provider.get(methodName, [
  188. { type: "int", value: BigInt(priceFeedId) },
  189. ]);
  190. const price = result.stack.readNumber();
  191. const conf = result.stack.readNumber();
  192. const expo = result.stack.readNumber();
  193. const publishTime = result.stack.readNumber();
  194. return {
  195. price,
  196. conf,
  197. expo,
  198. publishTime,
  199. };
  200. }
  201. async getPriceNoOlderThan(
  202. provider: ContractProvider,
  203. timePeriod: number,
  204. priceFeedId: HexString,
  205. methodName: string,
  206. ) {
  207. const result = await provider.get(methodName, [
  208. { type: "int", value: BigInt(timePeriod) },
  209. { type: "int", value: BigInt(priceFeedId) },
  210. ]);
  211. const price = result.stack.readNumber();
  212. const conf = result.stack.readNumber();
  213. const expo = result.stack.readNumber();
  214. const publishTime = result.stack.readNumber();
  215. return {
  216. price,
  217. conf,
  218. expo,
  219. publishTime,
  220. };
  221. }
  222. async getEmaPriceUnsafe(
  223. provider: ContractProvider,
  224. priceFeedId: HexString,
  225. methodName: string,
  226. ) {
  227. const result = await provider.get(methodName, [
  228. { type: "int", value: BigInt(priceFeedId) },
  229. ]);
  230. const price = result.stack.readNumber();
  231. const conf = result.stack.readNumber();
  232. const expo = result.stack.readNumber();
  233. const publishTime = result.stack.readNumber();
  234. return {
  235. price,
  236. conf,
  237. expo,
  238. publishTime,
  239. };
  240. }
  241. async getEmaPriceNoOlderThan(
  242. provider: ContractProvider,
  243. timePeriod: number,
  244. priceFeedId: HexString,
  245. methodName: string,
  246. ) {
  247. const result = await provider.get(methodName, [
  248. { type: "int", value: BigInt(timePeriod) },
  249. { type: "int", value: BigInt(priceFeedId) },
  250. ]);
  251. const price = result.stack.readNumber();
  252. const conf = result.stack.readNumber();
  253. const expo = result.stack.readNumber();
  254. const publishTime = result.stack.readNumber();
  255. return {
  256. price,
  257. conf,
  258. expo,
  259. publishTime,
  260. };
  261. }
  262. async getUpdateFee(
  263. provider: ContractProvider,
  264. vm: Buffer,
  265. methodName: string,
  266. ) {
  267. const result = await provider.get(methodName, [
  268. { type: "slice", cell: createCellChain(vm) },
  269. ]);
  270. return result.stack.readNumber();
  271. }
  272. async getSingleUpdateFee(provider: ContractProvider, methodName: string) {
  273. const result = await provider.get(methodName, []);
  274. return result.stack.readNumber();
  275. }
  276. }