Compiling and Testing Contracts
Last updated
Last updated
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import "forge-std/Test.sol";
import {Whitelist} from "../src/Whitelist.sol";
contract WhitelistTest is Test {
Whitelist wl;
address INVESTOR_A = vm.addr(1);
address INVESTOR_B = vm.addr(2);
address PROJECT_A = address(0xA1);
address PROJECT_B = address(0xB2);
event Approved(address indexed project, address indexed investor);
event Revoked(address indexed project, address indexed investor);
function setUp() public {
wl = new Whitelist();
}
function test_defaultFalse() public view {
assertFalse(wl.isApproved(PROJECT_A, INVESTOR_A));
assertFalse(wl.isApproved(PROJECT_A, INVESTOR_B));
}
function test_approve() public {
vm.expectEmit(true, true, false, false);
emit Approved(PROJECT_A, INVESTOR_A);
wl.approve(PROJECT_A, INVESTOR_A);
assertTrue(wl.isApproved(PROJECT_A, INVESTOR_A));
}
function test_revoke() public {
wl.approve(PROJECT_A, INVESTOR_A);
vm.expectEmit(true, true, false, false);
emit Revoked(PROJECT_A, INVESTOR_A);
wl.revoke(PROJECT_A, INVESTOR_A);
assertFalse(wl.isApproved(PROJECT_A, INVESTOR_A));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import "forge-std/Test.sol";
import {RealEstateFactory} from "../src/RealEstateFactory.sol";
import {RealEstateToken} from "../src/RealEstateToken.sol";
interface IWhiteList {
function isApproved(address, address) external view returns (bool);
}
contract MockWhitelistAlwaysTrue is IWhiteList {
function isApproved(address, address) external pure returns (bool) {
return true;
}
}
contract RealEstateFactoryTest is Test {
RealEstateFactory factory;
MockWhitelistAlwaysTrue wl;
address INVESTOR_A = vm.addr(1); // used as issuer in one test
address INVESTOR_B = vm.addr(2);
string NAME = "Project A";
string SYMBOL = "PJA";
string PROPERTY_ID = "PJA-001";
string JURIS = "AU";
string META_URI = "ipfs://cid-a";
function setUp() public {
wl = new MockWhitelistAlwaysTrue();
factory = new RealEstateFactory(address(wl));
}
function test_constructor() public view {
assertEq(factory.whitelist(), address(wl));
assertEq(factory.owner(), address(this));
}
function test_deployProject() public {
address tokenAddr = factory.deployProject(
NAME,
SYMBOL,
PROPERTY_ID,
JURIS,
META_URI
);
assertTrue(tokenAddr != address(0));
address[] memory all = factory.getAllProjects();
assertEq(all.length, 1);
assertEq(all[0], tokenAddr);
RealEstateToken t = RealEstateToken(tokenAddr);
assertEq(t.name(), NAME);
assertEq(t.symbol(), SYMBOL);
assertEq(t.propertyId(), PROPERTY_ID);
assertEq(t.jurisdiction(), JURIS);
assertEq(t.metadataUri(), META_URI);
assertEq(address(t.whitelist()), address(wl));
assertEq(t.owner(), address(this));
}
function test_multipleDeployers() public {
vm.prank(INVESTOR_A);
address a = factory.deployProject("A", "A", "A", "AU", "ipfs://a");
vm.prank(INVESTOR_B);
address b = factory.deployProject("B", "B", "B", "AU", "ipfs://b");
address[] memory all = factory.getAllProjects();
assertEq(all.length, 2);
assertEq(RealEstateToken(a).owner(), INVESTOR_A);
assertEq(RealEstateToken(b).owner(), INVESTOR_B);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import "forge-std/Test.sol";
import {RealEstateToken} from "../src/RealEstateToken.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
interface IWhiteList {
function isApproved(
address project,
address investor
) external view returns (bool);
}
contract MockWhitelist is IWhiteList {
mapping(address => mapping(address => bool)) public approved;
function set(address project, address investor, bool ok) external {
approved[project][investor] = ok;
}
function isApproved(
address project,
address investor
) external view returns (bool) {
return approved[project][investor];
}
}
contract RealEstateTokenTest is Test {
MockWhitelist wl;
RealEstateToken token;
address INVESTOR_A = vm.addr(1);
address INVESTOR_B = vm.addr(2);
string NAME = "Project A";
string SYMBOL = "PJA";
string PROPERTY_ID = "PJA-001";
string JURIS = "AU";
string META_URI = "ipfs://cid-a";
function setUp() public {
wl = new MockWhitelist();
token = new RealEstateToken(
NAME,
SYMBOL,
address(wl),
PROPERTY_ID,
JURIS,
META_URI,
address(this) // project owner
);
}
function test_metadata() public view {
assertEq(token.name(), NAME);
assertEq(token.symbol(), SYMBOL);
assertEq(token.propertyId(), PROPERTY_ID);
assertEq(token.jurisdiction(), JURIS);
assertEq(token.metadataUri(), META_URI);
assertEq(token.decimals(), 0);
assertEq(token.MAX_SUPPLY(), 100);
assertEq(address(token.whitelist()), address(wl));
assertEq(token.owner(), address(this));
}
function test_mintWhenApproved() public {
wl.set(address(token), INVESTOR_A, true);
token.mint(INVESTOR_A, 10);
assertEq(token.totalSupply(), 10);
assertEq(token.balanceOf(INVESTOR_A), 10);
assertTrue(token.isShareholder(INVESTOR_A));
}
function test_mintWhenNotApprovedReverts() public {
vm.expectRevert(bytes("Investor not approved for this project."));
token.mint(INVESTOR_A, 1);
}
function test_transferRequiresWhitelist() public {
wl.set(address(token), INVESTOR_A, true);
wl.set(address(token), INVESTOR_B, true);
token.mint(INVESTOR_A, 5);
vm.prank(INVESTOR_A);
token.transfer(INVESTOR_B, 3);
assertEq(token.balanceOf(INVESTOR_A), 2);
assertEq(token.balanceOf(INVESTOR_B), 3);
assertTrue(token.isShareholder(INVESTOR_B));
}
function test_transferToUnapprovedReverts() public {
wl.set(address(token), INVESTOR_A, true);
token.mint(INVESTOR_A, 1);
// INVESTOR_B not approved
vm.prank(INVESTOR_A);
vm.expectRevert(bytes("Recipient is not approved for this project"));
token.transfer(INVESTOR_B, 1);
}
function test_adminBurnOnlyOwner() public {
wl.set(address(token), INVESTOR_A, true);
token.mint(INVESTOR_A, 4);
// non-owner cannot call adminBurn
vm.prank(INVESTOR_A);
vm.expectRevert(
abi.encodeWithSelector(
Ownable.OwnableUnauthorizedAccount.selector,
INVESTOR_A
)
);
token.adminBurn(INVESTOR_A, 1);
// owner can burn
token.adminBurn(INVESTOR_A, 2);
assertEq(token.totalSupply(), 2);
assertEq(token.balanceOf(INVESTOR_A), 2);
}
}