wormhole.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. const jsonfile = require('jsonfile');
  2. const elliptic = require('elliptic');
  3. const path = require('path');
  4. const Wormhole = artifacts.require("Wormhole");
  5. const MockImplementation = artifacts.require("MockImplementation");
  6. const Implementation = artifacts.require("Implementation");
  7. const testSigner1PK = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0";
  8. const testSigner2PK = "892330666a850761e7370376430bb8c2aa1494072d3bfeaed0c4fa3d5a9135fe";
  9. const testSigner3PK = "87b45997ea577b93073568f06fc4838cffc1d01f90fc4d57f936957f3c4d99fb";
  10. const ImplementationFullABI = jsonfile.readFileSync("build/contracts/Implementation.json").abi
  11. // Taken from https://medium.com/fluidity/standing-the-time-of-test-b906fcc374a9
  12. advanceTimeAndBlock = async (time) => {
  13. await advanceTime(time);
  14. await advanceBlock();
  15. return Promise.resolve(web3.eth.getBlock('latest'));
  16. }
  17. advanceTime = (time) => {
  18. return new Promise((resolve, reject) => {
  19. web3.currentProvider.send({
  20. jsonrpc: "2.0",
  21. method: "evm_increaseTime",
  22. params: [time],
  23. id: new Date().getTime()
  24. }, (err, result) => {
  25. if (err) {
  26. return reject(err);
  27. }
  28. return resolve(result);
  29. });
  30. });
  31. }
  32. advanceBlock = () => {
  33. return new Promise((resolve, reject) => {
  34. web3.currentProvider.send({
  35. jsonrpc: "2.0",
  36. method: "evm_mine",
  37. id: new Date().getTime()
  38. }, (err, result) => {
  39. if (err) {
  40. return reject(err);
  41. }
  42. const newBlockHash = web3.eth.getBlock('latest').hash;
  43. return resolve(newBlockHash)
  44. });
  45. });
  46. }
  47. contract("Wormhole", function () {
  48. const testSigner1 = web3.eth.accounts.privateKeyToAccount(testSigner1PK);
  49. const testSigner2 = web3.eth.accounts.privateKeyToAccount(testSigner2PK);
  50. const testSigner3 = web3.eth.accounts.privateKeyToAccount(testSigner3PK);
  51. const testChainId = "2";
  52. const testGovernanceChainId = "3";
  53. const testGovernanceContract = "0x0000000000000000000000000000000000000000000000000000000000000004";
  54. it("should be initialized with the correct signers and values", async function () {
  55. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  56. const index = await initialized.methods.getCurrentGuardianSetIndex().call();
  57. const set = (await initialized.methods.getGuardianSet(index).call());
  58. // check set
  59. assert.lengthOf(set[0], 1);
  60. assert.equal(set[0][0], testSigner1.address);
  61. // check expiration
  62. assert.equal(set.expirationTime, "0");
  63. // chain id
  64. const chainId = await initialized.methods.chainId().call();
  65. assert.equal(chainId, testChainId);
  66. // governance
  67. const governanceChainId = await initialized.methods.governanceChainId().call();
  68. assert.equal(governanceChainId, testGovernanceChainId);
  69. const governanceContract = await initialized.methods.governanceContract().call();
  70. assert.equal(governanceContract, testGovernanceContract);
  71. })
  72. it("initialize should be non-reentrant", async function () {
  73. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  74. try {
  75. await initialized.methods.initialize([
  76. testSigner1.address
  77. ], testChainId, testGovernanceChainId, testGovernanceContract).estimateGas();
  78. } catch (error) {
  79. assert.equal(error.message, "Returned error: VM Exception while processing transaction: revert already initialized")
  80. return
  81. }
  82. assert.fail("did not fail")
  83. })
  84. it("should log a published message correctly", async function () {
  85. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  86. const accounts = await web3.eth.getAccounts();
  87. const log = await initialized.methods.publishMessage(
  88. "0x123",
  89. "0x123321",
  90. false,
  91. 32
  92. ).send({
  93. value: 0, // fees are set to 0 initially
  94. from: accounts[0]
  95. })
  96. assert.equal(log.events.LogMessagePublished.returnValues.sender.toString(), accounts[0]);
  97. assert.equal(log.events.LogMessagePublished.returnValues.sequence.toString(), "0");
  98. assert.equal(log.events.LogMessagePublished.returnValues.nonce, 291);
  99. assert.equal(log.events.LogMessagePublished.returnValues.payload.toString(), "0x123321");
  100. assert.equal(log.events.LogMessagePublished.returnValues.consistencyLevel, 32);
  101. assert.equal(log.events.LogMessagePublished.returnValues.persistMessage, false);
  102. })
  103. it("should increase the sequence for an account", async function () {
  104. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  105. const accounts = await web3.eth.getAccounts();
  106. const log = await initialized.methods.publishMessage(
  107. "0x1",
  108. "0x1",
  109. false,
  110. 32
  111. ).send({
  112. value: 0, // fees are set to 0 initially
  113. from: accounts[0]
  114. })
  115. assert.equal(log.events.LogMessagePublished.returnValues.sequence.toString(), "1");
  116. })
  117. it("parses VMs correctly", async function () {
  118. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  119. const timestamp = 1000;
  120. const nonce = 1001;
  121. const emitterChainId = 11;
  122. const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
  123. const data = "0xaaaaaa";
  124. const vm = await signAndEncodeVM(
  125. timestamp,
  126. nonce,
  127. emitterChainId,
  128. emitterAddress,
  129. 1337,
  130. data,
  131. [
  132. testSigner1PK,
  133. ],
  134. 0,
  135. 2
  136. );
  137. let result
  138. try {
  139. result = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  140. } catch (err) {
  141. console.log(err)
  142. assert.fail("parseAndVerifyVM failed")
  143. }
  144. assert.equal(result.vm.version, 1);
  145. assert.equal(result.vm.timestamp, timestamp);
  146. assert.equal(result.vm.nonce, nonce);
  147. assert.equal(result.vm.emitterChainId, emitterChainId);
  148. assert.equal(result.vm.emitterAddress, emitterAddress);
  149. assert.equal(result.vm.payload, data);
  150. assert.equal(result.vm.guardianSetIndex, 0);
  151. assert.equal(result.vm.sequence, 1337);
  152. assert.equal(result.vm.consistencyLevel, 2);
  153. assert.equal(result.valid, true);
  154. assert.equal(result.reason, "");
  155. })
  156. it("should set and enforce fees", async function () {
  157. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  158. const accounts = await web3.eth.getAccounts();
  159. const timestamp = 1000;
  160. const nonce = 1001;
  161. const emitterChainId = testGovernanceChainId;
  162. const emitterAddress = testGovernanceContract
  163. let data = "0x00000000000000000000000000000000000000000000000000000000436f726503";
  164. data += [
  165. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  166. web3.eth.abi.encodeParameter("uint256", 1111).substring(2),
  167. web3.eth.abi.encodeParameter("uint256", 2222).substring(2),
  168. ].join('')
  169. const vm = await signAndEncodeVM(
  170. timestamp,
  171. nonce,
  172. emitterChainId,
  173. emitterAddress,
  174. 0,
  175. data,
  176. [
  177. testSigner1PK,
  178. ],
  179. 0,
  180. 2
  181. );
  182. let before = await initialized.methods.messageFee().call();
  183. let set = await initialized.methods.submitSetMessageFee("0x" + vm).send({
  184. value: 0,
  185. from: accounts[0],
  186. gasLimit: 1000000
  187. });
  188. let after = await initialized.methods.messageFee().call();
  189. assert.notEqual(before, after);
  190. assert.equal(after, 1111);
  191. // test non-persisted message
  192. await initialized.methods.publishMessage(
  193. "0x123",
  194. "0x123321",
  195. false,
  196. 32
  197. ).send({
  198. from: accounts[0],
  199. value: 1111
  200. })
  201. // test persisted message
  202. await initialized.methods.publishMessage(
  203. "0x123",
  204. "0x123321",
  205. true,
  206. 32
  207. ).send({
  208. from: accounts[0],
  209. value: 2222
  210. })
  211. let failed = false;
  212. try {
  213. await initialized.methods.publishMessage(
  214. "0x123",
  215. "0x123321",
  216. false,
  217. 32
  218. ).send({
  219. value: 1110,
  220. from: accounts[0]
  221. })
  222. } catch (e) {
  223. failed = true
  224. }
  225. assert.equal(failed, true);
  226. })
  227. it("should transfer out collected fees", async function () {
  228. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  229. const accounts = await web3.eth.getAccounts();
  230. const receiver = "0x" + zeroPadBytes(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16), 20);
  231. const timestamp = 1000;
  232. const nonce = 1001;
  233. const emitterChainId = testGovernanceChainId;
  234. const emitterAddress = testGovernanceContract
  235. let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
  236. data += [
  237. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  238. web3.eth.abi.encodeParameter("uint256", 11).substring(2),
  239. web3.eth.abi.encodeParameter("address", receiver).substring(2),
  240. ].join('')
  241. const vm = await signAndEncodeVM(
  242. timestamp,
  243. nonce,
  244. emitterChainId,
  245. emitterAddress,
  246. 0,
  247. data,
  248. [
  249. testSigner1PK,
  250. ],
  251. 0,
  252. 2
  253. );
  254. let WHBefore = await web3.eth.getBalance(Wormhole.address);
  255. let receiverBefore = await web3.eth.getBalance(receiver);
  256. let set = await initialized.methods.submitTransferFees("0x" + vm).send({
  257. value: 0,
  258. from: accounts[0],
  259. gasLimit: 1000000
  260. });
  261. let WHAfter = await web3.eth.getBalance(Wormhole.address);
  262. let receiverAfter = await web3.eth.getBalance(receiver);
  263. assert.equal(WHBefore - WHAfter, 11);
  264. assert.equal(receiverAfter - receiverBefore, 11);
  265. })
  266. it("should accept a new guardian set", async function () {
  267. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  268. const accounts = await web3.eth.getAccounts();
  269. const timestamp = 1000;
  270. const nonce = 1001;
  271. const emitterChainId = testGovernanceChainId;
  272. const emitterAddress = testGovernanceContract
  273. let data = "0x00000000000000000000000000000000000000000000000000000000436f726502";
  274. let oldIndex = Number(await initialized.methods.getCurrentGuardianSetIndex().call());
  275. data += [
  276. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  277. web3.eth.abi.encodeParameter("uint32", oldIndex + 1).substring(2 + (64 - 8)),
  278. web3.eth.abi.encodeParameter("uint8", 3).substring(2 + (64 - 2)),
  279. web3.eth.abi.encodeParameter("address", testSigner1.address).substring(2 + (64 - 40)),
  280. web3.eth.abi.encodeParameter("address", testSigner2.address).substring(2 + (64 - 40)),
  281. web3.eth.abi.encodeParameter("address", testSigner3.address).substring(2 + (64 - 40)),
  282. ].join('')
  283. const vm = await signAndEncodeVM(
  284. timestamp,
  285. nonce,
  286. emitterChainId,
  287. emitterAddress,
  288. 0,
  289. data,
  290. [
  291. testSigner1PK,
  292. ],
  293. 0,
  294. 2
  295. );
  296. let set = await initialized.methods.submitNewGuardianSet("0x" + vm).send({
  297. value: 0,
  298. from: accounts[0],
  299. gasLimit: 1000000
  300. });
  301. let index = await initialized.methods.getCurrentGuardianSetIndex().call();
  302. assert.equal(oldIndex + 1, index);
  303. assert.equal(index, 1);
  304. let guardians = await initialized.methods.getGuardianSet(index).call();
  305. assert.equal(guardians.expirationTime, 0);
  306. assert.lengthOf(guardians[0], 3);
  307. assert.equal(guardians[0][0], testSigner1.address);
  308. assert.equal(guardians[0][1], testSigner2.address);
  309. assert.equal(guardians[0][2], testSigner3.address);
  310. let oldGuardians = await initialized.methods.getGuardianSet(oldIndex).call();
  311. const time = (await web3.eth.getBlock("latest")).timestamp;
  312. // old guardian set expiry is set
  313. assert.ok(
  314. oldGuardians.expirationTime > Number(time) + 86000
  315. && oldGuardians.expirationTime < Number(time) + 88000
  316. );
  317. })
  318. it("should accept smart contract upgrades", async function () {
  319. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  320. const accounts = await web3.eth.getAccounts();
  321. const mock = await MockImplementation.new();
  322. const timestamp = 1000;
  323. const nonce = 1001;
  324. const emitterChainId = testGovernanceChainId;
  325. const emitterAddress = testGovernanceContract
  326. let data = "0x00000000000000000000000000000000000000000000000000000000436f726501";
  327. data += [
  328. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  329. web3.eth.abi.encodeParameter("address", mock.address).substring(2),
  330. ].join('')
  331. const vm = await signAndEncodeVM(
  332. timestamp,
  333. nonce,
  334. emitterChainId,
  335. emitterAddress,
  336. 0,
  337. data,
  338. [
  339. testSigner1PK,
  340. testSigner2PK,
  341. testSigner3PK
  342. ],
  343. 1,
  344. 2
  345. );
  346. let before = await web3.eth.getStorageAt(Wormhole.address, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
  347. assert.equal(before.toLowerCase(), Implementation.address.toLowerCase());
  348. let set = await initialized.methods.submitContractUpgrade("0x" + vm).send({
  349. value: 0,
  350. from: accounts[0],
  351. gasLimit: 1000000
  352. });
  353. let after = await web3.eth.getStorageAt(Wormhole.address, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
  354. assert.equal(after.toLowerCase(), mock.address.toLowerCase());
  355. const mockImpl = new web3.eth.Contract(MockImplementation.abi, Wormhole.address);
  356. let isUpgraded = await mockImpl.methods.testNewImplementationActive().call();
  357. assert.ok(isUpgraded);
  358. })
  359. it("should revert governance packets from old guardian set", async function () {
  360. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  361. const accounts = await web3.eth.getAccounts();
  362. let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
  363. data += [
  364. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  365. web3.eth.abi.encodeParameter("uint256", 1).substring(2),
  366. web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
  367. ].join('')
  368. const vm = await signAndEncodeVM(
  369. 0,
  370. 0,
  371. testGovernanceChainId,
  372. testGovernanceContract,
  373. 0,
  374. data,
  375. [
  376. testSigner1PK,
  377. ],
  378. 0,
  379. 2
  380. );
  381. let failed = false;
  382. try {
  383. await initialized.methods.submitTransferFees("0x" + vm).send({
  384. value: 0,
  385. from: accounts[0],
  386. gasLimit: 1000000
  387. });
  388. asset.fail("governance packet of old guardian set accepted")
  389. } catch (e) {
  390. assert.equal(e.data[Object.keys(e.data)[0]].reason, "not signed by current guardian set")
  391. }
  392. })
  393. it("should time out old gardians", async function () {
  394. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  395. const timestamp = 1000;
  396. const nonce = 1001;
  397. const emitterChainId = 11;
  398. const emitterAddress = "0x0000000000000000000000000000000000000000000000000000000000000eee"
  399. const data = "0xaaaaaa";
  400. const vm = await signAndEncodeVM(
  401. timestamp,
  402. nonce,
  403. emitterChainId,
  404. emitterAddress,
  405. 0,
  406. data,
  407. [
  408. testSigner1PK,
  409. ],
  410. 0,
  411. 2
  412. );
  413. // this should pass
  414. const current = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  415. assert.equal(current.valid, true)
  416. await advanceTimeAndBlock(100000);
  417. const expired = await initialized.methods.parseAndVerifyVM("0x" + vm).call();
  418. assert.equal(expired.valid, false)
  419. })
  420. it("should revert governance packets from wrong governance chain", async function () {
  421. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  422. const accounts = await web3.eth.getAccounts();
  423. let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
  424. data += [
  425. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  426. web3.eth.abi.encodeParameter("uint256", 1).substring(2),
  427. web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
  428. ].join('')
  429. const vm = await signAndEncodeVM(
  430. 0,
  431. 0,
  432. 999,
  433. testGovernanceContract,
  434. 0,
  435. data,
  436. [
  437. testSigner1PK,
  438. testSigner2PK,
  439. testSigner3PK,
  440. ],
  441. 1,
  442. 2
  443. );
  444. try {
  445. await initialized.methods.submitTransferFees("0x" + vm).send({
  446. value: 0,
  447. from: accounts[0],
  448. gasLimit: 1000000
  449. });
  450. asset.fail("governance packet from wrong governance chain accepted")
  451. } catch (e) {
  452. assert.equal(e.data[Object.keys(e.data)[0]].reason, "wrong governance chain")
  453. }
  454. })
  455. it("should revert governance packets from wrong governance contract", async function () {
  456. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  457. const accounts = await web3.eth.getAccounts();
  458. let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
  459. data += [
  460. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  461. web3.eth.abi.encodeParameter("uint256", 1).substring(2),
  462. web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
  463. ].join('')
  464. const vm = await signAndEncodeVM(
  465. 0,
  466. 0,
  467. testGovernanceChainId,
  468. "0x00000000000000000000000000000000000000000000000000000000436f7265",
  469. 0,
  470. data,
  471. [
  472. testSigner1PK,
  473. testSigner2PK,
  474. testSigner3PK,
  475. ],
  476. 1,
  477. 2
  478. );
  479. try {
  480. await initialized.methods.submitTransferFees("0x" + vm).send({
  481. value: 0,
  482. from: accounts[0],
  483. gasLimit: 1000000
  484. });
  485. asset.fail("governance packet from wrong governance contract accepted")
  486. } catch (e) {
  487. assert.equal(e.data[Object.keys(e.data)[0]].reason, "wrong governance contract")
  488. }
  489. })
  490. it("should revert on governance packets that already have been applied", async function () {
  491. const initialized = new web3.eth.Contract(ImplementationFullABI, Wormhole.address);
  492. const accounts = await web3.eth.getAccounts();
  493. let data = "0x00000000000000000000000000000000000000000000000000000000436f726504";
  494. data += [
  495. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  496. web3.eth.abi.encodeParameter("uint256", 1).substring(2),
  497. web3.eth.abi.encodeParameter("address", "0x0000000000000000000000000000000000000000").substring(2),
  498. ].join('')
  499. const vm = await signAndEncodeVM(
  500. 0,
  501. 0,
  502. testGovernanceChainId,
  503. testGovernanceContract,
  504. 0,
  505. data,
  506. [
  507. testSigner1PK,
  508. testSigner2PK,
  509. testSigner3PK,
  510. ],
  511. 1,
  512. 2
  513. );
  514. await initialized.methods.submitTransferFees("0x" + vm).send({
  515. value: 0,
  516. from: accounts[0],
  517. gasLimit: 1000000
  518. });
  519. try {
  520. await initialized.methods.submitTransferFees("0x" + vm).send({
  521. value: 0,
  522. from: accounts[0],
  523. gasLimit: 1000000
  524. });
  525. asset.fail("governance packet accepted twice")
  526. } catch (e) {
  527. assert.equal(e.data[Object.keys(e.data)[0]].reason, "governance action already consumed")
  528. }
  529. })
  530. });
  531. const signAndEncodeVM = async function (
  532. timestamp,
  533. nonce,
  534. emitterChainId,
  535. emitterAddress,
  536. sequence,
  537. data,
  538. signers,
  539. guardianSetIndex,
  540. consistencyLevel
  541. ) {
  542. const body = [
  543. web3.eth.abi.encodeParameter("uint32", timestamp).substring(2 + (64 - 8)),
  544. web3.eth.abi.encodeParameter("uint32", nonce).substring(2 + (64 - 8)),
  545. web3.eth.abi.encodeParameter("uint16", emitterChainId).substring(2 + (64 - 4)),
  546. web3.eth.abi.encodeParameter("bytes32", emitterAddress).substring(2),
  547. web3.eth.abi.encodeParameter("uint64", sequence).substring(2 + (64 - 16)),
  548. web3.eth.abi.encodeParameter("uint8", consistencyLevel).substring(2 + (64 - 2)),
  549. data.substr(2)
  550. ]
  551. const hash = web3.utils.soliditySha3(web3.utils.soliditySha3("0x" + body.join("")))
  552. let signatures = "";
  553. for (let i in signers) {
  554. const ec = new elliptic.ec("secp256k1");
  555. const key = ec.keyFromPrivate(signers[i]);
  556. const signature = key.sign(hash.substr(2), {canonical: true});
  557. const packSig = [
  558. web3.eth.abi.encodeParameter("uint8", i).substring(2 + (64 - 2)),
  559. zeroPadBytes(signature.r.toString(16), 32),
  560. zeroPadBytes(signature.s.toString(16), 32),
  561. web3.eth.abi.encodeParameter("uint8", signature.recoveryParam).substr(2 + (64 - 2)),
  562. ]
  563. signatures += packSig.join("")
  564. }
  565. const vm = [
  566. web3.eth.abi.encodeParameter("uint8", 1).substring(2 + (64 - 2)),
  567. web3.eth.abi.encodeParameter("uint32", guardianSetIndex).substring(2 + (64 - 8)),
  568. web3.eth.abi.encodeParameter("uint8", signers.length).substring(2 + (64 - 2)),
  569. signatures,
  570. body.join("")
  571. ].join("");
  572. return vm
  573. }
  574. function zeroPadBytes(value, length) {
  575. while (value.length < 2 * length) {
  576. value = "0" + value;
  577. }
  578. return value;
  579. }