Module: Mpp::Methods::Evm::Authorization
- Defined in:
- lib/mpp/methods/evm/authorization.rb
Overview
EIP-712 TransferWithAuthorization hashing and recovery.
Constant Summary collapse
- DOMAIN_TYPE_HASH =
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"- TRANSFER_TYPE_HASH =
"TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)"
Class Method Summary collapse
- .abi_encode(*values) ⇒ Object
- .address_word(value) ⇒ Object
- .bytes32_word(value) ⇒ Object
- .challenge_hash(challenge) ⇒ Object
- .checksum_address(address) ⇒ Object
- .domain_separator(name:, version:, chain_id:, verifying_contract:) ⇒ Object
- .keccak256(data) ⇒ Object
- .recover(authorization:, chain_id:, currency:, payload:) ⇒ Object
- .recover_address(hash, signature) ⇒ Object
- .signing_hash(authorization:, chain_id:, currency:, from:, to:, value:, valid_after:, valid_before:, nonce:) ⇒ Object
- .struct_hash(from:, to:, value:, valid_after:, valid_before:, nonce:) ⇒ Object
- .uint256(value) ⇒ Object
Class Method Details
.abi_encode(*values) ⇒ Object
102 103 104 |
# File 'lib/mpp/methods/evm/authorization.rb', line 102 def abi_encode(*values) values.join end |
.address_word(value) ⇒ Object
113 114 115 116 117 118 |
# File 'lib/mpp/methods/evm/authorization.rb', line 113 def address_word(value) hex = value.to_s.delete_prefix("0x") raise ArgumentError, "invalid address: #{value}" unless hex.match?(/\A[a-fA-F0-9]{40}\z/) [hex].pack("H*").rjust(32, "\x00".b) end |
.bytes32_word(value) ⇒ Object
120 121 122 123 124 125 |
# File 'lib/mpp/methods/evm/authorization.rb', line 120 def bytes32_word(value) hex = value.to_s.delete_prefix("0x") raise ArgumentError, "invalid bytes32: #{value}" unless hex.match?(/\A[a-fA-F0-9]{64}\z/) [hex].pack("H*") end |
.challenge_hash(challenge) ⇒ Object
21 22 23 24 |
# File 'lib/mpp/methods/evm/authorization.rb', line 21 def challenge_hash(challenge) digest = keccak256("#{challenge.id}#{challenge.realm}") "0x#{digest.unpack1("H*")}" end |
.checksum_address(address) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/mpp/methods/evm/authorization.rb', line 26 def checksum_address(address) raw = address.to_s hex = raw.delete_prefix("0x") raise ArgumentError, "invalid address: #{address}" unless hex.match?(/\A[a-fA-F0-9]{40}\z/) begin hash = keccak256(hex.downcase).unpack1("H*") rescue LoadError return raw.start_with?("0x") ? raw : "0x#{hex}" end checksummed = hex.downcase.chars.each_with_index.map { |char, index| (char.match?(/[a-f]/) && hash[index].to_i(16) >= 8) ? char.upcase : char }.join "0x#{checksummed}" end |
.domain_separator(name:, version:, chain_id:, verifying_contract:) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/mpp/methods/evm/authorization.rb', line 76 def domain_separator(name:, version:, chain_id:, verifying_contract:) keccak256( abi_encode( keccak256(DOMAIN_TYPE_HASH), keccak256(name), keccak256(version), uint256(chain_id), address_word() ) ) end |
.keccak256(data) ⇒ Object
14 15 16 17 18 19 |
# File 'lib/mpp/methods/evm/authorization.rb', line 14 def keccak256(data) Kernel.require "eth" Eth::Util.keccak256(data) rescue Gem::LoadError raise LoadError, "eth gem is not available" end |
.recover(authorization:, chain_id:, currency:, payload:) ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/mpp/methods/evm/authorization.rb', line 61 def recover(authorization:, chain_id:, currency:, payload:) hash = signing_hash( authorization: , chain_id: chain_id, currency: currency, from: payload["from"], to: payload["to"], value: payload["value"], valid_after: payload["validAfter"], valid_before: payload["validBefore"], nonce: payload["nonce"] ) recover_address(hash, payload["signature"]) end |
.recover_address(hash, signature) ⇒ Object
127 128 129 130 131 132 133 134 135 136 |
# File 'lib/mpp/methods/evm/authorization.rb', line 127 def recover_address(hash, signature) Kernel.require "eth" sig = signature.to_s sig = "0x#{sig}" unless sig.start_with?("0x") recovered_key = Eth::Signature.recover(hash, sig) Eth::Util.public_key_to_address(recovered_key).to_s rescue nil end |
.signing_hash(authorization:, chain_id:, currency:, from:, to:, value:, valid_after:, valid_before:, nonce:) ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/mpp/methods/evm/authorization.rb', line 43 def signing_hash(authorization:, chain_id:, currency:, from:, to:, value:, valid_after:, valid_before:, nonce:) domain = domain_separator( name: ["name"] || [:name], version: ["version"] || [:version], chain_id: chain_id, verifying_contract: currency ) struct = struct_hash( from: from, to: to, value: value, valid_after: valid_after, valid_before: valid_before, nonce: nonce ) keccak256("\x19\x01".b + domain + struct) end |
.struct_hash(from:, to:, value:, valid_after:, valid_before:, nonce:) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/mpp/methods/evm/authorization.rb', line 88 def struct_hash(from:, to:, value:, valid_after:, valid_before:, nonce:) keccak256( abi_encode( keccak256(TRANSFER_TYPE_HASH), address_word(from), address_word(to), uint256(value), uint256(valid_after), uint256(valid_before), bytes32_word(nonce) ) ) end |
.uint256(value) ⇒ Object
106 107 108 109 110 111 |
# File 'lib/mpp/methods/evm/authorization.rb', line 106 def uint256(value) value = Integer(value) raise ArgumentError, "uint256 out of range" if value.negative? || value >= (1 << 256) [value.to_s(16).rjust(64, "0")].pack("H*") end |