0

I have someone working on building an ERC721 token for me and I came across this line of code (refer to "address private signer):

contract SingleToken is Ownable, IERC721, IERC721Metadata, ERC721Burnable, ERC721Base {
    
    address private signer = "wallet address";
    mapping(uint8 => mapping(bytes32 => mapping(bytes32 => bool))) isUsed;
    
    constructor (string memory name, string memory symbol, address newOwner, string memory contractURI, string memory tokenURIPrefix) public ERC721Base(name, symbol, contractURI, tokenURIPrefix) {
        _registerInterface(bytes4(keccak256('MINT_WITH_ADDRESS')));
        transferOwnership(newOwner);
    }

    function mint(uint256 tokenId, Fee[] memory _fees, string memory tokenURI, bytes32 signedMessage, uint8 v, bytes32 r, bytes32 s) public {
        require(isUsed[v][r][s] == false, 'SingleToken: used signature');
        require(signer == ecrecover(signedMessage, v, r, s), 'SingleToken: invalid signer');
        
        isUsed[v][r][s] = true;
        _mint(msg.sender, tokenId, _fees);
        _setTokenURI(tokenId, tokenURI);
        tokenStatus[tokenId] = STATUS.BASIC;
    }

    function setTokenURIPrefix(string memory tokenURIPrefix) public onlyOwner {
        _setTokenURIPrefix(tokenURIPrefix);
    }

    function setContractURI(string memory contractURI) public onlyOwner {
        _setContractURI(contractURI);
    }

    function setTokenOnSale(uint256 tokenId) external {
        require(_isApprovedOrOwner(msg.sender, tokenId) == true, 'Not owner or approved.');
        tokenStatus[tokenId] = STATUS.SALE;
    }

    function cancelTokenSale(uint256 tokenId) external {
        require(_isApprovedOrOwner(msg.sender, tokenId) == true, 'Not owner or approved.');
        tokenStatus[tokenId] = STATUS.BASIC;
    }

    function setTokenOnAuction(uint256 tokenId) external {
        require(_isApprovedOrOwner(msg.sender, tokenId) == true, 'Not owner or approved.');
        tokenStatus[tokenId] = STATUS.AUCTION;
    }
    
    function setTokenOnBasic(uint256 tokenId) external {
        require(_isApprovedOrOwner(msg.sender, tokenId) == true, 'Not owner or approved.');
        tokenStatus[tokenId] = STATUS.BASIC;
    }
}

I was curious as to what the private signer address is and what it does? Should it be my wallet address? In the actual code he has what appears to be his wallet address in the "wallet address" section and I'm not sure if it's supposed to be that.

1
  • It is not possible to answer this question without complete context Commented Jul 20, 2021 at 23:21

1 Answer 1

1

There's a single use for signer in the provided code:

function mint(uint256 tokenId, Fee[] memory _fees, string memory tokenURI, bytes32 signedMessage, uint8 v, bytes32 r, bytes32 s) public {
    require(isUsed[v][r][s] == false, 'SingleToken: used signature');
    require(signer == ecrecover(signedMessage, v, r, s), 'SingleToken: invalid signer');
    
    isUsed[v][r][s] = true;
    _mint(msg.sender, tokenId, _fees);
    _setTokenURI(tokenId, tokenURI);
    tokenStatus[tokenId] = STATUS.BASIC;
}

A valid signature from the signer will allow to mint tokens.

From a security point I don't like that signedMessage is a bytes32 blob that isn't related to the tokenId, the sender nor the token address (this).

Another problem is that ecrecover has some malleability issues that are well known. It is recommended to use it with OpenZeppelin or similar library that add the extra validation.

I suggest to request a security audit to ensure there are no more issues.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.