Skip to content

Commit e4c965b

Browse files
authored
Merge pull request #13 from darcys22/pausable-token-converter
add pause functionality
2 parents 55d1125 + 76c8119 commit e4c965b

File tree

2 files changed

+88
-3
lines changed

2 files changed

+88
-3
lines changed

contracts/utils/TokenConverter.sol

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ pragma solidity ^0.8.26;
44
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
55
import "@openzeppelin/contracts/access/Ownable.sol";
66
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7+
import "@openzeppelin/contracts/utils/Pausable.sol";
78

8-
contract TokenConverter is Ownable, ReentrancyGuard {
9+
contract TokenConverter is Ownable, ReentrancyGuard, Pausable {
910
using SafeERC20 for IERC20;
1011

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

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

56-
function convertTokens(uint256 _amountA) external nonReentrant {
57+
function convertTokens(uint256 _amountA) external nonReentrant whenNotPaused {
5758
require(_amountA > 0, "Amount must be greater than 0");
5859
uint256 amountB = (_amountA * conversionRateNumerator) / conversionRateDenominator;
5960
require(tokenB.balanceOf(address(this)) >= amountB, "Insufficient Token B in contract");
6061
emit Conversion(msg.sender, _amountA, amountB);
6162
tokenA.safeTransferFrom(msg.sender, address(this), _amountA);
6263
tokenB.safeTransfer(msg.sender, amountB);
6364
}
65+
66+
function pause() external onlyOwner {
67+
_pause();
68+
}
69+
70+
function unpause() external onlyOwner {
71+
_unpause();
72+
}
6473
}

test/unit-js/TokenConverter.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,80 @@ describe("TokenConverter Contract Tests", function () {
166166
.to.be.revertedWith("Insufficient Token B in contract");
167167
});
168168
});
169+
170+
describe("Pausable functionality", function () {
171+
beforeEach(async function () {
172+
// Ensure contract is seeded with TokenB for conversion tests
173+
let testAmountInContract = ethers.parseUnits("10000", decimalsTokenB);
174+
await tokenBERC20.approve(tokenConverter, bigAtomicTestAmountInContract);
175+
await tokenConverter.depositTokenB(bigAtomicTestAmountInContract);
176+
});
177+
178+
it("Should allow owner to pause and unpause", async function () {
179+
expect(await tokenConverter.paused()).to.be.false;
180+
await expect(tokenConverter.connect(owner).pause())
181+
.to.emit(tokenConverter, "Paused")
182+
.withArgs(owner.address);
183+
expect(await tokenConverter.paused()).to.be.true;
184+
await expect(tokenConverter.connect(owner).unpause())
185+
.to.emit(tokenConverter, "Unpaused")
186+
.withArgs(owner.address);
187+
expect(await tokenConverter.paused()).to.be.false;
188+
});
189+
190+
it("Should not allow non-owner to pause or unpause", async function () {
191+
await expect(tokenConverter.connect(user).pause())
192+
.to.be.revertedWithCustomError(tokenConverter, "OwnableUnauthorizedAccount")
193+
.withArgs(user.address);
194+
await expect(tokenConverter.connect(user).unpause())
195+
.to.be.revertedWithCustomError(tokenConverter, "OwnableUnauthorizedAccount")
196+
.withArgs(user.address);
197+
});
198+
199+
it("convertTokens should revert when paused", async function () {
200+
await tokenConverter.connect(owner).pause(); // Pause the contract
201+
expect(await tokenConverter.paused()).to.be.true;
202+
await expect(tokenConverter.connect(user).convertTokens(bigAtomicTestAmount))
203+
.to.be.revertedWithCustomError(tokenConverter, "EnforcedPause");
204+
});
205+
206+
it("convertTokens should work when unpaused", async function () {
207+
await tokenConverter.connect(owner).pause(); // Pause
208+
expect(await tokenConverter.paused()).to.be.true;
209+
await tokenConverter.connect(owner).unpause(); // Unpause
210+
expect(await tokenConverter.paused()).to.be.false;
211+
212+
const initialTokenBBalance = await tokenBERC20.balanceOf(user.address);
213+
await tokenConverter.connect(user).convertTokens(bigAtomicTestAmount);
214+
const amountB = bigAtomicTestAmount * firstRate.numerator / firstRate.denominator;
215+
expect(await tokenBERC20.balanceOf(user.address)).to.equal(initialTokenBBalance + amountB);
216+
});
217+
218+
it("depositTokenB should still work when paused", async function () {
219+
await tokenConverter.connect(owner).pause(); // Pause the contract
220+
expect(await tokenConverter.paused()).to.be.true;
221+
222+
const depositAmount = ethers.parseUnits("100", decimalsTokenB);
223+
const initialContractTokenBBalance = await tokenBERC20.balanceOf(tokenConverter.getAddress());
224+
225+
await tokenBERC20.connect(owner).approve(tokenConverter.getAddress(), depositAmount);
226+
await expect(tokenConverter.connect(owner).depositTokenB(depositAmount)).to.not.be.reverted;
227+
228+
expect(await tokenBERC20.balanceOf(tokenConverter.getAddress())).to.equal(initialContractTokenBBalance + depositAmount);
229+
});
230+
231+
it("withdrawTokenB should still work when paused", async function () {
232+
// Deposit some tokens first to withdraw
233+
const depositAmount = ethers.parseUnits("100", decimalsTokenB);
234+
await tokenBERC20.connect(owner).approve(tokenConverter.getAddress(), depositAmount);
235+
await tokenConverter.connect(owner).depositTokenB(depositAmount);
236+
const initialOwnerTokenBBalance = await tokenBERC20.balanceOf(owner.address);
237+
238+
await tokenConverter.connect(owner).pause(); // Pause the contract
239+
expect(await tokenConverter.paused()).to.be.true;
240+
241+
await expect(tokenConverter.connect(owner).withdrawTokenB(depositAmount)).to.not.be.reverted;
242+
expect(await tokenBERC20.balanceOf(owner.address)).to.equal(initialOwnerTokenBBalance + depositAmount);
243+
});
244+
});
169245
});

0 commit comments

Comments
 (0)