// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.10;
import '@openzeppelin/contracts/utils/Base64.sol';
import '@openzeppelin/contracts/utils/Strings.sol';
library ProfileTokenURILogic {
uint8 internal constant DEFAULT_FONT_SIZE = 24;
uint8 internal constant MAX_HANDLE_LENGTH_WITH_DEFAULT_FONT_SIZE = 17;
/**
* @notice Generates the token URI for the profile NFT.
*
* @dev The decoded token URI JSON metadata contains the following fields: name, description, image and attributes.
* The image field contains a base64-encoded SVG. Both the JSON metadata and the image are generated fully on-chain.
*
* @param id The token ID of the profile.
* @param followers The number of profile's followers.
* @param owner The address which owns the profile.
* @param handle The profile's handle.
* @param imageURI The profile's picture URI. An empty string if has not been set.
*
* @return The profile's token URI as a base64-encoded JSON string.
*/
function getProfileTokenURI(
uint256 id,
uint256 followers,
address owner,
string memory handle,
string memory imageURI
) external pure returns (string memory) {
string memory handleWithAtSymbol = string(abi.encodePacked('@', handle));
return
string(
abi.encodePacked(
'data:application/json;base64,',
Base64.encode(
abi.encodePacked(
'{"name":"',
handleWithAtSymbol,
'","description":"',
handleWithAtSymbol,
' - Lens profile","image":"data:image/svg+xml;base64,',
_getSVGImageBase64Encoded(handleWithAtSymbol, imageURI),
'","attributes":[{"trait_type":"id","value":"#',
Strings.toString(id),
'"},{"trait_type":"followers","value":"',
Strings.toString(followers),
'"},{"trait_type":"owner","value":"',
Strings.toHexString(uint160(owner)),
'"},{"trait_type":"handle","value":"',
handleWithAtSymbol,
'"}]}'
)
)
)
);
}
/**
* @notice Generates the token image.
*
* @dev If the image URI was set and meets URI format conditions, it will be embedded in the token image.
* Otherwise, a default picture will be used. Handle font size is a function of handle length.
*
* @param handleWithAtSymbol The profile's handle beginning with "@" symbol.
* @param imageURI The profile's picture URI. An empty string if has not been set.
*
* @return The profile token image as a base64-encoded SVG.
*/
function _getSVGImageBase64Encoded(string memory handleWithAtSymbol, string memory imageURI)
internal
pure
returns (string memory)
{
return
Base64.encode(
abi.encodePacked(
''
)
);
}
/**
* @notice Gets the fragment of the SVG correponding to the profile picture.
*
* @dev If the image URI was set and meets URI format conditions, this will return an image tag referencing it.
* Otherwise, a group tag that renders the default picture will be returned.
*
* @param imageURI The profile's picture URI. An empty string if has not been set.
*
* @return The fragment of the SVG token's image correspondending to the profile picture.
*/
function _getSVGProfilePicture(string memory imageURI) internal pure returns (string memory) {
if (_shouldUseCustomPicture(imageURI)) {
return
string(
abi.encodePacked(
''
)
);
} else {
return
'';
}
}
/**
* @notice Maps the handle length to a font size.
*
* @dev Gives the font size as a function of handle length using the following formula:
*
* fontSize(handleLength) = 24 when handleLength <= 17
* fontSize(handleLength) = 24 - (handleLength - 12) / 2 when handleLength > 17
*
* @param handleLength The profile's handle length.
* @return The font size.
*/
function _handleLengthToFontSize(uint256 handleLength) internal pure returns (uint256) {
return
handleLength <= MAX_HANDLE_LENGTH_WITH_DEFAULT_FONT_SIZE
? DEFAULT_FONT_SIZE
: DEFAULT_FONT_SIZE - (handleLength - 12) / 2;
}
/**
* @notice Decides if Profile NFT should use user provided custom profile picture or the default one.
*
* @dev It checks if there is a custom imageURI set and makes sure it does not contain double-quotes to prevent
* injection attacks through the generated SVG.
*
* @param imageURI The imageURI set by the profile owner.
*
* @return A boolean indicating whether custom profile picture should be used or not.
*/
function _shouldUseCustomPicture(string memory imageURI) internal pure returns (bool) {
bytes memory imageURIBytes = bytes(imageURI);
if (imageURIBytes.length == 0) {
return false;
}
for (uint256 i = 0; i < imageURIBytes.length; i++) {
if (imageURIBytes[i] == '"') {
// Avoids embedding a user provided imageURI containing double-quotes to prevent injection attacks
return false;
}
}
return true;
}
}