Skip to content

Commit

Permalink
5.6: Added variable minAmount to nftclaim in order to facilitate exte…
Browse files Browse the repository at this point in the history
…rnal price checking for slippage comparisson
  • Loading branch information
Hesnicewithit committed Feb 4, 2024
1 parent baec95a commit 9214afd
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 35 deletions.
63 changes: 37 additions & 26 deletions src/FluxToken.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-3
pragma solidity ^0.8.15;

import "lib/forge-std/src/console2.sol";

import "src/interfaces/IFluxToken.sol";
import "src/interfaces/IVotingEscrow.sol";
import "src/interfaces/IAlchemechNFT.sol";
Expand Down Expand Up @@ -112,35 +110,22 @@ contract FluxToken is ERC20("Flux", "FLUX"), IFluxToken {
}

/// @inheritdoc IFluxToken
function nftClaim(address _nft, uint256 _tokenId) external {
function nftClaim(address _nft, uint256 _tokenId, uint256 _minAmount) external {
// require claim to be within a year of deploy date
require(block.timestamp < deployDate + oneYear, "claim period has passed");

require(!claimed[_nft][_tokenId], "already claimed");

// value of the NFT
uint256 tokenData = 0;

// determine which nft is being claimed
if (_nft == alchemechNFT) {
// require sender to be owner of the NFT
require(IAlchemechNFT(_nft).ownerOf(_tokenId) == msg.sender, "not owner of Alchemech NFT");

tokenData = IAlchemechNFT(_nft).tokenData(_tokenId);
} else if (_nft == patronNFT) {
// require sender to be owner of the NFT
require(IAlEthNFT(_nft).ownerOf(_tokenId) == msg.sender, "not owner of Patron NFT");

tokenData = IAlEthNFT(_nft).tokenData(_tokenId);
} else {
revert("invalid NFT");
}
uint256 tokenData = getNFTValue(_nft, _tokenId);

// mark the token as claimed
claimed[_nft][_tokenId] = true;

uint256 amount = getClaimableFlux(tokenData, _nft);

require(amount >= _minAmount, "Slippage Exceeded");

_mint(msg.sender, amount);
}

Expand Down Expand Up @@ -196,21 +181,47 @@ contract FluxToken is ERC20("Flux", "FLUX"), IFluxToken {
function getClaimableFlux(uint256 _amount, address _nft) public view returns (uint256 claimableFlux) {
uint256 bpt = _calculateBPT(_amount);

uint256 veMul = IVotingEscrow(veALCX).MULTIPLIER();
uint256 veMax = IVotingEscrow(veALCX).MAXTIME();
uint256 fluxPerVe = IVotingEscrow(veALCX).fluxPerVeALCX();
uint256 fluxMul = IVotingEscrow(veALCX).fluxMultiplier();
uint256 slope = (bpt * IVotingEscrow(veALCX).MULTIPLIER()) / IVotingEscrow(veALCX).MAXTIME();

// Calculate as if time is maxtime
uint256 bias = (slope * ((block.timestamp + IVotingEscrow(veALCX).MAXTIME()) - block.timestamp));

// Total amount of flux that would be earned from the amount
uint256 totalFlux = (bias * (IVotingEscrow(veALCX).fluxPerVeALCX() + BPS)) / BPS;

// Amount of flux earned in 1 yr from _amount assuming it was deposited for maxtime
claimableFlux = (((bpt * veMul) / veMax) * veMax * (fluxPerVe + BPS)) / BPS / fluxMul;
// Amount of flux that would be claimable
claimableFlux = totalFlux / IVotingEscrow(veALCX).fluxMultiplier();

// Claimable flux for alchemechNFT is different than patronNFT
if (_nft == alchemechNFT) {
claimableFlux = (claimableFlux * alchemechMultiplier) / BPS;
}
}

function _calculateBPT(uint256 _amount) public view returns (uint256 bptOut) {
// Retrieves tokenData from the specified nft
function getNFTValue(address _nft, uint256 _tokenId) public view returns (uint256) {
// value of the NFT
uint256 tokenData = 0;

// determine which nft is being claimed
if (_nft == alchemechNFT) {
// require sender to be owner of the NFT
require(IAlchemechNFT(_nft).ownerOf(_tokenId) == msg.sender, "not owner of Alchemech NFT");

tokenData = IAlchemechNFT(_nft).tokenData(_tokenId);
} else if (_nft == patronNFT) {
// require sender to be owner of the NFT
require(IAlEthNFT(_nft).ownerOf(_tokenId) == msg.sender, "not owner of Patron NFT");

tokenData = IAlEthNFT(_nft).tokenData(_tokenId);
} else {
revert("invalid NFT");
}

return tokenData;
}

function _calculateBPT(uint256 _amount) internal view returns (uint256 bptOut) {
address distributor = IVotingEscrow(veALCX).distributor();

(bytes32 balancerPoolId, address balancerPool, address balancerVault) = IRewardsDistributor(distributor)
Expand Down
3 changes: 2 additions & 1 deletion src/interfaces/IFluxToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ interface IFluxToken is IERC20 {
* @notice Mints tokens to a recipient.
* @param _nft NFT contract address to claim from.
* @param _tokenId NFT tokenId to claim from.
* @param _minAmount Minimum tokens to accept from the claim.
* @dev This will revert after set claim period, if the NFT has already been claimed, or if the caller is not the owner of the NFT
* @dev Amount of FLUX minted is one year of potential FLUX rewards given the NFTs value.
*/
function nftClaim(address _nft, uint256 _tokenId) external;
function nftClaim(address _nft, uint256 _tokenId, uint256 _minAmount) external;

/**
* @dev Burns `amount` tokens from `account`, deducting from the caller's allowance.
Expand Down
20 changes: 12 additions & 8 deletions src/test/FluxToken.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ contract FluxTokenTest is BaseTest {
uint256 alchemechTotalNoMultiplier = flux.getClaimableFlux(tokenData2, patronNFT);

assertGt(alchemechTotalNoMultiplier, alchemechTotal, "non multiplier calc should be higher");

uint256 expectedAmount = flux.getClaimableFlux(flux.getNFTValue(patronNFT, tokenId), patronNFT);

hevm.prank(ownerOfPatronNFT);
flux.nftClaim(patronNFT, tokenId);
flux.nftClaim(patronNFT, tokenId, expectedAmount * 9_900 / 10_000);

expectedAmount = flux.getClaimableFlux(flux.getNFTValue(alchemechNFT, tokenId), alchemechNFT);

hevm.prank(ownerOfAlchemechNFT);
flux.nftClaim(alchemechNFT, tokenId);
flux.nftClaim(alchemechNFT, tokenId, expectedAmount * 9_900 / 10_000);

assertEq(flux.balanceOf(ownerOfPatronNFT), patronTotal, "owner should have patron flux");
assertEq(flux.balanceOf(ownerOfAlchemechNFT), alchemechTotal, "owner should have alchemech flux");
Expand All @@ -43,28 +47,28 @@ contract FluxTokenTest is BaseTest {

hevm.expectRevert(abi.encodePacked("invalid NFT"));
hevm.prank(ownerOfPatronNFT);
flux.nftClaim(address(0), tokenId);
flux.nftClaim(address(0), tokenId, 0);

hevm.expectRevert(abi.encodePacked("not owner of Patron NFT"));
hevm.prank(ownerOfAlchemechNFT);
flux.nftClaim(patronNFT, tokenId);
flux.nftClaim(patronNFT, tokenId, 0);

hevm.expectRevert(abi.encodePacked("not owner of Alchemech NFT"));
hevm.prank(ownerOfPatronNFT);
flux.nftClaim(alchemechNFT, tokenId);
flux.nftClaim(alchemechNFT, tokenId, 0);

// Attempt to claim twice
hevm.startPrank(ownerOfPatronNFT);
flux.nftClaim(patronNFT, tokenId);
flux.nftClaim(patronNFT, tokenId, 0);

hevm.expectRevert(abi.encodePacked("already claimed"));
flux.nftClaim(patronNFT, tokenId);
flux.nftClaim(patronNFT, tokenId, 0);
hevm.stopPrank();

// Attempt to claim after claim period (1 year)
hevm.warp(block.timestamp + 366 days);
hevm.expectRevert(abi.encodePacked("claim period has passed"));
flux.nftClaim(patronNFT, tokenId);
flux.nftClaim(patronNFT, tokenId, 0);
}

function testFluxAccrual() external {
Expand Down

0 comments on commit 9214afd

Please sign in to comment.