bridge.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. const jsonfile = require('jsonfile');
  2. const elliptic = require('elliptic');
  3. const BigNumber = require('bignumber.js');
  4. const Wormhole = artifacts.require("Wormhole");
  5. const TokenBridge = artifacts.require("TokenBridge");
  6. const BridgeImplementation = artifacts.require("BridgeImplementation");
  7. const TokenImplementation = artifacts.require("TokenImplementation");
  8. const MockBridgeImplementation = artifacts.require("MockBridgeImplementation");
  9. const MockWETH9 = artifacts.require("MockWETH9");
  10. const testSigner1PK = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0";
  11. const testSigner2PK = "892330666a850761e7370376430bb8c2aa1494072d3bfeaed0c4fa3d5a9135fe";
  12. const WormholeImplementationFullABI = jsonfile.readFileSync("build/contracts/Implementation.json").abi
  13. const BridgeImplementationFullABI = jsonfile.readFileSync("build/contracts/BridgeImplementation.json").abi
  14. const TokenImplementationFullABI = jsonfile.readFileSync("build/contracts/TokenImplementation.json").abi
  15. contract("Bridge", function () {
  16. const testSigner1 = web3.eth.accounts.privateKeyToAccount(testSigner1PK);
  17. const testSigner2 = web3.eth.accounts.privateKeyToAccount(testSigner2PK);
  18. const testChainId = "2";
  19. const testGovernanceChainId = "3";
  20. const testGovernanceContract = "0x0000000000000000000000000000000000000000000000000000000000000004";
  21. let WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
  22. const testForeignChainId = "1";
  23. const testForeignBridgeContract = "0x000000000000000000000000000000000000000000000000000000000000ffff";
  24. const testBridgedAssetChain = "0001";
  25. const testBridgedAssetAddress = "000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e";
  26. it("should be initialized with the correct signers and values", async function(){
  27. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  28. const weth = await initialized.methods.WETH().call();
  29. assert.equal(weth, WETH);
  30. const tokenImplentation = await initialized.methods.tokenImplementation().call();
  31. assert.equal(tokenImplentation, TokenImplementation.address);
  32. // test beacon functionality
  33. const beaconImplementation = await initialized.methods.implementation().call();
  34. assert.equal(beaconImplementation, TokenImplementation.address);
  35. // chain id
  36. const chainId = await initialized.methods.chainId().call();
  37. assert.equal(chainId, testChainId);
  38. // governance
  39. const governanceChainId = await initialized.methods.governanceChainId().call();
  40. assert.equal(governanceChainId, testGovernanceChainId);
  41. const governanceContract = await initialized.methods.governanceContract().call();
  42. assert.equal(governanceContract, testGovernanceContract);
  43. })
  44. it("initialize should be non-reentrant", async function(){
  45. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  46. try{
  47. await initialized.methods.initialize(
  48. 1,
  49. Wormhole.address,
  50. 1,
  51. testGovernanceContract,
  52. TokenImplementation.address,
  53. WETH
  54. ).estimateGas();
  55. } catch (error) {
  56. assert.equal(error.message, "Returned error: VM Exception while processing transaction: revert already initialized")
  57. return
  58. }
  59. assert.fail("did not fail")
  60. })
  61. it("should register a foreign bridge implementation correctly", async function() {
  62. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  63. const accounts = await web3.eth.getAccounts();
  64. let data = [
  65. "0x",
  66. "000000000000000000000000000000000000000000546f6b656e427269646765",
  67. "01",
  68. "0000",
  69. web3.eth.abi.encodeParameter("uint16", testForeignChainId).substring(2 + (64 - 4)),
  70. web3.eth.abi.encodeParameter("bytes32", testForeignBridgeContract).substring(2),
  71. ].join('')
  72. const vm = await signAndEncodeVM(
  73. 1,
  74. 1,
  75. testGovernanceChainId,
  76. testGovernanceContract,
  77. 0,
  78. data,
  79. [
  80. testSigner1PK
  81. ],
  82. 0,
  83. 0
  84. );
  85. let before = await initialized.methods.bridgeContracts(testForeignChainId).call();
  86. assert.equal(before, "0x0000000000000000000000000000000000000000000000000000000000000000");
  87. await initialized.methods.registerChain("0x" + vm).send({
  88. value : 0,
  89. from : accounts[0],
  90. gasLimit : 2000000
  91. });
  92. let after = await initialized.methods.bridgeContracts(testForeignChainId).call();
  93. assert.equal(after, testForeignBridgeContract);
  94. })
  95. it("should accept a valid upgrade", async function() {
  96. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  97. const accounts = await web3.eth.getAccounts();
  98. const mock = await MockBridgeImplementation.new();
  99. let data = [
  100. "0x",
  101. "000000000000000000000000000000000000000000546f6b656e427269646765",
  102. "02",
  103. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)),
  104. web3.eth.abi.encodeParameter("address", mock.address).substring(2),
  105. ].join('')
  106. const vm = await signAndEncodeVM(
  107. 1,
  108. 1,
  109. testGovernanceChainId,
  110. testGovernanceContract,
  111. 0,
  112. data,
  113. [
  114. testSigner1PK
  115. ],
  116. 0,
  117. 0
  118. );
  119. let before = await web3.eth.getStorageAt(TokenBridge.address, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
  120. assert.equal(before.toLowerCase(), BridgeImplementation.address.toLowerCase());
  121. await initialized.methods.upgrade("0x" + vm).send({
  122. value : 0,
  123. from : accounts[0],
  124. gasLimit : 2000000
  125. });
  126. let after = await web3.eth.getStorageAt(TokenBridge.address, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
  127. assert.equal(after.toLowerCase(), mock.address.toLowerCase());
  128. const mockImpl = new web3.eth.Contract(MockBridgeImplementation.abi, TokenBridge.address);
  129. let isUpgraded = await mockImpl.methods.testNewImplementationActive().call();
  130. assert.ok(isUpgraded);
  131. })
  132. it("bridged tokens should only be mint- and burn-able by owner", async function() {
  133. const accounts = await web3.eth.getAccounts();
  134. // initialize our template token contract
  135. const token = new web3.eth.Contract(TokenImplementation.abi, TokenImplementation.address);
  136. await token.methods.initialize(
  137. "TestToken",
  138. "TT",
  139. 18,
  140. accounts[0],
  141. 0,
  142. "0x0"
  143. ).send({
  144. value : 0,
  145. from : accounts[0],
  146. gasLimit : 2000000
  147. });
  148. await token.methods.mint(accounts[0], 10).send({
  149. from : accounts[0],
  150. gasLimit : 2000000
  151. });
  152. await token.methods.burn(accounts[0], 5).send({
  153. from : accounts[0],
  154. gasLimit : 2000000
  155. });
  156. let failed = false
  157. try {
  158. await token.methods.mint(accounts[0], 10).send({
  159. from : accounts[1],
  160. gasLimit : 2000000
  161. });
  162. } catch(e) {
  163. failed = true
  164. }
  165. assert.ok(failed)
  166. failed = false
  167. try {
  168. await token.methods.burn(accounts[0], 5).send({
  169. from : accounts[1],
  170. gasLimit : 2000000
  171. });
  172. } catch(e) {
  173. failed = true
  174. }
  175. assert.ok(failed)
  176. await token.methods.burn(accounts[0], 5).send({
  177. from : accounts[0],
  178. gasLimit : 2000000
  179. });
  180. })
  181. it("should attest a token correctly", async function() {
  182. const accounts = await web3.eth.getAccounts();
  183. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  184. await initialized.methods.attestToken(TokenImplementation.address, "234").send({
  185. value : 0,
  186. from : accounts[0],
  187. gasLimit : 2000000
  188. });
  189. const wormhole = new web3.eth.Contract(WormholeImplementationFullABI, Wormhole.address);
  190. const log = (await wormhole.getPastEvents('LogMessagePublished', {
  191. fromBlock : 'latest'
  192. }))[0].returnValues
  193. assert.equal(log.sender, TokenBridge.address)
  194. assert.equal(log.payload.length - 2, 200);
  195. // payload id
  196. assert.equal(log.payload.substr(2, 2), "02");
  197. // token address
  198. assert.equal(log.payload.substr(4, 64), web3.eth.abi.encodeParameter("address", TokenImplementation.address).substring(2));
  199. // chain id
  200. assert.equal(log.payload.substr(68, 4), web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + 64 - 4))
  201. // decimals
  202. assert.equal(log.payload.substr(72, 2), web3.eth.abi.encodeParameter("uint8", 18).substring(2 + 64 - 2))
  203. // symbol (TT)
  204. assert.equal(log.payload.substr(74, 64), "5454000000000000000000000000000000000000000000000000000000000000")
  205. // name (TestToken)
  206. assert.equal(log.payload.substr(138, 64), "54657374546f6b656e0000000000000000000000000000000000000000000000")
  207. })
  208. it("should correctly deploy a wrapped asset for a token attestation", async function() {
  209. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  210. const accounts = await web3.eth.getAccounts();
  211. const data = "0x02" +
  212. // tokenAddress
  213. testBridgedAssetAddress +
  214. // tokenchain
  215. testBridgedAssetChain +
  216. // decimals
  217. "12" +
  218. // symbol
  219. "5454000000000000000000000000000000000000000000000000000000000000" +
  220. // name
  221. "54657374546f6b656e0000000000000000000000000000000000000000000000";
  222. const vm = await signAndEncodeVM(
  223. 0,
  224. 0,
  225. testForeignChainId,
  226. testForeignBridgeContract,
  227. 0,
  228. data,
  229. [
  230. testSigner1PK
  231. ],
  232. 0,
  233. 0
  234. );
  235. await initialized.methods.createWrapped("0x" + vm).send({
  236. value : 0,
  237. from : accounts[0],
  238. gasLimit : 2000000
  239. });
  240. const wrappedAddress = await initialized.methods.wrappedAsset("0x0001", "0x000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e").call();
  241. assert.ok(await initialized.methods.isWrappedAsset(wrappedAddress).call())
  242. const initializedWrappedAsset = new web3.eth.Contract(TokenImplementation.abi, wrappedAddress);
  243. const symbol = await initializedWrappedAsset.methods.symbol().call();
  244. assert.equal(symbol, "TT");
  245. const name = await initializedWrappedAsset.methods.name().call();
  246. assert.equal(name, "TestToken");
  247. const decimals = await initializedWrappedAsset.methods.decimals().call();
  248. assert.equal(decimals, 18);
  249. const chainId = await initializedWrappedAsset.methods.chainId().call();
  250. assert.equal(chainId, 1);
  251. const nativeContract = await initializedWrappedAsset.methods.nativeContract().call();
  252. assert.equal(nativeContract, "0x000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e");
  253. })
  254. it("should deposit and log transfers correctly", async function() {
  255. const accounts = await web3.eth.getAccounts();
  256. const amount = "1000000000000000000";
  257. const fee = "100000000000000000";
  258. // mint and approve tokens
  259. const token = new web3.eth.Contract(TokenImplementation.abi, TokenImplementation.address);
  260. await token.methods.mint(accounts[0], amount).send({
  261. value : 0,
  262. from : accounts[0],
  263. gasLimit : 2000000
  264. });
  265. await token.methods.approve(TokenBridge.address, amount).send({
  266. value : 0,
  267. from : accounts[0],
  268. gasLimit : 2000000
  269. });
  270. // deposit tokens
  271. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  272. const accountBalanceBefore = await token.methods.balanceOf(accounts[0]).call();
  273. const bridgeBalanceBefore = await token.methods.balanceOf(TokenBridge.address).call();
  274. assert.equal(accountBalanceBefore.toString(10), amount);
  275. assert.equal(bridgeBalanceBefore.toString(10), "0");
  276. await initialized.methods.transferTokens(
  277. TokenImplementation.address,
  278. amount,
  279. "10",
  280. "0x000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e",
  281. fee,
  282. "234"
  283. ).send({
  284. value : 0,
  285. from : accounts[0],
  286. gasLimit : 2000000
  287. });
  288. const accountBalanceAfter = await token.methods.balanceOf(accounts[0]).call();
  289. const bridgeBalanceAfter = await token.methods.balanceOf(TokenBridge.address).call();
  290. assert.equal(accountBalanceAfter.toString(10), "0");
  291. assert.equal(bridgeBalanceAfter.toString(10), amount);
  292. // check transfer log
  293. const wormhole = new web3.eth.Contract(WormholeImplementationFullABI, Wormhole.address);
  294. const log = (await wormhole.getPastEvents('LogMessagePublished', {
  295. fromBlock : 'latest'
  296. }))[0].returnValues
  297. assert.equal(log.sender, TokenBridge.address)
  298. assert.equal(log.payload.length - 2, 266);
  299. // payload id
  300. assert.equal(log.payload.substr(2, 2), "01");
  301. // amount
  302. assert.equal(log.payload.substr(4, 64), web3.eth.abi.encodeParameter("uint256", new BigNumber(amount).div(1e10).toString()).substring(2));
  303. // token
  304. assert.equal(log.payload.substr(68, 64), web3.eth.abi.encodeParameter("address", TokenImplementation.address).substring(2));
  305. // chain id
  306. assert.equal(log.payload.substr(132, 4), web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + 64 - 4))
  307. // to
  308. assert.equal(log.payload.substr(136, 64), "000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e");
  309. // to chain id
  310. assert.equal(log.payload.substr(200, 4), web3.eth.abi.encodeParameter("uint16", 10).substring(2 + 64 - 4))
  311. // fee
  312. assert.equal(log.payload.substr(204, 64), web3.eth.abi.encodeParameter("uint256", new BigNumber(fee).div(1e10).toString()).substring(2))
  313. })
  314. it("should transfer out locked assets for a valid transfer vm", async function() {
  315. const accounts = await web3.eth.getAccounts();
  316. const amount = "1000000000000000000";
  317. const token = new web3.eth.Contract(TokenImplementation.abi, TokenImplementation.address);
  318. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  319. const accountBalanceBefore = await token.methods.balanceOf(accounts[0]).call();
  320. const bridgeBalanceBefore = await token.methods.balanceOf(TokenBridge.address).call();
  321. assert.equal(accountBalanceBefore.toString(10), "0");
  322. assert.equal(bridgeBalanceBefore.toString(10), amount);
  323. const data = "0x" +
  324. "01" +
  325. // amount
  326. web3.eth.abi.encodeParameter("uint256", new BigNumber(amount).div(1e10).toString()).substring(2) +
  327. // tokenaddress
  328. web3.eth.abi.encodeParameter("address", TokenImplementation.address).substr(2) +
  329. // tokenchain
  330. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)) +
  331. // receiver
  332. web3.eth.abi.encodeParameter("address", accounts[0]).substr(2) +
  333. // receiving chain
  334. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)) +
  335. // fee
  336. "0000000000000000000000000000000000000000000000000000000000000000";
  337. const vm = await signAndEncodeVM(
  338. 0,
  339. 0,
  340. testForeignChainId,
  341. testForeignBridgeContract,
  342. 0,
  343. data,
  344. [
  345. testSigner1PK
  346. ],
  347. 0,
  348. 0
  349. );
  350. await initialized.methods.completeTransfer("0x" + vm).send({
  351. value : 0,
  352. from : accounts[0],
  353. gasLimit : 2000000
  354. });
  355. const accountBalanceAfter = await token.methods.balanceOf(accounts[0]).call();
  356. const bridgeBalanceAfter = await token.methods.balanceOf(TokenBridge.address).call();
  357. assert.equal(accountBalanceAfter.toString(10), amount);
  358. assert.equal(bridgeBalanceAfter.toString(10), "0");
  359. })
  360. it("should mint bridged assets wrappers on transfer from another chain", async function() {
  361. const accounts = await web3.eth.getAccounts();
  362. const amount = "1000000000000000000";
  363. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  364. const wrappedAddress = await initialized.methods.wrappedAsset("0x"+testBridgedAssetChain, "0x"+testBridgedAssetAddress).call();
  365. const wrappedAsset = new web3.eth.Contract(TokenImplementation.abi, wrappedAddress);
  366. const totalSupply = await wrappedAsset.methods.totalSupply().call();
  367. assert.equal(totalSupply.toString(10), "0");
  368. // we are using the asset where we created a wrapper in the previous test
  369. const data = "0x" +
  370. "01" +
  371. // amount
  372. web3.eth.abi.encodeParameter("uint256", new BigNumber(amount).div(1e10).toString()).substring(2) +
  373. // tokenaddress
  374. testBridgedAssetAddress +
  375. // tokenchain
  376. testBridgedAssetChain +
  377. // receiver
  378. web3.eth.abi.encodeParameter("address", accounts[0]).substr(2) +
  379. // receiving chain
  380. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)) +
  381. // fee
  382. "0000000000000000000000000000000000000000000000000000000000000000";
  383. const vm = await signAndEncodeVM(
  384. 0,
  385. 0,
  386. testForeignChainId,
  387. testForeignBridgeContract,
  388. 0,
  389. data,
  390. [
  391. testSigner1PK
  392. ],
  393. 0,
  394. 0
  395. );
  396. await initialized.methods.completeTransfer("0x" + vm).send({
  397. value : 0,
  398. from : accounts[0],
  399. gasLimit : 2000000
  400. });
  401. const accountBalanceAfter = await wrappedAsset.methods.balanceOf(accounts[0]).call();
  402. const totalSupplyAfter = await wrappedAsset.methods.totalSupply().call();
  403. assert.equal(accountBalanceAfter.toString(10), amount);
  404. assert.equal(totalSupplyAfter.toString(10), amount);
  405. })
  406. it("should burn bridged assets wrappers on transfer to another chain", async function() {
  407. const accounts = await web3.eth.getAccounts();
  408. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  409. const amount = "1000000000000000000";
  410. const wrappedAddress = await initialized.methods.wrappedAsset("0x"+testBridgedAssetChain, "0x"+testBridgedAssetAddress).call();
  411. const wrappedAsset = new web3.eth.Contract(TokenImplementation.abi, wrappedAddress);
  412. await wrappedAsset.methods.approve(TokenBridge.address, amount).send({
  413. value : 0,
  414. from : accounts[0],
  415. gasLimit : 2000000
  416. });
  417. // deposit tokens
  418. const accountBalanceBefore = await wrappedAsset.methods.balanceOf(accounts[0]).call();
  419. assert.equal(accountBalanceBefore.toString(10), amount);
  420. await initialized.methods.transferTokens(
  421. wrappedAddress,
  422. amount,
  423. "11",
  424. "0x000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e",
  425. "0",
  426. "234"
  427. ).send({
  428. value : 0,
  429. from : accounts[0],
  430. gasLimit : 2000000
  431. });
  432. const accountBalanceAfter = await wrappedAsset.methods.balanceOf(accounts[0]).call();
  433. assert.equal(accountBalanceAfter.toString(10), "0");
  434. const bridgeBalanceAfter = await wrappedAsset.methods.balanceOf(TokenBridge.address).call();
  435. assert.equal(bridgeBalanceAfter.toString(10), "0");
  436. const totalSupplyAfter = await wrappedAsset.methods.totalSupply().call();
  437. assert.equal(totalSupplyAfter.toString(10), "0");
  438. })
  439. it("should handle ETH deposits correctly", async function() {
  440. const accounts = await web3.eth.getAccounts();
  441. const amount = "100000000000000000";
  442. const fee = "10000000000000000";
  443. // mint and approve tokens
  444. WETH = (await MockWETH9.new()).address;
  445. const token = new web3.eth.Contract(MockWETH9.abi, WETH);
  446. // set WETH contract
  447. const mock = new web3.eth.Contract(MockBridgeImplementation.abi, TokenBridge.address);
  448. mock.methods.testUpdateWETHAddress(WETH).send({
  449. from : accounts[0],
  450. gasLimit : 2000000
  451. });
  452. // deposit tokens
  453. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  454. const totalWETHSupply = await token.methods.totalSupply().call();
  455. const bridgeBalanceBefore = await token.methods.balanceOf(TokenBridge.address).call();
  456. assert.equal(totalWETHSupply.toString(10), "0");
  457. assert.equal(bridgeBalanceBefore.toString(10), "0");
  458. await initialized.methods.wrapAndTransferETH(
  459. "10",
  460. "0x000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e",
  461. fee,
  462. "234"
  463. ).send({
  464. value : amount,
  465. from : accounts[0],
  466. gasLimit : 2000000
  467. });
  468. const totalWETHSupplyAfter = await token.methods.totalSupply().call();
  469. const bridgeBalanceAfter = await token.methods.balanceOf(TokenBridge.address).call();
  470. assert.equal(totalWETHSupplyAfter.toString(10), amount);
  471. assert.equal(bridgeBalanceAfter.toString(10), amount);
  472. // check transfer log
  473. const wormhole = new web3.eth.Contract(WormholeImplementationFullABI, Wormhole.address);
  474. const log = (await wormhole.getPastEvents('LogMessagePublished', {
  475. fromBlock : 'latest'
  476. }))[0].returnValues
  477. assert.equal(log.sender, TokenBridge.address)
  478. assert.equal(log.payload.length - 2, 266);
  479. // payload id
  480. assert.equal(log.payload.substr(2, 2), "01");
  481. // amount
  482. assert.equal(log.payload.substr(4, 64), web3.eth.abi.encodeParameter("uint256", new BigNumber(amount).div(1e10).toString()).substring(2));
  483. // token
  484. assert.equal(log.payload.substr(68, 64), web3.eth.abi.encodeParameter("address", WETH).substring(2));
  485. // chain id
  486. assert.equal(log.payload.substr(132, 4), web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + 64 - 4))
  487. // to
  488. assert.equal(log.payload.substr(136, 64), "000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e");
  489. // to chain id
  490. assert.equal(log.payload.substr(200, 4), web3.eth.abi.encodeParameter("uint16", 10).substring(2 + 64 - 4))
  491. // fee
  492. assert.equal(log.payload.substr(204, 64), web3.eth.abi.encodeParameter("uint256", new BigNumber(fee).div(1e10).toString()).substring(2))
  493. })
  494. it("should handle ETH withdrawals and fees correctly", async function() {
  495. const accounts = await web3.eth.getAccounts();
  496. const amount = "100000000000000000";
  497. const fee = "50000000000000000";
  498. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  499. const token = new web3.eth.Contract(MockWETH9.abi, WETH);
  500. const totalSupply = await token.methods.totalSupply().call();
  501. assert.equal(totalSupply.toString(10), amount);
  502. const feeRecipientBalanceBefore = await web3.eth.getBalance(accounts[0]);
  503. const accountBalanceBefore = await web3.eth.getBalance(accounts[1]);
  504. // we are using the asset where we created a wrapper in the previous test
  505. const data = "0x" +
  506. "01" +
  507. // amount
  508. web3.eth.abi.encodeParameter("uint256", new BigNumber(amount).div(1e10).toString()).substring(2) +
  509. // tokenaddress
  510. web3.eth.abi.encodeParameter("address", WETH).substr(2) +
  511. // tokenchain
  512. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)) +
  513. // receiver
  514. web3.eth.abi.encodeParameter("address", accounts[1]).substr(2) +
  515. // receiving chain
  516. web3.eth.abi.encodeParameter("uint16", testChainId).substring(2 + (64 - 4)) +
  517. // fee
  518. web3.eth.abi.encodeParameter("uint256", new BigNumber(fee).div(1e10).toString()).substring(2);
  519. const vm = await signAndEncodeVM(
  520. 0,
  521. 0,
  522. testForeignChainId,
  523. testForeignBridgeContract,
  524. 0,
  525. data,
  526. [
  527. testSigner1PK
  528. ],
  529. 0,
  530. 0
  531. );
  532. const transferTX = await initialized.methods.completeTransferAndUnwrapETH("0x" + vm).send({
  533. from : accounts[0],
  534. gasLimit : 2000000
  535. });
  536. const totalSupplyAfter = await token.methods.totalSupply().call();
  537. assert.equal(totalSupplyAfter.toString(10), "0");
  538. const accountBalanceAfter = await web3.eth.getBalance(accounts[1]);
  539. const feeRecipientBalanceAfter = await web3.eth.getBalance(accounts[0]);
  540. assert.equal((new BigNumber(accountBalanceAfter)).minus(accountBalanceBefore).toString(10), (new BigNumber(amount)).minus(fee).toString(10))
  541. assert.ok((new BigNumber(feeRecipientBalanceAfter)).gt(feeRecipientBalanceBefore))
  542. })
  543. it("should revert on transfer out of a total of > max(uint64) tokens", async function() {
  544. const accounts = await web3.eth.getAccounts();
  545. const supply = "184467440737095516160000000000";
  546. const firstTransfer = "1000000000000";
  547. // mint and approve tokens
  548. const token = new web3.eth.Contract(TokenImplementation.abi, TokenImplementation.address);
  549. await token.methods.mint(accounts[0], supply).send({
  550. value : 0,
  551. from : accounts[0],
  552. gasLimit : 2000000
  553. });
  554. await token.methods.approve(TokenBridge.address, supply).send({
  555. value : 0,
  556. from : accounts[0],
  557. gasLimit : 2000000
  558. });
  559. // deposit tokens
  560. const initialized = new web3.eth.Contract(BridgeImplementationFullABI, TokenBridge.address);
  561. await initialized.methods.transferTokens(
  562. TokenImplementation.address,
  563. firstTransfer,
  564. "10",
  565. "0x000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e",
  566. "0",
  567. "0"
  568. ).send({
  569. value : 0,
  570. from : accounts[0],
  571. gasLimit : 2000000
  572. });
  573. let failed = false;
  574. try {
  575. await initialized.methods.transferTokens(
  576. TokenImplementation.address,
  577. new BigNumber(supply).minus(firstTransfer).toString(10),
  578. "10",
  579. "0x000000000000000000000000b7a2211e8165943192ad04f5dd21bedc29ff003e",
  580. "0",
  581. "0"
  582. ).send({
  583. value : 0,
  584. from : accounts[0],
  585. gasLimit : 2000000
  586. });
  587. } catch(error) {
  588. assert.equal(error.message, "Returned error: VM Exception while processing transaction: revert transfer exceeds max outstanding bridged token amount")
  589. failed = true
  590. }
  591. assert.ok(failed)
  592. })
  593. });
  594. const signAndEncodeVM = async function (
  595. timestamp,
  596. nonce,
  597. emitterChainId,
  598. emitterAddress,
  599. sequence,
  600. data,
  601. signers,
  602. guardianSetIndex,
  603. consistencyLevel
  604. ) {
  605. const body = [
  606. web3.eth.abi.encodeParameter("uint32", timestamp).substring(2 + (64 - 8)),
  607. web3.eth.abi.encodeParameter("uint32", nonce).substring(2 + (64 - 8)),
  608. web3.eth.abi.encodeParameter("uint16", emitterChainId).substring(2 + (64 - 4)),
  609. web3.eth.abi.encodeParameter("bytes32", emitterAddress).substring(2),
  610. web3.eth.abi.encodeParameter("uint64", sequence).substring(2 + (64 - 16)),
  611. web3.eth.abi.encodeParameter("uint8", consistencyLevel).substring(2 + (64 - 2)),
  612. data.substr(2)
  613. ]
  614. const hash = web3.utils.soliditySha3(web3.utils.soliditySha3("0x" + body.join("")))
  615. let signatures = "";
  616. for (let i in signers) {
  617. const ec = new elliptic.ec("secp256k1");
  618. const key = ec.keyFromPrivate(signers[i]);
  619. const signature = key.sign(hash.substr(2), {canonical: true});
  620. const packSig = [
  621. web3.eth.abi.encodeParameter("uint8", i).substring(2 + (64 - 2)),
  622. zeroPadBytes(signature.r.toString(16), 32),
  623. zeroPadBytes(signature.s.toString(16), 32),
  624. web3.eth.abi.encodeParameter("uint8", signature.recoveryParam).substr(2 + (64 - 2)),
  625. ]
  626. signatures += packSig.join("")
  627. }
  628. const vm = [
  629. web3.eth.abi.encodeParameter("uint8", 1).substring(2 + (64 - 2)),
  630. web3.eth.abi.encodeParameter("uint32", guardianSetIndex).substring(2 + (64 - 8)),
  631. web3.eth.abi.encodeParameter("uint8", signers.length).substring(2 + (64 - 2)),
  632. signatures,
  633. body.join("")
  634. ].join("");
  635. return vm
  636. }
  637. function zeroPadBytes(value, length) {
  638. while (value.length < 2 * length) {
  639. value = "0" + value;
  640. }
  641. return value;
  642. }