diff --git a/.gitignore b/.gitignore index e2face9..219878e 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ pnpm-error.log # Editor and OS files .DS_Store .idea + + +# Soldeer +/dependencies diff --git a/contracts/AbraOFTAdapterUpgradeable.sol b/contracts/AbraOFTAdapterUpgradeable.sol index 09abe82..80bd386 100644 --- a/contracts/AbraOFTAdapterUpgradeable.sol +++ b/contracts/AbraOFTAdapterUpgradeable.sol @@ -10,7 +10,7 @@ import { SenderWithFees } from "./SenderWithFees.sol"; contract AbraOFTAdapterUpgradeable is OFTAdapterUpgradeable, SenderWithFees { constructor(address _token, address _lzEndpoint) OFTAdapterUpgradeable(_token, _lzEndpoint) {} - function initialize(address _delegate) public initializer { + function initialize(address _delegate) public virtual initializer { __OFTAdapter_init(_delegate); __Ownable_init(_delegate); } diff --git a/contracts/AbraOFTUpgradeable.sol b/contracts/AbraOFTUpgradeable.sol index 78fbe1c..992a104 100644 --- a/contracts/AbraOFTUpgradeable.sol +++ b/contracts/AbraOFTUpgradeable.sol @@ -10,7 +10,7 @@ import { SenderWithFees } from "./SenderWithFees.sol"; contract AbraOFTUpgradeable is OFTUpgradeable, SenderWithFees { constructor(address _lzEndpoint) OFTUpgradeable(_lzEndpoint) {} - function initialize(string memory _name, string memory _symbol, address _delegate) public initializer { + function initialize(string memory _name, string memory _symbol, address _delegate) public virtual initializer { __OFT_init(_name, _symbol, _delegate); __Ownable_init(_delegate); } diff --git a/contracts/Feehandler.sol b/contracts/FeeHandler.sol similarity index 98% rename from contracts/Feehandler.sol rename to contracts/FeeHandler.sol index cb205b8..5a2c9b3 100644 --- a/contracts/Feehandler.sol +++ b/contracts/FeeHandler.sol @@ -5,6 +5,11 @@ import { IFeeHandler } from "./interfaces/IFeeHandler.sol"; import { IAggregator } from "./interfaces/IAggregator.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +enum QuoteType { + Oracle, + Fixed +} + contract FeeHandler is IFeeHandler, Ownable { event FixedNativeFeeChanged(uint256 previous, uint256 current); event OracleImplementationChange(IAggregator indexed previous, IAggregator indexed current); @@ -15,11 +20,6 @@ contract FeeHandler is IFeeHandler, Ownable { error InvalidQuoteType(QuoteType); error Unauthorized(); - enum QuoteType { - Oracle, - Fixed - } - uint256 public constant DEFAULT_USD_FEE = 1e18; address public override feeTo; diff --git a/contracts/SenderWithFees.sol b/contracts/SenderWithFees.sol index aba9ac3..48922a5 100644 --- a/contracts/SenderWithFees.sol +++ b/contracts/SenderWithFees.sol @@ -35,10 +35,6 @@ abstract contract SenderWithFees is OAppSenderUpgradeable { ); } - if (msg.value < _fee.nativeFee + protocolNativeFees) { - revert NotEnoughNative(msg.value); - } - if (_fee.lzTokenFee > 0) { _payLzToken(_fee.lzTokenFee); } diff --git a/contracts/mocks/AbraOFTAdapterUpgradeableMock.sol b/contracts/mocks/AbraOFTAdapterUpgradeableMock.sol deleted file mode 100644 index 203423e..0000000 --- a/contracts/mocks/AbraOFTAdapterUpgradeableMock.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.22; - -import { AbraOFTAdapterUpgradeable } from "../AbraOFTAdapterUpgradeable.sol"; - -// @dev WARNING: This is for testing purposes only -contract AbraOFTAdapterUpgradeableMock is AbraOFTAdapterUpgradeable { - constructor(address _token, address _lzEndpoint) AbraOFTAdapterUpgradeable(_token, _lzEndpoint) {} -} diff --git a/contracts/mocks/AbraOFTpgradeableMock.sol b/contracts/mocks/AbraOFTpgradeableMock.sol deleted file mode 100644 index 722d244..0000000 --- a/contracts/mocks/AbraOFTpgradeableMock.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.22; - -import { AbraOFTUpgradeable } from "../AbraOFTUpgradeable.sol"; - -// @dev WARNING: This is for testing purposes only -contract AbraOFTUpgradeableMock is AbraOFTUpgradeable { - constructor(address _lzEndpoint) AbraOFTUpgradeable(_lzEndpoint) {} - - function mint(address _to, uint256 _amount) public { - _mint(_to, _amount); - } -} diff --git a/foundry.toml b/foundry.toml index c039ea8..84d0a41 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,20 +1,30 @@ [profile.default] -src = "src" -out = "artifacts" -libs = ["node_modules"] -remappings = [ - "@axelar-network/=node_modules/@axelar-network/", - "@chainlink/=node_modules/@chainlink/", - "@ensdomains/=node_modules/@ensdomains/", - "@eth-optimism/=node_modules/@eth-optimism/", - "@ethereum-waffle/=node_modules/@ethereum-waffle/", - "@layerzerolabs/=node_modules/@layerzerolabs/", - "@openzeppelin/=node_modules/@openzeppelin/", - "ds-test/=node_modules/ds-test/", - "forge-std/=node_modules/forge-std/", - "hardhat-deploy/=node_modules/hardhat-deploy/", - "hardhat/=node_modules/hardhat/", - "solidity-bytes-utils/=node_modules/solidity-bytes-utils/", +solc-version = '0.8.22' +src = 'contracts' +out = 'out' +test = 'test' +cache_path = 'cache/foundry' +optimizer = true +optimizer_runs = 200 + +libs = [ + # We provide a set of useful contract utilities + # in the lib directory of @layerzerolabs/toolbox-foundry: + # + # - forge-std + # - ds-test + # - solidity-bytes-utils + 'node_modules/@layerzerolabs/toolbox-foundry/lib', + 'node_modules', ] -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options +remappings = [ + # Due to a misconfiguration of solidity-bytes-utils, an outdated version + # of forge-std is being dragged in + # + # To remedy this, we'll remap the ds-test and forge-std imports to ou own versions + 'ds-test/=node_modules/@layerzerolabs/toolbox-foundry/lib/ds-test', + 'forge-std/=node_modules/@layerzerolabs/toolbox-foundry/lib/forge-std', + '@layerzerolabs/=node_modules/@layerzerolabs/', + '@openzeppelin/=node_modules/@openzeppelin/', +] \ No newline at end of file diff --git a/test/AbraOFTUpgradeable.t.sol b/test/AbraOFTUpgradeable.t.sol new file mode 100644 index 0000000..e7e31cf --- /dev/null +++ b/test/AbraOFTUpgradeable.t.sol @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import { TestHelperOz5 } from "@layerzerolabs/test-devtools-evm-foundry/contracts/TestHelperOz5.sol"; +import { OptionsBuilder } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol"; +import { IOFT, SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol"; +import { MessagingFee, MessagingReceipt } from "@layerzerolabs/oft-evm-upgradeable/contracts/oft/OFTCoreUpgradeable.sol"; +import { OFTInspectorMock } from "@layerzerolabs/oft-evm-upgradeable/test/mocks/OFTInspectorMock.sol"; +import { OFTComposerMock } from "@layerzerolabs/oft-evm-upgradeable/test/mocks/OFTComposerMock.sol"; +import { ProxyAdmin } from "hardhat-deploy/solc_0.8/openzeppelin/proxy/transparent/ProxyAdmin.sol"; +import { TransparentUpgradeableProxy } from "hardhat-deploy/solc_0.8/openzeppelin/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { IOAppOptionsType3, EnforcedOptionParam } from "@layerzerolabs/oapp-evm-upgradeable/contracts/oapp/libs/OAppOptionsType3Upgradeable.sol"; +import { OFTMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTMsgCodec.sol"; +import { OFTComposeMsgCodec } from "@layerzerolabs/oft-evm/contracts/libs/OFTComposeMsgCodec.sol"; +import { OFTInspectorMock, IOAppMsgInspector } from "@layerzerolabs/oft-evm-upgradeable/test/mocks/OFTInspectorMock.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { ERC20Mock } from "./mocks/ERC20Mock.sol"; +import { FeeHandler, QuoteType } from "contracts/FeeHandler.sol"; +import { MockAggregator } from "./mocks/MockAggregator.sol"; +import { AbraOFTUpgradeable } from "contracts/AbraOFTUpgradeable.sol"; +import { AbraOFTAdapterUpgradeable } from "contracts/AbraOFTAdapterUpgradeable.sol"; + +contract AbraOFTUpgradeableTest is TestHelperOz5 { + using OptionsBuilder for bytes; + + uint32 aEid = 1; + uint32 bEid = 2; + uint32 cEid = 3; + + AbraOFTUpgradeable aOFT; + AbraOFTUpgradeable bOFT; + AbraOFTAdapterUpgradeable cOFTAdapter; + ERC20Mock cERC20Mock; + + OFTInspectorMock oAppInspector; + + address public userA = address(0x1); + address public userB = address(0x2); + address public userC = address(0x3); + uint256 public initialBalance = 100 ether; + + address public proxyAdmin = makeAddr("proxyAdmin"); + + function setUp() public virtual override { + vm.deal(userA, 1000 ether); + vm.deal(userB, 1000 ether); + vm.deal(userC, 1000 ether); + + super.setUp(); + setUpEndpoints(3, LibraryType.UltraLightNode); + + aOFT = AbraOFTUpgradeable( + _deployContractAndProxy( + type(AbraOFTUpgradeable).creationCode, + abi.encode(address(endpoints[aEid])), + abi.encodeWithSelector(AbraOFTUpgradeable.initialize.selector, "aOFT", "aOFT", address(this)) + ) + ); + + bOFT = AbraOFTUpgradeable( + _deployContractAndProxy( + type(AbraOFTUpgradeable).creationCode, + abi.encode(address(endpoints[bEid])), + abi.encodeWithSelector(AbraOFTUpgradeable.initialize.selector, "bOFT", "bOFT", address(this)) + ) + ); + + cERC20Mock = new ERC20Mock("cToken", "cToken"); + cOFTAdapter = AbraOFTAdapterUpgradeable( + _deployContractAndProxy( + type(AbraOFTAdapterUpgradeable).creationCode, + abi.encode(address(cERC20Mock), address(endpoints[cEid])), + abi.encodeWithSelector(AbraOFTAdapterUpgradeable.initialize.selector, address(this)) + ) + ); + + // config and wire the ofts + address[] memory ofts = new address[](3); + ofts[0] = address(aOFT); + ofts[1] = address(bOFT); + ofts[2] = address(cOFTAdapter); + this.wireOApps(ofts); + + // mint tokens + deal(address(aOFT), userA, initialBalance, true); + deal(address(bOFT), userB, initialBalance, true); + + cERC20Mock.mint(userC, initialBalance); + + // deploy a universal inspector, can be used by each oft + oAppInspector = new OFTInspectorMock(); + } + + function _deployContractAndProxy( + bytes memory _oappBytecode, + bytes memory _constructorArgs, + bytes memory _initializeArgs + ) internal returns (address addr) { + bytes memory bytecode = bytes.concat(abi.encodePacked(_oappBytecode), _constructorArgs); + assembly { + addr := create(0, add(bytecode, 0x20), mload(bytecode)) + if iszero(extcodesize(addr)) { + revert(0, 0) + } + } + + return address(new TransparentUpgradeableProxy(addr, proxyAdmin, _initializeArgs)); + } + + function test_constructor() public { + assertEq(aOFT.owner(), address(this)); + assertEq(bOFT.owner(), address(this)); + assertEq(cOFTAdapter.owner(), address(this)); + + assertEq(aOFT.balanceOf(userA), initialBalance); + assertEq(bOFT.balanceOf(userB), initialBalance); + assertEq(IERC20(cOFTAdapter.token()).balanceOf(userC), initialBalance); + + assertEq(aOFT.token(), address(aOFT)); + assertEq(bOFT.token(), address(bOFT)); + assertEq(cOFTAdapter.token(), address(cERC20Mock)); + } + + function test_oftVersion() public { + (bytes4 interfaceId, ) = aOFT.oftVersion(); + bytes4 expectedId = 0x02e49c2c; + assertEq(interfaceId, expectedId); + } + + function test_send_oft() public { + uint256 tokensToSend = 1 ether; + bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 0); + SendParam memory sendParam = SendParam( + bEid, + addressToBytes32(userB), + tokensToSend, + tokensToSend, + options, + "", + "" + ); + MessagingFee memory fee = aOFT.quoteSend(sendParam, false); + + assertEq(aOFT.balanceOf(userA), initialBalance); + assertEq(bOFT.balanceOf(userB), initialBalance); + + vm.prank(userA); + aOFT.send{ value: fee.nativeFee }(sendParam, fee, payable(address(this))); + verifyPackets(bEid, addressToBytes32(address(bOFT))); + + assertEq(aOFT.balanceOf(userA), initialBalance - tokensToSend); + assertEq(bOFT.balanceOf(userB), initialBalance + tokensToSend); + } + + function test_feeHandler_revert() public { + FeeHandler feeHandler = new FeeHandler( + 1 ether, + address(new MockAggregator()), + address(this), + QuoteType.Fixed, + address(this) + ); + + // Test unauthorized access + vm.prank(address(0xdead)); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, address(0xdead))); + feeHandler.setFeeTo(address(0x123)); + } + + function test_feeHandler() public { + // Test fixed fee quote type + FeeHandler feeHandler = new FeeHandler( + 1 ether, // fixedNativeFee + address(new MockAggregator()), // aggregator + address(this), // feeTo + QuoteType.Fixed, // quoteType + address(this) // owner + ); + + // Test fixed native fee quote + uint256 nativeFee = feeHandler.quoteNativeFee(bEid, "", "", 0, 0); + assertEq(nativeFee, 1 ether); + + // Test oracle quote type + MockAggregator mockAggregator = new MockAggregator(); + mockAggregator.setAnswer(2000e8); // $2000 per ETH + + feeHandler = new FeeHandler(1 ether, address(mockAggregator), address(this), QuoteType.Oracle, address(this)); + + // Test oracle-based fee quote + nativeFee = feeHandler.quoteNativeFee(bEid, "", "", 0, 0); + // Expected fee should be $1 worth of ETH at $2000/ETH rate + assertEq(nativeFee, 0.0005 ether); + + // Test admin functions + address newFeeTo = address(0x123); + feeHandler.setFeeTo(newFeeTo); + assertEq(feeHandler.feeTo(), newFeeTo); + + uint256 newFixedFee = 2 ether; + feeHandler.setFixedNativeFee(newFixedFee); + assertEq(feeHandler.fixedNativeFee(), newFixedFee); + + // Test quote type change + feeHandler.setQuoteType(QuoteType.Fixed); + assertEq(uint256(feeHandler.quoteType()), uint256(QuoteType.Fixed)); + + // Test USD fee change + uint256 newUsdFee = 2e18; // $2 + feeHandler.setUsdFee(newUsdFee); + assertEq(feeHandler.usdFee(), newUsdFee); + } + + function test_send_with_fee_collection() public { + // Setup fee handler + MockAggregator mockAggregator = new MockAggregator(); + address feeTo = makeAddr("feeTo"); + FeeHandler feeHandler = new FeeHandler(0, address(mockAggregator), feeTo, QuoteType.Oracle, address(this)); + + feeHandler.setUsdFee(1e18); // $1 fee + mockAggregator.setAnswer(2000e8); // $2000 per ETH + + // Set fee handler in OFTs + aOFT.setFeeHandler(address(feeHandler)); + bOFT.setFeeHandler(address(feeHandler)); + + uint256 tokensToSend = 1 ether; + bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(200000, 0); + SendParam memory sendParam = SendParam( + bEid, + addressToBytes32(userB), + tokensToSend, + tokensToSend, + options, + "", + "" + ); + + MessagingFee memory fee = aOFT.quoteSend(sendParam, false); + + uint256 feeHandlerFee = feeHandler.quoteNativeFee(bEid, "", "", fee.nativeFee, fee.lzTokenFee); + + // Record initial balances + uint256 feeToInitialBalance = feeTo.balance; + + // Send tokens + vm.prank(userA); + aOFT.send{ value: fee.nativeFee }(sendParam, fee, payable(address(this))); + verifyPackets(bEid, addressToBytes32(address(bOFT))); + + // Verify token transfers + assertEq(aOFT.balanceOf(userA), initialBalance - tokensToSend, "aOFT balance of userA"); + assertEq(bOFT.balanceOf(userB), initialBalance + tokensToSend, "bOFT balance of userB"); + + // Verify fee collection + assertEq(feeTo.balance - feeToInitialBalance, feeHandlerFee, "feeTo balance"); + } + + function test_upgrade() public { + // Deploy initial implementation + AbraOFTUpgradeable initialImpl = new AbraOFTUpgradeable(address(endpoints[aEid])); + + // Deploy ProxyAdmin + ProxyAdmin admin = new ProxyAdmin(address(this)); + + // Deploy proxy with initialization data + bytes memory initData = abi.encodeWithSelector( + AbraOFTUpgradeable.initialize.selector, + "MyOFT", + "MOFT", + address(this) + ); + + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(initialImpl), + address(admin), + initData + ); + + AbraOFTUpgradeable oft = AbraOFTUpgradeable(address(proxy)); + + // Get initial implementation + address initialImplAddress = admin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(proxy)))); + + // Deploy mock implementation with mint function + AbraOFTUpgradeableMock mockImpl = new AbraOFTUpgradeableMock(address(endpoints[aEid])); + + // Upgrade to mock implementation + vm.prank(address(this)); + admin.upgrade(TransparentUpgradeableProxy(payable(address(proxy))), address(mockImpl)); + + // Verify implementation changed + address newImplAddress = address(admin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(proxy))))); + assertTrue(initialImplAddress != newImplAddress, "Implementation should change"); + assertEq(newImplAddress, address(mockImpl), "Should point to mock implementation"); + + // Test new mint functionality + uint256 balanceBefore = oft.balanceOf(userA); + AbraOFTUpgradeableMock(address(oft)).mint(userA, 100); + assertEq(oft.balanceOf(userA), balanceBefore + 100, "Should mint new tokens"); + + // Deploy new implementation without mint + AbraOFTUpgradeable newImpl = new AbraOFTUpgradeable(address(endpoints[aEid])); + + // Upgrade to new implementation + vm.prank(address(this)); + admin.upgrade(TransparentUpgradeableProxy(payable(address(proxy))), address(newImpl)); + + // Verify final balance remains after upgrade + assertEq(oft.balanceOf(userA), balanceBefore + 100, "Balance should be preserved after upgrade"); + } + + function test_upgrade_revert_unauthorized() public { + // Deploy new implementation + AbraOFTUpgradeable newImpl = new AbraOFTUpgradeable(address(endpoints[aEid])); + + // Deploy ProxyAdmin + ProxyAdmin admin = new ProxyAdmin(address(this)); + + // Try to upgrade from non-admin address + vm.prank(userA); + vm.expectRevert("Ownable: caller is not the owner"); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(address(aOFT))), address(newImpl), ""); + } + + // Helper function to get implementation address + function _getImplementationAddress(address proxy) internal view returns (address implementation) { + // Storage slot for implementation address in EIP-1967 + bytes32 slot = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + assembly { + implementation := sload(slot) + } + } +} + +contract AbraOFTUpgradeableMock is AbraOFTUpgradeable { + constructor(address _lzEndpoint) AbraOFTUpgradeable(_lzEndpoint) {} + + function mint(address _to, uint256 _amount) external { + _mint(_to, _amount); + } +} diff --git a/test/Counter.t.sol b/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/test/MyOFTUpgradeable.test.ts b/test/MyOFTUpgradeable.test.ts deleted file mode 100644 index 739075c..0000000 --- a/test/MyOFTUpgradeable.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { Contract, ContractFactory } from 'ethers' -import { deployments, ethers, upgrades } from 'hardhat' - -describe('MyOFTUpgradeable Test', () => { - // Constant representing a mock Endpoint ID for testing purposes - const eidA = 1 - const eidB = 2 - // Declaration of variables to be used in the test suite - let MyOFT: ContractFactory - let EndpointV2Mock: ContractFactory - let ownerA: SignerWithAddress - let ownerB: SignerWithAddress - let endpointOwner: SignerWithAddress - let myOFTA: Contract - let myOFTB: Contract - let mockEndpointV2A: Contract - let mockEndpointV2B: Contract - - before(async function () { - MyOFT = await ethers.getContractFactory('AbraOFTUpgradeable') - - // Fetching the first three signers (accounts) from Hardhat's local Ethereum network - const signers = await ethers.getSigners() - - ;[ownerA, ownerB, endpointOwner] = signers - - // The EndpointV2Mock contract comes from @layerzerolabs/test-devtools-evm-hardhat package - // and its artifacts are connected as external artifacts to this project - // - // Unfortunately, hardhat itself does not yet provide a way of connecting external artifacts, - // so we rely on hardhat-deploy to create a ContractFactory for EndpointV2Mock - // - // See https://github.com/NomicFoundation/hardhat/issues/1040 - const EndpointV2MockArtifact = await deployments.getArtifact('EndpointV2Mock') - EndpointV2Mock = new ContractFactory(EndpointV2MockArtifact.abi, EndpointV2MockArtifact.bytecode, endpointOwner) - }) - - // beforeEach hook for setup that runs before each test in the block - beforeEach(async function () { - // Deploying a mock LZEndpoint with the given Endpoint ID - mockEndpointV2A = await EndpointV2Mock.deploy(eidA) - mockEndpointV2B = await EndpointV2Mock.deploy(eidB) - - // Deploying two instances of MyOFT contract with different identifiers and linking them to the mock LZEndpoint - myOFTA = await MyOFT.deploy(mockEndpointV2A.address) - myOFTB = await MyOFT.deploy(mockEndpointV2B.address) - - // Setting destination endpoints in the LZEndpoint mock for each MyOFT instance - await mockEndpointV2A.setDestLzEndpoint(myOFTB.address, mockEndpointV2B.address) - await mockEndpointV2B.setDestLzEndpoint(myOFTA.address, mockEndpointV2A.address) - }) - - it('should upgrade', async () => { - // Deploying the upgradeable contract - const MyOFTUpgradeable = await ethers.getContractFactory('AbraOFTUpgradeable') - const myOFTUpgradeable = await upgrades.deployProxy(MyOFTUpgradeable, ['MyOFT', 'MOFT', ownerA.address], { - initializer: 'initialize', - constructorArgs: [mockEndpointV2A.address], - unsafeAllow: ['constructor', 'state-variable-immutable'], - }) - const myOFTUpgradeableImpl = (await upgrades.admin.getInstance(ownerA)).functions.getProxyImplementation( - myOFTUpgradeable.address - ) - - // Upgrade the contract to the mock, so it has a "mint" function - const MyOFTUpgradeableMock = await ethers.getContractFactory('AbraOFTUpgradeableMock') - const myOFTUpgradeableMock = await upgrades.upgradeProxy(myOFTUpgradeable.address, MyOFTUpgradeableMock, { - constructorArgs: [mockEndpointV2A.address], - unsafeAllow: ['constructor', 'state-variable-immutable'], - }) - - // Ensure the proxy remains constant after the upgrade - expect(myOFTUpgradeable.address).to.equal(myOFTUpgradeableMock.address) - const myOFTUpgradeableMockImpl = (await upgrades.admin.getInstance(ownerA)).functions.getProxyImplementation( - myOFTUpgradeableMock.address - ) - // ensure the implementation address changed - expect(myOFTUpgradeableImpl).to.not.equal(myOFTUpgradeableMockImpl) - const [intialBalance] = await myOFTUpgradeableMock.functions.balanceOf(ownerA.address) - // ensure we can mint now - await (await myOFTUpgradeableMock.functions.mint(ownerA.address, 100)).wait() - const [finalBalance] = await myOFTUpgradeableMock.functions.balanceOf(ownerA.address) - expect(finalBalance.toNumber()).to.equal(intialBalance.add(100).toNumber()) - - // Downgrade the contract to remove mint - const myOFTUpgradeableAgain = await upgrades.upgradeProxy(myOFTUpgradeableMock.address, MyOFTUpgradeable, { - constructorArgs: [mockEndpointV2A.address], - unsafeAllow: ['constructor', 'state-variable-immutable'], - }) - // Ensure the proxy remains constant after the upgrade - expect(myOFTUpgradeableMock.address).to.equal(myOFTUpgradeableAgain.address) - // Ensure that the tokens don't disappear into thin air - const [postUpgradeBalance] = await myOFTUpgradeableMock.functions.balanceOf(ownerA.address) - expect(postUpgradeBalance.toNumber()).to.equal(finalBalance.toNumber()) - }) -}) diff --git a/contracts/mocks/ERC20Mock.sol b/test/mocks/ERC20Mock.sol similarity index 100% rename from contracts/mocks/ERC20Mock.sol rename to test/mocks/ERC20Mock.sol diff --git a/contracts/mocks/MockAggregator.sol b/test/mocks/MockAggregator.sol similarity index 99% rename from contracts/mocks/MockAggregator.sol rename to test/mocks/MockAggregator.sol index b12d3e4..ae809a6 100644 --- a/contracts/mocks/MockAggregator.sol +++ b/test/mocks/MockAggregator.sol @@ -12,4 +12,4 @@ contract MockAggregator { function latestAnswer() external view returns (int256) { return answer; } -} \ No newline at end of file +}