ton.ts 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import { Chain, TonChain } from "../chains";
  2. import { WormholeContract } from "./wormhole";
  3. import { PriceFeed, PriceFeedContract, PrivateKey, TxResult } from "../base";
  4. import { TokenQty } from "../token";
  5. import { DataSource } from "@pythnetwork/xc-admin-common";
  6. import { Address, Cell, OpenedContract } from "@ton/ton";
  7. import {
  8. calculateUpdatePriceFeedsFee,
  9. PythContract,
  10. } from "@pythnetwork/pyth-ton-js";
  11. export class TonWormholeContract extends WormholeContract {
  12. static type = "TonWormholeContract";
  13. getId(): string {
  14. return `${this.chain.getId()}_${this.address}_${TonWormholeContract.type}`;
  15. }
  16. getChain(): TonChain {
  17. return this.chain;
  18. }
  19. getType(): string {
  20. return TonWormholeContract.type;
  21. }
  22. toJson() {
  23. return {
  24. chain: this.chain.getId(),
  25. address: this.address,
  26. type: TonWormholeContract.type,
  27. };
  28. }
  29. static fromJson(
  30. chain: Chain,
  31. parsed: {
  32. type: string;
  33. address: string;
  34. }
  35. ): TonWormholeContract {
  36. if (parsed.type !== TonWormholeContract.type)
  37. throw new Error("Invalid type");
  38. if (!(chain instanceof TonChain))
  39. throw new Error(`Wrong chain type ${chain}`);
  40. return new TonWormholeContract(chain, parsed.address);
  41. }
  42. constructor(public chain: TonChain, public address: string) {
  43. super();
  44. }
  45. async getContract(): Promise<OpenedContract<PythContract>> {
  46. const provider = await this.chain.getContractProvider(this.address);
  47. const contract = provider.open(
  48. PythContract.createFromAddress(Address.parse(this.address))
  49. );
  50. return contract;
  51. }
  52. async getCurrentGuardianSetIndex(): Promise<number> {
  53. const contract = await this.getContract();
  54. const result = await contract.getCurrentGuardianSetIndex();
  55. return result;
  56. }
  57. async getChainId(): Promise<number> {
  58. const contract = await this.getContract();
  59. const result = await contract.getChainId();
  60. return Number(result);
  61. }
  62. async getGuardianSet(): Promise<string[]> {
  63. const contract = await this.getContract();
  64. const guardianSetIndex = await this.getCurrentGuardianSetIndex();
  65. const result = await contract.getGuardianSet(guardianSetIndex);
  66. return result.keys;
  67. }
  68. async upgradeGuardianSets(
  69. senderPrivateKey: PrivateKey,
  70. vaa: Buffer
  71. ): Promise<TxResult> {
  72. const contract = await this.getContract();
  73. const provider = await this.chain.getContractProvider(this.address);
  74. const sender = await this.chain.getSender(senderPrivateKey);
  75. const wallet = await this.chain.getWallet(senderPrivateKey);
  76. await contract.sendUpdateGuardianSet(sender, vaa);
  77. // Get recent transactions for this address
  78. const transactions = await provider.getTransactions(
  79. wallet.address,
  80. BigInt(0),
  81. Buffer.alloc(0),
  82. 1
  83. );
  84. return {
  85. id: transactions[0].hash.toString(),
  86. info: JSON.stringify("0x1"),
  87. };
  88. }
  89. }
  90. export class TonPriceFeedContract extends PriceFeedContract {
  91. static type = "TonPriceFeedContract";
  92. constructor(public chain: TonChain, public address: string) {
  93. super();
  94. }
  95. static fromJson(
  96. chain: Chain,
  97. parsed: { type: string; address: string }
  98. ): TonPriceFeedContract {
  99. if (parsed.type !== TonPriceFeedContract.type)
  100. throw new Error("Invalid type");
  101. if (!(chain instanceof TonChain))
  102. throw new Error(`Wrong chain type ${chain}`);
  103. return new TonPriceFeedContract(chain, parsed.address);
  104. }
  105. getId(): string {
  106. return `${this.chain.getId()}_${this.address}_${TonPriceFeedContract.type}`;
  107. }
  108. getChain(): TonChain {
  109. return this.chain;
  110. }
  111. getType(): string {
  112. return TonPriceFeedContract.type;
  113. }
  114. async getContract(): Promise<OpenedContract<PythContract>> {
  115. const provider = await this.chain.getContractProvider(this.address);
  116. const contract = provider.open(
  117. PythContract.createFromAddress(Address.parse(this.address))
  118. );
  119. return contract;
  120. }
  121. async getTotalFee(): Promise<TokenQty> {
  122. const client = await this.chain.getClient();
  123. const balance = await client.getBalance(Address.parse(this.address));
  124. return {
  125. amount: balance,
  126. denom: this.chain.getNativeToken(),
  127. };
  128. }
  129. async getLastExecutedGovernanceSequence(): Promise<number> {
  130. const contract = await this.getContract();
  131. const result = await contract.getLastExecutedGovernanceSequence();
  132. return Number(result);
  133. }
  134. async getPriceFeed(feedId: string): Promise<PriceFeed | undefined> {
  135. const contract = await this.getContract();
  136. const feedIdWithPrefix = `0x${feedId}`;
  137. try {
  138. const price = await contract.getPriceUnsafe(feedIdWithPrefix);
  139. const emaPrice = await contract.getEmaPriceUnsafe(feedIdWithPrefix);
  140. return {
  141. price: {
  142. price: price.price.toString(),
  143. conf: price.conf.toString(),
  144. expo: price.expo.toString(),
  145. publishTime: price.publishTime.toString(),
  146. },
  147. emaPrice: {
  148. price: emaPrice.price.toString(),
  149. conf: emaPrice.conf.toString(),
  150. expo: emaPrice.expo.toString(),
  151. publishTime: emaPrice.publishTime.toString(),
  152. },
  153. };
  154. } catch (e) {
  155. console.error(e);
  156. return undefined;
  157. }
  158. }
  159. async getValidTimePeriod(): Promise<number> {
  160. // Not supported but return 1 because it's required by the abstract class
  161. return 1;
  162. }
  163. async getWormholeContract(): Promise<TonWormholeContract> {
  164. // Price feed contract and wormhole contract live at same address in TON
  165. return new TonWormholeContract(this.chain, this.address);
  166. }
  167. async getBaseUpdateFee() {
  168. const contract = await this.getContract();
  169. const amount = await contract.getSingleUpdateFee();
  170. return {
  171. amount: amount.toString(),
  172. denom: this.chain.getNativeToken(),
  173. };
  174. }
  175. async getDataSources(): Promise<DataSource[]> {
  176. const contract = await this.getContract();
  177. const dataSources = await contract.getDataSources();
  178. return dataSources.map((ds: DataSource) => ({
  179. emitterChain: ds.emitterChain,
  180. emitterAddress: ds.emitterAddress.replace("0x", ""),
  181. }));
  182. }
  183. async getGovernanceDataSource(): Promise<DataSource> {
  184. const contract = await this.getContract();
  185. const result = await contract.getGovernanceDataSource();
  186. if (result === null) {
  187. throw new Error("Governance data source not found");
  188. }
  189. return {
  190. emitterChain: result.emitterChain,
  191. emitterAddress: result.emitterAddress,
  192. };
  193. }
  194. async executeUpdatePriceFeed(
  195. senderPrivateKey: PrivateKey,
  196. vaas: Buffer[]
  197. ): Promise<TxResult> {
  198. const client = await this.chain.getClient();
  199. const contract = await this.getContract();
  200. const wallet = await this.chain.getWallet(senderPrivateKey);
  201. const sender = await this.chain.getSender(senderPrivateKey);
  202. for (const vaa of vaas) {
  203. const fee = await contract.getUpdateFee(vaa);
  204. console.log(fee);
  205. await contract.sendUpdatePriceFeeds(
  206. sender,
  207. vaa,
  208. calculateUpdatePriceFeedsFee(BigInt(fee)) + BigInt(fee)
  209. );
  210. }
  211. const txDetails = await client.getTransactions(wallet.address, {
  212. limit: 1,
  213. });
  214. const txHash = Buffer.from(txDetails[0].hash()).toString("hex");
  215. const txInfo = JSON.stringify(txDetails[0].description, (_, value) =>
  216. typeof value === "bigint" ? value.toString() : value
  217. );
  218. return {
  219. id: txHash,
  220. info: txInfo,
  221. };
  222. }
  223. async executeGovernanceInstruction(
  224. senderPrivateKey: PrivateKey,
  225. vaa: Buffer
  226. ): Promise<TxResult> {
  227. const client = await this.chain.getClient();
  228. const contract = await this.getContract();
  229. const wallet = await this.chain.getWallet(senderPrivateKey);
  230. const sender = await this.chain.getSender(senderPrivateKey);
  231. await contract.sendExecuteGovernanceAction(sender, vaa);
  232. const txDetails = await client.getTransactions(wallet.address, {
  233. limit: 1,
  234. });
  235. const txHash = Buffer.from(txDetails[0].hash()).toString("hex");
  236. const txInfo = JSON.stringify(txDetails[0].description, (_, value) =>
  237. typeof value === "bigint" ? value.toString() : value
  238. );
  239. return {
  240. id: txHash,
  241. info: txInfo,
  242. };
  243. }
  244. async upgradeContract(
  245. senderPrivateKey: PrivateKey,
  246. newCode: Cell
  247. ): Promise<TxResult> {
  248. const client = await this.chain.getClient();
  249. const contract = await this.getContract();
  250. const wallet = await this.chain.getWallet(senderPrivateKey);
  251. const sender = await this.chain.getSender(senderPrivateKey);
  252. await contract.sendUpgradeContract(sender, newCode);
  253. const txDetails = await client.getTransactions(wallet.address, {
  254. limit: 1,
  255. });
  256. const txHash = Buffer.from(txDetails[0].hash()).toString("hex");
  257. const txInfo = JSON.stringify(txDetails[0].description, (_, value) =>
  258. typeof value === "bigint" ? value.toString() : value
  259. );
  260. return {
  261. id: txHash,
  262. info: txInfo,
  263. };
  264. }
  265. toJson() {
  266. return {
  267. chain: this.chain.getId(),
  268. address: this.address,
  269. type: TonPriceFeedContract.type,
  270. };
  271. }
  272. }