Skip to content

Commit 400f2ff

Browse files
author
Yash Agrawal
committed
feat: use metadata and image storing in tokenUri
1 parent 4de33bc commit 400f2ff

File tree

4 files changed

+241
-52
lines changed

4 files changed

+241
-52
lines changed

contracts/SBTToken.sol

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,44 @@
11
// SPDX-License-Identifier: MPL-2.0
22
pragma solidity =0.8.9;
33

4-
import {Counters} from "@openzeppelin/contracts/utils/Counters.sol";
4+
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
5+
import {Base64} from "@devprotocol/util-contracts/contracts/utils/Base64.sol";
56
import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
6-
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
7+
import {AddressLib} from "@devprotocol/util-contracts/contracts/utils/AddressLib.sol";
78
import {ERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
89

910
import {ISBTToken} from "./interfaces/ISBTToken.sol";
11+
import {DecimalString} from "./libs/DecimalString.sol";
1012

1113
contract SBTToken is ISBTToken, ERC721EnumerableUpgradeable {
14+
using Base64 for bytes;
15+
using Strings for uint256;
16+
using AddressLib for address;
17+
using DecimalString for uint256;
18+
1219
/// @dev EOA with minting rights.
13-
address private minter;
20+
address private _minter;
1421
/// @dev Account with proxy adming rights.
15-
address private proxyAdmin;
22+
address private _proxyAdmin;
23+
1624
/// @dev Holds the URI information of a SBT token.
17-
mapping(uint256 => string) private tokenUriImage;
25+
mapping(uint256 => string) private _tokenUriImage;
26+
/// @dev Holds the generic metadata and attribute information of a SBT token.
27+
mapping(uint256 => Metadata) private _tokenMetadata;
1828

1929
modifier onlyMinter() {
20-
require(minter == _msgSender(), "Illegal access");
30+
require(_minter == _msgSender(), "Illegal access");
2131
_;
2232
}
2333

24-
event SetProxyAdmin(address _proxyAdmin);
25-
26-
function initialize(address _minter) external initializer {
27-
__ERC721_init("Dev Protocol SBT V1", "DEV-SBT-V1");
28-
minter = _minter;
29-
}
30-
31-
function setProxyAdmin(address _proxyAdmin) external {
32-
require(proxyAdmin == address(0), "Already set");
33-
proxyAdmin = _proxyAdmin;
34-
emit SetProxyAdmin(_proxyAdmin);
35-
}
36-
37-
function setTokenURIImage(
38-
uint256 _tokenId,
39-
string memory _data
40-
) external override onlyMinter {
41-
tokenUriImage[_tokenId] = _data;
42-
}
43-
44-
function mint(
45-
address _owner
46-
) external override onlyMinter returns (uint256 tokenId_) {
47-
uint256 currentId = this.currentIndex();
48-
_mint(_owner, currentId);
49-
emit Minted(currentId, _owner);
50-
return currentId;
34+
function _setTokenURI(
35+
uint256 tokenId,
36+
Metadata memory tokenMetadata,
37+
string memory tokenUriImage
38+
) private {
39+
_tokenMetadata[tokenId] = tokenMetadata;
40+
_tokenUriImage[tokenId] = tokenUriImage;
41+
emit SetSBTTokenURI(tokenId, abi.encode(tokenMetadata, tokenUriImage));
5142
}
5243

5344
function _beforeTokenTransfer(
@@ -71,24 +62,56 @@ contract SBTToken is ISBTToken, ERC721EnumerableUpgradeable {
7162
}
7263
}
7364

74-
function _tokenURI(uint256 _tokenId) private view returns (string memory) {
75-
return tokenUriImage[_tokenId];
65+
function initialize(address minter) external initializer {
66+
__ERC721_init("Dev Protocol SBT V1", "DEV-SBT-V1");
67+
_minter = minter;
7668
}
7769

78-
function owner() external view returns (address) {
79-
return ProxyAdmin(proxyAdmin).owner();
70+
function setProxyAdmin(address proxyAdmin) external {
71+
require(_proxyAdmin == address(0), "Already set");
72+
_proxyAdmin = proxyAdmin;
73+
emit SetProxyAdmin(proxyAdmin);
74+
}
75+
76+
function setTokenURI(
77+
uint256 tokenId,
78+
Metadata memory tokenMetadata,
79+
string memory tokenUriImage
80+
) external override onlyMinter {
81+
require(tokenId < currentIndex(), "Token not found");
82+
_setTokenURI(tokenId, tokenMetadata, tokenUriImage);
83+
}
84+
85+
function mint(
86+
address to,
87+
Metadata memory tokenMetadata,
88+
string memory tokenUriImage
89+
) external override onlyMinter returns (uint256 tokenId_) {
90+
uint256 currentId = currentIndex();
91+
_mint(to, currentId);
92+
emit Minted(currentId, to);
93+
_setTokenURI(currentId, tokenMetadata, tokenUriImage);
94+
return currentId;
95+
}
96+
97+
function _tokenURI(uint256 tokenId) private view returns (string memory) {
98+
return _tokenUriImage[tokenId];
8099
}
81100

82101
function tokenURI(
83-
uint256 _tokenId
102+
uint256 tokenId
84103
) public view override returns (string memory) {
85-
return _tokenURI(_tokenId);
104+
return _tokenURI(tokenId);
86105
}
87106

88-
function currentIndex() external view override returns (uint256) {
107+
function currentIndex() public view override returns (uint256) {
89108
return super.totalSupply();
90109
}
91110

111+
function owner() external view returns (address) {
112+
return ProxyAdmin(_proxyAdmin).owner();
113+
}
114+
92115
function tokensOfOwner(
93116
address _owner
94117
) external view override returns (uint256[] memory) {

contracts/interfaces/ISBTToken.sol

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,75 @@
22
pragma solidity ^0.8.9;
33

44
interface ISBTToken {
5+
/// @dev Data strucutre to represent all String attributes of the SBT.
6+
struct StringAttribute {
7+
string trait_type;
8+
string value;
9+
}
10+
11+
/// @dev Data strucutre to represent all Numeric attributes of the SBT.
12+
struct NumberAttribute {
13+
string trait_type;
14+
string display_type;
15+
uint256 value;
16+
}
17+
18+
/// @dev Data strucutre to represent all generic metadata of the SBT.
19+
struct Metadata {
20+
string name;
21+
string image;
22+
string description;
23+
bytes stringAttributes;
24+
bytes NumberAttributes;
25+
}
26+
527
/*
628
* @dev The event fired when a token is minted.
7-
* @param tokenId The ID of the created new staking position
8-
* @param owner The address of the owner of the new staking position
29+
* @param tokenId The ID of the created SBT.
30+
* @param owner The address of the owner of the SBT.
931
*/
1032
event Minted(uint256 tokenId, address owner);
1133

34+
/*
35+
* @dev The event fired when proxy admin is set.
36+
* @param _proxyAdmin The address who is set a proxy admin.
37+
*/
38+
event SetProxyAdmin(address _proxyAdmin);
39+
40+
/*
41+
* @dev The event fired when SBTTokenURI is updated.
42+
* @param tokenId The SBT id.
43+
* @param data Bytes representation of the token uri data.
44+
*/
45+
event SetSBTTokenURI(uint256 tokenId, bytes data);
46+
1247
/*
1348
* @dev Creates the new staking position for the caller.
1449
* Mint must be called by the minter address.
15-
* @param _owner The address of the owner of the new staking position
50+
* @param _owner The address of the owner of the new SBT.
51+
* @param metadata The tokenURI data of the new SBT.
52+
* @param tokenUriImage The link for the image of the SBT.
1653
*/
17-
function mint(address _owner) external returns (uint256);
54+
function mint(
55+
address _owner,
56+
Metadata memory metadata,
57+
string memory tokenURIImage
58+
) external returns (uint256);
1859

1960
/*
2061
* @dev Sets the token URI image for a token.
2162
* @notice must be called by the minter address.
2263
* @param _tokenId The token for which we are setting the image uri.
23-
* @param _data The data that is used as uri.
64+
* @param metadata The generic metadata of the SBT (e.g name, description).
65+
* @param stringAttributes The string attributes that form the SBT.
66+
* @param numericAttributes The numeric attributes that form the SBT.
67+
* @param tokenUriImage The link for the image of the SBT.
2468
*/
25-
function setTokenURIImage(uint256 _tokenId, string memory _data) external;
69+
function setTokenURI(
70+
uint256 _tokenId,
71+
Metadata memory metadata,
72+
string memory tokenURIImage
73+
) external;
2674

2775
/*
2876
* @dev get token ids by owner

contracts/libs/DecimalString.sol

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
pragma solidity =0.8.9;
3+
4+
library DecimalString {
5+
function decimalString(
6+
uint256 number,
7+
uint8 decimals
8+
) internal pure returns (string memory) {
9+
uint256 tenPowDecimals = 10 ** decimals;
10+
11+
uint256 temp = number;
12+
uint8 digits;
13+
uint8 numSigfigs;
14+
while (temp != 0) {
15+
if (numSigfigs > 0) {
16+
// count all digits preceding least significant figure
17+
numSigfigs++;
18+
} else if (temp % 10 != 0) {
19+
numSigfigs++;
20+
}
21+
digits++;
22+
temp /= 10;
23+
}
24+
25+
DecimalStringParams memory params;
26+
if ((digits - numSigfigs) >= decimals) {
27+
// no decimals, ensure we preserve all trailing zeros
28+
params.sigfigs = number / tenPowDecimals;
29+
params.sigfigIndex = digits - decimals;
30+
params.bufferLength = params.sigfigIndex;
31+
} else {
32+
// chop all trailing zeros for numbers with decimals
33+
params.sigfigs = number / (10 ** (digits - numSigfigs));
34+
if (tenPowDecimals > number) {
35+
// number is less tahn one
36+
// in this case, there may be leading zeros after the decimal place
37+
// that need to be added
38+
39+
// offset leading zeros by two to account for leading '0.'
40+
params.zerosStartIndex = 2;
41+
params.zerosEndIndex = decimals - digits + 2;
42+
params.sigfigIndex = numSigfigs + params.zerosEndIndex;
43+
params.bufferLength = params.sigfigIndex;
44+
params.isLessThanOne = true;
45+
} else {
46+
// In this case, there are digits before and
47+
// after the decimal place
48+
params.sigfigIndex = numSigfigs + 1;
49+
params.decimalIndex = digits - decimals + 1;
50+
}
51+
}
52+
params.bufferLength = params.sigfigIndex;
53+
return generateDecimalString(params);
54+
}
55+
56+
struct DecimalStringParams {
57+
// significant figures of decimal
58+
uint256 sigfigs;
59+
// length of decimal string
60+
uint8 bufferLength;
61+
// ending index for significant figures (funtion works backwards when copying sigfigs)
62+
uint8 sigfigIndex;
63+
// index of decimal place (0 if no decimal)
64+
uint8 decimalIndex;
65+
// start index for trailing/leading 0's for very small/large numbers
66+
uint8 zerosStartIndex;
67+
// end index for trailing/leading 0's for very small/large numbers
68+
uint8 zerosEndIndex;
69+
// true if decimal number is less than one
70+
bool isLessThanOne;
71+
}
72+
73+
function generateDecimalString(
74+
DecimalStringParams memory params
75+
) private pure returns (string memory) {
76+
bytes memory buffer = new bytes(params.bufferLength);
77+
if (params.isLessThanOne) {
78+
buffer[0] = "0";
79+
buffer[1] = ".";
80+
}
81+
82+
// add leading/trailing 0's
83+
for (
84+
uint256 zerosCursor = params.zerosStartIndex;
85+
zerosCursor < params.zerosEndIndex;
86+
zerosCursor++
87+
) {
88+
buffer[zerosCursor] = bytes1(uint8(48));
89+
}
90+
// add sigfigs
91+
while (params.sigfigs > 0) {
92+
if (
93+
params.decimalIndex > 0 &&
94+
params.sigfigIndex == params.decimalIndex
95+
) {
96+
buffer[--params.sigfigIndex] = ".";
97+
}
98+
buffer[--params.sigfigIndex] = bytes1(
99+
uint8(uint256(48) + (params.sigfigs % 10))
100+
);
101+
params.sigfigs /= 10;
102+
}
103+
return string(buffer);
104+
}
105+
}

hardhat.config.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,26 @@ const mnemnoc =
1212

1313
const config: HardhatUserConfig = {
1414
solidity: {
15-
version: '0.8.9',
16-
settings: {
17-
optimizer: {
18-
enabled: true,
19-
runs: 1000,
15+
compilers: [
16+
{
17+
version: '0.8.9',
18+
settings: {
19+
optimizer: {
20+
enabled: true,
21+
runs: 1000,
22+
},
23+
},
2024
},
21-
},
25+
{
26+
version: '0.8.4',
27+
settings: {
28+
optimizer: {
29+
enabled: true,
30+
runs: 1000,
31+
},
32+
},
33+
},
34+
],
2235
},
2336
networks: {
2437
mainnet: {

0 commit comments

Comments
 (0)