Skip to content

Commit 7eeb197

Browse files
committed
Add tests
1 parent 556d896 commit 7eeb197

File tree

2 files changed

+86
-3
lines changed

2 files changed

+86
-3
lines changed

contracts/LiquidityHub.sol

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import {
88
ERC4626Upgradeable,
99
Math
1010
} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
11+
import {AccessControlUpgradeable} from '@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol';
1112
import {ERC7201Helper} from './utils/ERC7201Helper.sol';
1213
import {IManagedToken} from './interfaces/IManagedToken.sol';
1314

14-
contract LiquidityHub is ERC4626Upgradeable {
15+
contract LiquidityHub is ERC4626Upgradeable, AccessControlUpgradeable {
1516
using Math for uint256;
1617

1718
IManagedToken immutable public SHARES;
19+
bytes32 public constant ASSETS_UPDATE_ROLE = "ASSETS_UPDATE_ROLE";
1820

1921
error ZeroAddress();
2022
error NotImplemented();
@@ -37,14 +39,15 @@ contract LiquidityHub is ERC4626Upgradeable {
3739
_disableInitializers();
3840
}
3941

40-
function initialize(IERC20 asset_) external initializer() {
42+
function initialize(IERC20 asset_, address admin) external initializer() {
4143
ERC4626Upgradeable.__ERC4626_init(asset_);
4244
require(
4345
IERC20Metadata(address(asset_)).decimals() <= IERC20Metadata(address(SHARES)).decimals(),
4446
IncompatibleAssetsAndShares()
4547
);
4648
// Deliberately not initializing ERC20Upgradable because its
4749
// functionality is delegated to SHARES.
50+
_grantRole(DEFAULT_ADMIN_ROLE, admin);
4851
}
4952

5053
function name() public pure override(IERC20Metadata, ERC20Upgradeable) returns (string memory) {
@@ -88,6 +91,28 @@ contract LiquidityHub is ERC4626Upgradeable {
8891
return _getStorage().totalAssets;
8992
}
9093

94+
function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual override returns (uint256) {
95+
(uint256 supplyShares, uint256 supplyAssets) = _getTotals();
96+
return assets.mulDiv(supplyShares, supplyAssets, rounding);
97+
}
98+
99+
function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual override returns (uint256) {
100+
(uint256 supplyShares, uint256 supplyAssets) = _getTotals();
101+
return shares.mulDiv(supplyAssets, supplyShares, rounding);
102+
}
103+
104+
function _getTotals() internal view returns (uint256 supply, uint256 assets) {
105+
supply = totalSupply();
106+
if (supply == 0) {
107+
supply = 10 ** _decimalsOffset();
108+
}
109+
assets = totalAssets();
110+
if (assets == 0) {
111+
assets = 1;
112+
}
113+
return (supply, assets);
114+
}
115+
91116
function _update(address from, address to, uint256 value) internal virtual override {
92117
if (from == address(0)) {
93118
SHARES.mint(to, value);

test/LiquidityHub.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe("LiquidityHub", function () {
2424
const LP = 10n ** (await lpToken.decimals());
2525

2626
const liquidityHubImpl = await deploy("LiquidityHub", deployer, {nonce: startingNonce + 1}, lpToken.target);
27-
const liquidityHubInit = (await liquidityHubImpl.initialize.populateTransaction(usdc.target)).data;
27+
const liquidityHubInit = (await liquidityHubImpl.initialize.populateTransaction(usdc.target, admin.address)).data;
2828
const liquidityHubProxy = await deploy(
2929
"TransparentUpgradeableProxy", deployer, {nonce: startingNonce + 2},
3030
liquidityHubImpl.target, admin, liquidityHubInit
@@ -83,6 +83,34 @@ describe("LiquidityHub", function () {
8383
expect(await usdc.balanceOf(liquidityHub.target)).to.equal(10n * USDC);
8484
});
8585

86+
it("Should allow to deposit twice", async function () {
87+
const {lpToken, liquidityHub, usdc, deployer, user, USDC, LP} = await loadFixture(deployAll);
88+
89+
await usdc.connect(deployer).transfer(user.address, 10n * USDC);
90+
await usdc.connect(user).approve(liquidityHub.target, 10n * USDC);
91+
const tx = liquidityHub.connect(user).deposit(3n * USDC, user.address);
92+
await expect(tx)
93+
.to.emit(lpToken, "Transfer")
94+
.withArgs(ZERO_ADDRESS, user.address, 3n * LP);
95+
await expect(tx)
96+
.to.emit(usdc, "Transfer")
97+
.withArgs(user.address, liquidityHub.target, 3n * USDC);
98+
const tx2 = liquidityHub.connect(user).deposit(7n * USDC, user.address);
99+
await expect(tx2)
100+
.to.emit(lpToken, "Transfer")
101+
.withArgs(ZERO_ADDRESS, user.address, 7n * LP);
102+
await expect(tx2)
103+
.to.emit(usdc, "Transfer")
104+
.withArgs(user.address, liquidityHub.target, 7n * USDC);
105+
expect(await lpToken.balanceOf(user.address)).to.equal(10n * LP);
106+
expect(await lpToken.totalSupply()).to.equal(10n * LP);
107+
expect(await liquidityHub.totalSupply()).to.equal(10n * LP);
108+
expect(await liquidityHub.totalAssets()).to.equal(10n * USDC);
109+
expect(await liquidityHub.balanceOf(user.address)).to.equal(10n * LP);
110+
expect(await usdc.balanceOf(user.address)).to.equal(0n);
111+
expect(await usdc.balanceOf(liquidityHub.target)).to.equal(10n * USDC);
112+
});
113+
86114
it("Should allow to mint", async function () {
87115
const {lpToken, liquidityHub, usdc, deployer, user, USDC, LP} = await loadFixture(deployAll);
88116

@@ -195,4 +223,34 @@ describe("LiquidityHub", function () {
195223
expect(await usdc.balanceOf(user2.address)).to.equal(10n * USDC);
196224
expect(await usdc.balanceOf(liquidityHub.target)).to.equal(0n);
197225
});
226+
227+
it("Should allow to deposit and withdraw multiple times", async function () {
228+
const {lpToken, liquidityHub, usdc, deployer, user, USDC, LP} = await loadFixture(deployAll);
229+
230+
await usdc.connect(deployer).transfer(user.address, 10n * USDC);
231+
await usdc.connect(user).approve(liquidityHub.target, 10n * USDC);
232+
const tx = liquidityHub.connect(user).deposit(3n * USDC, user.address);
233+
await expect(tx)
234+
.to.emit(lpToken, "Transfer")
235+
.withArgs(ZERO_ADDRESS, user.address, 3n * LP);
236+
await expect(tx)
237+
.to.emit(usdc, "Transfer")
238+
.withArgs(user.address, liquidityHub.target, 3n * USDC);
239+
await liquidityHub.connect(user).withdraw(1n * USDC, user.address, user.address);
240+
const tx2 = liquidityHub.connect(user).deposit(7n * USDC, user.address);
241+
await expect(tx2)
242+
.to.emit(lpToken, "Transfer")
243+
.withArgs(ZERO_ADDRESS, user.address, 7n * LP);
244+
await expect(tx2)
245+
.to.emit(usdc, "Transfer")
246+
.withArgs(user.address, liquidityHub.target, 7n * USDC);
247+
await liquidityHub.connect(user).withdraw(4n * USDC, user.address, user.address);
248+
expect(await lpToken.balanceOf(user.address)).to.equal(5n * LP);
249+
expect(await lpToken.totalSupply()).to.equal(5n * LP);
250+
expect(await liquidityHub.totalSupply()).to.equal(5n * LP);
251+
expect(await liquidityHub.totalAssets()).to.equal(5n * USDC);
252+
expect(await liquidityHub.balanceOf(user.address)).to.equal(5n * LP);
253+
expect(await usdc.balanceOf(user.address)).to.equal(5n * USDC);
254+
expect(await usdc.balanceOf(liquidityHub.target)).to.equal(5n * USDC);
255+
});
198256
});

0 commit comments

Comments
 (0)