Skip to content

Commit

Permalink
nft staking: repay
Browse files Browse the repository at this point in the history
  • Loading branch information
Birua committed Oct 8, 2024
1 parent 778b9ad commit 32e1cdd
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/NFTStakingAndBorrowing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,39 @@ contract NFTStakingAndBorrowing is ERC1155Holder, Ownable {
emit NFTStaked(msg.sender, nftAddress, tokenId, amount);
}

function unstakeNFT(address nftAddress, uint256 tokenId, uint256 amount) external {
if (!whitelistedNFTs[nftAddress]) revert NFTNotWhitelisted();
// Only NFT owner can unstake anytime
if (userNFTs[msg.sender][nftAddress][tokenId] < amount) revert InsufficientNFTBalance();

IBondNFT.Metadata memory metadata = IBondNFT(nftAddress).getMetaData(tokenId);
uint256 totalUnstakeValue = (metadata.value + metadata.couponValue) * amount * (UNIT - SAFETY_FEE) / UNIT;

updateUserDebtAndAvailable(msg.sender);
updateTotalDebt();

// Check if user has enough collateral
if (
calculateMaxBorrow(totalUnstakeValue, block.timestamp, metadata.expirationTimestamp)
> userStats[msg.sender].nominalAvailable - userStats[msg.sender].debt
) {
revert NotEnoughCollateral(userStats[msg.sender].nominalAvailable - userStats[msg.sender].debt);
}

userNFTs[msg.sender][nftAddress][tokenId] -= amount;

userStats[msg.sender].staked -= totalUnstakeValue;
userStats[msg.sender].nominalAvailable -=
calculateMaxBorrow(totalUnstakeValue, block.timestamp, metadata.expirationTimestamp);
totalStats.staked -= totalUnstakeValue;

IBondNFT(nftAddress).safeTransferFrom(address(this), msg.sender, tokenId, amount, "");

stableToken.burn(address(this), totalUnstakeValue);

emit NFTUnstaked(msg.sender, nftAddress, tokenId, amount);
}

function borrow(uint256 amount) public {
updateUserDebtAndAvailable(msg.sender);
updateTotalDebt();
Expand All @@ -198,6 +231,24 @@ contract NFTStakingAndBorrowing is ERC1155Holder, Ownable {
stableToken.transfer(msg.sender, amount);
}

function repay(uint256 amount) external {
updateUserDebtAndAvailable(msg.sender);
updateTotalDebt();

if (amount == 0) amount = userStats[msg.sender].debt;

if (stableToken.balanceOf(msg.sender) < amount) revert InsufficientBalanceToRepay();

stableToken.transferFrom(msg.sender, address(this), amount);
userStats[msg.sender].debt -= amount;
userStats[msg.sender].borrowed -= amount;

totalStats.borrowed -= amount;
totalStats.debt -= amount;

emit Repaid(msg.sender, amount);
}

function _borrow(uint256 amount, address user_address) internal {
userStats[user_address].debt += amount;
userStats[user_address].borrowed += amount;
Expand Down
76 changes: 76 additions & 0 deletions test/NFTStakingAndBorrowing.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,80 @@ contract NFTStakingAndBorrowingTest is Test {
assertEq(totalStats.debt, 501_614410);
assertEq(userStats.debt, 501_614410);
}

function test_unstake() public {
owner = address(1);
address client1 = address(2);
address client2 = address(3);

vm.startPrank(owner);
bondNFT.setAllowedMints(client1, 2, 10);
bondNFT.setAllowedMints(client2, 3, 10);
vm.stopPrank();

vm.prank(client1);
bondNFT.mint(2, 10, "");
vm.prank(client2);
bondNFT.mint(3, 10, "");

vm.prank(client1);
bondNFT.setApprovalForAll(address(nftStaking), true);
vm.prank(client2);
bondNFT.setApprovalForAll(address(nftStaking), true);

vm.warp(30 days);
vm.roll(3);
// Client1 makes some staking
vm.prank(client1);
nftStaking.stakeNFT(address(bondNFT), 2, 5);

vm.warp(35 days);
vm.roll(4);
vm.prank(client1);
nftStaking.stakeNFT(address(bondNFT), 2, 5);

NFTStakingAndBorrowing.UserStats memory userStats = nftStaking.getUserStats(client1);
assertEq(userStats.staked, 970_000000);

// Client borrows less than a half of available
uint256 borrow_amount = nftStaking.userAvailableToBorrow(client1) / 2;
vm.prank(client1);
nftStaking.borrow(borrow_amount);

// User unstakes
vm.prank(client1);
nftStaking.unstakeNFT(address(bondNFT), 2, 4);

userStats = nftStaking.getUserStats(client1);
assertEq(userStats.staked, 6 * 970_000000 / 10);
assertLe(nftStaking.userAvailableToBorrow(client1), borrow_amount);
}

function test_repay() public {
owner = address(1);

vm.startPrank(owner);
nftStaking.stakeNFT(address(bondNFT), 1, 10);
NFTStakingAndBorrowing.UserStats memory userStats = nftStaking.getUserStats(owner);

assertEq(userStats.staked, 970_000000);
assertEq(nftStaking.userAvailableToBorrow(owner), 932_692306);

nftStaking.borrow(500_000000);

userStats = nftStaking.getUserStats(owner);

assertEq(userStats.borrowed, 500_000000);
assertEq(nftStaking.userAvailableToBorrow(owner), 432_692307);

stableBondCoins.approve(address(nftStaking), 500_000000);
nftStaking.repay(500_000000);

vm.stopPrank();

userStats = nftStaking.getUserStats(owner);

assertEq(userStats.borrowed, 0);
assertEq(nftStaking.userAvailableToBorrow(owner), 932_692306);
}
}

0 comments on commit 32e1cdd

Please sign in to comment.