bridge.js 29 KB

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