Skip to content

Commit

Permalink
chore(XPNFTToken): add base XPNFTToken
Browse files Browse the repository at this point in the history
  • Loading branch information
gravityblast committed Oct 2, 2024
1 parent 24c3469 commit ad46c7a
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 0 deletions.
128 changes: 128 additions & 0 deletions src/XPNFTToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

interface IERC20 {
function balanceOf(address account) external view returns (uint256);
}

contract XPNFTToken is Ownable {
error XPNFT__TransferNotAllowed();
error XPNFT__InvalidTokenId();

string private _name = "XPNFT";
string private _symbol = "XPNFT";
string private _imagePrefix = "";
string private _imageSuffix = "";

IERC20 private _xpToken;

event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

modifier onlyValidTokenId(uint256 tokenId) {
if (tokenId > type(uint160).max) {
revert XPNFT__InvalidTokenId();
}
_;
}

constructor(address xpTokenAddress, string memory imagePrefix, string memory imageSuffix) Ownable(msg.sender) {
_xpToken = IERC20(xpTokenAddress);
_imagePrefix = imagePrefix;
_imageSuffix = imageSuffix;
}

function setImageStrings(string memory imagePrefix, string memory imageSuffix) external onlyOwner {
_imagePrefix = imagePrefix;
_imageSuffix = imageSuffix;
}

function name() external view returns (string memory) {
return _name;
}

function symbol() external view returns (string memory) {
return _symbol;
}

function mint() external {
emit Transfer(msg.sender, msg.sender, uint256(uint160(msg.sender)));
}

function balanceOf(address) external pure returns (uint256) {
return 1;
}

function ownerOf(uint256 tokenId) external pure onlyValidTokenId(tokenId) returns (address) {
address owner = address(uint160(tokenId));
return owner;
}

function safeTransferFrom(address, address, uint256, bytes calldata) external pure {
revert XPNFT__TransferNotAllowed();
}

function safeTransferFrom(address, address, uint256) external pure {
revert XPNFT__TransferNotAllowed();
}

function transferFrom(address, address, uint256) external pure {
revert XPNFT__TransferNotAllowed();
}

function approve(address, uint256) external pure {
revert XPNFT__TransferNotAllowed();
}

function setApprovalForAll(address, bool) external pure {
revert XPNFT__TransferNotAllowed();
}

function getApproved(uint256) external pure returns (address) {
return address(0);
}

function isApprovedForAll(address, address) external pure returns (bool) {
return false;
}

function tokenURI(uint256 tokenId) external view onlyValidTokenId(tokenId) returns (string memory) {
address owner = address(uint160(tokenId));
return _createTokenURI(owner);
}

function _createTokenURI(address owner) internal view returns (string memory) {
string memory baseName = "XPNFT Token ";
string memory baseDescription = "This is a XPNFT token for address ";
uint256 balance = _xpToken.balanceOf(owner) / 1e18;

string memory propName = string(abi.encodePacked(baseName, Strings.toHexString(owner)));
string memory propDescription = string(
abi.encodePacked(baseDescription, Strings.toHexString(owner), " with balance ", Strings.toString(balance))
);
string memory image = _generateImage(balance);

bytes memory json = abi.encodePacked(
'{"name":"',

Check failure on line 109 in src/XPNFTToken.sol

View workflow job for this annotation

GitHub Actions / lint

Use double quotes for string literals
propName,
'","description":"',

Check failure on line 111 in src/XPNFTToken.sol

View workflow job for this annotation

GitHub Actions / lint

Use double quotes for string literals
propDescription,
'","image":"data:image/svg+xml;base64,',

Check failure on line 113 in src/XPNFTToken.sol

View workflow job for this annotation

GitHub Actions / lint

Use double quotes for string literals
image,
'"}'

Check failure on line 115 in src/XPNFTToken.sol

View workflow job for this annotation

GitHub Actions / lint

Use double quotes for string literals
);

string memory jsonBase64 = Base64.encode(json);
return string(abi.encodePacked("data:application/json;base64,", jsonBase64));
}

function _generateImage(uint256 balance) internal view returns (string memory) {
string memory text = Strings.toString(balance);
bytes memory svg = abi.encodePacked(_imagePrefix, text, _imageSuffix);

return Base64.encode(svg);
}
}
32 changes: 32 additions & 0 deletions test/XPNFTToken.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { Test, console } from "forge-std/Test.sol";
import { MockToken } from "./mocks/MockToken.sol";
import { XPNFTToken } from "../src/XPNFTToken.sol";

contract XPNFTTokenTest is Test {
MockToken erc20Token;
XPNFTToken nft;

address alice = makeAddr("alice");

string imagePrefix =
'<svg xmlns="http://www.w3.org/2000/svg" height="200" width="200"><rect width="100%" height="100%" fill="blue"/><text x="50%" y="50%" fill="white" font-size="20" text-anchor="middle">';

Check failure on line 15 in test/XPNFTToken.t.sol

View workflow job for this annotation

GitHub Actions / lint

Line length must be no more than 120 but current length is 193

Check failure on line 15 in test/XPNFTToken.t.sol

View workflow job for this annotation

GitHub Actions / lint

Use double quotes for string literals
string imageSuffix = "</text></svg>";

function setUp() public {
erc20Token = new MockToken("Test", "TEST");
nft = new XPNFTToken(address(erc20Token), imagePrefix, imageSuffix);

address[1] memory users = [alice];
for (uint256 i = 0; i < users.length; i++) {
erc20Token.mint(users[i], 10e18);
}
}

function test() public {
string memory metadata = nft.tokenURI(uint256(uint160(alice)));
console.log(metadata);
}
}

0 comments on commit ad46c7a

Please sign in to comment.