-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
174 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Compatible with OpenZeppelin Contracts ^5.0.0 | ||
pragma solidity ^0.8.20; | ||
|
||
import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; | ||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import {ERC1155Supply} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol"; | ||
|
||
contract BondNFT is ERC1155, Ownable, ERC1155Supply { | ||
struct Metadata { | ||
uint256 value; | ||
uint256 couponValue; | ||
uint256 issueTimestamp; | ||
uint256 expirationTimestamp; | ||
string ISIN; | ||
} | ||
|
||
// Custom event | ||
event MintAllowanceSet(address user, uint256 id, uint256 allowedAmount); | ||
|
||
// Custom errors | ||
error NftMintingNotAllowed(); | ||
error NftMintingLimitExceeded(uint256); | ||
error NftInsufficientBalanceToBurn(); | ||
|
||
mapping(uint256 => Metadata) public metadata; | ||
|
||
// Mapping to store the allowed mints per user per token ID | ||
mapping(address => mapping(uint256 => uint256)) public allowedMints; | ||
|
||
// Mapping to track how many mints have been used per user per token ID | ||
mapping(address => mapping(uint256 => uint256)) public mintedPerUser; | ||
|
||
constructor(address initialOwner, string memory _uri) ERC1155(_uri) Ownable(initialOwner) {} | ||
|
||
function setURI(string memory newuri) public onlyOwner { | ||
_setURI(newuri); | ||
} | ||
|
||
function setMetaData(uint256 id, Metadata memory _metadata) external onlyOwner { | ||
metadata[id] = _metadata; | ||
} | ||
|
||
function getMetaData(uint256 id) external view returns (Metadata memory) { | ||
return metadata[id]; | ||
} | ||
|
||
// Function to set allowed mints for a user per token ID | ||
function setAllowedMints(address user, uint256 id, uint256 allowedAmount) external onlyOwner { | ||
allowedMints[user][id] = allowedAmount; | ||
emit MintAllowanceSet(user, id, allowedAmount); | ||
} | ||
|
||
// Function to mint tokens, ensuring the user has remaining allowed mints | ||
function mint(uint256 id, uint256 amount, bytes memory data) public { | ||
if (allowedMints[msg.sender][id] == 0) revert NftMintingNotAllowed(); | ||
if (mintedPerUser[msg.sender][id] + amount > allowedMints[msg.sender][id]) { | ||
revert NftMintingLimitExceeded(allowedMints[msg.sender][id] - mintedPerUser[msg.sender][id]); | ||
} | ||
|
||
// Track the number of minted tokens per user for the given ID | ||
mintedPerUser[msg.sender][id] += amount; | ||
|
||
_mint(msg.sender, id, amount, data); | ||
} | ||
|
||
// Function to burn tokens, ensuring only the token owner can burn | ||
function burn(uint256 id, uint256 amount) public { | ||
if (balanceOf(msg.sender, id) < amount) revert NftInsufficientBalanceToBurn(); | ||
|
||
// Burn the tokens | ||
_burn(msg.sender, id, amount); | ||
} | ||
|
||
// The following functions are overrides required by Solidity. | ||
function _update(address from, address to, uint256[] memory ids, uint256[] memory values) | ||
internal | ||
override(ERC1155, ERC1155Supply) | ||
{ | ||
super._update(from, to, ids, values); | ||
} | ||
|
||
// View function to get how many mints are remaining for a user per token ID | ||
function remainingMints(address user, uint256 id) external view returns (uint256) { | ||
return allowedMints[user][id] - mintedPerUser[user][id]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import {Test, console} from "forge-std/Test.sol"; | ||
import {BondNFT} from "../src/BondNFT.sol"; | ||
|
||
contract BondNFTTest is Test { | ||
BondNFT public bondNFT; | ||
address public owner; | ||
address public account1; | ||
|
||
function setUp() public { | ||
owner = address(this); | ||
account1 = address(1); | ||
bondNFT = new BondNFT(owner, "https://example.com/{id}.json"); | ||
bondNFT.setAllowedMints(account1, 1, 10); | ||
} | ||
|
||
function testInitialOwner() public view { | ||
assertEq(bondNFT.owner(), owner); | ||
} | ||
|
||
function testSetURI() public { | ||
string memory newURI = "https://newexample.com/{id}.json"; | ||
bondNFT.setURI(newURI); | ||
assertEq(bondNFT.uri(1), newURI); | ||
} | ||
|
||
function testSetMetaData() public { | ||
BondNFT.Metadata memory metadata = BondNFT.Metadata({ | ||
value: 100, | ||
couponValue: 5, | ||
issueTimestamp: block.timestamp, | ||
expirationTimestamp: block.timestamp + 365 days, | ||
ISIN: "US1234567890" | ||
}); | ||
bondNFT.setMetaData(1, metadata); | ||
(uint256 value, uint256 couponValue, uint256 issueTimestamp, uint256 expirationTimestamp, string memory ISIN) = | ||
bondNFT.metadata(1); | ||
assertEq(value, metadata.value); | ||
assertEq(couponValue, metadata.couponValue); | ||
assertEq(issueTimestamp, metadata.issueTimestamp); | ||
assertEq(expirationTimestamp, metadata.expirationTimestamp); | ||
assertEq(ISIN, metadata.ISIN); | ||
} | ||
|
||
function testMint() public { | ||
uint256 id = 1; | ||
uint256 amount = 10; | ||
vm.prank(account1); | ||
bondNFT.mint(id, amount, ""); | ||
assertEq(bondNFT.balanceOf(account1, id), amount); | ||
} | ||
|
||
function testBurn() public { | ||
uint256 id = 1; | ||
uint256 amount = 10; | ||
vm.prank(account1); | ||
bondNFT.mint(id, amount, ""); | ||
vm.prank(account1); | ||
bondNFT.burn(id, amount); | ||
assertEq(bondNFT.balanceOf(account1, id), 0); | ||
} | ||
|
||
// Test minting with no allowed mints | ||
function testMintNotAllowed() public { | ||
vm.prank(owner); | ||
vm.expectRevert(); | ||
bondNFT.mint(1, 10, ""); | ||
} | ||
|
||
// Test minting with exceeded allowed mints | ||
function testMintLimitExceeded() public { | ||
bondNFT.setAllowedMints(account1, 1, 5); | ||
vm.prank(account1); | ||
bondNFT.mint(1, 5, ""); | ||
vm.expectRevert(); | ||
bondNFT.mint(1, 1, ""); | ||
} | ||
|
||
// Test burning with insufficient balance | ||
function testBurnInsufficientBalance() public { | ||
vm.prank(account1); | ||
vm.expectRevert(); | ||
bondNFT.burn(1, 100); | ||
} | ||
} |