From 9e4ae67d647d092c5c830b9938ce6bb12c48f9e6 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 27 Jun 2022 12:48:11 -0300 Subject: [PATCH] Add Pauser role (#17) * Add Pauser role * Add tests * Guard for pauserAddress == 0 --- .../contracts/ZetaConnector.base.sol | 36 ++++++++-- .../contracts/ZetaConnector.eth.sol | 5 +- .../contracts/ZetaConnector.non-eth.sol | 5 +- .../contracts/interfaces/ConnectorErrors.sol | 2 + .../test/Zeta.non-eth.spec.ts | 5 +- .../test/ZetaConnector.spec.ts | 72 ++++++++++++++----- 6 files changed, 97 insertions(+), 28 deletions(-) diff --git a/packages/protocol-contracts/contracts/ZetaConnector.base.sol b/packages/protocol-contracts/contracts/ZetaConnector.base.sol index 242c5db3..5a574ad2 100644 --- a/packages/protocol-contracts/contracts/ZetaConnector.base.sol +++ b/packages/protocol-contracts/contracts/ZetaConnector.base.sol @@ -10,6 +10,8 @@ import "./interfaces/ZetaInterfaces.sol"; contract ZetaConnectorBase is ConnectorErrors, Pausable { address public zetaToken; + address public pauserAddress; + /** * @dev Collectively held by Zeta blockchain validators. */ @@ -20,8 +22,8 @@ contract ZetaConnectorBase is ConnectorErrors, Pausable { event ZetaSent( address sourceTxOriginAddress, address indexed zetaTxSenderAddress, - uint256 destinationChainId, - bytes destinationAddress, + uint256 indexed destinationChainId, + bytes indexed destinationAddress, uint256 zetaValueAndGas, uint256 destinationGasLimit, bytes message, @@ -49,18 +51,32 @@ contract ZetaConnectorBase is ConnectorErrors, Pausable { event TSSAddressUpdated(address zetaTxSenderAddress, address newTssAddress); + event PauserAddressUpdated(address updaterAddress, address newTssAddress); + constructor( address zetaToken_, address tssAddress_, - address tssAddressUpdater_ + address tssAddressUpdater_, + address pauserAddress_ ) { - if (zetaToken_ == address(0) || tssAddress_ == address(0) || tssAddressUpdater_ == address(0)) { + if ( + zetaToken_ == address(0) || + tssAddress_ == address(0) || + tssAddressUpdater_ == address(0) || + pauserAddress_ == address(0) + ) { revert InvalidAddress(); } zetaToken = zetaToken_; tssAddress = tssAddress_; tssAddressUpdater = tssAddressUpdater_; + pauserAddress = pauserAddress_; + } + + modifier onlyPauser() { + if (msg.sender != pauserAddress) revert CallerIsNotPauser(msg.sender); + _; } modifier onlyTssAddress() { @@ -73,6 +89,14 @@ contract ZetaConnectorBase is ConnectorErrors, Pausable { _; } + function updatePauserAddress(address pauserAddress_) external onlyPauser { + if (pauserAddress_ == address(0)) revert InvalidAddress(); + + pauserAddress = pauserAddress_; + + emit PauserAddressUpdated(msg.sender, pauserAddress_); + } + function updateTssAddress(address tssAddress_) external { if (msg.sender != tssAddress && msg.sender != tssAddressUpdater) revert CallerIsNotTssOrUpdater(msg.sender); if (tssAddress_ == address(0)) revert InvalidAddress(); @@ -91,11 +115,11 @@ contract ZetaConnectorBase is ConnectorErrors, Pausable { tssAddressUpdater = tssAddress; } - function pause() external onlyTssUpdater { + function pause() external onlyPauser { _pause(); } - function unpause() external onlyTssUpdater { + function unpause() external onlyPauser { _unpause(); } diff --git a/packages/protocol-contracts/contracts/ZetaConnector.eth.sol b/packages/protocol-contracts/contracts/ZetaConnector.eth.sol index 3a17d221..09a32d6d 100644 --- a/packages/protocol-contracts/contracts/ZetaConnector.eth.sol +++ b/packages/protocol-contracts/contracts/ZetaConnector.eth.sol @@ -11,8 +11,9 @@ contract ZetaConnectorEth is ZetaConnectorBase { constructor( address zetaToken_, address tssAddress_, - address tssAddressUpdater_ - ) ZetaConnectorBase(zetaToken_, tssAddress_, tssAddressUpdater_) {} + address tssAddressUpdater_, + address pauserAddress_ + ) ZetaConnectorBase(zetaToken_, tssAddress_, tssAddressUpdater_, pauserAddress_) {} function getLockedAmount() external view returns (uint256) { return IERC20(zetaToken).balanceOf(address(this)); diff --git a/packages/protocol-contracts/contracts/ZetaConnector.non-eth.sol b/packages/protocol-contracts/contracts/ZetaConnector.non-eth.sol index 428c3917..6eeb6efa 100644 --- a/packages/protocol-contracts/contracts/ZetaConnector.non-eth.sol +++ b/packages/protocol-contracts/contracts/ZetaConnector.non-eth.sol @@ -22,8 +22,9 @@ contract ZetaConnectorNonEth is ZetaConnectorBase { constructor( address zetaTokenAddress_, address tssAddress_, - address tssAddressUpdater_ - ) ZetaConnectorBase(zetaTokenAddress_, tssAddress_, tssAddressUpdater_) {} + address tssAddressUpdater_, + address pauserAddress_ + ) ZetaConnectorBase(zetaTokenAddress_, tssAddress_, tssAddressUpdater_, pauserAddress_) {} function getLockedAmount() external view returns (uint256) { return ZetaToken(zetaToken).balanceOf(address(this)); diff --git a/packages/protocol-contracts/contracts/interfaces/ConnectorErrors.sol b/packages/protocol-contracts/contracts/interfaces/ConnectorErrors.sol index fb2a7a39..370b50af 100644 --- a/packages/protocol-contracts/contracts/interfaces/ConnectorErrors.sol +++ b/packages/protocol-contracts/contracts/interfaces/ConnectorErrors.sol @@ -2,6 +2,8 @@ pragma solidity 0.8.7; interface ConnectorErrors { + error CallerIsNotPauser(address caller); + error CallerIsNotTss(address caller); error CallerIsNotTssUpdater(address caller); diff --git a/packages/protocol-contracts/test/Zeta.non-eth.spec.ts b/packages/protocol-contracts/test/Zeta.non-eth.spec.ts index 28545b76..18e0a6e2 100644 --- a/packages/protocol-contracts/test/Zeta.non-eth.spec.ts +++ b/packages/protocol-contracts/test/Zeta.non-eth.spec.ts @@ -13,6 +13,7 @@ describe("ZetaNonEth tests", () => { let tssUpdater: SignerWithAddress; let tssSigner: SignerWithAddress; let randomSigner: SignerWithAddress; + let pauserSigner: SignerWithAddress; const tssUpdaterApproveConnectorNonEth = async () => { await (await zetaTokenNonEthContract.approve(zetaConnectorNonEthContract.address, parseEther("100000"))).wait(); @@ -36,7 +37,7 @@ describe("ZetaNonEth tests", () => { beforeEach(async () => { const accounts = await ethers.getSigners(); - [tssUpdater, tssSigner, randomSigner] = accounts; + [tssUpdater, tssSigner, randomSigner, pauserSigner] = accounts; zetaTokenNonEthContract = await deployZetaNonEth({ args: [tssSigner.address, tssUpdater.address], @@ -44,7 +45,7 @@ describe("ZetaNonEth tests", () => { zetaReceiverMockContract = await deployZetaReceiverMock(); zetaConnectorNonEthContract = await deployZetaConnectorNonEth({ - args: [zetaTokenNonEthContract.address, tssSigner.address, tssUpdater.address], + args: [zetaTokenNonEthContract.address, tssSigner.address, tssUpdater.address, pauserSigner.address], }); await zetaTokenNonEthContract.updateTssAndConnectorAddresses( diff --git a/packages/protocol-contracts/test/ZetaConnector.spec.ts b/packages/protocol-contracts/test/ZetaConnector.spec.ts index 46dfb845..2a061324 100644 --- a/packages/protocol-contracts/test/ZetaConnector.spec.ts +++ b/packages/protocol-contracts/test/ZetaConnector.spec.ts @@ -30,6 +30,7 @@ describe("ZetaConnector tests", () => { let tssUpdater: SignerWithAddress; let tssSigner: SignerWithAddress; let randomSigner: SignerWithAddress; + let pauserSigner: SignerWithAddress; const tssUpdaterApproveConnectorEth = async () => { await (await zetaTokenEthContract.approve(zetaConnectorEthContract.address, parseEther("100000"))).wait(); @@ -61,7 +62,7 @@ describe("ZetaConnector tests", () => { beforeEach(async () => { const accounts = await ethers.getSigners(); - [tssUpdater, tssSigner, randomSigner] = accounts; + [tssUpdater, tssSigner, randomSigner, pauserSigner] = accounts; zetaTokenEthContract = await deployZetaEth({ args: [100_000], @@ -73,13 +74,13 @@ describe("ZetaConnector tests", () => { zetaReceiverMockContract = await deployZetaReceiverMock(); zetaConnectorBaseContract = await deployZetaConnectorBase({ - args: [zetaTokenEthContract.address, tssSigner.address, tssUpdater.address], + args: [zetaTokenEthContract.address, tssSigner.address, tssUpdater.address, pauserSigner.address], }); zetaConnectorEthContract = await deployZetaConnectorEth({ - args: [zetaTokenEthContract.address, tssSigner.address, tssUpdater.address], + args: [zetaTokenEthContract.address, tssSigner.address, tssUpdater.address, pauserSigner.address], }); zetaConnectorNonEthContract = await deployZetaConnectorNonEth({ - args: [zetaTokenNonEthContract.address, tssSigner.address, tssUpdater.address], + args: [zetaTokenNonEthContract.address, tssSigner.address, tssUpdater.address, pauserSigner.address], }); await zetaTokenNonEthContract.updateTssAndConnectorAddresses( @@ -121,23 +122,62 @@ describe("ZetaConnector tests", () => { }); }); + describe("updatePauserAddress", () => { + it("Should revert if the caller is not the Pauser", async () => { + await expect( + zetaConnectorBaseContract.connect(randomSigner).updatePauserAddress(randomSigner.address) + ).to.revertedWith(`CallerIsNotPauser("${randomSigner.address}")`); + }); + + it("Should revert if the new Pauser address is invalid", async () => { + await expect( + zetaConnectorBaseContract + .connect(pauserSigner) + .updatePauserAddress("0x0000000000000000000000000000000000000000") + ).to.revertedWith(`InvalidAddress()`); + }); + + it("Should change the Pauser address if called by Pauser", async () => { + await (await zetaConnectorBaseContract.connect(pauserSigner).updatePauserAddress(randomSigner.address)).wait(); + + const address = await zetaConnectorBaseContract.pauserAddress(); + + expect(address).to.equal(randomSigner.address); + }); + + it("Should emit `PauserAddressUpdated` on success", async () => { + const pauserAddressUpdatedFilter = zetaConnectorBaseContract.filters.PauserAddressUpdated(); + const e1 = await zetaConnectorBaseContract.queryFilter(pauserAddressUpdatedFilter); + expect(e1.length).to.equal(0); + + await (await zetaConnectorBaseContract.connect(pauserSigner).updatePauserAddress(randomSigner.address)).wait(); + + const address = await zetaConnectorBaseContract.pauserAddress(); + + expect(address).to.equal(randomSigner.address); + + const e2 = await zetaConnectorBaseContract.queryFilter(pauserAddressUpdatedFilter); + expect(e2.length).to.equal(1); + }); + }); + describe("pause, unpause", () => { - it("Should revert if not called by the TSS updater", async () => { + it("Should revert if not called by the Pauser", async () => { await expect(zetaConnectorBaseContract.connect(randomSigner).pause()).to.revertedWith( - `CallerIsNotTssUpdater("${randomSigner.address}")` + `CallerIsNotPauser("${randomSigner.address}")` ); await expect(zetaConnectorBaseContract.connect(randomSigner).unpause()).to.revertedWith( - `CallerIsNotTssUpdater("${randomSigner.address}")` + `CallerIsNotPauser("${randomSigner.address}")` ); }); - it("Should pause if called by the TSS updater", async () => { - await (await zetaConnectorBaseContract.pause()).wait(); + it("Should pause if called by the Pauser", async () => { + await (await zetaConnectorBaseContract.connect(pauserSigner).pause()).wait(); const paused1 = await zetaConnectorBaseContract.paused(); expect(paused1).to.equal(true); - await (await zetaConnectorBaseContract.unpause()).wait(); + await (await zetaConnectorBaseContract.connect(pauserSigner).unpause()).wait(); const paused2 = await zetaConnectorBaseContract.paused(); expect(paused2).to.equal(false); }); @@ -147,7 +187,7 @@ describe("ZetaConnector tests", () => { describe("ZetaConnector.eth", () => { describe("send", () => { it("Should revert if the contract is paused", async () => { - await (await zetaConnectorEthContract.pause()).wait(); + await (await zetaConnectorEthContract.connect(pauserSigner).pause()).wait(); const paused1 = await zetaConnectorEthContract.paused(); expect(paused1).to.equal(true); @@ -259,7 +299,7 @@ describe("ZetaConnector tests", () => { describe("onReceive", () => { it("Should revert if the contract is paused", async () => { - await (await zetaConnectorEthContract.pause()).wait(); + await (await zetaConnectorEthContract.connect(pauserSigner).pause()).wait(); const paused1 = await zetaConnectorEthContract.paused(); expect(paused1).to.equal(true); @@ -358,7 +398,7 @@ describe("ZetaConnector tests", () => { describe("onRevert", () => { it("Should revert if the contract is paused", async () => { - await (await zetaConnectorEthContract.pause()).wait(); + await (await zetaConnectorEthContract.connect(pauserSigner).pause()).wait(); const paused1 = await zetaConnectorEthContract.paused(); expect(paused1).to.equal(true); @@ -448,7 +488,7 @@ describe("ZetaConnector tests", () => { describe("ZetaConnector.non-eth", () => { describe("send", () => { it("Should revert if the contract is paused", async () => { - await (await zetaConnectorNonEthContract.pause()).wait(); + await (await zetaConnectorNonEthContract.connect(pauserSigner).pause()).wait(); const paused1 = await zetaConnectorNonEthContract.paused(); expect(paused1).to.equal(true); @@ -554,7 +594,7 @@ describe("ZetaConnector tests", () => { describe("onReceive", () => { it("Should revert if the contract is paused", async () => { - await (await zetaConnectorNonEthContract.pause()).wait(); + await (await zetaConnectorNonEthContract.connect(pauserSigner).pause()).wait(); const paused1 = await zetaConnectorNonEthContract.paused(); expect(paused1).to.equal(true); @@ -650,7 +690,7 @@ describe("ZetaConnector tests", () => { describe("onRevert", () => { it("Should revert if the contract is paused", async () => { - await (await zetaConnectorNonEthContract.pause()).wait(); + await (await zetaConnectorNonEthContract.connect(pauserSigner).pause()).wait(); const paused1 = await zetaConnectorNonEthContract.paused(); expect(paused1).to.equal(true);