run forge fmt and leave audit notes up to claimNft

This commit is contained in:
han 2025-01-17 21:18:33 +07:00
parent 1157105b63
commit 9055f078ba

View File

@ -16,12 +16,9 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
* as possible.This is the first project that i code alone, in blockchain, foundry and solidity. * as possible.This is the first project that i code alone, in blockchain, foundry and solidity.
* Thank you so much for read it. * Thank you so much for read it.
*/ */
contract TokenDivider is IERC721Receiver, Ownable {
contract TokenDivider is IERC721Receiver,Ownable {
error TokenDivider__NotFromNftOwner(); error TokenDivider__NotFromNftOwner();
error TokenDivider__NotEnoughErc20Balance(); error TokenDivider__NotEnoughErc20Balance();
error TokenDivider__NftTransferFailed(); error TokenDivider__NftTransferFailed();
error TokenDivider__InsuficientBalance(); error TokenDivider__InsuficientBalance();
error TokenDivider__CantTransferToAddressZero(); error TokenDivider__CantTransferToAddressZero();
@ -45,20 +42,18 @@ contract TokenDivider is IERC721Receiver,Ownable {
uint256 amount; uint256 amount;
} }
/** /**
* @dev balances Relates a user with an amount of a erc20 token, this erc20 tokens is an nft fraction * @dev balances Relates a user with an amount of a erc20 token, this erc20 tokens is an nft fraction
*
@dev nftToErc20Info Relates an nft with the erc20 pegged, and othe data like the erc20 amount, or the tokenId * @dev nftToErc20Info Relates an nft with the erc20 pegged, and othe data like the erc20 amount, or the tokenId
*
@dev s_userToSellOrders Relates a user with an array of sell orders, that each sell order * @dev s_userToSellOrders Relates a user with an array of sell orders, that each sell order
has a seller, an erc20 that is the token to sell, a price and an amount of erc20 to sell * has a seller, an erc20 that is the token to sell, a price and an amount of erc20 to sell
*/ */
mapping(address user => mapping(address erc20Address => uint256 amount)) balances; mapping(address user => mapping(address erc20Address => uint256 amount)) balances;
mapping(address nft => ERC20Info) nftToErc20Info; mapping(address nft => ERC20Info) nftToErc20Info;
mapping(address user => SellOrder[] orders) s_userToSellOrders; mapping(address user => SellOrder[] orders) s_userToSellOrders;
mapping(address erc20 => address nft) erc20ToNft; mapping(address erc20 => address nft) erc20ToNft;
mapping(address erc20 => uint256 totalErc20Minted) erc20ToMintedAmount; mapping(address erc20 => uint256 totalErc20Minted) erc20ToMintedAmount;
event NftDivided(address indexed nftAddress, uint256 indexed amountErc20Minted, address indexed erc20Minted); event NftDivided(address indexed nftAddress, uint256 indexed amountErc20Minted, address indexed erc20Minted);
@ -67,13 +62,12 @@ contract TokenDivider is IERC721Receiver,Ownable {
event OrderPublished(uint256 indexed amount, address indexed seller, address indexed nftPegged); event OrderPublished(uint256 indexed amount, address indexed seller, address indexed nftPegged);
event OrderSelled(address indexed buyer, uint256 price); event OrderSelled(address indexed buyer, uint256 price);
/** /**
* *
* Only the owner of the nft can call a function with this modifier * Only the owner of the nft can call a function with this modifier
*/ */
modifier onlyNftOwner(address nft, uint256 tokenId) { modifier onlyNftOwner(address nft, uint256 tokenId) {
if(msg.sender != IERC721(nft).ownerOf(tokenId)) { if (msg.sender != IERC721(nft).ownerOf(tokenId)) {
revert TokenDivider__NotFromNftOwner(); revert TokenDivider__NotFromNftOwner();
} }
_; _;
@ -81,15 +75,13 @@ contract TokenDivider is IERC721Receiver,Ownable {
constructor() Ownable(msg.sender) {} constructor() Ownable(msg.sender) {}
/** /**
* @dev Handles the receipt of an ERC721 token. This function is called whenever an ERC721 token is transferred to this contract. * @dev Handles the receipt of an ERC721 token. This function is called whenever an ERC721 token is transferred to this contract.
*/ */
function onERC721Received( function onERC721Received(
address /* operator */, address, /* operator */
address /* from */, address, /* from */
uint256 /* tokenId */, uint256, /* tokenId */
bytes calldata /* data */ bytes calldata /* data */
) external pure override returns (bytes4) { ) external pure override returns (bytes4) {
// Return this value to confirm the receipt of the NFT // Return this value to confirm the receipt of the NFT
@ -105,25 +97,31 @@ contract TokenDivider is IERC721Receiver,Ownable {
* @dev in this function, the nft passed as parameter, is locked by transfering it to this contract, then, it gives to the * @dev in this function, the nft passed as parameter, is locked by transfering it to this contract, then, it gives to the
* person calling this function an amount of erc20, beeing like a fraction of this nft. * person calling this function an amount of erc20, beeing like a fraction of this nft.
*/ */
function divideNft(address nftAddress, uint256 tokenId, uint256 amount)
function divideNft(address nftAddress, uint256 tokenId, uint256 amount) onlyNftOwner(nftAddress, tokenId) onlyNftOwner(nftAddress ,tokenId) external { external
onlyNftOwner(nftAddress, tokenId)
onlyNftOwner(nftAddress, tokenId)
if(nftAddress == address(0)) { revert TokenDivider__NftAddressIsZero(); } {
if(amount == 0) { revert TokenDivider__AmountCantBeZero(); } if (nftAddress == address(0)) revert TokenDivider__NftAddressIsZero();
if (amount == 0) revert TokenDivider__AmountCantBeZero();
// @audit: there is no checking of inExistence tokenId, it might handled on modifier but need to check
ERC20ToGenerateNftFraccion erc20Contract = new ERC20ToGenerateNftFraccion( ERC20ToGenerateNftFraccion erc20Contract = new ERC20ToGenerateNftFraccion(
string(abi.encodePacked(ERC721(nftAddress).name(), "Fraccion")), string(abi.encodePacked(ERC721(nftAddress).name(), "Fraccion")),
string(abi.encodePacked("F", ERC721(nftAddress).symbol()))); string(abi.encodePacked("F", ERC721(nftAddress).symbol()))
);
// @audit: can we mint again? out of this function?
erc20Contract.mint(address(this), amount); erc20Contract.mint(address(this), amount);
address erc20 = address(erc20Contract); address erc20 = address(erc20Contract);
IERC721(nftAddress).safeTransferFrom(msg.sender, address(this), tokenId, ""); IERC721(nftAddress).safeTransferFrom(msg.sender, address(this), tokenId, "");
if(IERC721(nftAddress).ownerOf(tokenId) == msg.sender) { revert TokenDivider__NftTransferFailed(); } // @audit: what if we send the NFT to other address during this period?
if (IERC721(nftAddress).ownerOf(tokenId) == msg.sender) revert TokenDivider__NftTransferFailed();
balances[msg.sender][erc20] = amount; balances[msg.sender][erc20] = amount;
// @audit: what happen if the given nft address already registered in this CA, then another token id register again?
nftToErc20Info[nftAddress] = ERC20Info({erc20Address: erc20, tokenId: tokenId}); nftToErc20Info[nftAddress] = ERC20Info({erc20Address: erc20, tokenId: tokenId});
erc20ToMintedAmount[erc20] = amount; erc20ToMintedAmount[erc20] = amount;
erc20ToNft[erc20] = nftAddress; erc20ToNft[erc20] = nftAddress;
@ -131,7 +129,7 @@ contract TokenDivider is IERC721Receiver,Ownable {
emit NftDivided(nftAddress, amount, erc20); emit NftDivided(nftAddress, amount, erc20);
bool transferSuccess = IERC20(erc20).transfer(msg.sender, amount); bool transferSuccess = IERC20(erc20).transfer(msg.sender, amount);
if(!transferSuccess) { if (!transferSuccess) {
revert TokenDivider__TransferFailed(); revert TokenDivider__TransferFailed();
} }
} }
@ -143,30 +141,31 @@ contract TokenDivider is IERC721Receiver,Ownable {
* @dev in this function, if you have all the erc20 minted for the nft, you can call this function to claim the nft, * @dev in this function, if you have all the erc20 minted for the nft, you can call this function to claim the nft,
* giving to the contract all the erc20 and it will give you back the nft * giving to the contract all the erc20 and it will give you back the nft
*/ */
function claimNft(address nftAddress) external { function claimNft(address nftAddress) external {
if (nftAddress == address(0)) {
if(nftAddress == address(0)) {
revert TokenDivider__NftAddressIsZero(); revert TokenDivider__NftAddressIsZero();
} }
// @audit: what if the nft address is not stored?
ERC20Info storage tokenInfo = nftToErc20Info[nftAddress]; ERC20Info storage tokenInfo = nftToErc20Info[nftAddress];
if(balances[msg.sender][tokenInfo.erc20Address] < erc20ToMintedAmount[tokenInfo.erc20Address]) { if (balances[msg.sender][tokenInfo.erc20Address] < erc20ToMintedAmount[tokenInfo.erc20Address]) {
revert TokenDivider__NotEnoughErc20Balance(); revert TokenDivider__NotEnoughErc20Balance();
} }
ERC20ToGenerateNftFraccion(tokenInfo.erc20Address).burnFrom(msg.sender, erc20ToMintedAmount[tokenInfo.erc20Address]); ERC20ToGenerateNftFraccion(tokenInfo.erc20Address).burnFrom(
msg.sender, erc20ToMintedAmount[tokenInfo.erc20Address]
);
balances[msg.sender][tokenInfo.erc20Address] = 0; balances[msg.sender][tokenInfo.erc20Address] = 0;
erc20ToMintedAmount[tokenInfo.erc20Address] = 0; erc20ToMintedAmount[tokenInfo.erc20Address] = 0;
emit NftClaimed(nftAddress); emit NftClaimed(nftAddress);
// @audit: what happen if there is overlaps in same NFT address with different token id? will someone get a different NFT?
IERC721(nftAddress).safeTransferFrom(address(this), msg.sender, tokenInfo.tokenId); IERC721(nftAddress).safeTransferFrom(address(this), msg.sender, tokenInfo.tokenId);
} }
/** /**
* *
* @param nftAddress The nft address pegged to the erc20 * @param nftAddress The nft address pegged to the erc20
@ -175,27 +174,25 @@ contract TokenDivider is IERC721Receiver,Ownable {
* *
* @dev you can use this function to transfer nft franctions 100% securily and registered by te contract * @dev you can use this function to transfer nft franctions 100% securily and registered by te contract
*/ */
function transferErcTokens(address nftAddress, address to, uint256 amount) external {
function transferErcTokens(address nftAddress,address to, uint256 amount) external { if (nftAddress == address(0)) {
if(nftAddress == address(0)) {
revert TokenDivider__NftAddressIsZero(); revert TokenDivider__NftAddressIsZero();
} }
if(to == address(0)) { if (to == address(0)) {
revert TokenDivider__CantTransferToAddressZero(); revert TokenDivider__CantTransferToAddressZero();
} }
if(amount == 0) { if (amount == 0) {
revert TokenDivider__AmountCantBeZero(); revert TokenDivider__AmountCantBeZero();
} }
ERC20Info memory tokenInfo = nftToErc20Info[nftAddress]; ERC20Info memory tokenInfo = nftToErc20Info[nftAddress];
if(to == address(0)) { if (to == address(0)) {
revert TokenDivider__CantTransferToAddressZero(); revert TokenDivider__CantTransferToAddressZero();
} }
if(balances[msg.sender][tokenInfo.erc20Address] < amount) { if (balances[msg.sender][tokenInfo.erc20Address] < amount) {
revert TokenDivider__NotEnoughErc20Balance(); revert TokenDivider__NotEnoughErc20Balance();
} }
@ -204,7 +201,7 @@ contract TokenDivider is IERC721Receiver,Ownable {
emit TokensTransfered(amount, tokenInfo.erc20Address); emit TokensTransfered(amount, tokenInfo.erc20Address);
IERC20(tokenInfo.erc20Address).transferFrom(msg.sender,to, amount); IERC20(tokenInfo.erc20Address).transferFrom(msg.sender, to, amount);
} }
/** /**
@ -217,38 +214,31 @@ contract TokenDivider is IERC721Receiver,Ownable {
* firstly, once you call this function, the amount of tokens that you passed into as a parameter, get blocked, by sending it * firstly, once you call this function, the amount of tokens that you passed into as a parameter, get blocked, by sending it
* to this contract, then a new order is created and published. * to this contract, then a new order is created and published.
*/ */
function sellErc20(address nftPegged, uint256 price, uint256 amount) external {
function sellErc20(address nftPegged, uint256 price,uint256 amount) external { if (nftPegged == address(0)) {
if(nftPegged == address(0)) {
revert TokenDivider__NftAddressIsZero(); revert TokenDivider__NftAddressIsZero();
} }
if( amount == 0) { if (amount == 0) {
revert TokenDivider__AmountCantBeZero(); revert TokenDivider__AmountCantBeZero();
} }
ERC20Info memory tokenInfo = nftToErc20Info[nftPegged]; ERC20Info memory tokenInfo = nftToErc20Info[nftPegged];
if(balances[msg.sender][tokenInfo.erc20Address] < amount) { if (balances[msg.sender][tokenInfo.erc20Address] < amount) {
revert TokenDivider__InsuficientBalance(); revert TokenDivider__InsuficientBalance();
} }
balances[msg.sender][tokenInfo.erc20Address] -= amount; balances[msg.sender][tokenInfo.erc20Address] -= amount;
s_userToSellOrders[msg.sender].push( s_userToSellOrders[msg.sender].push(
SellOrder({ SellOrder({seller: msg.sender, erc20Address: tokenInfo.erc20Address, price: price, amount: amount})
seller: msg.sender,
erc20Address: tokenInfo.erc20Address,
price: price,
amount: amount
})
); );
emit OrderPublished(amount,msg.sender, nftPegged); emit OrderPublished(amount, msg.sender, nftPegged);
IERC20(tokenInfo.erc20Address).transferFrom(msg.sender,address(this), amount); IERC20(tokenInfo.erc20Address).transferFrom(msg.sender, address(this), amount);
} }
/** /**
* *
* @param orderIndex The index of the order in all the orders array of the seller (the seller can have multiple orders active) * @param orderIndex The index of the order in all the orders array of the seller (the seller can have multiple orders active)
@ -257,29 +247,24 @@ contract TokenDivider is IERC721Receiver,Ownable {
* @dev when the buyer call this function, the eth or any token accepted to pay, is sent to the seller * @dev when the buyer call this function, the eth or any token accepted to pay, is sent to the seller
* if the transfer executed correctly, then this contract, wich has all the tokens, send the tokens to the msg.sender * if the transfer executed correctly, then this contract, wich has all the tokens, send the tokens to the msg.sender
*/ */
function buyOrder(uint256 orderIndex, address seller) external payable { function buyOrder(uint256 orderIndex, address seller) external payable {
if(seller == address(0)) { if (seller == address(0)) {
revert TokenDivider__InvalidSeller(); revert TokenDivider__InvalidSeller();
} }
SellOrder memory order = s_userToSellOrders[seller][orderIndex]; SellOrder memory order = s_userToSellOrders[seller][orderIndex];
if(msg.value < order.price) { if (msg.value < order.price) {
revert TokenDivider__IncorrectEtherAmount(); revert TokenDivider__IncorrectEtherAmount();
} }
uint256 fee = order.price / 100; uint256 fee = order.price / 100;
uint256 sellerFee = fee / 2; uint256 sellerFee = fee / 2;
if (msg.value < order.price + sellerFee) {
if(msg.value < order.price + sellerFee) {
revert TokenDivider__InsuficientEtherForFees(); revert TokenDivider__InsuficientEtherForFees();
} }
balances[msg.sender][order.erc20Address] += order.amount; balances[msg.sender][order.erc20Address] += order.amount;
s_userToSellOrders[seller][orderIndex] = s_userToSellOrders[seller][s_userToSellOrders[seller].length - 1]; s_userToSellOrders[seller][orderIndex] = s_userToSellOrders[seller][s_userToSellOrders[seller].length - 1];
@ -289,39 +274,37 @@ contract TokenDivider is IERC721Receiver,Ownable {
// Transfer The Ether // Transfer The Ether
(bool success, ) = payable(order.seller).call{value: (order.price - sellerFee)}(""); (bool success,) = payable(order.seller).call{value: (order.price - sellerFee)}("");
if(!success) { if (!success) {
revert TokenDivider__TransferFailed(); revert TokenDivider__TransferFailed();
} }
(bool taxSuccess, ) = payable(owner()).call{value: fee}(""); (bool taxSuccess,) = payable(owner()).call{value: fee}("");
if (!taxSuccess) {
if(!taxSuccess) {
revert TokenDivider__TransferFailed(); revert TokenDivider__TransferFailed();
} }
IERC20(order.erc20Address).transfer(msg.sender, order.amount); IERC20(order.erc20Address).transfer(msg.sender, order.amount);
} }
/** Getters */ /**
* Getters
function getBalanceOf(address user, address token) public view returns(uint256) { */
function getBalanceOf(address user, address token) public view returns (uint256) {
return balances[user][token]; return balances[user][token];
} }
function getErc20TotalMintedAmount(address erc20) public view returns(uint256) { function getErc20TotalMintedAmount(address erc20) public view returns (uint256) {
return erc20ToMintedAmount[erc20]; return erc20ToMintedAmount[erc20];
} }
function getErc20InfoFromNft(address nft) public view returns(ERC20Info memory) { function getErc20InfoFromNft(address nft) public view returns (ERC20Info memory) {
return nftToErc20Info[nft]; return nftToErc20Info[nft];
} }
function getOrderPrice(address seller, uint256 index) public view returns(uint256 price) { function getOrderPrice(address seller, uint256 index) public view returns (uint256 price) {
price = s_userToSellOrders[seller][index].price; price = s_userToSellOrders[seller][index].price;
} }
} }