diff --git a/contracts/core/SharesERC1155.sol b/contracts/core/SharesERC1155.sol index 8ff610f..392ab5a 100644 --- a/contracts/core/SharesERC1155.sol +++ b/contracts/core/SharesERC1155.sol @@ -11,6 +11,7 @@ import { IShare } from "../interface/IShare.sol"; contract SharesERC1155 is ERC1155Supply, Ownable, IShare { address public _FACTORY_; string private _baseURI; + mapping(uint256 => string) public tokenURIs; event Mint(address indexed user, uint256 indexed id, uint256 amount); event Burn(address indexed user, uint256 indexed id, uint256 amount); @@ -32,6 +33,10 @@ contract SharesERC1155 is ERC1155Supply, Ownable, IShare { _baseURI = newURI; } + function setTokenURI(uint256 tokenId, string memory tokenURI) public onlyFactory { + tokenURIs[tokenId] = tokenURI; + } + function shareMint(address to, uint256 id, uint256 amount) public onlyFactory { _mint(to, id, amount, ""); emit Mint(to, id, amount); diff --git a/contracts/core/SharesFactoryV1.sol b/contracts/core/SharesFactoryV1.sol index 0504921..f645c94 100644 --- a/contracts/core/SharesFactoryV1.sol +++ b/contracts/core/SharesFactoryV1.sol @@ -48,7 +48,7 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard { event ClaimYield(uint256 amount, address indexed to); event SetCurve(uint8 indexed curveType); event SetFee(uint256 indexed feePercent, string feeType); - event Mint(uint256 indexed id, address indexed creator, uint8 indexed curveType); + event Mint(uint256 indexed id, address indexed creator, uint8 indexed curveType, string uri); event Buy(uint256 indexed id, address indexed buyer, uint32 quantity, uint256 totalPrice); event Sell(uint256 indexed id, address indexed seller, uint32 quantity, uint256 totalPrice); @@ -182,10 +182,11 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard { * @notice Mint a share and buy it in one transaction. * @param curveType The type of the curve. * @param quantity The quantity of shares. + * @param uri The URI of the share. Arweave, IPFS or any permanent id. * @param referral The address of the referral fee recipient. */ - function mintAndBuyShare(uint8 curveType, uint32 quantity, address referral) public payable { - mintShare(curveType); + function mintAndBuyShare(uint8 curveType, uint32 quantity, string memory uri, address referral) public payable { + mintShare(curveType, uri); buyShare(shareIndex - 1, quantity, referral); } @@ -193,13 +194,14 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard { * @notice Mint a share with an auto-incremented ID. * @dev The share ID is identical to the ERC1155 ID. */ - function mintShare(uint8 curveType) public { + function mintShare(uint8 curveType, string memory uri) public { require(curvesMap[curveType].exists, "Invalid curveType"); Share memory newShare = Share({ creator: msg.sender, curveType: curveType }); sharesMap[shareIndex] = newShare; + IShare(ERC1155).setTokenURI(shareIndex, uri); - emit Mint(shareIndex, msg.sender, curveType); + emit Mint(shareIndex, msg.sender, curveType, uri); shareIndex++; } diff --git a/contracts/interface/IShare.sol b/contracts/interface/IShare.sol index 0a9d9cc..99b0616 100644 --- a/contracts/interface/IShare.sol +++ b/contracts/interface/IShare.sol @@ -3,6 +3,8 @@ pragma solidity 0.8.25; interface IShare { + function setTokenURI(uint256 tokenId, string memory tokenURI) external; + function shareMint(address to, uint256 id, uint256 amount) external; function shareBurn(address from, uint256 id, uint256 amount) external; diff --git a/test/integration/BaseIntegrationTest.t.sol b/test/integration/BaseIntegrationTest.t.sol index e44202b..da467b0 100644 --- a/test/integration/BaseIntegrationTest.t.sol +++ b/test/integration/BaseIntegrationTest.t.sol @@ -132,7 +132,7 @@ contract BaseIntegrationTest is Test { vm.deal(LONG_TERM_TRADER, 100 ether); vm.prank(DAILY_TRADER); - sharesFactory.mintShare(DEFAULT_CURVE_TYPE); + sharesFactory.mintShare(DEFAULT_CURVE_TYPE, ''); uint256 shareId = sharesFactory.shareIndex() - 1; for (uint32 i = 0; i < tradeCount; i++) { diff --git a/test/integration/IncomeSimulator.t.sol b/test/integration/IncomeSimulator.t.sol index 0126195..15ddbaf 100644 --- a/test/integration/IncomeSimulator.t.sol +++ b/test/integration/IncomeSimulator.t.sol @@ -216,7 +216,7 @@ contract IncomeSimulator is BaseIntegrationTest, LogUtil { // mint shares vm.prank(FACTORY_OWNER); - sharesFactory.mintShare(curveType); + sharesFactory.mintShare(curveType, ''); // record start balance uint256 founderBalanceBefore = aWETH.balanceOf(address(SHARES_FOUNDER)); diff --git a/test/integration/handlers/BoundedIntegrationContextHandler.t.sol b/test/integration/handlers/BoundedIntegrationContextHandler.t.sol index dd654be..16b1836 100644 --- a/test/integration/handlers/BoundedIntegrationContextHandler.t.sol +++ b/test/integration/handlers/BoundedIntegrationContextHandler.t.sol @@ -56,7 +56,7 @@ contract BoundedIntegrationContextHandler is StdUtils { uint256 shareId = 0; vm.prank(factoryOwner); - sharesFactory.mintShare(0); + sharesFactory.mintShare(0, ''); vm.prank(TRADER); diff --git a/test/unit/SharesERC1155.t.sol b/test/unit/SharesERC1155.t.sol index ba1b10d..179ab6e 100644 --- a/test/unit/SharesERC1155.t.sol +++ b/test/unit/SharesERC1155.t.sol @@ -37,6 +37,17 @@ contract ERC1155Tests is BaseTest { assertEq(userBal, 10); } + function test_setTokenURI() public { + vm.expectRevert(bytes("Caller is not the factory")); + sharesNFT.setTokenURI(0, "https://vv.com/0"); + + vm.prank(address(sharesFactory)); + sharesNFT.setTokenURI(0, "https://vv.com/0"); + + string memory tokenURI = sharesNFT.tokenURIs(0); + assertEq(tokenURI, "https://vv.com/0"); + } + function test_shareMint() public { vm.prank(address(sharesFactory)); sharesNFT.shareMint(addrUser, 0, 10); diff --git a/test/unit/SharesFactory.t.sol b/test/unit/SharesFactory.t.sol index f24b664..369a419 100644 --- a/test/unit/SharesFactory.t.sol +++ b/test/unit/SharesFactory.t.sol @@ -8,6 +8,8 @@ import { IYieldAggregator } from "contracts/interface/IYieldAggregator.sol"; import { BaseTest } from "../BaseTest.t.sol"; contract SharesFactoryTests is BaseTest { + string private constant URI = "https://vv.com/uri/"; + function setUp() public { createFactory(); _setUpShare(); @@ -19,7 +21,7 @@ contract SharesFactoryTests is BaseTest { // Alice mint & buy 1 share with 0 id vm.prank(addrAlice); - sharesFactory.mintShare(defaultCurveType); + sharesFactory.mintShare(defaultCurveType, URI); _buyShare(addrAlice, 0, 1, referralReceiver); // Bob mintAndBuy 1 share with 1 id @@ -39,16 +41,18 @@ contract SharesFactoryTests is BaseTest { function test_mintShare() public { vm.prank(addrAlice); - sharesFactory.mintShare(defaultCurveType); + sharesFactory.mintShare(defaultCurveType, URI); uint256 shareIndex = sharesFactory.shareIndex(); (address creator, uint8 curveType) = sharesFactory.getShare(shareIndex - 1); + string memory tokenURI = sharesNFT.tokenURIs(shareIndex - 1); assertEq(creator, addrAlice); assertEq(curveType, defaultCurveType); + assertEq(tokenURI, URI); vm.expectRevert(bytes("Invalid curveType")); - sharesFactory.mintShare(99); + sharesFactory.mintShare(99, URI); } function test_minAndBuyShare() public { @@ -59,10 +63,12 @@ contract SharesFactoryTests is BaseTest { uint256 shareId = sharesFactory.shareIndex() - 1; uint256 bobShareBal = sharesNFT.shareBalanceOf(addrBob, shareId); (address creator, uint8 curveType) = sharesFactory.getShare(shareId); + string memory tokenURI = sharesNFT.tokenURIs(shareId); assertEq(creator, addrBob); assertEq(curveType, defaultCurveType); assertEq(bobShareBal, 99); + assertEq(tokenURI, URI); } function test_buyShares() public { @@ -534,7 +540,7 @@ contract SharesFactoryTests is BaseTest { uint256 buyPrice = sharesFactory.getSubTotal(0, quantity, curveType); vm.prank(address(sender)); - sharesFactory.mintAndBuyShare{ value: buyPrice * 110 / 100 }(curveType, quantity, referral); + sharesFactory.mintAndBuyShare{ value: buyPrice * 110 / 100 }(curveType, quantity, URI, referral); } function _buyShare(address sender, uint256 shareId, uint32 quantity, address referral) internal {