Skip to content

add pause functionality #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions contracts/utils/TokenConverter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ pragma solidity ^0.8.26;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";

contract TokenConverter is Ownable, ReentrancyGuard {
contract TokenConverter is Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;

IERC20 public immutable tokenA;
Expand All @@ -23,7 +24,7 @@ contract TokenConverter is Ownable, ReentrancyGuard {
address _tokenB,
uint256 _initialNumerator,
uint256 _initialDenominator
) Ownable(msg.sender) {
) Ownable(msg.sender) Pausable() {
require(_tokenA != address(0) && _tokenB != address(0), "Invalid token address");
require(_initialNumerator > 0 && _initialDenominator > 0, "Conversion rate must be greater than 0");

Expand Down Expand Up @@ -53,12 +54,20 @@ contract TokenConverter is Ownable, ReentrancyGuard {
tokenB.safeTransfer(msg.sender, _amount);
}

function convertTokens(uint256 _amountA) external nonReentrant {
function convertTokens(uint256 _amountA) external nonReentrant whenNotPaused {
require(_amountA > 0, "Amount must be greater than 0");
uint256 amountB = (_amountA * conversionRateNumerator) / conversionRateDenominator;
require(tokenB.balanceOf(address(this)) >= amountB, "Insufficient Token B in contract");
emit Conversion(msg.sender, _amountA, amountB);
tokenA.safeTransferFrom(msg.sender, address(this), _amountA);
tokenB.safeTransfer(msg.sender, amountB);
}

function pause() external onlyOwner {
_pause();
}

function unpause() external onlyOwner {
_unpause();
}
}
76 changes: 76 additions & 0 deletions test/unit-js/TokenConverter.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,80 @@ describe("TokenConverter Contract Tests", function () {
.to.be.revertedWith("Insufficient Token B in contract");
});
});

describe("Pausable functionality", function () {
beforeEach(async function () {
// Ensure contract is seeded with TokenB for conversion tests
let testAmountInContract = ethers.parseUnits("10000", decimalsTokenB);
await tokenBERC20.approve(tokenConverter, bigAtomicTestAmountInContract);
await tokenConverter.depositTokenB(bigAtomicTestAmountInContract);
});

it("Should allow owner to pause and unpause", async function () {
expect(await tokenConverter.paused()).to.be.false;
await expect(tokenConverter.connect(owner).pause())
.to.emit(tokenConverter, "Paused")
.withArgs(owner.address);
expect(await tokenConverter.paused()).to.be.true;
await expect(tokenConverter.connect(owner).unpause())
.to.emit(tokenConverter, "Unpaused")
.withArgs(owner.address);
expect(await tokenConverter.paused()).to.be.false;
});

it("Should not allow non-owner to pause or unpause", async function () {
await expect(tokenConverter.connect(user).pause())
.to.be.revertedWithCustomError(tokenConverter, "OwnableUnauthorizedAccount")
.withArgs(user.address);
await expect(tokenConverter.connect(user).unpause())
.to.be.revertedWithCustomError(tokenConverter, "OwnableUnauthorizedAccount")
.withArgs(user.address);
});

it("convertTokens should revert when paused", async function () {
await tokenConverter.connect(owner).pause(); // Pause the contract
expect(await tokenConverter.paused()).to.be.true;
await expect(tokenConverter.connect(user).convertTokens(bigAtomicTestAmount))
.to.be.revertedWithCustomError(tokenConverter, "EnforcedPause");
});

it("convertTokens should work when unpaused", async function () {
await tokenConverter.connect(owner).pause(); // Pause
expect(await tokenConverter.paused()).to.be.true;
await tokenConverter.connect(owner).unpause(); // Unpause
expect(await tokenConverter.paused()).to.be.false;

const initialTokenBBalance = await tokenBERC20.balanceOf(user.address);
await tokenConverter.connect(user).convertTokens(bigAtomicTestAmount);
const amountB = bigAtomicTestAmount * firstRate.numerator / firstRate.denominator;
expect(await tokenBERC20.balanceOf(user.address)).to.equal(initialTokenBBalance + amountB);
});

it("depositTokenB should still work when paused", async function () {
await tokenConverter.connect(owner).pause(); // Pause the contract
expect(await tokenConverter.paused()).to.be.true;

const depositAmount = ethers.parseUnits("100", decimalsTokenB);
const initialContractTokenBBalance = await tokenBERC20.balanceOf(tokenConverter.getAddress());

await tokenBERC20.connect(owner).approve(tokenConverter.getAddress(), depositAmount);
await expect(tokenConverter.connect(owner).depositTokenB(depositAmount)).to.not.be.reverted;

expect(await tokenBERC20.balanceOf(tokenConverter.getAddress())).to.equal(initialContractTokenBBalance + depositAmount);
});

it("withdrawTokenB should still work when paused", async function () {
// Deposit some tokens first to withdraw
const depositAmount = ethers.parseUnits("100", decimalsTokenB);
await tokenBERC20.connect(owner).approve(tokenConverter.getAddress(), depositAmount);
await tokenConverter.connect(owner).depositTokenB(depositAmount);
const initialOwnerTokenBBalance = await tokenBERC20.balanceOf(owner.address);

await tokenConverter.connect(owner).pause(); // Pause the contract
expect(await tokenConverter.paused()).to.be.true;

await expect(tokenConverter.connect(owner).withdrawTokenB(depositAmount)).to.not.be.reverted;
expect(await tokenBERC20.balanceOf(owner.address)).to.equal(initialOwnerTokenBBalance + depositAmount);
});
});
});