function deposit(uint256 assets, address receiver) public override returns (uint256) {
...
uint256 fee = _getParticipationFee(assets);
uint256 stakeAsset = assets - fee;
stakedAsset[receiver] = stakeAsset;
uint256 participantShares = _convertToShares(stakeAsset);
@> IERC20(asset()).safeTransferFrom(msg.sender, participationFeeAddress, fee);
@> IERC20(asset()).safeTransferFrom(msg.sender, address(this), stakeAsset);
_mint(msg.sender, participantShares);
...
}
function _convertToShares(uint256 assets) internal view returns (uint256 shares) {
@> uint256 balanceOfVault = IERC20(asset()).balanceOf(address(this));
uint256 totalShares = totalSupply();
if (totalShares == 0 || balanceOfVault == 0) {
return assets;
}
shares = Math.mulDiv(assets, totalShares, balanceOfVault);
}
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
import {BriVault} from "./BriVault.sol";
contract FeeToken is ERC20 {
uint256 constant FEE_BPS = 100;
constructor() ERC20("FeeToken", "FEE") {}
function mint(address to, uint256 amount) external {
_mint(to, amount);
}
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
uint256 fee = (amount * FEE_BPS) / 10000;
_burn(from, fee);
_transfer(from, to, amount - fee);
return true;
}
function transfer(address to, uint256 amount) public override returns (bool) {
uint256 fee = (amount * FEE_BPS) / 10000;
_burn(msg.sender, fee);
_transfer(msg.sender, to, amount - fee);
return true;
}
}
contract BriVaultPoCTest is Test {
BriVault vault;
FeeToken asset;
address owner = address(this);
address user = address(0x123);
uint256 participationFeeBsp = 100;
uint256 eventStartDate = block.timestamp + 1 days;
uint256 eventEndDate = block.timestamp + 2 days;
uint256 minimumAmount = 100;
address participationFeeAddress = address(0xfee);
function setUp() public {
asset = new FeeToken();
vault = new BriVault(IERC20(address(asset)), participationFeeBsp, eventStartDate, participationFeeAddress, minimumAmount, eventEndDate);
string[48] memory countries;
for (uint i = 0; i < 48; i++) {
countries[i] = "Country";
}
vault.setCountry(countries);
asset.mint(user, 1e18 + 1e16);
vm.prank(user);
asset.approve(address(vault), 1e18 + 1e16);
}
function testFeeOnTransferOverMint() public {
uint256 depositAmount = 1000;
uint256 expectedReceived = depositAmount * (10000 - 100) / 10000;
vm.prank(user);
uint256 shares = vault.deposit(depositAmount, user);
uint256 actualBalance = IERC20(address(asset)).balanceOf(address(vault));
assertLt(actualBalance, depositAmount - (depositAmount * participationFeeBsp / 10000), "Vault received less due to fee token");
}
}
+ function isCompatibleToken(IERC20 token) internal view returns (bool) {
+ // Test transfer and balance consistency
+ return true; // Implement checks (e.g., transfer 1 wei, verify balance)
+ }
constructor (...) {
+ if (!isCompatibleToken(_asset)) revert("Incompatible asset");
...
}
+ uint256 internal constant VIRTUAL_ASSETS = 1;
+ uint256 internal constant VIRTUAL_SHARES = 1e18;
function totalAssets() public view override returns (uint256) {
- return IERC20(asset()).balanceOf(address(this));
+ return IERC20(asset()).balanceOf(address(this)) + VIRTUAL_ASSETS;
}