8 Commits

Author SHA1 Message Date
han
1c7ff31322 prep audit test for divideNft 2025-01-19 17:57:32 +07:00
han
7d438f1c44 finish up contract audit comments 2025-01-19 17:22:25 +07:00
han
50370b5b0c add comment on TokenDividerTest 2025-01-19 16:43:31 +07:00
han
9055f078ba run forge fmt and leave audit notes up to claimNft 2025-01-17 21:18:33 +07:00
han
1157105b63 ignore vim buffer files 2025-01-17 21:18:05 +07:00
han
bede329cdf update dependencies 2025-01-17 21:17:52 +07:00
han
5b7b107375 install dependencies 2025-01-17 21:17:33 +07:00
han
20889f4985 remove github workflow 2025-01-17 16:40:56 +07:00
9 changed files with 418 additions and 376 deletions

View File

@@ -1,45 +0,0 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
env:
FOUNDRY_PROFILE: ci
jobs:
check:
strategy:
fail-fast: true
name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Show Forge version
run: |
forge --version
- name: Run Forge fmt
run: |
forge fmt --check
id: fmt
- name: Run Forge build
run: |
forge build --sizes
id: build
- name: Run Forge tests
run: |
forge test -vvv
id: test

3
.gitignore vendored
View File

@@ -12,3 +12,6 @@ docs/
# Dotenv file # Dotenv file
.env .env
# Vim buffer file
*.swp

2
.gitmodules vendored
View File

@@ -3,7 +3,7 @@
url = https://github.com/foundry-rs/forge-std url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts"] [submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts path = lib/openzeppelin-contracts
url = https://github.com/openzeppelin/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/foundry-devops"] [submodule "lib/foundry-devops"]
path = lib/foundry-devops path = lib/foundry-devops
url = https://github.com/Cyfrin/foundry-devops url = https://github.com/Cyfrin/foundry-devops

1
lib/forge-std Submodule

Submodule lib/forge-std added at b93cf4bc34

1
lib/foundry-devops Submodule

Submodule lib/foundry-devops added at 90b6dde613

View File

@@ -1,327 +1,325 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.18; pragma solidity ^0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20ToGenerateNftFraccion} from "src/token/ERC20ToGenerateNftFraccion.sol"; import {ERC20ToGenerateNftFraccion} from "src/token/ERC20ToGenerateNftFraccion.sol";
import {IERC721, ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import {IERC721, ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
/** /**
* @title TokenDivider * @title TokenDivider
* @author Juan Pedro Ventura Baltian, 14 years old * @author Juan Pedro Ventura Baltian, 14 years old
* @notice This contracts was created, with the intention to make a new market of nft franctions * @notice This contracts was created, with the intention to make a new market of nft franctions
* There are a function to divide an nft, then you can sell and buy some fraction of nft, that are basicaly * There are a function to divide an nft, then you can sell and buy some fraction of nft, that are basicaly
* erc20 tokens, each nft pegged to an nft.There are some validations, to make the platforme the most secure * erc20 tokens, each nft pegged to an nft.There are some validations, to make the platforme the most secure
* 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 {
error TokenDivider__NotFromNftOwner();
contract TokenDivider is IERC721Receiver,Ownable { error TokenDivider__NotEnoughErc20Balance();
error TokenDivider__NftTransferFailed();
error TokenDivider__NotFromNftOwner(); error TokenDivider__InsuficientBalance();
error TokenDivider__NotEnoughErc20Balance(); error TokenDivider__CantTransferToAddressZero();
error TokenDivider__NftTransferFailed(); error TokenDivider__TransferFailed();
error TokenDivider__InsuficientBalance(); error TokenDivider__NftAddressIsZero();
error TokenDivider__CantTransferToAddressZero(); error TokenDivider__AmountCantBeZero();
error TokenDivider__TransferFailed(); error TokenDivider__InvalidSeller();
error TokenDivider__NftAddressIsZero(); error TokenDivier__InvalidAmount();
error TokenDivider__AmountCantBeZero(); error TokenDivider__IncorrectEtherAmount();
error TokenDivider__InvalidSeller(); error TokenDivider__InsuficientEtherForFees();
error TokenDivier__InvalidAmount();
error TokenDivider__IncorrectEtherAmount(); struct ERC20Info {
error TokenDivider__InsuficientEtherForFees(); address erc20Address;
uint256 tokenId;
struct ERC20Info { }
address erc20Address;
uint256 tokenId; struct SellOrder {
} address seller;
address erc20Address;
struct SellOrder { uint256 price;
address seller; uint256 amount;
address erc20Address; }
uint256 price;
uint256 amount; /**
} * @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 balances Relates a user with an amount of a erc20 token, this erc20 tokens is an nft fraction * @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
@dev nftToErc20Info Relates an nft with the erc20 pegged, and othe data like the erc20 amount, or the tokenId */
mapping(address user => mapping(address erc20Address => uint256 amount)) balances;
@dev s_userToSellOrders Relates a user with an array of sell orders, that each sell order mapping(address nft => ERC20Info) nftToErc20Info;
has a seller, an erc20 that is the token to sell, a price and an amount of erc20 to sell mapping(address user => SellOrder[] orders) s_userToSellOrders;
mapping(address erc20 => address nft) erc20ToNft;
*/ mapping(address erc20 => uint256 totalErc20Minted) erc20ToMintedAmount;
mapping(address user => mapping(address erc20Address => uint256 amount)) balances;
mapping(address nft => ERC20Info) nftToErc20Info; event NftDivided(address indexed nftAddress, uint256 indexed amountErc20Minted, address indexed erc20Minted);
mapping(address user => SellOrder[] orders) s_userToSellOrders; event NftClaimed(address indexed nftAddress);
mapping(address erc20 => address nft) erc20ToNft; event TokensTransfered(uint256 indexed amount, address indexed erc20Address);
mapping(address erc20 => uint256 totalErc20Minted) erc20ToMintedAmount; event OrderPublished(uint256 indexed amount, address indexed seller, address indexed nftPegged);
event OrderSelled(address indexed buyer, uint256 price);
event NftDivided(address indexed nftAddress, uint256 indexed amountErc20Minted, address indexed erc20Minted);
event NftClaimed(address indexed nftAddress); /**
event TokensTransfered(uint256 indexed amount, address indexed erc20Address); *
event OrderPublished(uint256 indexed amount, address indexed seller, address indexed nftPegged); * Only the owner of the nft can call a function with this modifier
event OrderSelled(address indexed buyer, uint256 price); */
modifier onlyNftOwner(address nft, uint256 tokenId) {
if (msg.sender != IERC721(nft).ownerOf(tokenId)) {
/** revert TokenDivider__NotFromNftOwner();
* }
* Only the owner of the nft can call a function with this modifier _;
*/ }
modifier onlyNftOwner(address nft, uint256 tokenId) {
if(msg.sender != IERC721(nft).ownerOf(tokenId)) { constructor() Ownable(msg.sender) {}
revert TokenDivider__NotFromNftOwner();
} /**
_; * @dev Handles the receipt of an ERC721 token. This function is called whenever an ERC721 token is transferred to this contract.
} */
function onERC721Received(
constructor() Ownable(msg.sender) {} address, /* operator */
address, /* from */
uint256, /* tokenId */
bytes calldata /* data */
/** ) external pure override returns (bytes4) {
* @dev Handles the receipt of an ERC721 token. This function is called whenever an ERC721 token is transferred to this contract. // Return this value to confirm the receipt of the NFT
*/ return this.onERC721Received.selector;
function onERC721Received( }
address /* operator */,
address /* from */, /**
uint256 /* tokenId */, *
bytes calldata /* data */ * @param nftAddress The addres of the nft to divide
) external pure override returns (bytes4) { * @param tokenId The id of the token to divide
// Return this value to confirm the receipt of the NFT * @param amount The amount of erc20 tokens to mint for the nft
return this.onERC721Received.selector; *
} * @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.
/** */
* function divideNft(address nftAddress, uint256 tokenId, uint256 amount)
* @param nftAddress The addres of the nft to divide external
* @param tokenId The id of the token to divide onlyNftOwner(nftAddress, tokenId)
* @param amount The amount of erc20 tokens to mint for the nft onlyNftOwner(nftAddress, tokenId)
* {
* @dev in this function, the nft passed as parameter, is locked by transfering it to this contract, then, it gives to the if (nftAddress == address(0)) revert TokenDivider__NftAddressIsZero();
* person calling this function an amount of erc20, beeing like a fraction of this nft. if (amount == 0) revert TokenDivider__AmountCantBeZero();
*/ // @audit: there is no checking of inExistence tokenId, it might handled on modifier but need to check
function divideNft(address nftAddress, uint256 tokenId, uint256 amount) onlyNftOwner(nftAddress, tokenId) onlyNftOwner(nftAddress ,tokenId) external { ERC20ToGenerateNftFraccion erc20Contract = new ERC20ToGenerateNftFraccion(
string(abi.encodePacked(ERC721(nftAddress).name(), "Fraccion")),
string(abi.encodePacked("F", ERC721(nftAddress).symbol()))
if(nftAddress == address(0)) { revert TokenDivider__NftAddressIsZero(); } );
if(amount == 0) { revert TokenDivider__AmountCantBeZero(); }
// @audit: can we mint again? out of this function?
ERC20ToGenerateNftFraccion erc20Contract = new ERC20ToGenerateNftFraccion( erc20Contract.mint(address(this), amount);
string(abi.encodePacked(ERC721(nftAddress).name(), "Fraccion")), address erc20 = address(erc20Contract);
string(abi.encodePacked("F", ERC721(nftAddress).symbol())));
IERC721(nftAddress).safeTransferFrom(msg.sender, address(this), tokenId, "");
erc20Contract.mint(address(this), amount);
address erc20 = address(erc20Contract); // @audit: what if we send the NFT to other address during this period?
if (IERC721(nftAddress).ownerOf(tokenId) == msg.sender) revert TokenDivider__NftTransferFailed();
IERC721(nftAddress).safeTransferFrom(msg.sender, address(this), tokenId, "");
balances[msg.sender][erc20] = amount;
if(IERC721(nftAddress).ownerOf(tokenId) == msg.sender) { revert TokenDivider__NftTransferFailed(); } // @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});
balances[msg.sender][erc20] = amount; erc20ToMintedAmount[erc20] = amount;
nftToErc20Info[nftAddress] = ERC20Info({erc20Address: erc20, tokenId: tokenId}); erc20ToNft[erc20] = nftAddress;
erc20ToMintedAmount[erc20] = amount;
erc20ToNft[erc20] = nftAddress; emit NftDivided(nftAddress, amount, erc20);
emit NftDivided(nftAddress, amount, erc20); bool transferSuccess = IERC20(erc20).transfer(msg.sender, amount);
if (!transferSuccess) {
bool transferSuccess = IERC20(erc20).transfer(msg.sender, amount); revert TokenDivider__TransferFailed();
if(!transferSuccess) { }
revert TokenDivider__TransferFailed(); }
}
} /**
*
/** * @param nftAddress The address of the nft to claim
* *
* @param nftAddress The address of the nft to claim * @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
* @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 function claimNft(address nftAddress) external {
*/ if (nftAddress == address(0)) {
revert TokenDivider__NftAddressIsZero();
function claimNft(address nftAddress) external { }
if(nftAddress == address(0)) { // @audit: what if the nft address is not stored?
revert TokenDivider__NftAddressIsZero(); ERC20Info storage tokenInfo = nftToErc20Info[nftAddress];
}
if (balances[msg.sender][tokenInfo.erc20Address] < erc20ToMintedAmount[tokenInfo.erc20Address]) {
ERC20Info storage tokenInfo = nftToErc20Info[nftAddress]; revert TokenDivider__NotEnoughErc20Balance();
}
if(balances[msg.sender][tokenInfo.erc20Address] < erc20ToMintedAmount[tokenInfo.erc20Address]) {
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 * @param to The reciver of the erc20
* @param to The reciver of the erc20 * @param amount The amount of erc20 to transfer
* @param amount The amount of erc20 to transfer *
* * @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 {
if (nftAddress == address(0)) {
function transferErcTokens(address nftAddress,address to, uint256 amount) external { revert TokenDivider__NftAddressIsZero();
}
if(nftAddress == address(0)) {
revert TokenDivider__NftAddressIsZero(); if (to == address(0)) {
} revert TokenDivider__CantTransferToAddressZero();
}
if(to == address(0)) {
revert TokenDivider__CantTransferToAddressZero(); if (amount == 0) {
} revert TokenDivider__AmountCantBeZero();
}
if(amount == 0) {
revert TokenDivider__AmountCantBeZero(); ERC20Info memory tokenInfo = nftToErc20Info[nftAddress];
}
if (to == address(0)) {
ERC20Info memory tokenInfo = nftToErc20Info[nftAddress]; revert TokenDivider__CantTransferToAddressZero();
}
if(to == address(0)) { if (balances[msg.sender][tokenInfo.erc20Address] < amount) {
revert TokenDivider__CantTransferToAddressZero(); revert TokenDivider__NotEnoughErc20Balance();
} }
if(balances[msg.sender][tokenInfo.erc20Address] < amount) {
revert TokenDivider__NotEnoughErc20Balance(); balances[msg.sender][tokenInfo.erc20Address] -= amount;
} balances[to][tokenInfo.erc20Address] += amount;
balances[msg.sender][tokenInfo.erc20Address] -= amount; emit TokensTransfered(amount, tokenInfo.erc20Address);
balances[to][tokenInfo.erc20Address] += amount;
IERC20(tokenInfo.erc20Address).transferFrom(msg.sender, to, amount);
emit TokensTransfered(amount, tokenInfo.erc20Address); }
IERC20(tokenInfo.erc20Address).transferFrom(msg.sender,to, amount); /**
} *
* @param nftPegged The nft address pegged to the tokens to sell
/** * @param price The price of all the tokens to sell
* * @param amount The amount of tokens to sell
* @param nftPegged The nft address pegged to the tokens to sell *
* @param price The price of all the tokens to sell * @dev this function creates a new order, is like publish you assets into a marketplace, where other persons can buy it.
* @param amount The amount of tokens to sell * 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.
* @dev this function creates a new order, is like publish you assets into a marketplace, where other persons can buy it. */
* firstly, once you call this function, the amount of tokens that you passed into as a parameter, get blocked, by sending it function sellErc20(address nftPegged, uint256 price, uint256 amount) external {
* to this contract, then a new order is created and published. if (nftPegged == address(0)) {
*/ revert TokenDivider__NftAddressIsZero();
}
function sellErc20(address nftPegged, uint256 price,uint256 amount) external {
if(nftPegged == address(0)) { if (amount == 0) {
revert TokenDivider__NftAddressIsZero(); revert TokenDivider__AmountCantBeZero();
} }
if( amount == 0) { // @audit: no check for 0 price?
revert TokenDivider__AmountCantBeZero();
} ERC20Info memory tokenInfo = nftToErc20Info[nftPegged];
if (balances[msg.sender][tokenInfo.erc20Address] < amount) {
ERC20Info memory tokenInfo = nftToErc20Info[nftPegged]; revert TokenDivider__InsuficientBalance();
if(balances[msg.sender][tokenInfo.erc20Address] < amount) { }
revert TokenDivider__InsuficientBalance();
} balances[msg.sender][tokenInfo.erc20Address] -= amount; // token amount reduced from its holder on sell order even tho no one buy it
balances[msg.sender][tokenInfo.erc20Address] -= amount; // push sell order to array of sender
s_userToSellOrders[msg.sender].push(
s_userToSellOrders[msg.sender].push( SellOrder({seller: msg.sender, erc20Address: tokenInfo.erc20Address, price: price, amount: amount})
SellOrder({ );
seller: msg.sender,
erc20Address: tokenInfo.erc20Address, emit OrderPublished(amount, msg.sender, nftPegged);
price: price,
amount: amount // send erc20 token from owner to this contract
}) // @audit: what if the owner of ERC20 rejected this transaction?
); IERC20(tokenInfo.erc20Address).transferFrom(msg.sender, address(this), amount);
}
emit OrderPublished(amount,msg.sender, nftPegged);
/**
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 seller The person who is selling this tokens
*
/** * @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
* @param orderIndex The index of the order in all the orders array of the seller (the seller can have multiple orders active) */
* @param seller The person who is selling this tokens function buyOrder(uint256 orderIndex, address seller) external payable {
* if (seller == address(0)) {
* @dev when the buyer call this function, the eth or any token accepted to pay, is sent to the seller revert TokenDivider__InvalidSeller();
* if the transfer executed correctly, then this contract, wich has all the tokens, send the tokens to the msg.sender }
*/
SellOrder memory order = s_userToSellOrders[seller][orderIndex];
function buyOrder(uint256 orderIndex, address seller) external payable {
if(seller == address(0)) { // msg.value can be greater than order price?
revert TokenDivider__InvalidSeller(); if (msg.value < order.price) {
} revert TokenDivider__IncorrectEtherAmount();
}
// if price 100, then fee is 1
SellOrder memory order = s_userToSellOrders[seller][orderIndex]; uint256 fee = order.price / 100;
// if price 100, then seller fee is 0.5
if(msg.value < order.price) { uint256 sellerFee = fee / 2;
revert TokenDivider__IncorrectEtherAmount();
} if (msg.value < order.price + sellerFee) {
revert TokenDivider__InsuficientEtherForFees();
uint256 fee = order.price / 100; }
uint256 sellerFee = fee / 2;
// record erc20 amount on storage variable onchain
balances[msg.sender][order.erc20Address] += order.amount;
if(msg.value < order.price + sellerFee) {
revert TokenDivider__InsuficientEtherForFees(); // override the current orderIndex with tail, then pop the tail out of array
} s_userToSellOrders[seller][orderIndex] = s_userToSellOrders[seller][s_userToSellOrders[seller].length - 1];
s_userToSellOrders[seller].pop();
balances[msg.sender][order.erc20Address] += order.amount; emit OrderSelled(msg.sender, order.price);
s_userToSellOrders[seller][orderIndex] = s_userToSellOrders[seller][s_userToSellOrders[seller].length - 1]; // Transfer The Ether
s_userToSellOrders[seller].pop();
// send ether to seller, reduced with seller fee
emit OrderSelled(msg.sender, order.price); (bool success,) = payable(order.seller).call{value: (order.price - sellerFee)}("");
// Transfer The Ether if (!success) {
revert TokenDivider__TransferFailed();
(bool success, ) = payable(order.seller).call{value: (order.price - sellerFee)}(""); }
if(!success) { // send "fee" to "owner"?
revert TokenDivider__TransferFailed(); // @audit: if buyer needs to pay price + seller fee. then seller is given price - seller fee. does fee is guarranted to be paid by buyer?
} // @audit: who owner?
(bool taxSuccess,) = payable(owner()).call{value: fee}("");
(bool taxSuccess, ) = payable(owner()).call{value: fee}("");
if (!taxSuccess) {
revert TokenDivider__TransferFailed();
if(!taxSuccess) { }
revert TokenDivider__TransferFailed();
} // send erc20 token to buyer address
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; }
} }
}

View File

@@ -0,0 +1,83 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Test, console} from 'forge-std/Test.sol';
import {DeployTokenDivider} from 'script/DeployTokenDivider.s.sol';
import {TokenDivider} from 'src/TokenDivider.sol';
import {ERC721Mock} from '../mocks/ERC721Mock.sol';
import {ERC20Mock} from '@openzeppelin/contracts/mocks/token/ERC20Mock.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
contract TokenDiverTest is Test {
DeployTokenDivider deployer;
TokenDivider tokenDivider;
ERC721Mock erc721Mock;
address public USER = makeAddr("user");
address public USER2 = makeAddr("user2");
uint256 constant public STARTING_USER_BALANCE = 10e18;
uint256 constant public AMOUNT = 2e18;
uint256 constant public TOKEN_ID = 0;
function setUp() public {
deployer = new DeployTokenDivider();
tokenDivider = deployer.run();
erc721Mock = new ERC721Mock();
erc721Mock.mint(USER); // @auditor: user has the NFT
vm.deal(USER2, STARTING_USER_BALANCE); // @auditor: user2 got 10 ether
}
function test_assertSetUp() public {
// then what?
// USER has no ERC721 NFT of TOKEN_ID
assertTrue(address(erc721Mock.ownerOf(TOKEN_ID)) == address(USER2));
// USER has no balance
// USER2 has STARTING_USER_BALANCE
}
/*
function testDivideNft() public {
vm.startPrank(USER);
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
tokenDivider.divideNft(address(erc721Mock), TOKEN_ID, AMOUNT);
vm.stopPrank();
ERC20Mock erc20Mock = ERC20Mock(tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address);
console.log("ERC20 Token name is: ", erc20Mock.name());
console.log("ERC20 Token symbol is: ", erc20Mock.symbol());
assertEq(tokenDivider.getErc20TotalMintedAmount(address(erc20Mock)), AMOUNT);
assertEq(erc721Mock.ownerOf(TOKEN_ID), address(tokenDivider));
assertEq(tokenDivider.getBalanceOf(USER, address(erc20Mock)), AMOUNT);
}
modifier nftDivided() {
vm.startPrank(USER);
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
tokenDivider.divideNft(address(erc721Mock), TOKEN_ID, AMOUNT);
vm.stopPrank();
_;
}
function testDivideNftFailsIsSenderIsNotNftOwner() public {
vm.prank(USER);
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
vm.startPrank(USER2);
vm.expectRevert(TokenDivider.TokenDivider__NotFromNftOwner.selector);
tokenDivider.divideNft(address(erc721Mock), TOKEN_ID, AMOUNT);
vm.stopPrank();
}
*/
}

View File

@@ -27,8 +27,8 @@ contract TokenDiverTest is Test {
erc721Mock = new ERC721Mock(); erc721Mock = new ERC721Mock();
erc721Mock.mint(USER); erc721Mock.mint(USER); // @auditor: user has the NFT
vm.deal(USER2, STARTING_USER_BALANCE); vm.deal(USER2, STARTING_USER_BALANCE); // @auditor: user2 got 10 ether
} }
function testDivideNft() public { function testDivideNft() public {
@@ -151,4 +151,4 @@ contract TokenDiverTest is Test {
} }
} }
} }