eth-offline

EVM token minter frontend for offline issuance using ERC712 structured signatures.
Log | Files | Refs

commit 6802d8018d8b85e7e119a0f1d136a0cdb21c664a
Author: lash <dev@holbrook.no>
Date:   Mon, 13 Mar 2023 12:12:17 +0000

Initial commit

Diffstat:
Asolidity/OfflineMinter.sol | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+), 0 deletions(-)

diff --git a/solidity/OfflineMinter.sol b/solidity/OfflineMinter.sol @@ -0,0 +1,94 @@ +pragma solidity ^0.8.0; + +contract OfflineMinter { + + bytes26 constant prefix = bytes26(abi.encodePacked("0x19457468657265756d205369676e6564204d6573736167653a0a")); + + function verifyRequest(address _owner, bytes memory _data, bytes memory _signature) public pure returns(bool) { + address beneficiary; + bytes memory extra; + bytes memory message; + bytes32 messageDigest; + string memory messageLength; + uint256 messageLengthLength; + + (beneficiary, extra) = splitData(_data); + messageLength = uintToString(uint(_data.length)); + messageLengthLength = bytes(messageLength).length; + + message = new bytes(_data.length + 26 + messageLengthLength); + messageDigest = keccak256(abi.encodePacked(prefix, messageLength, _data)); + + return _owner == recoverSigner(messageDigest, _signature); + } + + function splitData(bytes memory _data) public pure returns(address _beneficiary, bytes memory _extra) { + bytes memory beneficiary; + bytes memory extra; + + require(_data.length > 20, 'ERR_CONTENTS'); + + beneficiary = new bytes(20); + extra = new bytes(_data.length - 20); + + for (uint256 i; i < beneficiary.length; i++) { + beneficiary[i] = _data[i]; + } + for (uint256 i; i < extra.length; i++) { + extra[i] = _data[i+20]; + } + verifyExtraData(extra); + return (address(bytes20(beneficiary)), extra); + } + + function verifyExtraData(bytes memory _data) public pure { + revert('verification not implemented'); + } + + // from https://solidity-by-example.org/signature/ + function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) { + (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature); + return ecrecover(_ethSignedMessageHash, v, r, s); + } + + // from 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 { + /* + First 32 bytes stores the length of the signature + + add(sig, 32) = pointer of sig + 32 + effectively, skips first 32 bytes of signature + + mload(p) loads next 32 bytes starting at the memory address p into memory + */ + + // first 32 bytes, after the length prefix + r := mload(add(sig, 32)) + // second 32 bytes + s := mload(add(sig, 64)) + // final byte (first byte of the next 32 bytes) + v := byte(0, mload(add(sig, 96))) + } + + // implicitly return (r, s, v) + } + + function uintToString(uint v) public pure returns (string memory str) { + uint maxlength = 100; + bytes memory reversed = new bytes(maxlength); + uint i = 0; + while (v != 0) { + uint remainder = v % 10; + v = v / 10; + reversed[i++] = bytes1(bytes32(48 + remainder)); + } + bytes memory s = new bytes(i); + for (uint j = 0; j < i; j++) { + s[j] = reversed[i - 1 - j]; + } + str = string(s); + } +}