-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(nfts): trailblazer profile picture (#18009)
Co-authored-by: Bernat Canal Garceran <[email protected]>
- Loading branch information
1 parent
f1aeac3
commit 6f2004f
Showing
5 changed files
with
526 additions
and
0 deletions.
There are no files selected for viewing
97 changes: 97 additions & 0 deletions
97
packages/nfts/contracts/profile/RegisterProfilePicture.sol
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,97 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | ||
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; | ||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
import { UUPSUpgradeable } from | ||
"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; | ||
import { Ownable2StepUpgradeable } from | ||
"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; | ||
|
||
/// @title A store for trailblazer profile pictures | ||
/// @author Bennett Yogn | ||
/// @dev All function calls are currently implemented without side effects | ||
contract RegisterProfilePicture is Initializable, UUPSUpgradeable, Ownable2StepUpgradeable { | ||
error InvalidNFTContract(address nftContract); | ||
error NotTokenOwner(address nftContract, uint256 tokenId, address caller); | ||
|
||
/// @notice struct of nft contract address and token id | ||
struct ProfilePicture { | ||
address nftContract; | ||
uint256 tokenId; | ||
} | ||
|
||
/// @notice mapping of user id to profile picture | ||
mapping(address user => ProfilePicture pfp) public profilePicture; | ||
|
||
event ProfilePictureSet( | ||
address indexed user, address indexed nftContract, uint256 indexed tokenId | ||
); | ||
|
||
/// @notice Contract initializer | ||
function initialize() public initializer { | ||
_transferOwnership(_msgSender()); | ||
} | ||
|
||
/// @notice Set the profile picture | ||
/// @param nftContract The address of the nft to set as the profile picture | ||
/// @param tokenId The tokenId of the nft to set as the profile picture | ||
function setPFP(address nftContract, uint256 tokenId) external { | ||
if (IERC721(nftContract).supportsInterface(type(IERC721).interfaceId)) { | ||
// Check if the provided contract address is a valid ERC721 contract | ||
if (IERC721(nftContract).ownerOf(tokenId) != _msgSender()) { | ||
revert NotTokenOwner(nftContract, tokenId, _msgSender()); | ||
} | ||
} else if (IERC1155(nftContract).supportsInterface(type(IERC1155).interfaceId)) { | ||
// Check if the provided contract address is a valid ERC1155 contract | ||
if (IERC1155(nftContract).balanceOf(_msgSender(), tokenId) == 0) { | ||
revert NotTokenOwner(nftContract, tokenId, _msgSender()); | ||
} | ||
} else { | ||
// If the contract does not support ERC721 or ERC1155 interfaces | ||
revert InvalidNFTContract(nftContract); | ||
} | ||
|
||
// Set the PFP | ||
profilePicture[_msgSender()] = ProfilePicture(nftContract, tokenId); | ||
|
||
emit ProfilePictureSet(_msgSender(), nftContract, tokenId); | ||
} | ||
|
||
/// @notice Get the profile picture of a user | ||
/// @param user The address of user | ||
function getProfilePicture(address user) external view returns (string memory) { | ||
ProfilePicture memory userProfilePicture = profilePicture[user]; | ||
|
||
if (IERC721(userProfilePicture.nftContract).supportsInterface(type(IERC721).interfaceId)) { | ||
// ERC721 case: Check ownership before returning the URI | ||
if (IERC721(userProfilePicture.nftContract).ownerOf(userProfilePicture.tokenId) != user) | ||
{ | ||
revert NotTokenOwner( | ||
userProfilePicture.nftContract, userProfilePicture.tokenId, user | ||
); | ||
} | ||
return ERC721(userProfilePicture.nftContract).tokenURI(userProfilePicture.tokenId); | ||
} else if ( | ||
IERC1155(userProfilePicture.nftContract).supportsInterface(type(IERC1155).interfaceId) | ||
) { | ||
// ERC1155 case: Check ownership before returning the URI | ||
if ( | ||
IERC1155(userProfilePicture.nftContract).balanceOf(user, userProfilePicture.tokenId) | ||
== 0 | ||
) { | ||
revert NotTokenOwner( | ||
userProfilePicture.nftContract, userProfilePicture.tokenId, user | ||
); | ||
} | ||
return ERC1155(userProfilePicture.nftContract).uri(userProfilePicture.tokenId); | ||
} else { | ||
// If the contract does not support ERC721 or ERC1155 interfaces | ||
revert InvalidNFTContract(userProfilePicture.nftContract); | ||
} | ||
} | ||
|
||
/// @notice Internal method to authorize an upgrade | ||
function _authorizeUpgrade(address) internal virtual override onlyOwner { } | ||
} |
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,49 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
import { UtilsScript } from "./Utils.s.sol"; | ||
import { Script, console } from "forge-std/src/Script.sol"; | ||
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
import { RegisterProfilePicture } from "../../contracts/profile/RegisterProfilePicture.sol"; | ||
|
||
contract DeployScript is Script { | ||
UtilsScript public utils; | ||
string public jsonLocation; | ||
uint256 public deployerPrivateKey; | ||
address public deployerAddress; | ||
|
||
function setUp() public { | ||
utils = new UtilsScript(); | ||
utils.setUp(); | ||
|
||
jsonLocation = utils.getContractJsonLocation(); | ||
deployerPrivateKey = utils.getPrivateKey(); | ||
deployerAddress = utils.getAddress(); | ||
} | ||
|
||
function run() public { | ||
string memory jsonRoot = "root"; | ||
|
||
vm.startBroadcast(deployerPrivateKey); | ||
|
||
// deploy token with empty root | ||
address impl = address(new RegisterProfilePicture()); | ||
address proxy = address( | ||
new ERC1967Proxy( | ||
impl, | ||
abi.encodeCall( | ||
RegisterProfilePicture.initialize, () | ||
) | ||
) | ||
); | ||
|
||
RegisterProfilePicture profile = RegisterProfilePicture(proxy); | ||
|
||
console.log("Deployed TaikoPartyTicket to:", address(profile)); | ||
|
||
string memory finalJson = vm.serializeAddress(jsonRoot, "RegisterProfilePicture", address(profile)); | ||
vm.writeJson(finalJson, jsonLocation); | ||
|
||
vm.stopBroadcast(); | ||
} | ||
} |
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,61 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
import { Script, console } from "forge-std/src/Script.sol"; | ||
import "forge-std/src/StdJson.sol"; | ||
|
||
contract UtilsScript is Script { | ||
using stdJson for string; | ||
|
||
uint256 public chainId; | ||
|
||
string public lowercaseNetworkKey; | ||
string public uppercaseNetworkKey; | ||
|
||
function setUp() public { | ||
// load all network configs | ||
chainId = block.chainid; | ||
|
||
if (chainId == 31_337) { | ||
lowercaseNetworkKey = "localhost"; | ||
uppercaseNetworkKey = "LOCALHOST"; | ||
} else if (chainId == 17_000) { | ||
lowercaseNetworkKey = "holesky"; | ||
uppercaseNetworkKey = "HOLESKY"; | ||
} else if (chainId == 167_001) { | ||
lowercaseNetworkKey = "devnet"; | ||
uppercaseNetworkKey = "DEVNET"; | ||
} else if (chainId == 11_155_111) { | ||
lowercaseNetworkKey = "sepolia"; | ||
uppercaseNetworkKey = "SEPOLIA"; | ||
} else if (chainId == 167_008) { | ||
lowercaseNetworkKey = "katla"; | ||
uppercaseNetworkKey = "KATLA"; | ||
} else if (chainId == 167_000) { | ||
lowercaseNetworkKey = "mainnet"; | ||
uppercaseNetworkKey = "MAINNET"; | ||
} else if (chainId == 167_009) { | ||
lowercaseNetworkKey = "hekla"; | ||
uppercaseNetworkKey = "HEKLA"; | ||
} else { | ||
revert("Unsupported chainId"); | ||
} | ||
} | ||
|
||
function getPrivateKey() public view returns (uint256) { | ||
string memory lookupKey = string.concat(uppercaseNetworkKey, "_PRIVATE_KEY"); | ||
return vm.envUint(lookupKey); | ||
} | ||
|
||
function getAddress() public view returns (address) { | ||
string memory lookupKey = string.concat(uppercaseNetworkKey, "_ADDRESS"); | ||
return vm.envAddress(lookupKey); | ||
} | ||
|
||
function getContractJsonLocation() public view returns (string memory) { | ||
string memory root = vm.projectRoot(); | ||
return string.concat(root, "/deployments/profile/", lowercaseNetworkKey, ".json"); | ||
} | ||
|
||
function run() public { } | ||
} |
Oops, something went wrong.