contract Numerati is ERC721, Ownable {
	address public signer;
	uint256 public lastMint;
	uint256 public price = 123e15; // 0.123eth

	constructor (address _signer) ERC721("Numerati", "#")  {
		signer = _signer;
	}

	function setSigner(address _signer) public onlyOwner {
		signer = _signer;
	}

	function withdraw() public onlyOwner {
		payable(owner()).transfer(address(this).balance);
	}

	function buy(uint256 _num) public payable {
		require(msg.value >= price, "insufficient payment");
		_safeMint(msg.sender, _num);
		lastMint = _num;
	}

	function authClaim(uint256 _num, bytes memory _sig) public {
		require(verify(msg.sender, _num, _sig), "invalid signature");
		_safeMint(msg.sender, _num);
		lastMint = _num;
	}

	function verify(address _addr, uint256 _num, bytes memory _sig) public view returns (bool) {
		bytes32 digest = keccak256(abi.encodePacked(_addr, _num));
		(bytes32 r, bytes32 s, uint8 v) = splitSignature(_sig);
		return ecrecover(digest, v, r, s) == signer;
	}

	// https://solidity-by-example.org/signature/
	function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v) {
		require(sig.length == 65, "invalid signature length");
		assembly {
			r := mload(add(sig, 32))
			s := mload(add(sig, 64))
			v := byte(0, mload(add(sig, 96)))
		}
	}
}
function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v) {
	require(sig.length == 65, "invalid signature length");
	assembly {
		r := mload(add(sig, 32))
		s := mload(add(sig, 64))
		v := byte(0, mload(add(sig, 96)))
	}
}

// https://solidity-by-example.org/signature/