From a9eee78091bfdcfdab6dba8597fc0680165d39d1 Mon Sep 17 00:00:00 2001 From: GitGuru7 <128375421+GitGuru7@users.noreply.github.com> Date: Thu, 28 Dec 2023 16:21:51 +0530 Subject: [PATCH 1/3] refactor: increased test coverage --- test/bridgeAdmin.ts | 87 +++++- test/proxyOFT.ts | 690 ++++++++++++++++++++++++++++++++++++++++- test/tokenConvertor.ts | 120 +++++++ 3 files changed, 889 insertions(+), 8 deletions(-) create mode 100644 test/tokenConvertor.ts diff --git a/test/bridgeAdmin.ts b/test/bridgeAdmin.ts index 47c5c73..f6ad766 100644 --- a/test/bridgeAdmin.ts +++ b/test/bridgeAdmin.ts @@ -109,6 +109,92 @@ describe("Bridge Admin: ", function () { await loadFixture(grantPermissionsFixture); }); + it("Revert when inputs length mismatch in function registry", async function () { + const functionregistry = [ + "setOracle(address)", + "setMaxSingleTransactionLimit(uint16,uint256)", + "setMaxDailyLimit(uint16,uint256)", + "setMaxSingleReceiveTransactionLimit(uint16,uint256)", + "setMaxDailyReceiveLimit(uint16,uint256)", + "pause()", + "unpause()", + "setWhitelist(address,bool)", + "setConfig(uint16,uint16,uint256,bytes)", + "setSendVersion(uint16)", + "setReceiveVersion(uint16)", + "forceResumeReceive(uint16,bytes)", + "setTrustedRemote(uint16,bytes)", + "setTrustedRemoteAddress(uint16,bytes)", + "setPrecrime(address)", + "setMinDstGas(uint16,uint16,uint256)", + "setPayloadSizeLimit(uint16,uint256)", + "setUseCustomAdapterParams(bool)", + ]; + const activeArray = new Array(functionregistry.length - 1).fill(true); + + await expect(bridgeAdmin.upsertSignature(functionregistry, activeArray)).to.be.revertedWith( + "Input arrays must have the same length", + ); + }); + + it("Deletes from function registry", async function () { + const functionregistry = [ + "setOracle(address)", + "setMaxSingleTransactionLimit(uint16,uint256)", + "setMaxDailyLimit(uint16,uint256)", + "setMaxSingleReceiveTransactionLimit(uint16,uint256)", + "setMaxDailyReceiveLimit(uint16,uint256)", + "pause()", + "unpause()", + "setWhitelist(address,bool)", + "setConfig(uint16,uint16,uint256,bytes)", + "setSendVersion(uint16)", + "setReceiveVersion(uint16)", + "forceResumeReceive(uint16,bytes)", + "setTrustedRemote(uint16,bytes)", + "setTrustedRemoteAddress(uint16,bytes)", + "setPrecrime(address)", + "setMinDstGas(uint16,uint16,uint256)", + "setPayloadSizeLimit(uint16,uint256)", + "setUseCustomAdapterParams(bool)", + "fakeFunction(uint256)", + ]; + const activeArray = new Array(functionregistry.length).fill(true); + await bridgeAdmin.upsertSignature(functionregistry, activeArray); + await expect(bridgeAdmin.upsertSignature(["fakeFunction(uint256)"], [false])).to.emit( + bridgeAdmin, + "FunctionRegistryChanged", + ); + }); + + it("Reverts when non owner calls upsert signature", async function () { + const functionregistry = [ + "setOracle(address)", + "setMaxSingleTransactionLimit(uint16,uint256)", + "setMaxDailyLimit(uint16,uint256)", + "setMaxSingleReceiveTransactionLimit(uint16,uint256)", + "setMaxDailyReceiveLimit(uint16,uint256)", + "pause()", + "unpause()", + "setWhitelist(address,bool)", + "setConfig(uint16,uint16,uint256,bytes)", + "setSendVersion(uint16)", + "setReceiveVersion(uint16)", + "forceResumeReceive(uint16,bytes)", + "setTrustedRemote(uint16,bytes)", + "setTrustedRemoteAddress(uint16,bytes)", + "setPrecrime(address)", + "setMinDstGas(uint16,uint16,uint256)", + "setPayloadSizeLimit(uint16,uint256)", + "setUseCustomAdapterParams(bool)", + ]; + const activeArray = new Array(functionregistry.length - 1).fill(true); + + await expect(bridgeAdmin.connect(acc2).upsertSignature(functionregistry, activeArray)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + it("Revert if EOA called owner function of bridge", async function () { await expect(remoteOFT.connect(acc1).setTrustedRemote(localChainId, remotePath)).to.be.revertedWith( "Ownable: caller is not the owner", @@ -162,7 +248,6 @@ describe("Bridge Admin: ", function () { }), ).to.revertedWithCustomError(bridgeAdmin, "Unauthorized"); }); - it("Success if permissions are granted to call owner functions of bridge", async function () { let data = remoteOFT.interface.encodeFunctionData("setMaxDailyLimit", [localChainId, maxDailyTransactionLimit]); await acc2.sendTransaction({ diff --git a/test/proxyOFT.ts b/test/proxyOFT.ts index a91b777..450d7c6 100644 --- a/test/proxyOFT.ts +++ b/test/proxyOFT.ts @@ -44,6 +44,7 @@ describe("Proxy OFTV2: ", function () { bridgeAdminLocal: XVSBridgeAdmin, remoteToken: XVS, localPath: string, + remotePath: string, acc2: SignerWithAddress, acc3: SignerWithAddress, acc1: SignerWithAddress, @@ -100,9 +101,10 @@ describe("Proxy OFTV2: ", function () { // set each contracts source address so it can send to each other localPath = ethers.utils.solidityPack(["address", "address"], [localOFT.address, remoteOFT.address]); + remotePath = ethers.utils.solidityPack(["address", "address"], [remoteOFT.address, localOFT.address]); // Should revert admin of remoteOFT is BridgeAdmin contract - await expect(remoteOFT.setTrustedRemote(localChainId, localPath)).to.be.revertedWith( + await expect(remoteOFT.setTrustedRemoteAddress(localChainId, remoteOFT.address)).to.be.revertedWith( "Ownable: caller is not the owner", ); @@ -133,6 +135,10 @@ describe("Proxy OFTV2: ", function () { "setUseCustomAdapterParams(bool)", "removeTrustedRemote(uint16)", "updateSendAndCallEnabled(bool)", + "fallbackWithdraw(address,uint256)", + "fallbackDeposit(address,uint256)", + "sweepToken(address,address,uint256)", + "dropFailedMessage(uint16,bytes,uint64)", ]; const activeArray = new Array(functionregistry.length).fill(true); await bridgeAdminRemote.upsertSignature(functionregistry, activeArray); @@ -217,7 +223,124 @@ describe("Proxy OFTV2: ", function () { beforeEach(async function () { await loadFixture(bridgeFixture); }); + it("Reverts when incorrect argument passed to the constructor", async function () { + const ProxyOFTV2Src = await ethers.getContractFactory("XVSProxyOFTSrc"); + const LocalTokenFactory = await ethers.getContractFactory("MockToken"); + const oracle = await smock.fake("ResilientOracleInterface"); + const localEndpoint = await LZEndpointMock.deploy(localChainId); + // create two OmnichainFungibleToken instances + const localToken = await LocalTokenFactory.deploy(name, symbol, 18); + const fakeToken = await smock.fake("MockToken"); + fakeToken.decimals.returns(2); + + await expect( + ProxyOFTV2Src.deploy(ethers.constants.AddressZero, sharedDecimals, localEndpoint.address, oracle.address), + ).to.be.revertedWithCustomError(localOFT, "ZeroAddressNotAllowed"); + await expect( + ProxyOFTV2Src.deploy(localToken.address, sharedDecimals, ethers.constants.AddressZero, oracle.address), + ).to.be.revertedWithCustomError(localOFT, "ZeroAddressNotAllowed"); + await expect( + ProxyOFTV2Src.deploy(localToken.address, sharedDecimals, localEndpoint.address, ethers.constants.AddressZero), + ).to.be.revertedWithCustomError(localOFT, "ZeroAddressNotAllowed"); + await expect( + ProxyOFTV2Src.deploy(fakeToken.address, sharedDecimals, localEndpoint.address, oracle.address), + ).to.be.revertedWith("ProxyOFT: sharedDecimals must be <= decimals"); + }); + + it("Reverts when non admin access admin functions on remote", async function () { + await expect(remoteOFT.connect(acc2).setMinDstGas(localChainId, 0, 200000)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + + await expect(remoteOFT.connect(acc2).setMaxDailyLimit(localChainId, maxDailyTransactionLimit)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + await expect( + remoteOFT.connect(acc2).setMaxSingleTransactionLimit(localChainId, singleTransactionLimit), + ).to.be.revertedWith("Ownable: caller is not the owner"); + await expect( + remoteOFT.connect(acc2).setMaxDailyReceiveLimit(localChainId, maxDailyTransactionLimit), + ).to.be.revertedWith("Ownable: caller is not the owner"); + await expect( + remoteOFT.connect(acc2).setMaxSingleReceiveTransactionLimit(localChainId, singleTransactionLimit), + ).to.be.revertedWith("Ownable: caller is not the owner"); + await expect(remoteOFT.connect(acc2).pause()).to.be.revertedWith("Ownable: caller is not the owner"); + await expect(remoteOFT.connect(acc2).unpause()).to.be.revertedWith("Ownable: caller is not the owner"); + await expect(remoteOFT.connect(acc2).setOracle(oracle.address)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + await expect(remoteOFT.connect(acc2).dropFailedMessage(localChainId, localPath, 1)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + it("Reverts when non admin access admin functionalities on local", async function () { + const amount = ethers.utils.parseEther("1", 18); + await expect(localOFT.connect(acc2).fallbackWithdraw(acc1.address, amount)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + await expect(localOFT.connect(acc2).fallbackDeposit(acc1.address, amount)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + await expect(localOFT.connect(acc2).sweepToken(localToken.address, acc1.address, amount)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + await expect(localOFT.connect(acc2).removeTrustedRemote(remoteChainId)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + await expect(localOFT.connect(acc2).updateSendAndCallEnabled(true)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + await expect(localOFT.connect(acc2).setOracle(oracle.address)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + + it("Reverts when single transaction limit exceeds daily limit", async function () { + const data = remoteOFT.interface.encodeFunctionData("setMaxSingleTransactionLimit", [ + localChainId, + convertToUnit(110, 18), + ]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminRemote.address, + data: data, + }), + ).to.be.reverted; + }); + it("Reverts when daily limit is less than single transaction limit", async function () { + const data = remoteOFT.interface.encodeFunctionData("setMaxDailyLimit", [localChainId, convertToUnit(9, 18)]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminRemote.address, + data: data, + }), + ).to.be.reverted; + }); + it("Reverts when single limit exceeds daily limit on remote", async () => { + const data = remoteOFT.interface.encodeFunctionData("setMaxDailyReceiveLimit", [ + localChainId, + convertToUnit(9, 18), + ]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminRemote.address, + data: data, + }), + ).to.be.reverted; + }); + it("Reverts when single receive transaction limit exceeds daily limit", async () => { + const data = remoteOFT.interface.encodeFunctionData("setMaxSingleReceiveTransactionLimit", [ + localChainId, + convertToUnit(110, 18), + ]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminRemote.address, + data: data, + }), + ).to.be.reverted; + }); it("send tokens from proxy oft and receive them back", async function () { const initialAmount = ethers.utils.parseEther("1.0000000001", 18); // 1 ether const amount = ethers.utils.parseEther("1", 18); @@ -277,7 +400,73 @@ describe("Proxy OFTV2: ", function () { expect(await localToken.balanceOf(localOFT.address)).to.be.equal(halfAmount); expect(await localToken.balanceOf(acc2.address)).to.be.equal(halfAmount.add(dust)); }); + it("Reverts when caller and sender are different on remote ", async function () { + const initialAmount = ethers.utils.parseEther("1.0000000001", 18); // 1 ether + const amount = ethers.utils.parseEther("1", 18); + const dust = ethers.utils.parseEther("0.0000000001"); + await localToken.connect(acc2).faucet(initialAmount); + // verify acc2 has tokens and acc3 has no tokens on remote chain + expect(await localToken.balanceOf(acc2.address)).to.be.equal(initialAmount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(0); + // acc2 sends tokens to acc3 on remote chain + // approve the proxy to swap your tokens + await localToken.connect(acc2).approve(localOFT.address, initialAmount); + // swaps token to remote chain + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + let nativeFee = ( + await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, initialAmount, false, defaultAdapterParams) + ).nativeFee; + await localOFT + .connect(acc2) + .sendFrom( + acc2.address, + remoteChainId, + acc3AddressBytes32, + initialAmount, + [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ); + + // tokens are now owned by the proxy contract, because this is the original oft chain + expect(await localToken.balanceOf(localOFT.address)).to.equal(amount); + expect(await localOFT.circulatingSupply()).to.equal(amount.div(10 ** (18 - sharedDecimals))); + expect(await localToken.balanceOf(acc2.address)).to.equal(dust); + // tokens received on the remote chain + expect(await remoteOFT.circulatingSupply()).to.equal(amount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); + // acc3 send tokens back to acc2 from remote chain + const acc2AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc2.address]); + const halfAmount = amount.div(2); + nativeFee = ( + await remoteOFT.estimateSendFee(localChainId, acc2AddressBytes32, halfAmount, false, defaultAdapterParams) + ).nativeFee; + + await expect( + remoteOFT + .connect(acc3) + .sendFrom( + acc2.address, + localChainId, + acc2AddressBytes32, + halfAmount, + [acc3.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ), + ).to.be.revertedWith("ProxyOFT: owner is not send caller"); + }); + it("Reverts when single limit exceeds daily limit on local", async () => { + const data = localOFT.interface.encodeFunctionData("setMaxDailyReceiveLimit", [ + remoteChainId, + convertToUnit(9, 18), + ]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.be.reverted; + }); it("Reverts if single transaction limit exceed", async function () { const amount = ethers.utils.parseEther("11", 18); await localToken.connect(acc2).faucet(amount); @@ -647,7 +836,120 @@ describe("Proxy OFTV2: ", function () { expect(await remoteOFT.circulatingSupply()).to.equal(amount); expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); }); + it("Drops failed message on remote", async function () { + const initialAmount = ethers.utils.parseEther("20", 18); + const amount = ethers.utils.parseEther("10", 18); + await localToken.connect(acc2).faucet(initialAmount); + await localToken.connect(acc2).approve(localOFT.address, initialAmount); + + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + const defaultAdapterParams = ethers.utils.solidityPack(["uint16", "uint256"], [1, 200000]); + const nativeFee = ( + await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, amount, false, defaultAdapterParams) + ).nativeFee; + + let data = remoteOFT.interface.encodeFunctionData("pause"); + await acc1.sendTransaction({ + to: bridgeAdminRemote.address, + data: data, + }); + await localOFT + .connect(acc2) + .sendFrom( + acc2.address, + remoteChainId, + acc3AddressBytes32, + amount, + [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ); + expect(await remoteOFT.failedMessages(localChainId, localPath, 1)).to.not.equals(ethers.constants.HashZero); + + data = remoteOFT.interface.encodeFunctionData("dropFailedMessage", [localChainId, localPath, 1]); + await acc1.sendTransaction({ + to: bridgeAdminRemote.address, + data: data, + }); + expect(await remoteOFT.failedMessages(localChainId, localPath, 1)).to.equals(ethers.constants.HashZero); + data = remoteOFT.interface.encodeFunctionData("unpause"); + await acc1.sendTransaction({ + to: bridgeAdminRemote.address, + data: data, + }); + }); + + it("Drops failed message on local", async function () { + const initialAmount = ethers.utils.parseEther("1.0000000001", 18); // 1 ether + const amount = ethers.utils.parseEther("1", 18); + const dust = ethers.utils.parseEther("0.0000000001"); + await localToken.connect(acc2).faucet(initialAmount); + // verify acc2 has tokens and acc3 has no tokens on remote chain + expect(await localToken.balanceOf(acc2.address)).to.be.equal(initialAmount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(0); + // acc2 sends tokens to acc3 on remote chain + // approve the proxy to swap your tokens + await localToken.connect(acc2).approve(localOFT.address, initialAmount); + // swaps token to remote chain + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + let nativeFee = ( + await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, initialAmount, false, defaultAdapterParams) + ).nativeFee; + + await localOFT + .connect(acc2) + .sendFrom( + acc2.address, + remoteChainId, + acc3AddressBytes32, + initialAmount, + [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ); + + // tokens are now owned by the proxy contract, because this is the original oft chain + expect(await localToken.balanceOf(localOFT.address)).to.equal(amount); + expect(await localOFT.circulatingSupply()).to.equal(amount.div(10 ** (18 - sharedDecimals))); + expect(await localToken.balanceOf(acc2.address)).to.equal(dust); + // tokens received on the remote chain + expect(await remoteOFT.circulatingSupply()).to.equal(amount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); + // acc3 send tokens back to acc2 from remote chain + const acc2AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc2.address]); + const halfAmount = amount.div(2); + nativeFee = ( + await remoteOFT.estimateSendFee(localChainId, acc2AddressBytes32, halfAmount, false, defaultAdapterParams) + ).nativeFee; + let data = localOFT.interface.encodeFunctionData("pause"); + await acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }); + await remoteOFT + .connect(acc3) + .sendFrom( + acc3.address, + localChainId, + acc2AddressBytes32, + halfAmount, + [acc3.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ); + + expect(await localOFT.failedMessages(remoteChainId, remotePath, 1)).to.not.equals(ethers.constants.HashZero); + data = localOFT.interface.encodeFunctionData("dropFailedMessage", [remoteChainId, remotePath, 1]); + await acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }); + + expect(await localOFT.failedMessages(remoteChainId, remotePath, 1)).to.equals(ethers.constants.HashZero); + }); + it("Reverts when zero chain id provided in set trusted remote", async function () { + await expect(bridgeAdminRemote.setTrustedRemoteAddress(0, remoteOFT.address)).to.be.revertedWith( + "ChainId must not be zero", + ); + }); it("Reverts initialy and should fail on retry if trusted remote changed", async function () { const initialAmount = ethers.utils.parseEther("20", 18); const amount = ethers.utils.parseEther("10", 18); @@ -767,6 +1069,40 @@ describe("Proxy OFTV2: ", function () { ), ).to.be.revertedWith("OFTCore: amount too small"); }); + + it("Reverts when caller and sender are different on local", async function () { + const amount = ethers.utils.parseEther("10", 18); + await localToken.connect(acc2).faucet(amount); + await localToken.connect(acc2).approve(localOFT.address, amount); + + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + const nativeFee = ( + await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, amount, false, defaultAdapterParams) + ).nativeFee; + + await expect( + localOFT + .connect(acc1) + .sendFrom( + acc2.address, + remoteChainId, + acc3AddressBytes32, + amount, + [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ), + ).to.be.revertedWith("ProxyOFT: owner is not send caller"); + }); + it("Reverts when amount is too large", async function () { + const amount = ethers.utils.parseEther(Number.MAX_SAFE_INTEGER.toString(), 18); + await localToken.connect(acc2).faucet(amount); + await localToken.connect(acc2).approve(localOFT.address, amount); + + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + await expect( + localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, amount, false, defaultAdapterParams), + ).to.be.revertedWith("OFTCore: amountSD overflow"); + }); it("Successs on removal of trusted remote", async function () { const data = localOFT.interface.encodeFunctionData("removeTrustedRemote", [remoteChainId]); await acc1.sendTransaction({ @@ -858,9 +1194,7 @@ describe("Proxy OFTV2: ", function () { it("Reverts when sendAndCall is disabled", async function () { const amount = ethers.utils.parseEther("2", 18); const dstGasForCall_ = 0; - const uint160Value = BigInt("0x" + acc3.address.slice(2)); - const bytes32Value = uint160Value << BigInt(96); - const acc3AddressBytes32 = "0x" + bytes32Value.toString(16).padStart(32, "0"); + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); await expect( localOFT @@ -874,9 +1208,8 @@ describe("Proxy OFTV2: ", function () { }); it("Successfully call sendAndCall", async function () { - const uint160Value = BigInt("0x" + acc3.address.slice(2)); - const bytes32Value = uint160Value << BigInt(96); - const acc3AddressBytes32 = "0x" + bytes32Value.toString(16).padStart(32, "0"); + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + let data = localOFT.interface.encodeFunctionData("setMinDstGas", [remoteChainId, 1, 300000]); const amount = ethers.utils.parseEther("2", 18); const dstGasForCall_ = 0; @@ -916,4 +1249,347 @@ describe("Proxy OFTV2: ", function () { { value: nativeFee }, ); }); + it("Reverts when withdraw amount is greater than outbound amount", async function () { + const data = localOFT.interface.encodeFunctionData("fallbackWithdraw", [ + acc1.address, + ethers.utils.parseEther("20", 18), + ]); + + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.be.reverted; + }); + it("Recover tokens", async function () { + const amount = ethers.utils.parseEther("10", 18); + await localToken.connect(acc2).faucet(amount); + await localToken.connect(acc2).approve(localOFT.address, amount); + + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + const nativeFee = ( + await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, amount, false, defaultAdapterParams) + ).nativeFee; + + await localOFT + .connect(acc2) + .sendFrom( + acc2.address, + remoteChainId, + acc3AddressBytes32, + amount, + [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ); + expect(await localOFT.outboundAmount()).to.equals(amount); + const data = localOFT.interface.encodeFunctionData("fallbackWithdraw", [acc1.address, amount]); + + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.emit(localOFT, "FallbackWithdraw"); + }); + it("Locks tokens in contract", async function () { + const amount = ethers.utils.parseEther("10", 18); + await localToken.connect(acc1).faucet(amount); + + await localToken.connect(acc1).approve(localOFT.address, amount); + const data = localOFT.interface.encodeFunctionData("fallbackDeposit", [acc1.address, amount]); + + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.emit(localOFT, "FallbackDeposit"); + }); + it("Reverts when cap is reached in fallbackDeposit", async function () { + const amount = Number.MAX_SAFE_INTEGER; + const data = localOFT.interface.encodeFunctionData("fallbackDeposit", [ + acc1.address, + ethers.utils.parseEther(amount.toString(), 18), + ]); + + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.be.reverted; + }); + + it("Sweeps token back to the user", async function () { + const amount = ethers.utils.parseEther("10", 18); + await localToken.connect(acc1).faucet(amount); + await localToken.connect(acc1).transfer(localOFT.address, amount); + const data = localOFT.interface.encodeFunctionData("sweepToken", [localToken.address, acc1.address, amount]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.emit(localOFT, "SweepToken"); + }); + it("Reverts when amount exceeds balance", async function () { + const amount = ethers.utils.parseEther("10", 18); + const sweepAmount = ethers.utils.parseEther("11", 18); + await localToken.connect(acc1).faucet(amount); + await localToken.connect(acc1).transfer(localOFT.address, amount); + const data = localOFT.interface.encodeFunctionData("sweepToken", [localToken.address, acc1.address, sweepAmount]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.be.reverted; + }); + + it("Sets whitelisted user", async function () { + const data = localOFT.interface.encodeFunctionData("setWhitelist", [acc1.address, true]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.emit(localOFT, "SetWhitelist"); + }); + it("Reverts on zero address otherwise sets oracle", async function () { + let data = remoteOFT.interface.encodeFunctionData("setOracle", [ethers.constants.AddressZero]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.be.reverted; + const oracleNew = await smock.fake("ResilientOracleInterface"); + data = remoteOFT.interface.encodeFunctionData("setOracle", [oracleNew.address]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.emit(localOFT, "OracleChanged"); + }); + it("Migrates bridge", async function () { + const amount = ethers.utils.parseEther("1", 18); + await localToken.connect(acc2).faucet(amount); + expect(await localToken.balanceOf(acc2.address)).to.be.equal(amount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(0); + await localToken.connect(acc2).approve(localOFT.address, amount); + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + let nativeFee = ( + await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, amount, false, defaultAdapterParams) + ).nativeFee; + + await localOFT + .connect(acc2) + .sendFrom( + acc2.address, + remoteChainId, + acc3AddressBytes32, + amount, + [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ); + + expect(await localToken.balanceOf(localOFT.address)).to.equal(amount); + expect(await localOFT.circulatingSupply()).to.equal(0); + expect(await localToken.balanceOf(acc2.address)).to.equal(0); + expect(await remoteOFT.circulatingSupply()).to.equal(amount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); + + const localOFT2 = await ProxyOFTV2Src.deploy( + localToken.address, + sharedDecimals, + localEndpoint.address, + oracle.address, + ); + const remoteOFT2 = await ProxyOFTV2Dest.deploy( + remoteToken.address, + sharedDecimals, + remoteEndpoint.address, + oracle.address, + ); + + const bridgeAdminFactory = await ethers.getContractFactory("XVSBridgeAdmin"); + + const bridgeAdminRemote2 = await upgrades.deployProxy(bridgeAdminFactory, [accessControlManager.address], { + constructorArgs: [remoteOFT2.address], + initializer: "initialize", + }); + + const bridgeAdminLocal2 = await upgrades.deployProxy(bridgeAdminFactory, [accessControlManager.address], { + constructorArgs: [localOFT2.address], + initializer: "initialize", + }); + + await bridgeAdminLocal.deployed(); + + await remoteOFT2.transferOwnership(bridgeAdminRemote2.address); + await localOFT2.transferOwnership(bridgeAdminLocal2.address); + + await localEndpoint.setDestLzEndpoint(remoteOFT2.address, remoteEndpoint.address); + await remoteEndpoint.setDestLzEndpoint(localOFT2.address, localEndpoint.address); + + const functionregistry = [ + "setOracle(address)", + "setMaxSingleTransactionLimit(uint16,uint256)", + "setMaxDailyLimit(uint16,uint256)", + "setMaxSingleReceiveTransactionLimit(uint16,uint256)", + "setMaxDailyReceiveLimit(uint16,uint256)", + "pause()", + "unpause()", + "setWhitelist(address,bool)", + "setConfig(uint16,uint16,uint256,bytes)", + "setSendVersion(uint16)", + "setReceiveVersion(uint16)", + "forceResumeReceive(uint16,bytes)", + "setTrustedRemoteAddress(uint16,bytes)", + "setPrecrime(address)", + "setMinDstGas(uint16,uint16,uint256)", + "setPayloadSizeLimit(uint16,uint256)", + "setUseCustomAdapterParams(bool)", + "removeTrustedRemote(uint16)", + "updateSendAndCallEnabled(bool)", + "fallbackWithdraw(address,uint256)", + "fallbackDeposit(address,uint256)", + "sweepToken(address,address,uint256)", + "dropFailedMessage(uint16,bytes,uint64)", + ]; + const activeArray = new Array(functionregistry.length).fill(true); + await bridgeAdminRemote2.upsertSignature(functionregistry, activeArray); + await bridgeAdminLocal2.upsertSignature(functionregistry, activeArray); + await bridgeAdminLocal2.setTrustedRemoteAddress(remoteChainId, remoteOFT2.address); + let data = localOFT2.interface.encodeFunctionData("setMinDstGas", [remoteChainId, 0, 200000]); + await acc1.sendTransaction({ + to: bridgeAdminLocal2.address, + data: data, + }); + data = localOFT2.interface.encodeFunctionData("setMaxDailyLimit", [remoteChainId, maxDailyTransactionLimit]); + await acc1.sendTransaction({ + to: bridgeAdminLocal2.address, + data: data, + }); + + data = localOFT2.interface.encodeFunctionData("setMaxSingleTransactionLimit", [ + remoteChainId, + singleTransactionLimit, + ]); + await acc1.sendTransaction({ + to: bridgeAdminLocal2.address, + data: data, + }); + + data = localOFT2.interface.encodeFunctionData("setMaxDailyReceiveLimit", [remoteChainId, maxDailyTransactionLimit]); + await acc1.sendTransaction({ + to: bridgeAdminLocal2.address, + data: data, + }); + data = localOFT2.interface.encodeFunctionData("setMaxSingleReceiveTransactionLimit", [ + remoteChainId, + singleTransactionLimit, + ]); + await acc1.sendTransaction({ + to: bridgeAdminLocal2.address, + data: data, + }); + + await bridgeAdminRemote2.setTrustedRemoteAddress(localChainId, localOFT2.address); + + data = remoteOFT2.interface.encodeFunctionData("setMinDstGas", [localChainId, 0, 200000]); + await acc1.sendTransaction({ + to: bridgeAdminRemote2.address, + data: data, + }); + + data = remoteOFT2.interface.encodeFunctionData("setMaxDailyLimit", [localChainId, maxDailyTransactionLimit]); + await acc1.sendTransaction({ + to: bridgeAdminRemote2.address, + data: data, + }); + data = remoteOFT2.interface.encodeFunctionData("setMaxSingleTransactionLimit", [ + localChainId, + singleTransactionLimit, + ]); + await acc1.sendTransaction({ + to: bridgeAdminRemote2.address, + data: data, + }); + + data = remoteOFT2.interface.encodeFunctionData("setMaxDailyReceiveLimit", [localChainId, maxDailyTransactionLimit]); + await acc1.sendTransaction({ + to: bridgeAdminRemote2.address, + data: data, + }); + data = remoteOFT2.interface.encodeFunctionData("setMaxSingleReceiveTransactionLimit", [ + localChainId, + singleTransactionLimit, + ]); + await acc1.sendTransaction({ + to: bridgeAdminRemote2.address, + data: data, + }); + + const mintCap = ethers.utils.parseEther("2", 18); + await remoteToken.setMintCap(remoteOFT.address, mintCap); + expect(await remoteToken.minterToCap(remoteOFT.address)).to.equals(mintCap); + + await remoteToken.setMintCap(remoteOFT2.address, mintCap); + expect(await remoteToken.minterToCap(remoteOFT2.address)).to.equals(mintCap); + + await expect(remoteToken.migrateMinterTokens(remoteOFT.address, remoteOFT2.address)).to.emit( + remoteToken, + "MintedTokensMigrated", + ); + data = localOFT.interface.encodeFunctionData("fallbackWithdraw", [acc1.address, amount]); + + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal.address, + data: data, + }), + ).to.emit(localOFT, "FallbackWithdraw"); + + await localToken.approve(localOFT2.address, amount); + + data = localOFT2.interface.encodeFunctionData("fallbackDeposit", [acc1.address, amount]); + await expect( + acc1.sendTransaction({ + to: bridgeAdminLocal2.address, + data: data, + }), + ).to.emit(localOFT2, "FallbackDeposit"); + + const acc2AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc2.address]); + nativeFee = ( + await remoteOFT2.estimateSendFee(localChainId, acc2AddressBytes32, amount, false, defaultAdapterParams) + ).nativeFee; + + expect(await remoteOFT2.circulatingSupply()).to.equal(amount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); + expect(await localToken.balanceOf(localOFT2.address)).to.be.equal(amount); + + await expect( + remoteOFT2 + .connect(acc3) + .sendFrom( + acc3.address, + localChainId, + acc2AddressBytes32, + amount, + [acc3.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ), + ).to.emit(remoteOFT2, "SendToChain"); + + expect(await remoteOFT2.circulatingSupply()).to.equal(0); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(0); + + expect(await localToken.balanceOf(localOFT2.address)).to.be.equal(0); + expect(await localOFT2.outboundAmount()).to.be.equal(0); + expect(await localToken.balanceOf(acc2.address)).to.be.equal(amount); + }); }); diff --git a/test/tokenConvertor.ts b/test/tokenConvertor.ts new file mode 100644 index 0000000..5d9ef02 --- /dev/null +++ b/test/tokenConvertor.ts @@ -0,0 +1,120 @@ +import { FakeContract, smock } from "@defi-wonderland/smock"; +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { SignerWithAddress } from "hardhat-deploy-ethers/signers"; + +import { AccessControlManager, XVS, XVS__factory } from "../typechain"; + +describe("Token Convertor: ", function () { + let tokenFactory: XVS__factory, + token: XVS, + acc2: SignerWithAddress, + acc1: SignerWithAddress, + accessControlManager: FakeContract; + + const tokenFixture = async () => { + accessControlManager = await smock.fake("AccessControlManager"); + accessControlManager.isAllowedToCall.returns(true); + tokenFactory = await ethers.getContractFactory("XVS"); + token = await tokenFactory.deploy(accessControlManager.address); + acc1 = (await ethers.getSigners())[0]; + acc2 = (await ethers.getSigners())[1]; + }; + beforeEach(async function () { + await loadFixture(tokenFixture); + }); + it("Reverts when token is paused", async () => { + await token.pause(); + await expect(token.mint(acc1.address, ethers.utils.parseEther("2", 18))).to.be.revertedWith("Pausable: paused"); + await token.unpause(); + }); + it("Reverts when minting cap reached", async () => { + await token.connect(acc1).setMintCap(acc1.address, ethers.utils.parseEther("1", 18)); + await expect( + token.connect(acc1).mint(acc1.address, ethers.utils.parseEther("2", 18)), + ).to.be.revertedWithCustomError(token, "MintLimitExceed"); + }); + it("Mint succesfully", async () => { + const mintAmount = ethers.utils.parseEther("2", 18); + await token.connect(acc1).setMintCap(acc1.address, mintAmount); + await expect(token.connect(acc1).mint(acc1.address, mintAmount)).to.emit(token, "MintLimitDecreased"); + expect(await token.minterToMintedAmount(acc1.address)).to.equals(mintAmount); + }); + + it("Reverts when token is paused", async () => { + await token.pause(); + await expect(token.burn(acc1.address, ethers.utils.parseEther("2", 18))).to.be.revertedWith("Pausable: paused"); + }); + it("Reverts when burned token is greater than minted token", async () => { + await token.connect(acc1).setMintCap(acc1.address, ethers.utils.parseEther("2", 18)); + await expect(token.connect(acc1).mint(acc1.address, ethers.utils.parseEther("2", 18))).to.emit( + token, + "MintLimitDecreased", + ); + await expect(token.connect(acc1).burn(acc1.address, ethers.utils.parseEther("3", 18))).to.be.revertedWith( + "ERC20: burn amount exceeds balance", + ); + }); + it("Burn succesfully", async () => { + await token.connect(acc1).setMintCap(acc1.address, ethers.utils.parseEther("2", 18)); + await expect(token.connect(acc1).mint(acc1.address, ethers.utils.parseEther("2", 18))).to.emit( + token, + "MintLimitDecreased", + ); + await expect(token.connect(acc1).burn(acc1.address, ethers.utils.parseEther("2", 18))).to.emit( + token, + "MintLimitIncreased", + ); + }); + it("Update blackList", async () => { + await expect(token.connect(acc1).updateBlacklist(acc2.address, true)).to.emit(token, "BlacklistUpdated"); + expect(await token.isBlackListed(acc2.address)).to.be.true; + }); + it("Sets access control manager", async () => { + const accessControlManagerNew = await smock.fake("AccessControlManager"); + await expect(token.setAccessControlManager(accessControlManagerNew.address)).to.emit( + token, + "NewAccessControlManager", + ); + + await expect(token.setAccessControlManager(ethers.constants.AddressZero)).to.be.revertedWithCustomError( + token, + "ZeroAddressNotAllowed", + ); + }); + it("Reverts when source and destination address is same in migration", async () => { + await expect(token.migrateMinterTokens(acc1.address, acc1.address)).to.be.revertedWithCustomError( + token, + "AddressesMustDiffer", + ); + }); + it("Reverts when destination cap is less than source and destination cap", async () => { + const mintCap = ethers.utils.parseEther("2", 18); + await token.setMintCap(acc1.address, mintCap); + expect(await token.minterToCap(acc1.address)).to.equals(mintCap); + await token.setMintCap(acc2.address, mintCap); + expect(await token.minterToCap(acc2.address)).to.equals(mintCap); + const amount = ethers.utils.parseEther("1", 18); + await token.mint(acc1.address, amount); + await token.mint(acc2.address, amount); + await token.setMintCap(acc2.address, amount); + await expect(token.migrateMinterTokens(acc1.address, acc2.address)).to.be.revertedWithCustomError( + token, + "MintLimitExceed", + ); + }); + it("Migrates token from source minter to destination minter", async () => { + const mintCap = ethers.utils.parseEther("2", 18); + await token.setMintCap(acc1.address, mintCap); + expect(await token.minterToCap(acc1.address)).to.equals(mintCap); + await token.setMintCap(acc2.address, mintCap); + expect(await token.minterToCap(acc2.address)).to.equals(mintCap); + const amount = ethers.utils.parseEther("1", 18); + await token.mint(acc1.address, amount); + await token.mint(acc2.address, amount); + await expect(token.migrateMinterTokens(acc1.address, acc2.address)).to.emit(token, "MintLimitDecreased"); + await expect(token.migrateMinterTokens(acc1.address, acc2.address)).to.emit(token, "MintLimitIncreased"); + await expect(token.migrateMinterTokens(acc1.address, acc2.address)).to.emit(token, "MintedTokensMigrated"); + }); +}); From c8d7365c5830cc0c9719f8961d5f712a40a50a8d Mon Sep 17 00:00:00 2001 From: GitGuru7 <128375421+GitGuru7@users.noreply.github.com> Date: Thu, 28 Dec 2023 17:38:08 +0530 Subject: [PATCH 2/3] fix: remove redundant code --- test/bridgeAdmin.ts | 104 +++-------- test/proxyOFT.ts | 168 +++++------------- .../{tokenConvertor.ts => tokenController.ts} | 2 +- 3 files changed, 67 insertions(+), 207 deletions(-) rename test/{tokenConvertor.ts => tokenController.ts} (99%) diff --git a/test/bridgeAdmin.ts b/test/bridgeAdmin.ts index f6ad766..16071cd 100644 --- a/test/bridgeAdmin.ts +++ b/test/bridgeAdmin.ts @@ -21,6 +21,27 @@ describe("Bridge Admin: ", function () { const singleTransactionLimit = convertToUnit(10, 18); const maxDailyTransactionLimit = convertToUnit(100, 18); + const functionregistry = [ + "setOracle(address)", + "setMaxSingleTransactionLimit(uint16,uint256)", + "setMaxDailyLimit(uint16,uint256)", + "setMaxSingleReceiveTransactionLimit(uint16,uint256)", + "setMaxDailyReceiveLimit(uint16,uint256)", + "pause()", + "unpause()", + "setWhitelist(address,bool)", + "setConfig(uint16,uint16,uint256,bytes)", + "setSendVersion(uint16)", + "setReceiveVersion(uint16)", + "forceResumeReceive(uint16,bytes)", + "setTrustedRemote(uint16,bytes)", + "setTrustedRemoteAddress(uint16,bytes)", + "setPrecrime(address)", + "setMinDstGas(uint16,uint16,uint256)", + "setPayloadSizeLimit(uint16,uint256)", + "setUseCustomAdapterParams(bool)", + ]; + let LZEndpointMock: LZEndpointMock__factory, ProxyOFTV2Dest: XVSProxyOFTDest__factory, RemoteTokenFactory: XVS__factory, @@ -84,52 +105,13 @@ describe("Bridge Admin: ", function () { await remoteOFT.transferOwnership(bridgeAdmin.address); remotePath = ethers.utils.solidityPack(["address", "address"], [AddressOne, remoteOFT.address]); - const functionregistry = [ - "setOracle(address)", - "setMaxSingleTransactionLimit(uint16,uint256)", - "setMaxDailyLimit(uint16,uint256)", - "setMaxSingleReceiveTransactionLimit(uint16,uint256)", - "setMaxDailyReceiveLimit(uint16,uint256)", - "pause()", - "unpause()", - "setWhitelist(address,bool)", - "setConfig(uint16,uint16,uint256,bytes)", - "setSendVersion(uint16)", - "setReceiveVersion(uint16)", - "forceResumeReceive(uint16,bytes)", - "setTrustedRemote(uint16,bytes)", - "setTrustedRemoteAddress(uint16,bytes)", - "setPrecrime(address)", - "setMinDstGas(uint16,uint16,uint256)", - "setPayloadSizeLimit(uint16,uint256)", - "setUseCustomAdapterParams(bool)", - ]; + const activeArray = new Array(functionregistry.length).fill(true); await bridgeAdmin.upsertSignature(functionregistry, activeArray); await loadFixture(grantPermissionsFixture); }); it("Revert when inputs length mismatch in function registry", async function () { - const functionregistry = [ - "setOracle(address)", - "setMaxSingleTransactionLimit(uint16,uint256)", - "setMaxDailyLimit(uint16,uint256)", - "setMaxSingleReceiveTransactionLimit(uint16,uint256)", - "setMaxDailyReceiveLimit(uint16,uint256)", - "pause()", - "unpause()", - "setWhitelist(address,bool)", - "setConfig(uint16,uint16,uint256,bytes)", - "setSendVersion(uint16)", - "setReceiveVersion(uint16)", - "forceResumeReceive(uint16,bytes)", - "setTrustedRemote(uint16,bytes)", - "setTrustedRemoteAddress(uint16,bytes)", - "setPrecrime(address)", - "setMinDstGas(uint16,uint16,uint256)", - "setPayloadSizeLimit(uint16,uint256)", - "setUseCustomAdapterParams(bool)", - ]; const activeArray = new Array(functionregistry.length - 1).fill(true); await expect(bridgeAdmin.upsertSignature(functionregistry, activeArray)).to.be.revertedWith( @@ -138,29 +120,9 @@ describe("Bridge Admin: ", function () { }); it("Deletes from function registry", async function () { - const functionregistry = [ - "setOracle(address)", - "setMaxSingleTransactionLimit(uint16,uint256)", - "setMaxDailyLimit(uint16,uint256)", - "setMaxSingleReceiveTransactionLimit(uint16,uint256)", - "setMaxDailyReceiveLimit(uint16,uint256)", - "pause()", - "unpause()", - "setWhitelist(address,bool)", - "setConfig(uint16,uint16,uint256,bytes)", - "setSendVersion(uint16)", - "setReceiveVersion(uint16)", - "forceResumeReceive(uint16,bytes)", - "setTrustedRemote(uint16,bytes)", - "setTrustedRemoteAddress(uint16,bytes)", - "setPrecrime(address)", - "setMinDstGas(uint16,uint16,uint256)", - "setPayloadSizeLimit(uint16,uint256)", - "setUseCustomAdapterParams(bool)", - "fakeFunction(uint256)", - ]; const activeArray = new Array(functionregistry.length).fill(true); await bridgeAdmin.upsertSignature(functionregistry, activeArray); + await bridgeAdmin.upsertSignature(["fakeFunction(uint256)"], [true]); await expect(bridgeAdmin.upsertSignature(["fakeFunction(uint256)"], [false])).to.emit( bridgeAdmin, "FunctionRegistryChanged", @@ -168,26 +130,6 @@ describe("Bridge Admin: ", function () { }); it("Reverts when non owner calls upsert signature", async function () { - const functionregistry = [ - "setOracle(address)", - "setMaxSingleTransactionLimit(uint16,uint256)", - "setMaxDailyLimit(uint16,uint256)", - "setMaxSingleReceiveTransactionLimit(uint16,uint256)", - "setMaxDailyReceiveLimit(uint16,uint256)", - "pause()", - "unpause()", - "setWhitelist(address,bool)", - "setConfig(uint16,uint16,uint256,bytes)", - "setSendVersion(uint16)", - "setReceiveVersion(uint16)", - "forceResumeReceive(uint16,bytes)", - "setTrustedRemote(uint16,bytes)", - "setTrustedRemoteAddress(uint16,bytes)", - "setPrecrime(address)", - "setMinDstGas(uint16,uint16,uint256)", - "setPayloadSizeLimit(uint16,uint256)", - "setUseCustomAdapterParams(bool)", - ]; const activeArray = new Array(functionregistry.length - 1).fill(true); await expect(bridgeAdmin.connect(acc2).upsertSignature(functionregistry, activeArray)).to.be.revertedWith( diff --git a/test/proxyOFT.ts b/test/proxyOFT.ts index 450d7c6..19db13e 100644 --- a/test/proxyOFT.ts +++ b/test/proxyOFT.ts @@ -51,6 +51,35 @@ describe("Proxy OFTV2: ", function () { accessControlManager: FakeContract, oracle: FakeContract, defaultAdapterParams: any; + + async function sendTokensFromLocalToRemote() { + const amount = ethers.utils.parseEther("1", 18); + await localToken.connect(acc2).faucet(amount); + expect(await localToken.balanceOf(acc2.address)).to.be.equal(amount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(0); + await localToken.connect(acc2).approve(localOFT.address, amount); + const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); + const nativeFee = ( + await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, amount, false, defaultAdapterParams) + ).nativeFee; + + await localOFT + .connect(acc2) + .sendFrom( + acc2.address, + remoteChainId, + acc3AddressBytes32, + amount, + [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], + { value: nativeFee }, + ); + + expect(await localToken.balanceOf(localOFT.address)).to.equal(amount); + expect(await localOFT.circulatingSupply()).to.equal(0); + expect(await localToken.balanceOf(acc2.address)).to.equal(0); + expect(await remoteOFT.circulatingSupply()).to.equal(amount); + expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); + } const bridgeFixture = async () => { LZEndpointMock = await ethers.getContractFactory("LZEndpointMock"); ProxyOFTV2Src = await ethers.getContractFactory("XVSProxyOFTSrc"); @@ -400,46 +429,14 @@ describe("Proxy OFTV2: ", function () { expect(await localToken.balanceOf(localOFT.address)).to.be.equal(halfAmount); expect(await localToken.balanceOf(acc2.address)).to.be.equal(halfAmount.add(dust)); }); - it("Reverts when caller and sender are different on remote ", async function () { - const initialAmount = ethers.utils.parseEther("1.0000000001", 18); // 1 ether - const amount = ethers.utils.parseEther("1", 18); - const dust = ethers.utils.parseEther("0.0000000001"); - await localToken.connect(acc2).faucet(initialAmount); - // verify acc2 has tokens and acc3 has no tokens on remote chain - expect(await localToken.balanceOf(acc2.address)).to.be.equal(initialAmount); - expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(0); - // acc2 sends tokens to acc3 on remote chain - // approve the proxy to swap your tokens - await localToken.connect(acc2).approve(localOFT.address, initialAmount); - // swaps token to remote chain - const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); - let nativeFee = ( - await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, initialAmount, false, defaultAdapterParams) - ).nativeFee; - - await localOFT - .connect(acc2) - .sendFrom( - acc2.address, - remoteChainId, - acc3AddressBytes32, - initialAmount, - [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], - { value: nativeFee }, - ); + it("Reverts when caller and sender are different on remote", async function () { + await sendTokensFromLocalToRemote(); - // tokens are now owned by the proxy contract, because this is the original oft chain - expect(await localToken.balanceOf(localOFT.address)).to.equal(amount); - expect(await localOFT.circulatingSupply()).to.equal(amount.div(10 ** (18 - sharedDecimals))); - expect(await localToken.balanceOf(acc2.address)).to.equal(dust); - // tokens received on the remote chain - expect(await remoteOFT.circulatingSupply()).to.equal(amount); - expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); // acc3 send tokens back to acc2 from remote chain const acc2AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc2.address]); - const halfAmount = amount.div(2); - nativeFee = ( - await remoteOFT.estimateSendFee(localChainId, acc2AddressBytes32, halfAmount, false, defaultAdapterParams) + const amount = ethers.utils.parseEther("1", 18); + const nativeFee = ( + await remoteOFT.estimateSendFee(localChainId, acc2AddressBytes32, amount, false, defaultAdapterParams) ).nativeFee; await expect( @@ -449,7 +446,7 @@ describe("Proxy OFTV2: ", function () { acc2.address, localChainId, acc2AddressBytes32, - halfAmount, + amount, [acc3.address, ethers.constants.AddressZero, defaultAdapterParams], { value: nativeFee }, ), @@ -871,53 +868,14 @@ describe("Proxy OFTV2: ", function () { data: data, }); expect(await remoteOFT.failedMessages(localChainId, localPath, 1)).to.equals(ethers.constants.HashZero); - data = remoteOFT.interface.encodeFunctionData("unpause"); - await acc1.sendTransaction({ - to: bridgeAdminRemote.address, - data: data, - }); }); it("Drops failed message on local", async function () { - const initialAmount = ethers.utils.parseEther("1.0000000001", 18); // 1 ether - const amount = ethers.utils.parseEther("1", 18); - const dust = ethers.utils.parseEther("0.0000000001"); - await localToken.connect(acc2).faucet(initialAmount); - // verify acc2 has tokens and acc3 has no tokens on remote chain - expect(await localToken.balanceOf(acc2.address)).to.be.equal(initialAmount); - expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(0); - // acc2 sends tokens to acc3 on remote chain - // approve the proxy to swap your tokens - await localToken.connect(acc2).approve(localOFT.address, initialAmount); - // swaps token to remote chain - const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); - let nativeFee = ( - await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, initialAmount, false, defaultAdapterParams) - ).nativeFee; - - await localOFT - .connect(acc2) - .sendFrom( - acc2.address, - remoteChainId, - acc3AddressBytes32, - initialAmount, - [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], - { value: nativeFee }, - ); - - // tokens are now owned by the proxy contract, because this is the original oft chain - expect(await localToken.balanceOf(localOFT.address)).to.equal(amount); - expect(await localOFT.circulatingSupply()).to.equal(amount.div(10 ** (18 - sharedDecimals))); - expect(await localToken.balanceOf(acc2.address)).to.equal(dust); - // tokens received on the remote chain - expect(await remoteOFT.circulatingSupply()).to.equal(amount); - expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); - // acc3 send tokens back to acc2 from remote chain + await sendTokensFromLocalToRemote(); const acc2AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc2.address]); - const halfAmount = amount.div(2); - nativeFee = ( - await remoteOFT.estimateSendFee(localChainId, acc2AddressBytes32, halfAmount, false, defaultAdapterParams) + const amount = ethers.utils.parseEther("1", 18); + const nativeFee = ( + await remoteOFT.estimateSendFee(localChainId, acc2AddressBytes32, amount, false, defaultAdapterParams) ).nativeFee; let data = localOFT.interface.encodeFunctionData("pause"); await acc1.sendTransaction({ @@ -930,7 +888,7 @@ describe("Proxy OFTV2: ", function () { acc3.address, localChainId, acc2AddressBytes32, - halfAmount, + amount, [acc3.address, ethers.constants.AddressZero, defaultAdapterParams], { value: nativeFee }, ); @@ -1373,34 +1331,9 @@ describe("Proxy OFTV2: ", function () { }), ).to.emit(localOFT, "OracleChanged"); }); - it("Migrates bridge", async function () { + it("Replace bridge with new bridge", async function () { + await sendTokensFromLocalToRemote(); const amount = ethers.utils.parseEther("1", 18); - await localToken.connect(acc2).faucet(amount); - expect(await localToken.balanceOf(acc2.address)).to.be.equal(amount); - expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(0); - await localToken.connect(acc2).approve(localOFT.address, amount); - const acc3AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc3.address]); - let nativeFee = ( - await localOFT.estimateSendFee(remoteChainId, acc3AddressBytes32, amount, false, defaultAdapterParams) - ).nativeFee; - - await localOFT - .connect(acc2) - .sendFrom( - acc2.address, - remoteChainId, - acc3AddressBytes32, - amount, - [acc2.address, ethers.constants.AddressZero, defaultAdapterParams], - { value: nativeFee }, - ); - - expect(await localToken.balanceOf(localOFT.address)).to.equal(amount); - expect(await localOFT.circulatingSupply()).to.equal(0); - expect(await localToken.balanceOf(acc2.address)).to.equal(0); - expect(await remoteOFT.circulatingSupply()).to.equal(amount); - expect(await remoteToken.balanceOf(acc3.address)).to.be.equal(amount); - const localOFT2 = await ProxyOFTV2Src.deploy( localToken.address, sharedDecimals, @@ -1435,29 +1368,14 @@ describe("Proxy OFTV2: ", function () { await remoteEndpoint.setDestLzEndpoint(localOFT2.address, localEndpoint.address); const functionregistry = [ - "setOracle(address)", "setMaxSingleTransactionLimit(uint16,uint256)", "setMaxDailyLimit(uint16,uint256)", "setMaxSingleReceiveTransactionLimit(uint16,uint256)", "setMaxDailyReceiveLimit(uint16,uint256)", - "pause()", - "unpause()", - "setWhitelist(address,bool)", - "setConfig(uint16,uint16,uint256,bytes)", - "setSendVersion(uint16)", - "setReceiveVersion(uint16)", - "forceResumeReceive(uint16,bytes)", "setTrustedRemoteAddress(uint16,bytes)", - "setPrecrime(address)", "setMinDstGas(uint16,uint16,uint256)", - "setPayloadSizeLimit(uint16,uint256)", - "setUseCustomAdapterParams(bool)", - "removeTrustedRemote(uint16)", - "updateSendAndCallEnabled(bool)", "fallbackWithdraw(address,uint256)", "fallbackDeposit(address,uint256)", - "sweepToken(address,address,uint256)", - "dropFailedMessage(uint16,bytes,uint64)", ]; const activeArray = new Array(functionregistry.length).fill(true); await bridgeAdminRemote2.upsertSignature(functionregistry, activeArray); @@ -1564,7 +1482,7 @@ describe("Proxy OFTV2: ", function () { ).to.emit(localOFT2, "FallbackDeposit"); const acc2AddressBytes32 = ethers.utils.defaultAbiCoder.encode(["address"], [acc2.address]); - nativeFee = ( + const nativeFee = ( await remoteOFT2.estimateSendFee(localChainId, acc2AddressBytes32, amount, false, defaultAdapterParams) ).nativeFee; diff --git a/test/tokenConvertor.ts b/test/tokenController.ts similarity index 99% rename from test/tokenConvertor.ts rename to test/tokenController.ts index 5d9ef02..a17c51c 100644 --- a/test/tokenConvertor.ts +++ b/test/tokenController.ts @@ -6,7 +6,7 @@ import { SignerWithAddress } from "hardhat-deploy-ethers/signers"; import { AccessControlManager, XVS, XVS__factory } from "../typechain"; -describe("Token Convertor: ", function () { +describe("Token Controller: ", function () { let tokenFactory: XVS__factory, token: XVS, acc2: SignerWithAddress, From c1ecdaa4bcabe417807850c8a6767d6f51e1006f Mon Sep 17 00:00:00 2001 From: GitGuru7 <128375421+GitGuru7@users.noreply.github.com> Date: Mon, 8 Jan 2024 14:56:02 +0530 Subject: [PATCH 3/3] fix: resolve comments --- test/bridgeAdmin.ts | 8 +++++--- test/proxyOFT.ts | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/bridgeAdmin.ts b/test/bridgeAdmin.ts index 16071cd..842e73d 100644 --- a/test/bridgeAdmin.ts +++ b/test/bridgeAdmin.ts @@ -34,12 +34,14 @@ describe("Bridge Admin: ", function () { "setSendVersion(uint16)", "setReceiveVersion(uint16)", "forceResumeReceive(uint16,bytes)", - "setTrustedRemote(uint16,bytes)", "setTrustedRemoteAddress(uint16,bytes)", "setPrecrime(address)", "setMinDstGas(uint16,uint16,uint256)", "setPayloadSizeLimit(uint16,uint256)", - "setUseCustomAdapterParams(bool)", + "removeTrustedRemote(uint16)", + "updateSendAndCallEnabled(bool)", + "sweepToken(address,address,uint256)", + "dropFailedMessage(uint16,bytes,uint64)", ]; let LZEndpointMock: LZEndpointMock__factory, @@ -144,7 +146,7 @@ describe("Bridge Admin: ", function () { }); it("Revert if permissions are not granted to call owner functions of bridge", async function () { - let data = remoteOFT.interface.encodeFunctionData("setTrustedRemote", [localChainId, remotePath]); + let data = remoteOFT.interface.encodeFunctionData("setTrustedRemoteAddress", [localChainId, remotePath]); await expect( acc1.sendTransaction({ to: bridgeAdmin.address, diff --git a/test/proxyOFT.ts b/test/proxyOFT.ts index 19db13e..cfd9adc 100644 --- a/test/proxyOFT.ts +++ b/test/proxyOFT.ts @@ -161,7 +161,6 @@ describe("Proxy OFTV2: ", function () { "setPrecrime(address)", "setMinDstGas(uint16,uint16,uint256)", "setPayloadSizeLimit(uint16,uint256)", - "setUseCustomAdapterParams(bool)", "removeTrustedRemote(uint16)", "updateSendAndCallEnabled(bool)", "fallbackWithdraw(address,uint256)",