Skip to content

Commit

Permalink
move signature verification from SoulName to SoulStore
Browse files Browse the repository at this point in the history
  • Loading branch information
miquelcabot committed Jan 4, 2023
1 parent c30fd45 commit 6048b5d
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 194 deletions.
66 changes: 3 additions & 63 deletions contracts/SoulName.sol
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.7;

import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

Expand All @@ -16,7 +15,7 @@ import "./tokens/MasaNFT.sol";
/// @notice SoulName NFT that points to a Soulbound identity token
/// @dev SoulName NFT, that inherits from the NFT contract, and points to a Soulbound identity token.
/// It has an extension, and stores all the information about the identity names.
contract SoulName is MasaNFT, ISoulName, ReentrancyGuard, EIP712 {
contract SoulName is MasaNFT, ISoulName, ReentrancyGuard {
/* ========== STATE VARIABLES ========== */
using SafeMath for uint256;

Expand All @@ -36,8 +35,6 @@ contract SoulName is MasaNFT, ISoulName, ReentrancyGuard, EIP712 {
mapping(uint256 => TokenData) public tokenData; // used to store the data of the token id
mapping(string => NameData) public nameData; // stores the token id of the current active soul name

mapping(address => bool) public authorities;

struct TokenData {
string name; // Name with lowercase and uppercase
uint256 expirationDate;
Expand All @@ -61,7 +58,7 @@ contract SoulName is MasaNFT, ISoulName, ReentrancyGuard, EIP712 {
ISoulboundIdentity _soulboundIdentity,
string memory _extension,
string memory _contractURI
) MasaNFT(admin, "Masa Soul Name", "MSN", "") EIP712("SoulName", "1.0.0") {
) MasaNFT(admin, "Masa Soul Name", "MSN", "") {
if (address(_soulboundIdentity) == address(0)) revert ZeroAddress();

soulboundIdentity = _soulboundIdentity;
Expand Down Expand Up @@ -105,39 +102,22 @@ contract SoulName is MasaNFT, ISoulName, ReentrancyGuard, EIP712 {
contractURI = _contractURI;
}

/// @notice Adds a new authority to the list of authorities
/// @dev The caller must have the admin to call this function
/// @param _authority New authority to add
function addAuthority(address _authority) external onlyOwner {
if (_authority == address(0)) revert ZeroAddress();
if (authorities[_authority]) revert AlreadyAdded();

authorities[_authority] = true;
}

/* ========== MUTATIVE FUNCTIONS ========== */

/// @notice Mints a new soul name
/// @dev The caller can mint more than one name. The soul name must be unique.
/// @param to Address of the owner of the new soul name
/// @param name Name of the new soul name
/// @param nameLength Length of the name
/// @param yearsPeriod Years of validity of the name
/// @param _tokenURI URI of the NFT
/// @param authorityAddress Address of the authority
/// @param signature Signature of the authority
function mint(
address to,
string memory name,
uint256 nameLength,
uint256 yearsPeriod,
string memory _tokenURI,
address authorityAddress,
bytes calldata signature
string memory _tokenURI
) public override nonReentrant returns (uint256) {
if (!isAvailable(name)) revert NameAlreadyExists(name);
if (bytes(name).length == 0) revert ZeroLengthName(name);
if (nameLength == 0) revert ZeroLengthName(name);
if (yearsPeriod == 0) revert ZeroYearsPeriod(yearsPeriod);
if (soulboundIdentity.balanceOf(to) == 0)
revert AddressDoesNotHaveIdentity(to);
Expand All @@ -146,12 +126,6 @@ contract SoulName is MasaNFT, ISoulName, ReentrancyGuard, EIP712 {
!Utils.startsWith(_tokenURI, "ipfs://")
) revert InvalidTokenURI(_tokenURI);

_verify(
_hash(to, name, nameLength, yearsPeriod, _tokenURI),
signature,
authorityAddress
);

uint256 tokenId = _mintWithCounter(to);
_setTokenURI(tokenId, _tokenURI);

Expand Down Expand Up @@ -430,40 +404,6 @@ contract SoulName is MasaNFT, ISoulName, ReentrancyGuard, EIP712 {
_URIs[_tokenURI] = true;
}

function _verify(
bytes32 digest,
bytes memory signature,
address signer
) internal view {
address _signer = ECDSA.recover(digest, signature);
if (_signer != signer) revert InvalidSignature();
if (!authorities[_signer]) revert NotAuthorized(_signer);
}

function _hash(
address to,
string memory name,
uint256 nameLength,
uint256 yearsPeriod,
string memory _tokenURI
) internal view returns (bytes32) {
return
_hashTypedDataV4(
keccak256(
abi.encode(
keccak256(
"MintSoulName(address to,string name,uint256 nameLength,uint256 yearsPeriod,string _tokenURI)"
),
to,
bytes(name),
nameLength,
yearsPeriod,
bytes(_tokenURI)
)
)
);
}

/* ========== MODIFIERS ========== */

/* ========== EVENTS ========== */
Expand Down
78 changes: 63 additions & 15 deletions contracts/SoulStore.sol
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.7;

import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
Expand All @@ -15,7 +16,7 @@ import "./interfaces/ISoulName.sol";
/// @notice Soul Store, that can mint new Soulbound Identities and Soul Name NFTs, paying a fee
/// @dev From this smart contract we can mint new Soulbound Identities and Soul Name NFTs.
/// This minting can be done paying a fee in ETH, USDC or MASA
contract SoulStore is PaymentGateway, Pausable, ReentrancyGuard {
contract SoulStore is PaymentGateway, Pausable, ReentrancyGuard, EIP712 {
using SafeMath for uint256;

/* ========== STATE VARIABLES ========== */
Expand All @@ -24,6 +25,8 @@ contract SoulStore is PaymentGateway, Pausable, ReentrancyGuard {

mapping(uint256 => uint256) public nameRegistrationPricePerYear; // (length --> price in stable coin per year)

mapping(address => bool) public authorities;

/* ========== INITIALIZE ========== */

/// @notice Creates a new Soul Store
Expand All @@ -38,7 +41,7 @@ contract SoulStore is PaymentGateway, Pausable, ReentrancyGuard {
ISoulboundIdentity _soulBoundIdentity,
uint256 _nameRegistrationPricePerYear,
PaymentParams memory paymentParams
) PaymentGateway(owner, paymentParams) {
) PaymentGateway(owner, paymentParams) EIP712("SoulStore", "1.0.0") {
if (address(_soulBoundIdentity) == address(0)) revert ZeroAddress();

soulboundIdentity = _soulBoundIdentity;
Expand Down Expand Up @@ -78,6 +81,16 @@ contract SoulStore is PaymentGateway, Pausable, ReentrancyGuard {
] = _nameRegistrationPricePerYear;
}

/// @notice Adds a new authority to the list of authorities
/// @dev The caller must have the admin to call this function
/// @param _authority New authority to add
function addAuthority(address _authority) external onlyOwner {
if (_authority == address(0)) revert ZeroAddress();
if (authorities[_authority]) revert AlreadyAdded();

authorities[_authority] = true;
}

/// @notice Pauses the smart contract
/// @dev The caller must have the owner to call this function
function pause() public onlyOwner {
Expand Down Expand Up @@ -254,15 +267,18 @@ contract SoulStore is PaymentGateway, Pausable, ReentrancyGuard {
address authorityAddress,
bytes calldata signature
) internal returns (uint256) {
_verify(
_hash(to, name, nameLength, yearsPeriod, _tokenURI),
signature,
authorityAddress
);

// mint Soulbound identity token
uint256 tokenId = soulboundIdentity.mintIdentityWithName(
to,
name,
nameLength,
yearsPeriod,
_tokenURI,
authorityAddress,
signature
_tokenURI
);

emit SoulboundIdentityAndNamePurchased(to, tokenId, name, yearsPeriod);
Expand Down Expand Up @@ -304,24 +320,56 @@ contract SoulStore is PaymentGateway, Pausable, ReentrancyGuard {
address authorityAddress,
bytes calldata signature
) internal returns (uint256) {
_verify(
_hash(to, name, nameLength, yearsPeriod, _tokenURI),
signature,
authorityAddress
);

// mint Soul Name token
ISoulName soulName = soulboundIdentity.getSoulName();

uint256 tokenId = soulName.mint(
to,
name,
nameLength,
yearsPeriod,
_tokenURI,
authorityAddress,
signature
);
uint256 tokenId = soulName.mint(to, name, yearsPeriod, _tokenURI);

emit SoulNamePurchased(to, tokenId, name, yearsPeriod);

return tokenId;
}

function _verify(
bytes32 digest,
bytes memory signature,
address signer
) internal view {
address _signer = ECDSA.recover(digest, signature);
if (_signer != signer) revert InvalidSignature();
if (!authorities[_signer]) revert NotAuthorized(_signer);
}

function _hash(
address to,
string memory name,
uint256 nameLength,
uint256 yearsPeriod,
string memory _tokenURI
) internal view returns (bytes32) {
return
_hashTypedDataV4(
keccak256(
abi.encode(
keccak256(
"MintSoulName(address to,string name,uint256 nameLength,uint256 yearsPeriod,string _tokenURI)"
),
to,
bytes(name),
nameLength,
yearsPeriod,
bytes(_tokenURI)
)
)
);
}

/* ========== MODIFIERS ========== */

/* ========== EVENTS ========== */
Expand Down
18 changes: 2 additions & 16 deletions contracts/SoulboundIdentity.sol
Expand Up @@ -61,30 +61,16 @@ contract SoulboundIdentity is
/// @dev The caller can only mint one identity per address, and the name must be unique
/// @param to Address of the admin of the new identity
/// @param name Name of the new identity
/// @param nameLength Length of the name
/// @param yearsPeriod Years of validity of the name
/// @param _tokenURI URI of the NFT
/// @param authorityAddress Address of the authority
/// @param signature Signature of the authority
function mintIdentityWithName(
address to,
string memory name,
uint256 nameLength,
uint256 yearsPeriod,
string memory _tokenURI,
address authorityAddress,
bytes calldata signature
string memory _tokenURI
) external override soulNameAlreadySet nonReentrant returns (uint256) {
uint256 identityId = mint(to);
soulName.mint(
to,
name,
nameLength,
yearsPeriod,
_tokenURI,
authorityAddress,
signature
);
soulName.mint(to, name, yearsPeriod, _tokenURI);

return identityId;
}
Expand Down
5 changes: 1 addition & 4 deletions contracts/interfaces/ISoulName.sol
Expand Up @@ -5,11 +5,8 @@ interface ISoulName {
function mint(
address to,
string memory name,
uint256 nameLength,
uint256 yearsPeriod,
string memory _tokenURI,
address authorityAddress,
bytes calldata signature
string memory _tokenURI
) external returns (uint256);

function getExtension() external view returns (string memory);
Expand Down
5 changes: 1 addition & 4 deletions contracts/interfaces/ISoulboundIdentity.sol
Expand Up @@ -11,11 +11,8 @@ interface ISoulboundIdentity is ISBT {
function mintIdentityWithName(
address to,
string memory name,
uint256 nameLength,
uint256 yearsPeriod,
string memory _tokenURI,
address authorityAddress,
bytes calldata signature
string memory _tokenURI
) external returns (uint256);

function getSoulName() external view returns (ISoulName);
Expand Down

0 comments on commit 6048b5d

Please sign in to comment.