Skip to content

Commit

Permalink
Merge pull request #18 from lombard-finance/dev
Browse files Browse the repository at this point in the history
Fix decimals
  • Loading branch information
hashxtree authored Oct 4, 2024
2 parents 94ad806 + bf0ba85 commit af6858a
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 43 deletions.
9 changes: 7 additions & 2 deletions contracts/mock/WBTCMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ERC20Upgradeable, IERC20 } from "@openzeppelin/contracts-upgradeable/to
* @notice Use only for testing
*/
contract WBTCMock is ERC20Upgradeable {

uint8 _decimals;
/// @dev https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
Expand All @@ -19,13 +19,18 @@ contract WBTCMock is ERC20Upgradeable {

function initialize() external initializer {
__ERC20_init("Wrapped BTC Mock", "WBTCMOCK");
_decimals = 8;
}

function setDecimals(uint8 decimals_) external {
_decimals = decimals_;
}

function mint(address to, uint256 amount) external {
_mint(to, amount);
}

function decimals() public override view virtual returns (uint8) {
return 8;
return _decimals;
}
}
33 changes: 20 additions & 13 deletions contracts/pmm/BTCB/BTCB.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ pragma solidity 0.8.24;

import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

interface IMinteable {
interface ILBTC {
function mint(address to, uint256 amount) external;
function decimals() external view returns (uint256);
}

contract BTCBPMM is PausableUpgradeable, AccessControlUpgradeable {
using SafeERC20 for IERC20;
using SafeERC20 for IERC20Metadata;

struct PMMStorage {
IERC20 btcb;
IMinteable lbtc;
IERC20Metadata btcb;
ILBTC lbtc;

uint256 stakeLimit;
uint256 totalStake;
Expand All @@ -31,7 +31,7 @@ contract BTCBPMM is PausableUpgradeable, AccessControlUpgradeable {

error StakeLimitExceeded();
error UnauthorizedAccount(address account);

error ZeroAmount();
event StakeLimitSet(uint256 newStakeLimit);
event WithdrawalAddressSet(address newWithdrawAddress);

Expand All @@ -48,8 +48,8 @@ contract BTCBPMM is PausableUpgradeable, AccessControlUpgradeable {
$.stakeLimit = _stakeLimit;
$.withdrawAddress = withdrawAddress;

$.lbtc = IMinteable(_lbtc);
$.btcb = IERC20(_btcb);
$.lbtc = ILBTC(_lbtc);
$.btcb = IERC20Metadata(_btcb);
}

function initialize(address _lbtc, address _btcb, address admin,uint256 _stakeLimit, address withdrawAddress) external initializer {
Expand All @@ -60,11 +60,18 @@ contract BTCBPMM is PausableUpgradeable, AccessControlUpgradeable {

function swapBTCBToLBTC(uint256 amount) external whenNotPaused {
PMMStorage storage $ = _getPMMStorage();
if ($.totalStake + amount > $.stakeLimit) revert StakeLimitExceeded();

$.totalStake += amount;
$.btcb.safeTransferFrom(_msgSender(), address(this), amount);
$.lbtc.mint(_msgSender(), amount);
ILBTC lbtc = $.lbtc;
IERC20Metadata btcb = $.btcb;

uint256 decimalsDifference = 10 ** (btcb.decimals() - lbtc.decimals());
uint256 amountLBTC = (amount / decimalsDifference);
if(amountLBTC == 0) revert ZeroAmount();
if ($.totalStake + amountLBTC > $.stakeLimit) revert StakeLimitExceeded();

$.totalStake += amountLBTC;
btcb.safeTransferFrom(_msgSender(), address(this), amountLBTC * decimalsDifference);
lbtc.mint(_msgSender(), amountLBTC);
}

function withdrawBTCB(uint256 amount) external whenNotPaused onlyRole(DEFAULT_ADMIN_ROLE) {
Expand Down
65 changes: 37 additions & 28 deletions test/BTCBPMM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ describe("BTCBPMM", function () {
await lbtc.getAddress(),
await btcb.getAddress(),
await deployer.getAddress(),
30,
ethers.parseUnits("30", 8),
await withdrawalAddress.getAddress()
]);

// use btcb decimals of 8
await btcb.setDecimals(18);

snapshot = await takeSnapshot();
});

Expand Down Expand Up @@ -135,56 +138,62 @@ describe("BTCBPMM", function () {
await pmm.grantRole(await pmm.TIMELOCK_ROLE(), await timeLock.getAddress());

// some btcb for signers
await btcb.mint(await signer1.getAddress(), 100);
await btcb.mint(await signer2.getAddress(), 100);
await btcb.mint(await signer1.getAddress(), ethers.parseUnits("100", 18));
await btcb.mint(await signer2.getAddress(), ethers.parseUnits("100", 18));
});

it("should fail to swap is PMM is not whitelisted as minter", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), 1);
await expect(pmm.connect(signer1).swapBTCBToLBTC(1))
it("should fail to swap if PMM is not whitelisted as minter", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), ethers.parseUnits("10", 10));
await expect(pmm.connect(signer1).swapBTCBToLBTC(ethers.parseUnits("10", 10)))
.to.be.revertedWithCustomError(lbtc, "UnauthorizedAccount")
.withArgs(await pmm.getAddress());
});

it("should fail to swap if amount is to low and will result in 0 LBTC", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), 1);
await expect(pmm.connect(signer1).swapBTCBToLBTC(1))
.to.be.revertedWithCustomError(lbtc, "ZeroAmount");
});

describe("With whitelisted minter", function () {
beforeEach(async function () {
await lbtc.addMinter(await pmm.getAddress());
});

it("should allow swaps", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), 1);
await expect(pmm.connect(signer1).swapBTCBToLBTC(1))
await btcb.connect(signer1).approve(await pmm.getAddress(), ethers.parseUnits("11", 18) + 10n);
await expect(pmm.connect(signer1).swapBTCBToLBTC(ethers.parseUnits("11", 18) + 10n))
.to.emit(btcb, "Transfer")
.withArgs(signer1.address, await pmm.getAddress(), 1)
.withArgs(signer1.address, await pmm.getAddress(), ethers.parseUnits("11", 18))
.to.emit(lbtc, "Transfer")
.withArgs(ethers.ZeroAddress, signer1.address, 1);
expect(await pmm.remainingStake()).to.equal(29);
expect(await lbtc.balanceOf(signer1.address)).to.equal(1);
expect(await btcb.balanceOf(signer1.address)).to.equal(99);
expect(await btcb.balanceOf(await pmm.getAddress())).to.equal(1);
.withArgs(ethers.ZeroAddress, signer1.address, ethers.parseUnits("11", 8));
expect(await pmm.remainingStake()).to.equal(ethers.parseUnits("19", 8));
expect(await lbtc.balanceOf(signer1.address)).to.equal(ethers.parseUnits("11", 8));
expect(await btcb.balanceOf(signer1.address)).to.equal(ethers.parseUnits("89", 18));
expect(await btcb.balanceOf(await pmm.getAddress())).to.equal(ethers.parseUnits("11", 18));
});

it("should fail to swap more than limit", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), 35);
await expect(pmm.connect(signer1).swapBTCBToLBTC(35))
await btcb.connect(signer1).approve(await pmm.getAddress(), ethers.parseUnits("35", 18));
await expect(pmm.connect(signer1).swapBTCBToLBTC(ethers.parseUnits("35", 18)))
.to.be.revertedWithCustomError(pmm, "StakeLimitExceeded");
});

it("should allow more swaos if limit is increased", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), 30);
await pmm.connect(signer1).swapBTCBToLBTC(30);
it("should allow more swaps if limit is increased", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), ethers.parseUnits("30", 18));
await pmm.connect(signer1).swapBTCBToLBTC(ethers.parseUnits("30", 18));
expect(await pmm.remainingStake()).to.equal(0);

await pmm.connect(timeLock).setStakeLimit(40);
expect(await pmm.remainingStake()).to.equal(10);
await btcb.connect(signer2).approve(await pmm.getAddress(), 10);
await pmm.connect(signer2).swapBTCBToLBTC(10);
await pmm.connect(timeLock).setStakeLimit(ethers.parseUnits("40", 8));
expect(await pmm.remainingStake()).to.equal(ethers.parseUnits("10", 8));
await btcb.connect(signer2).approve(await pmm.getAddress(), ethers.parseUnits("10", 18));
await pmm.connect(signer2).swapBTCBToLBTC(ethers.parseUnits("10", 18));
expect(await pmm.remainingStake()).to.equal(0);
});

it("should allow withdrawals", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), 30);
await pmm.connect(signer1).swapBTCBToLBTC(30);
await btcb.connect(signer1).approve(await pmm.getAddress(), ethers.parseUnits("30", 18));
await pmm.connect(signer1).swapBTCBToLBTC(ethers.parseUnits("30", 18));

await expect(pmm.withdrawBTCB(1))
.to.emit(btcb, "Transfer")
Expand All @@ -193,10 +202,10 @@ describe("BTCBPMM", function () {
});

it("should have zero remaining stake if total stake is greater than limit", async function () {
await btcb.connect(signer1).approve(await pmm.getAddress(), 30);
await pmm.connect(signer1).swapBTCBToLBTC(30);
await btcb.connect(signer1).approve(await pmm.getAddress(), ethers.parseUnits("30", 18));
await pmm.connect(signer1).swapBTCBToLBTC(ethers.parseUnits("30", 18));

await pmm.connect(timeLock).setStakeLimit(20);
await pmm.connect(timeLock).setStakeLimit(ethers.parseUnits("20", 8));
expect(await pmm.remainingStake()).to.equal(0);
});

Expand Down

0 comments on commit af6858a

Please sign in to comment.