WormholeTest.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import {
  2. Address,
  3. beginCell,
  4. Cell,
  5. Contract,
  6. contractAddress,
  7. ContractProvider,
  8. Dictionary,
  9. Sender,
  10. SendMode,
  11. } from "@ton/core";
  12. import { createCellChain } from "../tests/utils";
  13. import { parseGuardianSetKeys } from "../tests/utils/wormhole";
  14. export type WormholeTestConfig = {
  15. guardianSetIndex: number;
  16. guardianSet: string[];
  17. chainId: number;
  18. governanceChainId: number;
  19. governanceContract: string;
  20. };
  21. export class WormholeTest implements Contract {
  22. constructor(
  23. readonly address: Address,
  24. readonly init?: { code: Cell; data: Cell }
  25. ) {}
  26. static createFromAddress(address: Address) {
  27. return new WormholeTest(address);
  28. }
  29. static createFromConfig(
  30. config: WormholeTestConfig,
  31. code: Cell,
  32. workchain = 0
  33. ) {
  34. const data = WormholeTest.getWormholeInitData(
  35. config.guardianSetIndex,
  36. config.guardianSet,
  37. config.chainId,
  38. config.governanceChainId,
  39. config.governanceContract
  40. );
  41. const init = { code, data };
  42. return new WormholeTest(contractAddress(workchain, init), init);
  43. }
  44. static getWormholeInitData(
  45. guardianSetIndex: number,
  46. guardianSet: string[],
  47. chainId: number,
  48. governanceChainId: number,
  49. governanceContract: string
  50. ): Cell {
  51. const guardianSetDict = Dictionary.empty(
  52. Dictionary.Keys.Uint(8),
  53. Dictionary.Values.Buffer(20)
  54. );
  55. guardianSet.forEach((key, index) => {
  56. guardianSetDict.set(index, Buffer.from(key.slice(2), "hex"));
  57. });
  58. const guardianSets = Dictionary.empty(
  59. Dictionary.Keys.Uint(32),
  60. Dictionary.Values.Cell()
  61. );
  62. const guardianSetCell = beginCell()
  63. .storeUint(0, 64) // expiration_time, set to 0 for testing
  64. .storeDict(guardianSetDict)
  65. .endCell();
  66. guardianSets.set(guardianSetIndex, guardianSetCell);
  67. return beginCell()
  68. .storeDict(Dictionary.empty()) // latest_price_feeds, empty for initial state
  69. .storeUint(0, 256) // single_update_fee, set to 0 for testing
  70. .storeUint(guardianSetIndex, 32)
  71. .storeDict(guardianSets)
  72. .storeUint(chainId, 16)
  73. .storeUint(governanceChainId, 16)
  74. .storeBuffer(Buffer.from(governanceContract, "hex"))
  75. .storeDict(Dictionary.empty()) // consumed_governance_actions, empty for initial state
  76. .endCell();
  77. }
  78. async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) {
  79. await provider.internal(via, {
  80. value,
  81. sendMode: SendMode.PAY_GAS_SEPARATELY,
  82. body: beginCell().endCell(),
  83. });
  84. }
  85. // NOTE: the function name has to start with "send" or "get" so that it automatically inserts `provider` as a first argument
  86. async getParseEncodedUpgrade(
  87. provider: ContractProvider,
  88. currentGuardianSetIndex: number,
  89. encodedUpgrade: Buffer
  90. ) {
  91. const result = await provider.get("test_parse_encoded_upgrade", [
  92. { type: "int", value: BigInt(currentGuardianSetIndex) },
  93. { type: "slice", cell: createCellChain(encodedUpgrade) },
  94. ]);
  95. return {
  96. action: result.stack.readNumber(),
  97. chain: result.stack.readNumber(),
  98. module: result.stack.readBigNumber(),
  99. newGuardianSetKeys: parseGuardianSetKeys(result.stack.readCell()),
  100. newGuardianSetIndex: result.stack.readNumber(),
  101. };
  102. }
  103. async getParseAndVerifyWormholeVm(provider: ContractProvider, vm: Buffer) {
  104. const cell = createCellChain(vm);
  105. const result = await provider.get("test_parse_and_verify_wormhole_vm", [
  106. { type: "slice", cell: cell },
  107. ]);
  108. const version = result.stack.readNumber();
  109. const vm_guardian_set_index = result.stack.readNumber();
  110. const timestamp = result.stack.readNumber();
  111. const nonce = result.stack.readNumber();
  112. const emitter_chain_id = result.stack.readNumber();
  113. const emitter_address = result.stack
  114. .readBigNumber()
  115. .toString(16)
  116. .padStart(64, "0");
  117. const sequence = result.stack.readNumber();
  118. const consistency_level = result.stack.readNumber();
  119. const payloadCell = result.stack.readCell();
  120. let payload = "";
  121. let currentCell = payloadCell;
  122. for (let i = 0; i < 4; i++) {
  123. // Original cell + up to 3 references since payload span across 4 cells
  124. const slice = currentCell.beginParse();
  125. payload += slice.loadBits(slice.remainingBits).toString().toLowerCase();
  126. if (slice.remainingRefs === 0) break;
  127. currentCell = slice.loadRef();
  128. }
  129. const hash = result.stack.readBigNumber().toString(16);
  130. return {
  131. version,
  132. vm_guardian_set_index,
  133. timestamp,
  134. nonce,
  135. emitter_chain_id,
  136. emitter_address,
  137. sequence,
  138. consistency_level,
  139. payload,
  140. hash,
  141. };
  142. }
  143. async getCurrentGuardianSetIndex(provider: ContractProvider) {
  144. const result = await provider.get(
  145. "test_get_current_guardian_set_index",
  146. []
  147. );
  148. return result.stack.readNumber();
  149. }
  150. async getUpdateGuardianSet(provider: ContractProvider, vm: Buffer) {
  151. const result = await provider.get("test_update_guardian_set", [
  152. { type: "slice", cell: createCellChain(vm) },
  153. ]);
  154. return result.stack.readNumber();
  155. }
  156. async getGuardianSet(provider: ContractProvider, index: number) {
  157. const result = await provider.get("test_get_guardian_set", [
  158. { type: "int", value: BigInt(index) },
  159. ]);
  160. const expirationTime = result.stack.readNumber();
  161. const keys = parseGuardianSetKeys(result.stack.readCell());
  162. const keyCount = result.stack.readNumber();
  163. return {
  164. expirationTime,
  165. keys,
  166. keyCount,
  167. };
  168. }
  169. }