remitmd 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7726bbaff2286aa99b1f12cf78138e376d620b6063dfc2c844cc3334d9d3af56
4
- data.tar.gz: 438d8d1cff61e10468d5ef368bde13ffc291a977362ba6d4bde447f1bbaf8892
3
+ metadata.gz: 1d8076d587f455a8a8f5bd4e80dc542a14cd11226bda0784c9816b45710f526c
4
+ data.tar.gz: '005762609902918f053fa2ce4c6b387f6e9af441770c212c9eb913d083113bf8'
5
5
  SHA512:
6
- metadata.gz: 54b55932ceeff519a091e3a745e862feae46ccf9fd9dcc53ce66a6556f11c9a641c57f96ec10b2d82d7d391deb931caff6402263edb320027d3db9ffd6dd213c
7
- data.tar.gz: 0aae67743e0462a6081ad907f97203d921b456beef974a81df3825c5b324e377d0c9702b8f72a7e7fed7e213ec8a2dab0455ac2c55f4b4e6148ee46cbbe8881c
6
+ metadata.gz: 939eea68d169df0628da6639dccb8f3e99961c7c12790f28c26c79e95933f1a93e8543034402eef3360a0f0697fa8346ef5409ac51b50d6801a6943faed94db4
7
+ data.tar.gz: bb7e3e396f9db6a9d8b03ea1cd8d01af21bf21d4682072212fbd0de7ef23250beb0659b1a5d20c97416a7fe7703a03f4ac0567db4bbbbbecf2f760de64541d58
data/README.md CHANGED
@@ -43,7 +43,7 @@ wallet = Remitmd::RemitWallet.from_env
43
43
  # Optional: REMITMD_CHAIN (default: "base"), REMITMD_API_URL
44
44
  ```
45
45
 
46
- Permits are auto-signed. Every payment method fetches the on-chain USDC nonce, signs an EIP-2612 permit, and includes it automatically.
46
+ Permits are auto-signed. Every payment method calls the server's `/permits/prepare` endpoint, signs the hash, and includes the permit automatically.
47
47
 
48
48
  ## CLI Signer (Recommended)
49
49
 
@@ -55,7 +55,7 @@ The CLI signer delegates key management to the `remit` CLI binary, which holds y
55
55
  # Windows: winget install remit-md.remit
56
56
  # Linux: curl -fsSL https://remit.md/install.sh | sh
57
57
 
58
- export REMIT_KEY_PASSWORD=your-keystore-password
58
+ export REMIT_SIGNER_KEY=your-keystore-password
59
59
  ```
60
60
 
61
61
  ```ruby
@@ -200,8 +200,7 @@ wallet.close_tab(tab_id, final_amount: nil, provider_sig: nil)
200
200
  wallet.sign_tab_charge(tab_contract, tab_id, total_charged, call_count) # String
201
201
 
202
202
  # EIP-2612 Permit (auto-signed when omitted from payment methods)
203
- wallet.sign_permit(spender, amount, deadline: nil) # PermitSignature
204
- wallet.sign_usdc_permit(spender, value, deadline, nonce, usdc_address: nil) # PermitSignature
203
+ wallet.sign_permit(flow, amount) # PermitSignature
205
204
 
206
205
  # Streams
207
206
  wallet.create_stream(payee, rate_per_second, max_total, permit: nil) # Stream
@@ -275,24 +274,15 @@ Remitmd::RemitWallet.new(private_key: key, chain: "base_sepolia") # Base Sepoli
275
274
 
276
275
  ### Manual Permit Signing
277
276
 
278
- Permits are auto-signed by default. If you need manual control (custom deadline, pre-signed permits, or offline signing), pass a `PermitSignature` explicitly:
277
+ Permits are auto-signed by default via the server's `/permits/prepare` endpoint. If you need manual control (pre-signed permits, multi-step workflows), pass a `PermitSignature` explicitly:
279
278
 
280
279
  ```ruby
281
- # sign_permit: convenience - auto-fetches nonce, converts amount to base units
282
- permit = wallet.sign_permit("0xRouterAddress...", 5.00, deadline: Time.now.to_i + 7200)
283
- tx = wallet.pay("0xRecipient...", 5.00, permit: permit)
284
-
285
- # sign_usdc_permit: full control - raw base units, explicit nonce
286
- permit = wallet.sign_usdc_permit(
287
- "0xRouterAddress...", # spender
288
- 5_000_000, # value in base units (6 decimals)
289
- Time.now.to_i + 3600, # deadline
290
- 0, # nonce
291
- usdc_address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
292
- )
280
+ permit = wallet.sign_permit("direct", 5.00)
293
281
  tx = wallet.pay("0xRecipient...", 5.00, permit: permit)
294
282
  ```
295
283
 
284
+ Supported flows: `"direct"`, `"escrow"`, `"tab"`, `"stream"`, `"bounty"`, `"deposit"`.
285
+
296
286
  ## License
297
287
 
298
288
  MIT - see [LICENSE](LICENSE)
@@ -71,7 +71,7 @@ module Remitmd
71
71
  #
72
72
  # 1. CLI binary found on PATH (via `which` / `where`)
73
73
  # 2. Meta file at ~/.remit/keys/default.meta (keychain — no password needed), OR
74
- # 3. Keystore file at ~/.remit/keys/default.enc AND REMIT_KEY_PASSWORD env var set
74
+ # 3. Keystore file at ~/.remit/keys/default.enc AND REMIT_SIGNER_KEY env var set
75
75
  #
76
76
  # @param cli_path [String] path or name of the remit CLI binary
77
77
  # @return [Boolean]
@@ -90,7 +90,7 @@ module Remitmd
90
90
  keystore = File.join(keys_dir, "default.enc")
91
91
  return false unless File.exist?(keystore)
92
92
 
93
- password = ENV["REMIT_KEY_PASSWORD"]
93
+ password = ENV["REMIT_SIGNER_KEY"] || ENV["REMIT_KEY_PASSWORD"]
94
94
  return false if password.nil? || password.empty?
95
95
 
96
96
  true
data/lib/remitmd/mock.rb CHANGED
@@ -108,6 +108,10 @@ module Remitmd
108
108
  def sign(_message)
109
109
  "0x" + SecureRandom.hex(32) + SecureRandom.hex(32) + "1b"
110
110
  end
111
+
112
+ def sign_hash(_hash_bytes)
113
+ "0x" + SecureRandom.hex(32) + SecureRandom.hex(32) + "1b"
114
+ end
111
115
  end
112
116
 
113
117
  # ─── Internal: MockTransport ───────────────────────────────────────────────
@@ -135,6 +139,15 @@ module Remitmd
135
139
  def handle(method, path, b) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
136
140
  case [method, path]
137
141
 
142
+ # Permits prepare (server-side permit signing)
143
+ in ["POST", "/permits/prepare"]
144
+ {
145
+ "hash" => "0x#{SecureRandom.hex(32)}",
146
+ "value" => ((b[:amount] || b["amount"]).to_f * 1_000_000).round.to_i,
147
+ "deadline" => Time.now.to_i + 3600,
148
+ "nonce" => 0,
149
+ }
150
+
138
151
  # Contracts (for auto_permit)
139
152
  in ["GET", "/contracts"]
140
153
  {
@@ -23,6 +23,12 @@ module Remitmd
23
23
  raise NotImplementedError, "#{self.class}#sign is not implemented"
24
24
  end
25
25
 
26
+ # Sign a 32-byte hash (raw binary bytes). Returns 0x-prefixed 65-byte hex signature.
27
+ # Used by /permits/prepare and /x402/prepare flows.
28
+ def sign_hash(hash_bytes)
29
+ sign(hash_bytes)
30
+ end
31
+
26
32
  # The Ethereum address corresponding to the signing key (0x-prefixed).
27
33
  def address
28
34
  raise NotImplementedError, "#{self.class}#address is not implemented"
@@ -5,11 +5,15 @@ require "bigdecimal/util"
5
5
  require "json"
6
6
 
7
7
  module Remitmd
8
- # Known USDC contract addresses per chain (EIP-2612 compatible).
9
- USDC_ADDRESSES = {
10
- "base-sepolia" => "0x2d846325766921935f37d5b4478196d3ef93707c",
11
- "base" => "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
12
- "localhost" => "0x5FbDB2315678afecb367f032d93F642f64180aa3",
8
+ # Contract name -> flow name for /permits/prepare.
9
+ CONTRACT_TO_FLOW = {
10
+ "router" => "direct",
11
+ "escrow" => "escrow",
12
+ "tab" => "tab",
13
+ "stream" => "stream",
14
+ "bounty" => "bounty",
15
+ "deposit" => "deposit",
16
+ "relayer" => "direct",
13
17
  }.freeze
14
18
 
15
19
  # Primary remit.md client. All payment operations are methods on RemitWallet.
@@ -89,7 +93,7 @@ module Remitmd
89
93
  unless key
90
94
  raise ArgumentError,
91
95
  "No signing method available. Either:\n" \
92
- " 1. Install the remit CLI and set REMIT_KEY_PASSWORD:\n" \
96
+ " 1. Install the remit CLI and set REMIT_SIGNER_KEY:\n" \
93
97
  " macOS: brew install remit-md/tap/remit\n" \
94
98
  " Windows: winget install remit-md.remit\n" \
95
99
  " Linux: curl -fsSL https://remit.md/install.sh | sh\n" \
@@ -346,86 +350,37 @@ module Remitmd
346
350
  @signer.sign(digest)
347
351
  end
348
352
 
349
- # ─── EIP-2612 Permit ─────────────────────────────────────────────────────
353
+ # ─── EIP-2612 Permit (via /permits/prepare) ────────────────────────────
350
354
 
351
- # Sign an EIP-2612 permit for USDC approval.
352
- # Domain: name="USD Coin", version="2", chainId, verifyingContract=USDC address
353
- # Type: Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline)
354
- # @param spender [String] contract address that will be approved
355
- # @param value [Integer] amount in USDC base units (6 decimals)
356
- # @param deadline [Integer] permit deadline (Unix timestamp)
357
- # @param nonce [Integer] current permit nonce for this wallet
358
- # @param usdc_address [String, nil] override the USDC contract address
359
- # @return [PermitSignature]
360
- def sign_usdc_permit(spender, value, deadline, nonce = 0, usdc_address: nil)
361
- usdc_addr = usdc_address || USDC_ADDRESSES[@chain_key]
362
- if usdc_addr.nil? || usdc_addr.empty?
363
- raise RemitError.new(
364
- RemitError::INVALID_ADDRESS,
365
- "No USDC address configured for chain #{@chain_key.inspect}. " \
366
- "Valid chains: #{USDC_ADDRESSES.keys.join(", ")}",
367
- context: { chain: @chain_key }
368
- )
369
- end
370
- chain_id = @chain_id || ChainId::BASE_SEPOLIA
371
-
372
- # Domain separator for USDC (EIP-2612)
373
- domain_type_hash = keccak256_raw(
374
- "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
375
- )
376
- name_hash = keccak256_raw("USD Coin")
377
- version_hash = keccak256_raw("2")
378
- chain_id_enc = abi_uint256(chain_id)
379
- contract_enc = abi_address(usdc_addr)
380
-
381
- domain_data = domain_type_hash + name_hash + version_hash + chain_id_enc + contract_enc
382
- domain_sep = keccak256_raw(domain_data)
383
-
384
- # Permit struct hash
385
- type_hash = keccak256_raw(
386
- "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
387
- )
388
- owner_enc = abi_address(address)
389
- spender_enc = abi_address(spender)
390
- value_enc = abi_uint256(value)
391
- nonce_enc = abi_uint256(nonce)
392
- deadline_enc = abi_uint256(deadline)
393
-
394
- struct_data = type_hash + owner_enc + spender_enc + value_enc + nonce_enc + deadline_enc
395
- struct_hash = keccak256_raw(struct_data)
396
-
397
- # EIP-712 digest
398
- digest = keccak256_raw("\x19\x01".b + domain_sep + struct_hash)
399
- sig_hex = @signer.sign(digest)
400
-
401
- # Parse r, s, v from the 65-byte signature
402
- sig_bytes = sig_hex.delete_prefix("0x")
403
- r = "0x#{sig_bytes[0, 64]}"
404
- s = "0x#{sig_bytes[64, 64]}"
405
- v = sig_bytes[128, 2].to_i(16)
406
-
407
- PermitSignature.new(value: value, deadline: deadline, v: v, r: r, s: s)
408
- end
409
-
410
- # Convenience: sign a USDC permit. Auto-fetches nonce, defaults deadline to 1 hour.
411
- # @param spender [String] contract address to approve (e.g. router, escrow)
355
+ # Sign a USDC permit via the server's /permits/prepare endpoint.
356
+ #
357
+ # The server computes the EIP-712 hash, manages nonces, and resolves
358
+ # contract addresses. The SDK only signs the hash.
359
+ #
360
+ # @param flow [String] payment flow ("direct", "escrow", "tab", "stream", "bounty", "deposit")
412
361
  # @param amount [Numeric] amount in USDC (e.g. 5.0 for $5.00)
413
- # @param deadline [Integer, nil] optional Unix timestamp; defaults to 1 hour from now
414
362
  # @return [PermitSignature]
415
- def sign_permit(spender, amount, deadline: nil)
416
- usdc_addr = USDC_ADDRESSES[@chain_key]
417
- if usdc_addr.nil? || usdc_addr.empty?
418
- raise RemitError.new(
419
- RemitError::INVALID_ADDRESS,
420
- "No USDC address configured for chain #{@chain_key.inspect}. " \
421
- "Valid chains: #{USDC_ADDRESSES.keys.join(", ")}",
422
- context: { chain: @chain_key }
423
- )
424
- end
425
- nonce = fetch_permit_nonce(usdc_addr)
426
- dl = deadline || (Time.now.to_i + 3600)
427
- raw = (amount * 1_000_000).round.to_i
428
- sign_usdc_permit(spender, raw, dl, nonce, usdc_address: usdc_addr)
363
+ def sign_permit(flow, amount)
364
+ data = @transport.post("/permits/prepare", {
365
+ flow: flow,
366
+ amount: amount.to_s,
367
+ owner: address
368
+ })
369
+
370
+ hash_hex = data["hash"]
371
+ hash_bytes = [hash_hex.delete_prefix("0x")].pack("H*")
372
+
373
+ sig = @signer.sign_hash(hash_bytes)
374
+ sig_hex = sig.start_with?("0x") ? sig[2..] : sig
375
+ r = "0x#{sig_hex[0, 64]}"
376
+ s = "0x#{sig_hex[64, 64]}"
377
+ v = sig_hex[128, 2].to_i(16)
378
+
379
+ PermitSignature.new(
380
+ value: data["value"].to_i,
381
+ deadline: data["deadline"].to_i,
382
+ v: v, r: r, s: s
383
+ )
429
384
  end
430
385
 
431
386
  # ─── Streams (Payment Streaming) ─────────────────────────────────────────
@@ -676,7 +631,7 @@ module Remitmd
676
631
  )
677
632
  end
678
633
 
679
- # ─── EIP-712 helpers (used by sign_tab_charge / sign_usdc_permit) ─────
634
+ # ─── EIP-712 helpers (used by sign_tab_charge) ─────────────────────
680
635
 
681
636
  def keccak256_raw(data)
682
637
  Remitmd::Keccak.digest(data.b)
@@ -693,51 +648,22 @@ module Remitmd
693
648
 
694
649
  # ─── Permit helpers ──────────────────────────────────────────────────
695
650
 
696
- # Fetch the EIP-2612 permit nonce from the API.
697
- # @param usdc_address [String] the USDC contract address
698
- # @return [Integer] current nonce
699
- def fetch_permit_nonce(_usdc_address)
700
- return 0 if @mock_mode
701
-
702
- data = @transport.get("/status/#{address}")
703
- nonce = data.is_a?(Hash) ? data["permit_nonce"] : nil
704
- if nonce.nil?
705
- raise RemitError.new(
706
- RemitError::NETWORK_ERROR,
707
- "permit_nonce not available from API for #{address}. " \
708
- "Ensure the server supports the permit_nonce field in GET /api/v1/status.",
709
- context: { address: address }
710
- )
711
- end
712
- nonce.to_i
713
- end
714
-
715
- # Auto-sign a permit for the given contract type and amount.
716
- # Returns nil on failure instead of raising, so callers can proceed without a permit.
651
+ # Internal: auto-sign a permit via /permits/prepare.
652
+ # Maps the contract name to a flow and calls sign_permit().
653
+ # Returns nil on failure so callers degrade gracefully.
717
654
  # @param contract [String] contract key - "router", "escrow", "tab", etc.
718
655
  # @param amount [Numeric] amount in USDC
719
656
  # @return [PermitSignature, nil]
720
657
  def auto_permit(contract, amount)
721
- contracts = get_contracts
722
- spender = contracts.send(contract.to_sym)
723
- return nil unless spender
724
-
725
- sign_permit(spender, amount)
658
+ flow = CONTRACT_TO_FLOW[contract]
659
+ unless flow
660
+ warn "[remitmd] unknown contract for permit: #{contract}"
661
+ return nil
662
+ end
663
+ sign_permit(flow, amount)
726
664
  rescue => e
727
665
  warn "[remitmd] auto-permit failed for #{contract} (amount=#{amount}): #{e.message}"
728
666
  nil
729
667
  end
730
-
731
- # Spender contract mapping for auto_permit.
732
- PERMIT_SPENDER = {
733
- pay: :router,
734
- create_escrow: :escrow,
735
- create_tab: :tab,
736
- create_stream: :stream,
737
- create_bounty: :bounty,
738
- place_deposit: :deposit,
739
- create_fund_link: :relayer,
740
- create_withdraw_link: :relayer,
741
- }.freeze
742
668
  end
743
669
  end
@@ -26,22 +26,25 @@ module Remitmd
26
26
  # On receiving a 402, the client:
27
27
  # 1. Decodes the PAYMENT-REQUIRED header (base64 JSON)
28
28
  # 2. Checks the amount is within max_auto_pay_usdc
29
- # 3. Builds and signs an EIP-3009 transferWithAuthorization
30
- # 4. Base64-encodes the PAYMENT-SIGNATURE header
31
- # 5. Retries the original request with payment attached
29
+ # 3. Calls /x402/prepare to get hash + authorization fields
30
+ # 4. Signs the hash
31
+ # 5. Base64-encodes the PAYMENT-SIGNATURE header
32
+ # 6. Retries the original request with payment attached
32
33
  #
33
34
  # @example
34
35
  # signer = Remitmd::PrivateKeySigner.new("0x...")
35
- # client = Remitmd::X402Client.new(wallet: signer)
36
+ # client = Remitmd::X402Client.new(wallet: signer, api_transport: transport)
36
37
  # response = client.fetch("https://api.provider.com/v1/data")
37
38
  #
38
39
  class X402Client
39
40
  attr_reader :last_payment
40
41
 
41
- # @param wallet [#sign, #address] a signer that can sign EIP-712 digests
42
+ # @param wallet [#sign_hash, #address] a signer that can sign raw hashes
43
+ # @param api_transport [#post] authenticated HTTP transport for calling /x402/prepare
42
44
  # @param max_auto_pay_usdc [Float] maximum USDC amount to auto-pay per request (default: 0.10)
43
- def initialize(wallet:, max_auto_pay_usdc: 0.10)
45
+ def initialize(wallet:, api_transport: nil, max_auto_pay_usdc: 0.10)
44
46
  @wallet = wallet
47
+ @api_transport = api_transport
45
48
  @max_auto_pay_usdc = max_auto_pay_usdc
46
49
  @last_payment = nil
47
50
  end
@@ -106,96 +109,44 @@ module Remitmd
106
109
  raise AllowanceExceededError.new(amount_usdc, @max_auto_pay_usdc)
107
110
  end
108
111
 
109
- # 4. Parse chainId from CAIP-2 network string (e.g. "eip155:84532" -> 84532).
110
- chain_id = required["network"].split(":")[1].to_i
111
-
112
- # 5. Build EIP-3009 authorization fields.
113
- now_secs = Time.now.to_i
114
- valid_before = now_secs + (required["maxTimeoutSeconds"] || 60).to_i
115
- nonce_bytes = SecureRandom.bytes(32)
116
- nonce_hex = "0x#{nonce_bytes.unpack1("H*")}"
117
-
118
- # 6. Sign EIP-712 typed data for TransferWithAuthorization.
119
- digest = eip3009_digest(
120
- chain_id: chain_id,
121
- asset: required["asset"],
122
- from: @wallet.address,
123
- to: required["payTo"],
124
- value: amount_base_units,
125
- valid_after: 0,
126
- valid_before: valid_before,
127
- nonce_bytes: nonce_bytes
128
- )
129
- signature = @wallet.sign(digest)
112
+ # 4. Call /x402/prepare to get the hash + authorization fields.
113
+ unless @api_transport
114
+ raise RemitError.new("SERVER_ERROR",
115
+ "x402 auto-pay requires an api_transport for calling /x402/prepare")
116
+ end
117
+
118
+ prepare_data = @api_transport.post("/x402/prepare", {
119
+ payment_required: raw,
120
+ payer: @wallet.address
121
+ })
122
+
123
+ # 5. Sign the hash.
124
+ hash_hex = prepare_data["hash"]
125
+ hash_bytes = [hash_hex.delete_prefix("0x")].pack("H*")
126
+ signature = @wallet.sign_hash(hash_bytes)
130
127
 
131
- # 7. Build PAYMENT-SIGNATURE JSON payload.
128
+ # 6. Build PAYMENT-SIGNATURE JSON payload.
132
129
  payment_payload = {
133
130
  scheme: required["scheme"],
134
131
  network: required["network"],
135
132
  x402Version: 1,
136
133
  payload: {
137
- signature: signature,
134
+ signature: signature,
138
135
  authorization: {
139
- from: @wallet.address,
140
- to: required["payTo"],
141
- value: required["amount"],
142
- validAfter: "0",
143
- validBefore: valid_before.to_s,
144
- nonce: nonce_hex,
136
+ from: prepare_data["from"],
137
+ to: prepare_data["to"],
138
+ value: prepare_data["value"],
139
+ validAfter: prepare_data["valid_after"] || prepare_data["validAfter"],
140
+ validBefore: prepare_data["valid_before"] || prepare_data["validBefore"],
141
+ nonce: prepare_data["nonce"],
145
142
  },
146
143
  },
147
144
  }
148
145
  payment_header = Base64.strict_encode64(JSON.generate(payment_payload))
149
146
 
150
- # 8. Retry with PAYMENT-SIGNATURE header.
147
+ # 7. Retry with PAYMENT-SIGNATURE header.
151
148
  new_headers = headers.merge("PAYMENT-SIGNATURE" => payment_header)
152
149
  make_request(uri, method, new_headers, body)
153
150
  end
154
-
155
- # Compute the EIP-712 hash for EIP-3009 TransferWithAuthorization.
156
- def eip3009_digest(chain_id:, asset:, from:, to:, value:, valid_after:, valid_before:, nonce_bytes:)
157
- # Domain separator: USD Coin / version 2
158
- domain_type_hash = keccak256(
159
- "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
160
- )
161
- name_hash = keccak256("USD Coin")
162
- version_hash = keccak256("2")
163
- chain_id_enc = abi_uint256(chain_id)
164
- contract_enc = abi_address(asset)
165
-
166
- domain_data = domain_type_hash + name_hash + version_hash + chain_id_enc + contract_enc
167
- domain_separator = keccak256(domain_data)
168
-
169
- # TransferWithAuthorization struct hash
170
- type_hash = keccak256(
171
- "TransferWithAuthorization(address from,address to,uint256 value," \
172
- "uint256 validAfter,uint256 validBefore,bytes32 nonce)"
173
- )
174
- struct_data = type_hash +
175
- abi_address(from) +
176
- abi_address(to) +
177
- abi_uint256(value) +
178
- abi_uint256(valid_after) +
179
- abi_uint256(valid_before) +
180
- nonce_bytes
181
-
182
- struct_hash = keccak256(struct_data)
183
-
184
- # Final EIP-712 hash
185
- keccak256("\x19\x01" + domain_separator + struct_hash)
186
- end
187
-
188
- def keccak256(data)
189
- Remitmd::Keccak.digest(data.b)
190
- end
191
-
192
- def abi_uint256(value)
193
- [value.to_i.to_s(16).rjust(64, "0")].pack("H*")
194
- end
195
-
196
- def abi_address(addr)
197
- hex = addr.to_s.delete_prefix("0x").rjust(64, "0")
198
- [hex].pack("H*")
199
- end
200
151
  end
201
152
  end
data/lib/remitmd.rb CHANGED
@@ -26,5 +26,5 @@ require_relative "remitmd/x402_paywall"
26
26
  # mock.was_paid?("0x0000000000000000000000000000000000000001", 1.00) # => true
27
27
  #
28
28
  module Remitmd
29
- VERSION = "0.2.4"
29
+ VERSION = "0.2.5"
30
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remitmd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - remit.md
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-28 00:00:00.000000000 Z
11
+ date: 2026-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec