Compare commits
9 Commits
audit-asah
...
audit
| Author | SHA1 | Date | |
|---|---|---|---|
| 7082e0bbc3 | |||
| 67295b4682 | |||
| 2443e5a16f | |||
| 4328ff75f4 | |||
| 964e75dfd6 | |||
| 4e07b8b28b | |||
| 32b6089241 | |||
| 385c78651f | |||
| 4a53c0dfa2 |
@ -105,6 +105,7 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
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
|
||||
// @audit-report: dependency has its own check
|
||||
|
||||
ERC20ToGenerateNftFraccion erc20Contract = new ERC20ToGenerateNftFraccion(
|
||||
string(abi.encodePacked(ERC721(nftAddress).name(), "Fraccion")),
|
||||
@ -112,16 +113,22 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
);
|
||||
|
||||
// @audit: can we mint again? out of this function?
|
||||
// @audit-finding: low
|
||||
// @audit-report: yes it can be minted again. It should not be able to do so.
|
||||
// @audit-report: but so far this contract only handle the recorded ERC20 transactions
|
||||
erc20Contract.mint(address(this), amount);
|
||||
address erc20 = address(erc20Contract);
|
||||
|
||||
IERC721(nftAddress).safeTransferFrom(msg.sender, address(this), tokenId, "");
|
||||
|
||||
// @audit: what if we send the NFT to other address during this period?
|
||||
// @audit-report: you cannot send the NFT to other address, related to unit testing test_failIf_sendNFTDuringDivide
|
||||
if (IERC721(nftAddress).ownerOf(tokenId) == msg.sender) revert TokenDivider__NftTransferFailed();
|
||||
|
||||
balances[msg.sender][erc20] = amount;
|
||||
// @audit: what happen if the given nft address already registered in this CA, then another token id register again?
|
||||
// @audit-finding: high (critical)
|
||||
// @audit-report: it overrides the previous divided NFT. The previously ERC20 tokens of divided NFT is no longer usable.
|
||||
nftToErc20Info[nftAddress] = ERC20Info({erc20Address: erc20, tokenId: tokenId});
|
||||
erc20ToMintedAmount[erc20] = amount;
|
||||
erc20ToNft[erc20] = nftAddress;
|
||||
@ -142,17 +149,22 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
* giving to the contract all the erc20 and it will give you back the nft
|
||||
*/
|
||||
function claimNft(address nftAddress) external {
|
||||
// @audit: check with "counterfeit" token
|
||||
// @audit-report: the "counterfeit" or extra minted token is not registered in this contract so its not working. tested test_failIf_claimNftWithCounterfeitNFT
|
||||
if (nftAddress == address(0)) {
|
||||
revert TokenDivider__NftAddressIsZero();
|
||||
}
|
||||
|
||||
// @audit: what if the nft address is not stored?
|
||||
// @audit-report: it returns weird error but you can't claim nft with inexistence nft address test_failIf_notExistingNftAddress
|
||||
ERC20Info storage tokenInfo = nftToErc20Info[nftAddress];
|
||||
|
||||
if (balances[msg.sender][tokenInfo.erc20Address] < erc20ToMintedAmount[tokenInfo.erc20Address]) {
|
||||
revert TokenDivider__NotEnoughErc20Balance();
|
||||
}
|
||||
|
||||
// @audit: what if token already sent to other address through ERC20 send function
|
||||
// @audit-report: there is an error which doesn't procceed the transaction further and it reverts
|
||||
ERC20ToGenerateNftFraccion(tokenInfo.erc20Address).burnFrom(
|
||||
msg.sender, erc20ToMintedAmount[tokenInfo.erc20Address]
|
||||
);
|
||||
@ -163,6 +175,8 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
emit NftClaimed(nftAddress);
|
||||
|
||||
// @audit: what happen if there is overlaps in same NFT address with different token id? will someone get a different NFT?
|
||||
// @audit-finding: high
|
||||
// @audit-report: you cannot claim the previous NFT with a weird error
|
||||
IERC721(nftAddress).safeTransferFrom(address(this), msg.sender, tokenInfo.tokenId);
|
||||
}
|
||||
|
||||
@ -175,6 +189,7 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
* @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 {
|
||||
// @audit: check with "counterfeit" token
|
||||
if (nftAddress == address(0)) {
|
||||
revert TokenDivider__NftAddressIsZero();
|
||||
}
|
||||
@ -215,6 +230,7 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
* to this contract, then a new order is created and published.
|
||||
*/
|
||||
function sellErc20(address nftPegged, uint256 price, uint256 amount) external {
|
||||
// @audit: check with "counterfeit" token
|
||||
if (nftPegged == address(0)) {
|
||||
revert TokenDivider__NftAddressIsZero();
|
||||
}
|
||||
@ -222,8 +238,8 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
if (amount == 0) {
|
||||
revert TokenDivider__AmountCantBeZero();
|
||||
}
|
||||
|
||||
// @audit: no check for 0 price?
|
||||
// @audit-report: no check but it successfully transfer the given amount without transfering the given ether
|
||||
|
||||
ERC20Info memory tokenInfo = nftToErc20Info[nftPegged];
|
||||
if (balances[msg.sender][tokenInfo.erc20Address] < amount) {
|
||||
@ -253,6 +269,7 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
* 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 {
|
||||
// @audit: check with "counterfeit" token
|
||||
if (seller == address(0)) {
|
||||
revert TokenDivider__InvalidSeller();
|
||||
}
|
||||
@ -293,7 +310,9 @@ contract TokenDivider is IERC721Receiver, Ownable {
|
||||
|
||||
// send "fee" to "owner"?
|
||||
// @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-report: yes it does. if the price is 0, then there is no value increase
|
||||
// @audit: who owner?
|
||||
// @audit-report: the deployer
|
||||
(bool taxSuccess,) = payable(owner()).call{value: fee}("");
|
||||
|
||||
if (!taxSuccess) {
|
||||
|
||||
293
test/unit/TokenDividerTest.audit.claimNft.t.sol
Normal file
293
test/unit/TokenDividerTest.audit.claimNft.t.sol
Normal file
@ -0,0 +1,293 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.18;
|
||||
|
||||
import {Test} 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';
|
||||
|
||||
contract TokenDividerClaimTest 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);
|
||||
|
||||
// divide the ERC721 and mint ERC20
|
||||
vm.startPrank(USER);
|
||||
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
TOKEN_ID,
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_assertSetUp() public {
|
||||
erc721Mock.mint(USER);
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
// assert if NFT already moved to tokenDivider
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(tokenDivider)
|
||||
);
|
||||
|
||||
// assert if USER has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT
|
||||
);
|
||||
}
|
||||
|
||||
function test_claimNftSuccessfully() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
vm.startPrank(USER);
|
||||
erc20Mock.approve(address(tokenDivider), AMOUNT);
|
||||
tokenDivider.claimNft(address(erc721Mock));
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
address(USER),
|
||||
address(erc20Mock)
|
||||
),
|
||||
0
|
||||
);
|
||||
assertEq(
|
||||
tokenDivider.getErc20TotalMintedAmount(
|
||||
address(erc20Mock)
|
||||
),
|
||||
0
|
||||
);
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(USER)
|
||||
);
|
||||
assertEq(
|
||||
erc20Mock.balanceOf(
|
||||
address(USER)
|
||||
),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
function test_failIf_claimNftWithCounterfeitNFT() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
vm.startPrank(USER2);
|
||||
erc20Mock.mint(address(USER2), AMOUNT);
|
||||
erc20Mock.approve(address(tokenDivider), AMOUNT);
|
||||
vm.expectRevert(
|
||||
TokenDivider.TokenDivider__NotEnoughErc20Balance.selector
|
||||
);
|
||||
tokenDivider.claimNft(address(erc721Mock));
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_failIf_notExistingNftAddress() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
vm.startPrank(USER);
|
||||
erc20Mock.approve(address(tokenDivider), AMOUNT);
|
||||
tokenDivider.claimNft(address(1));
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
address(USER),
|
||||
address(erc20Mock)
|
||||
),
|
||||
0
|
||||
);
|
||||
assertEq(
|
||||
tokenDivider.getErc20TotalMintedAmount(
|
||||
address(erc20Mock)
|
||||
),
|
||||
0
|
||||
);
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(USER)
|
||||
);
|
||||
assertEq(
|
||||
erc20Mock.balanceOf(
|
||||
address(USER)
|
||||
),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
function test_failIf_ERC20SentToOtherOnClaim() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
assertEq(
|
||||
erc20Mock.balanceOf(
|
||||
address(USER)
|
||||
),
|
||||
AMOUNT
|
||||
);
|
||||
|
||||
vm.startPrank(USER);
|
||||
erc20Mock.approve(address(tokenDivider), AMOUNT);
|
||||
erc20Mock.transfer(
|
||||
address(USER2),
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
address(USER),
|
||||
address(erc20Mock)
|
||||
),
|
||||
AMOUNT
|
||||
);
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
address(USER2),
|
||||
address(erc20Mock)
|
||||
),
|
||||
0
|
||||
);
|
||||
|
||||
vm.startPrank(USER);
|
||||
erc20Mock.approve(address(tokenDivider), AMOUNT);
|
||||
vm.expectRevert();
|
||||
tokenDivider.claimNft(address(erc721Mock));
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_failIf_claimOverlappingNFTWithDifferentId() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
// mint ERC721 with new TOKEN_ID
|
||||
erc721Mock.mint(USER2);
|
||||
uint256 newTokenId = TOKEN_ID + 1;
|
||||
|
||||
vm.startPrank(USER2);
|
||||
erc721Mock.approve(address(tokenDivider), newTokenId);
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
newTokenId,
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
ERC20Mock erc20MockWithNewTokenId = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
// assert if NFT already moved to tokenDivider
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(tokenDivider)
|
||||
);
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(newTokenId),
|
||||
address(tokenDivider)
|
||||
);
|
||||
// assert if USER2 has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
USER2,
|
||||
address(erc20MockWithNewTokenId)
|
||||
),
|
||||
AMOUNT
|
||||
);
|
||||
|
||||
// USER2 claim NFT
|
||||
vm.startPrank(USER2);
|
||||
erc20MockWithNewTokenId.approve(
|
||||
address(tokenDivider),
|
||||
AMOUNT
|
||||
);
|
||||
tokenDivider.claimNft(address(erc721Mock));
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(newTokenId),
|
||||
address(USER2)
|
||||
);
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(tokenDivider)
|
||||
);
|
||||
|
||||
// assert ERC20 address before USER claim NFT
|
||||
// @audit-report: it returns different erc20 address
|
||||
// @dev: feel free to uncomment to see the error
|
||||
/*
|
||||
assertEq(
|
||||
address(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
),
|
||||
address(erc20Mock)
|
||||
);
|
||||
*/
|
||||
|
||||
// USER claim NFT
|
||||
vm.startPrank(USER2);
|
||||
erc20MockWithNewTokenId.approve(
|
||||
address(tokenDivider),
|
||||
AMOUNT
|
||||
);
|
||||
// @dev: it weirdly returns ERC721InsufficientApproval
|
||||
// despite its already approved in above.
|
||||
// maybe because of different ERC20 address?
|
||||
// @dev: changing the ERC20 address and the USER prank
|
||||
// doesn't help
|
||||
/*
|
||||
tokenDivider.claimNft(address(erc721Mock));
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(USER)
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
// 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';
|
||||
@ -9,8 +8,7 @@ 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 {
|
||||
contract TokenDividerDivideTest is Test {
|
||||
DeployTokenDivider deployer;
|
||||
TokenDivider tokenDivider;
|
||||
ERC721Mock erc721Mock;
|
||||
@ -31,53 +29,251 @@ contract TokenDiverTest is Test {
|
||||
vm.deal(USER2, STARTING_USER_BALANCE); // @auditor: user2 got 10 ether
|
||||
}
|
||||
|
||||
function test_assertSetUp() public {
|
||||
// then what?
|
||||
function test_assertSetUp() public view {
|
||||
// USER has the ERC721 NFT of TOKEN_ID
|
||||
assertTrue(address(erc721Mock.ownerOf(TOKEN_ID)) == address(USER));
|
||||
|
||||
// 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
|
||||
// USER2 has no ERC721 NFT of TOKEN_ID
|
||||
assertTrue(address(erc721Mock.ownerOf(TOKEN_ID)) != address(USER2));
|
||||
}
|
||||
|
||||
/*
|
||||
function testDivideNft() public {
|
||||
|
||||
function test_divideNftSuccessfully() public {
|
||||
vm.startPrank(USER);
|
||||
|
||||
// approve the NFT to receive the NFT in 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);
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
TOKEN_ID,
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
_;
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address
|
||||
);
|
||||
|
||||
// assert if NFT already moved to tokenDivider
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(tokenDivider)
|
||||
);
|
||||
// assert if USER has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT
|
||||
);
|
||||
}
|
||||
|
||||
function testDivideNftFailsIsSenderIsNotNftOwner() public {
|
||||
vm.prank(USER);
|
||||
function test_failIf_sendNFTDuringDivide() public {
|
||||
vm.startPrank(USER);
|
||||
|
||||
// approve the NFT to receive the NFT in USER
|
||||
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
|
||||
|
||||
// send the NFT to USER2
|
||||
erc721Mock.safeTransferFrom(
|
||||
address(USER),
|
||||
address(USER2),
|
||||
TOKEN_ID
|
||||
);
|
||||
|
||||
vm.expectRevert(
|
||||
TokenDivider.TokenDivider__NotFromNftOwner.selector
|
||||
);
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
TOKEN_ID,
|
||||
AMOUNT
|
||||
);
|
||||
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_sendNFTBeforeDivide() public {
|
||||
vm.startPrank(USER);
|
||||
|
||||
// send the NFT to USER2
|
||||
erc721Mock.safeTransferFrom(
|
||||
address(USER),
|
||||
address(USER2),
|
||||
TOKEN_ID
|
||||
);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
vm.startPrank(USER2);
|
||||
vm.expectRevert(TokenDivider.TokenDivider__NotFromNftOwner.selector);
|
||||
tokenDivider.divideNft(address(erc721Mock), TOKEN_ID, AMOUNT);
|
||||
|
||||
// approve the NFT to receive the NFT in USER
|
||||
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
|
||||
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
TOKEN_ID,
|
||||
AMOUNT
|
||||
);
|
||||
|
||||
vm.stopPrank();
|
||||
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address
|
||||
);
|
||||
|
||||
// assert if USER2 has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER2, address(erc20Mock)),
|
||||
AMOUNT
|
||||
);
|
||||
}
|
||||
|
||||
function test_failIf_nonExistenceNftTokenId() public {
|
||||
vm.startPrank(USER);
|
||||
|
||||
// approve the NFT to receive the NFT in USER
|
||||
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
|
||||
|
||||
vm.expectRevert();
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
404,
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
}
|
||||
*/
|
||||
|
||||
function test_failWhen_reMintTheDividedERC20Token() public {
|
||||
vm.startPrank(USER);
|
||||
|
||||
// approve the NFT to receive the NFT in USER
|
||||
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
|
||||
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
TOKEN_ID,
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address
|
||||
);
|
||||
|
||||
// assert if USER has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT
|
||||
);
|
||||
|
||||
vm.startPrank(USER2);
|
||||
erc20Mock.mint(address(USER2), AMOUNT);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
USER,
|
||||
address(erc20Mock)
|
||||
),
|
||||
AMOUNT
|
||||
);
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
USER2,
|
||||
address(erc20Mock)
|
||||
),
|
||||
erc20Mock.balanceOf(address(USER2))
|
||||
);
|
||||
assertEq(
|
||||
erc20Mock.totalSupply(),
|
||||
AMOUNT
|
||||
);
|
||||
}
|
||||
|
||||
function test_divideSameNFTAddressWithDifferentTokenId() public {
|
||||
vm.startPrank(USER);
|
||||
|
||||
// approve the NFT to receive the NFT in USER
|
||||
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
|
||||
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
TOKEN_ID,
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address
|
||||
);
|
||||
|
||||
// assert if NFT already moved to tokenDivider
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(tokenDivider)
|
||||
);
|
||||
// assert if USER has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT
|
||||
);
|
||||
|
||||
uint256 counterfeitTokenId = TOKEN_ID + 1;
|
||||
erc721Mock.mint(USER2);
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(counterfeitTokenId),
|
||||
address(USER2)
|
||||
);
|
||||
|
||||
// USER2 divide the counterfeit
|
||||
vm.startPrank(USER2);
|
||||
|
||||
// approve the NFT to receive the NFT in USER2
|
||||
erc721Mock.approve(
|
||||
address(tokenDivider),
|
||||
counterfeitTokenId
|
||||
);
|
||||
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
counterfeitTokenId,
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
// now both of users (USER and USER2) has the same amount of registered ERC20 tokens despite it refer to different NFT Token Id
|
||||
|
||||
ERC20Mock erc20MockCounterfeit = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address
|
||||
);
|
||||
// assert if NFT already moved to tokenDivider
|
||||
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(counterfeitTokenId),
|
||||
address(tokenDivider)
|
||||
);
|
||||
|
||||
// assert if previous NFT is still being hold by contract
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(tokenDivider)
|
||||
);
|
||||
|
||||
// assert if USER2 has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
USER2,
|
||||
address(erc20MockCounterfeit)
|
||||
),
|
||||
AMOUNT
|
||||
);
|
||||
// assert if USER has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(
|
||||
USER,
|
||||
address(erc20MockCounterfeit)
|
||||
),
|
||||
AMOUNT
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
237
test/unit/TokenDividerTest.audit.sellErc20.t.sol
Normal file
237
test/unit/TokenDividerTest.audit.sellErc20.t.sol
Normal file
@ -0,0 +1,237 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.18;
|
||||
|
||||
import {Test} 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';
|
||||
|
||||
contract TokenDividerSellTest 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);
|
||||
|
||||
// divide the ERC721 and mint ERC20
|
||||
vm.startPrank(USER);
|
||||
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
|
||||
tokenDivider.divideNft(
|
||||
address(erc721Mock),
|
||||
TOKEN_ID,
|
||||
AMOUNT
|
||||
);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function test_assertSetUp() public {
|
||||
erc721Mock.mint(USER);
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
// assert if NFT already moved to tokenDivider
|
||||
assertEq(
|
||||
erc721Mock.ownerOf(TOKEN_ID),
|
||||
address(tokenDivider)
|
||||
);
|
||||
|
||||
// assert if USER has the ERC20 token
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT
|
||||
);
|
||||
}
|
||||
|
||||
function test_sellErc20() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
uint256 sellAmount = 1;
|
||||
vm.startPrank(USER);
|
||||
erc20Mock.approve(address(tokenDivider), sellAmount);
|
||||
tokenDivider.sellErc20(
|
||||
address(erc721Mock),
|
||||
1,
|
||||
sellAmount
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT - 1
|
||||
);
|
||||
}
|
||||
|
||||
function test_sellErc20WithZeroPrice() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
uint256 sellAmount = 1e17;
|
||||
uint256 sellPrice = 0;
|
||||
vm.startPrank(USER);
|
||||
erc20Mock.approve(address(tokenDivider), sellAmount);
|
||||
tokenDivider.sellErc20(
|
||||
address(erc721Mock),
|
||||
sellPrice,
|
||||
sellAmount
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT - sellAmount
|
||||
);
|
||||
|
||||
assertEq(
|
||||
sellPrice,
|
||||
tokenDivider.getOrderPrice(
|
||||
address(USER),
|
||||
0 // index of order price
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function test_sellAndBuyErc20() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
uint256 sellAmount = 1e17;
|
||||
uint256 sellPrice = 1 ether;
|
||||
vm.startPrank(USER);
|
||||
erc20Mock.approve(address(tokenDivider), sellAmount);
|
||||
tokenDivider.sellErc20(
|
||||
address(erc721Mock),
|
||||
sellPrice,
|
||||
sellAmount
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT - sellAmount
|
||||
);
|
||||
|
||||
uint256 buyPrice = sellPrice + ((sellPrice / 100) / 2);
|
||||
vm.deal(
|
||||
address(USER2),
|
||||
buyPrice
|
||||
);
|
||||
|
||||
uint256 contractInitialBalance = address(
|
||||
tokenDivider.owner()
|
||||
).balance;
|
||||
assertEq(
|
||||
address(USER).balance,
|
||||
0
|
||||
);
|
||||
|
||||
vm.startPrank(USER2);
|
||||
tokenDivider.buyOrder{value: buyPrice}(
|
||||
0,
|
||||
address(USER)
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
erc20Mock.balanceOf(address(USER2)),
|
||||
sellAmount
|
||||
);
|
||||
uint256 sellerFee = buyPrice - sellPrice;
|
||||
assertEq(
|
||||
address(
|
||||
tokenDivider.owner()
|
||||
).balance - contractInitialBalance,
|
||||
sellerFee * 2
|
||||
);
|
||||
assertEq(
|
||||
address(USER).balance,
|
||||
sellPrice - sellerFee
|
||||
);
|
||||
}
|
||||
|
||||
function test_sellAndBuyErc20With0Price() public {
|
||||
ERC20Mock erc20Mock = ERC20Mock(
|
||||
tokenDivider.getErc20InfoFromNft(
|
||||
address(erc721Mock)
|
||||
).erc20Address
|
||||
);
|
||||
|
||||
uint256 sellAmount = 1e17;
|
||||
uint256 sellPrice = 0 ether;
|
||||
vm.startPrank(USER);
|
||||
erc20Mock.approve(address(tokenDivider), sellAmount);
|
||||
tokenDivider.sellErc20(
|
||||
address(erc721Mock),
|
||||
sellPrice,
|
||||
sellAmount
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
tokenDivider.getBalanceOf(USER, address(erc20Mock)),
|
||||
AMOUNT - sellAmount
|
||||
);
|
||||
|
||||
uint256 buyPrice = sellPrice + ((sellPrice / 100) / 2);
|
||||
vm.deal(
|
||||
address(USER2),
|
||||
buyPrice
|
||||
);
|
||||
|
||||
uint256 contractInitialBalance = address(
|
||||
tokenDivider.owner()
|
||||
).balance;
|
||||
assertEq(
|
||||
address(USER).balance,
|
||||
0
|
||||
);
|
||||
|
||||
vm.startPrank(USER2);
|
||||
tokenDivider.buyOrder{value: buyPrice}(
|
||||
0,
|
||||
address(USER)
|
||||
);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(
|
||||
erc20Mock.balanceOf(address(USER2)),
|
||||
sellAmount
|
||||
);
|
||||
uint256 sellerFee = buyPrice - sellPrice;
|
||||
assertEq(
|
||||
address(
|
||||
tokenDivider.owner()
|
||||
).balance - contractInitialBalance,
|
||||
sellerFee * 2
|
||||
);
|
||||
assertEq(
|
||||
address(USER).balance,
|
||||
sellPrice - sellerFee
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user