bridge.js 35 KB

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