remitmd 0.1.7 → 0.1.9

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: b29ac02ca95fe1053bed848cca89111698f1744bbeb145681a76a3f9ed2d8e66
4
- data.tar.gz: d55c4f33eaaa686fa0036e3a0566ee6382a0a544c01093a3d9887e745b2eaa01
3
+ metadata.gz: 05f736527efb603117342dec70fc553420aa04d4dbc3ca8db5fccd8b31b7a9cb
4
+ data.tar.gz: a7f143bc6302d96cc1ca492c7f8393cdd98b039e86747fee1e7bf36a919b83d0
5
5
  SHA512:
6
- metadata.gz: 9c00d48fc571e6975d9dfd32c679d5b9cf46205742a8a725022907eb997d42de7a61ae040c22dfafb7d5a994277d25f5dafd2e589611e849f94a70c692d71102
7
- data.tar.gz: dff3c27cf317ce994999efd38d57f1173f0a1136da0bf4dbcdbf1ff5879a9f737bef96875802108ca130b4e46b3b94eec1cc0f52cace3152c89357018d9f9621
6
+ metadata.gz: d998125805f98cf6ccbd389ec8df96655cb66a47e51b57d274c6090cdbb00a31c713f915282f166982d85dc2d3db3ce14a9c294f7ff0c00d049252590c661f10
7
+ data.tar.gz: 0dad289442caafe80bcbf83b13f38f5e7d70c7adacd0ad8b6a12172e6c27a6aae872a9b97c29537fb6ee0783eb2d3261da4eb47889f677aba12a15c10bc05ab3
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > [Skill MD](https://remit.md) · [Docs](https://remit.md/docs) · [Agent Spec](https://remit.md/agent.md)
4
4
 
5
- Universal payment protocol for AI agents Ruby client library.
5
+ Universal payment protocol for AI agents - Ruby client library.
6
6
 
7
7
  [![CI](https://github.com/remit-md/sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/remit-md/sdk/actions/workflows/ci.yml)
8
8
  [![Gem Version](https://badge.fury.io/rb/remitmd.svg)](https://badge.fury.io/rb/remitmd)
@@ -24,7 +24,7 @@ gem install remitmd
24
24
  ```ruby
25
25
  require "remitmd"
26
26
 
27
- wallet = Remitmd::RemitWallet.new(private_key: ENV["REMITMD_PRIVATE_KEY"])
27
+ wallet = Remitmd::RemitWallet.new(private_key: ENV["REMITMD_KEY"])
28
28
 
29
29
  # Direct payment
30
30
  tx = wallet.pay("0xRecipient0000000000000000000000000000001", 1.50)
@@ -39,12 +39,32 @@ Or from environment variables:
39
39
 
40
40
  ```ruby
41
41
  wallet = Remitmd::RemitWallet.from_env
42
- # Requires: REMITMD_PRIVATE_KEY
42
+ # Requires: REMITMD_KEY (or REMIT_SIGNER_URL + REMIT_SIGNER_TOKEN)
43
43
  # Optional: REMITMD_CHAIN (default: "base"), REMITMD_API_URL
44
44
  ```
45
45
 
46
46
  Permits are auto-signed. Every payment method fetches the on-chain USDC nonce, signs an EIP-2612 permit, and includes it automatically.
47
47
 
48
+ ## Local Signer (Recommended)
49
+
50
+ The local signer delegates key management to `remit signer`, a localhost HTTP server that holds your encrypted key. Your agent only needs a URL and token - no private key in the environment.
51
+
52
+ ```bash
53
+ export REMIT_SIGNER_URL=http://127.0.0.1:7402
54
+ export REMIT_SIGNER_TOKEN=rmit_sk_...
55
+ ```
56
+
57
+ ```ruby
58
+ # Explicit
59
+ signer = Remitmd::HttpSigner.new(url: "http://127.0.0.1:7402", token: "rmit_sk_...")
60
+ wallet = Remitmd::RemitWallet.new(signer: signer)
61
+
62
+ # Or auto-detect from env (recommended)
63
+ wallet = Remitmd::RemitWallet.from_env # detects REMIT_SIGNER_URL automatically
64
+ ```
65
+
66
+ `RemitWallet.from_env` detects signer credentials automatically. Priority: `REMIT_SIGNER_URL` > `REMITMD_KEY`.
67
+
48
68
  ## Payment Models
49
69
 
50
70
  ### Direct Payment
@@ -73,7 +93,7 @@ contracts = wallet.get_contracts
73
93
  sig = wallet.sign_tab_charge(contracts.tab, tab.id, 3_000_000, 1)
74
94
  wallet.charge_tab(tab.id, 0.003, 0.003, 1, sig)
75
95
 
76
- # Close when done unused funds return
96
+ # Close when done - unused funds return
77
97
  wallet.close_tab(tab.id)
78
98
  ```
79
99
 
@@ -136,9 +156,9 @@ end
136
156
 
137
157
  ```ruby
138
158
  mock.was_paid?(address, amount) # true/false
139
- mock.total_paid_to(address) # BigDecimal sum of all payments to address
159
+ mock.total_paid_to(address) # BigDecimal - sum of all payments to address
140
160
  mock.transaction_count # Integer
141
- mock.balance # BigDecimal current balance
161
+ mock.balance # BigDecimal - current balance
142
162
  mock.transactions # Array<Transaction>
143
163
  mock.set_balance(amount) # Override starting balance
144
164
  mock.reset # Clear all state
@@ -254,11 +274,11 @@ Remitmd::RemitWallet.new(private_key: key, chain: "base_sepolia") # Base Sepoli
254
274
  Permits are auto-signed by default. If you need manual control (custom deadline, pre-signed permits, or offline signing), pass a `PermitSignature` explicitly:
255
275
 
256
276
  ```ruby
257
- # sign_permit: convenience auto-fetches nonce, converts amount to base units
277
+ # sign_permit: convenience - auto-fetches nonce, converts amount to base units
258
278
  permit = wallet.sign_permit("0xRouterAddress...", 5.00, deadline: Time.now.to_i + 7200)
259
279
  tx = wallet.pay("0xRecipient...", 5.00, permit: permit)
260
280
 
261
- # sign_usdc_permit: full control raw base units, explicit nonce
281
+ # sign_usdc_permit: full control - raw base units, explicit nonce
262
282
  permit = wallet.sign_usdc_permit(
263
283
  "0xRouterAddress...", # spender
264
284
  5_000_000, # value in base units (6 decimals)
@@ -271,6 +291,6 @@ tx = wallet.pay("0xRecipient...", 5.00, permit: permit)
271
291
 
272
292
  ## License
273
293
 
274
- MIT see [LICENSE](LICENSE)
294
+ MIT - see [LICENSE](LICENSE)
275
295
 
276
296
  [Documentation](https://remit.md/docs) · [Protocol Spec](https://remit.md) · [GitHub](https://github.com/remit-md/sdk)
data/lib/remitmd/a2a.rb CHANGED
@@ -135,7 +135,7 @@ module Remitmd
135
135
 
136
136
  # ─── A2A JSON-RPC client ────────────────────────────────────────────────────
137
137
 
138
- # A2A JSON-RPC client send payments and manage tasks via the A2A protocol.
138
+ # A2A JSON-RPC client - send payments and manage tasks via the A2A protocol.
139
139
  #
140
140
  # @example
141
141
  # card = Remitmd::AgentCard.discover("https://remit.md")
@@ -15,7 +15,7 @@ module Remitmd
15
15
  # puts e.doc_url # => "https://remit.md/docs/api-reference/error-codes#invalid_address"
16
16
  # end
17
17
  class RemitError < StandardError
18
- # Error code constants matches TS SDK (28 codes)
18
+ # Error code constants - matches TS SDK (28 codes)
19
19
  # Auth errors
20
20
  INVALID_SIGNATURE = "INVALID_SIGNATURE"
21
21
  NONCE_REUSED = "NONCE_REUSED"
@@ -80,7 +80,7 @@ module Remitmd
80
80
  @code = code
81
81
  @doc_url = "https://remit.md/docs/api-reference/error-codes##{code.downcase}"
82
82
  @context = context
83
- super("[#{code}] #{message} #{@doc_url}")
83
+ super("[#{code}] #{message} - #{@doc_url}")
84
84
  end
85
85
  end
86
86
  end
data/lib/remitmd/http.rb CHANGED
@@ -74,7 +74,7 @@ module Remitmd
74
74
  nonce_hex = "0x#{nonce_bytes.unpack1("H*")}"
75
75
  timestamp = Time.now.to_i
76
76
 
77
- # Strip query string before signing only the path is included in EIP-712.
77
+ # Strip query string before signing - only the path is included in EIP-712.
78
78
  sign_path = full_path.split("?").first
79
79
  http_method = method.to_s.upcase
80
80
  digest = eip712_hash(http_method, sign_path, timestamp, nonce_bytes)
@@ -100,7 +100,7 @@ module Remitmd
100
100
  # Domain: name="remit.md", version="0.1", chainId, verifyingContract
101
101
  # Struct: APIRequest(string method, string path, uint256 timestamp, bytes32 nonce)
102
102
  def eip712_hash(method, path, timestamp, nonce_bytes)
103
- # Type hashes (string constants keccak256 of the type string)
103
+ # Type hashes (string constants - keccak256 of the type string)
104
104
  domain_type_hash = keccak256_bytes(
105
105
  "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
106
106
  )
@@ -162,7 +162,7 @@ module Remitmd
162
162
  raise RemitError.new(code, err["message"] || "Bad request", context: parsed)
163
163
  when 401
164
164
  raise RemitError.new(RemitError::UNAUTHORIZED,
165
- "Authentication failed check your private key and chain ID")
165
+ "Authentication failed - check your private key and chain ID")
166
166
  when 429
167
167
  raise RemitError.new(RemitError::RATE_LIMITED,
168
168
  "Rate limit exceeded. See https://remit.md/docs/api-reference/rate-limits")
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "json"
6
+
7
+ module Remitmd
8
+ # Signer backed by a local HTTP signing server.
9
+ #
10
+ # Delegates digest signing to an HTTP server (typically
11
+ # `http://127.0.0.1:7402`). The signer server holds the encrypted key;
12
+ # this adapter only needs a bearer token and URL.
13
+ #
14
+ # @example
15
+ # signer = Remitmd::HttpSigner.new(url: "http://127.0.0.1:7402", token: "rmit_sk_...")
16
+ # wallet = Remitmd::RemitWallet.new(signer: signer, chain: "base")
17
+ #
18
+ class HttpSigner
19
+ include Signer
20
+
21
+ # Create an HttpSigner, fetching and caching the wallet address.
22
+ #
23
+ # @param url [String] signer server URL (e.g. "http://127.0.0.1:7402")
24
+ # @param token [String] bearer token for authentication
25
+ # @raise [RemitError] if the server is unreachable, returns an error, or returns no address
26
+ def initialize(url:, token:)
27
+ @url = url.chomp("/")
28
+ @token = token
29
+ @address = fetch_address
30
+ end
31
+
32
+ # Sign a 32-byte digest (raw binary bytes).
33
+ # Posts to /sign/digest with the hex-encoded digest.
34
+ # Returns a 0x-prefixed 65-byte hex signature.
35
+ #
36
+ # @param digest_bytes [String] 32-byte binary digest
37
+ # @return [String] 0x-prefixed 65-byte hex signature
38
+ # @raise [RemitError] on network, auth, policy, or server errors
39
+ def sign(digest_bytes)
40
+ hex = "0x#{digest_bytes.unpack1("H*")}"
41
+ uri = URI("#{@url}/sign/digest")
42
+ http = build_http(uri)
43
+
44
+ req = Net::HTTP::Post.new(uri.path)
45
+ req["Content-Type"] = "application/json"
46
+ req["Authorization"] = "Bearer #{@token}"
47
+ req.body = { digest: hex }.to_json
48
+
49
+ resp = begin
50
+ http.request(req)
51
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, SocketError => e
52
+ raise RemitError.new(
53
+ RemitError::NETWORK_ERROR,
54
+ "HttpSigner: cannot reach signer server at #{@url}: #{e.message}"
55
+ )
56
+ end
57
+
58
+ handle_sign_response(resp)
59
+ end
60
+
61
+ # The cached Ethereum address (0x-prefixed).
62
+ # @return [String]
63
+ attr_reader :address
64
+
65
+ # Never expose the bearer token in inspect/to_s output.
66
+ def inspect
67
+ "#<Remitmd::HttpSigner address=#{@address}>"
68
+ end
69
+
70
+ alias to_s inspect
71
+
72
+ private
73
+
74
+ # Fetch the wallet address from GET /address during construction.
75
+ # @return [String] the 0x-prefixed Ethereum address
76
+ # @raise [RemitError] on any failure
77
+ def fetch_address
78
+ uri = URI("#{@url}/address")
79
+ http = build_http(uri)
80
+
81
+ req = Net::HTTP::Get.new(uri.path)
82
+ req["Authorization"] = "Bearer #{@token}"
83
+
84
+ resp = begin
85
+ http.request(req)
86
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, SocketError => e
87
+ raise RemitError.new(
88
+ RemitError::NETWORK_ERROR,
89
+ "HttpSigner: cannot reach signer server at #{@url}: #{e.message}"
90
+ )
91
+ end
92
+
93
+ status = resp.code.to_i
94
+
95
+ if status == 401
96
+ raise RemitError.new(
97
+ RemitError::UNAUTHORIZED,
98
+ "HttpSigner: unauthorized -- check your REMIT_SIGNER_TOKEN"
99
+ )
100
+ end
101
+
102
+ unless (200..299).cover?(status)
103
+ raise RemitError.new(
104
+ RemitError::SERVER_ERROR,
105
+ "HttpSigner: GET /address failed (#{status})"
106
+ )
107
+ end
108
+
109
+ body = begin
110
+ JSON.parse(resp.body.to_s)
111
+ rescue JSON::ParserError
112
+ raise RemitError.new(
113
+ RemitError::SERVER_ERROR,
114
+ "HttpSigner: GET /address returned malformed JSON"
115
+ )
116
+ end
117
+
118
+ addr = body["address"]
119
+ if addr.nil? || addr.to_s.empty?
120
+ raise RemitError.new(
121
+ RemitError::SERVER_ERROR,
122
+ "HttpSigner: GET /address returned no address"
123
+ )
124
+ end
125
+
126
+ addr.to_s
127
+ end
128
+
129
+ # Handle the response from POST /sign/digest.
130
+ # @param resp [Net::HTTPResponse]
131
+ # @return [String] the 0x-prefixed hex signature
132
+ # @raise [RemitError] on any error
133
+ def handle_sign_response(resp)
134
+ status = resp.code.to_i
135
+
136
+ if status == 401
137
+ raise RemitError.new(
138
+ RemitError::UNAUTHORIZED,
139
+ "HttpSigner: unauthorized -- check your REMIT_SIGNER_TOKEN"
140
+ )
141
+ end
142
+
143
+ if status == 403
144
+ reason = begin
145
+ data = JSON.parse(resp.body.to_s)
146
+ data["reason"] || "unknown"
147
+ rescue JSON::ParserError
148
+ "unknown"
149
+ end
150
+ raise RemitError.new(
151
+ RemitError::UNAUTHORIZED,
152
+ "HttpSigner: policy denied -- #{reason}"
153
+ )
154
+ end
155
+
156
+ unless (200..299).cover?(status)
157
+ detail = begin
158
+ data = JSON.parse(resp.body.to_s)
159
+ data["reason"] || data["error"] || "server error"
160
+ rescue JSON::ParserError
161
+ "server error"
162
+ end
163
+ raise RemitError.new(
164
+ RemitError::SERVER_ERROR,
165
+ "HttpSigner: sign failed (#{status}): #{detail}"
166
+ )
167
+ end
168
+
169
+ body = begin
170
+ JSON.parse(resp.body.to_s)
171
+ rescue JSON::ParserError
172
+ raise RemitError.new(
173
+ RemitError::SERVER_ERROR,
174
+ "HttpSigner: POST /sign/digest returned malformed JSON"
175
+ )
176
+ end
177
+
178
+ sig = body["signature"]
179
+ if sig.nil? || sig.to_s.empty?
180
+ raise RemitError.new(
181
+ RemitError::SERVER_ERROR,
182
+ "HttpSigner: server returned no signature"
183
+ )
184
+ end
185
+
186
+ sig.to_s
187
+ end
188
+
189
+ # Build a Net::HTTP client for the given URI.
190
+ # @param uri [URI] the target URI
191
+ # @return [Net::HTTP]
192
+ def build_http(uri)
193
+ http = Net::HTTP.new(uri.host, uri.port)
194
+ http.use_ssl = uri.scheme == "https"
195
+ http.open_timeout = 5
196
+ http.read_timeout = 10
197
+ http
198
+ end
199
+ end
200
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Remitmd
4
- # Pure-Ruby Keccak-256 (Ethereum variant NOT SHA-3).
4
+ # Pure-Ruby Keccak-256 (Ethereum variant - NOT SHA-3).
5
5
  #
6
6
  # SHA-3 uses different padding (0x06 instead of 0x01).
7
7
  # This implementation matches Ethereum's keccak256.
data/lib/remitmd/mock.rb CHANGED
@@ -175,7 +175,7 @@ module Remitmd
175
175
  @state[:pending_invoices][id] = b
176
176
  { "id" => id, "status" => "pending" }
177
177
 
178
- # Escrow create (step 2 fund with invoice_id)
178
+ # Escrow create (step 2 - fund with invoice_id)
179
179
  in ["POST", "/escrows"]
180
180
  invoice_id = fetch!(b, :invoice_id)
181
181
  inv = @state[:pending_invoices].delete(invoice_id)
@@ -4,12 +4,12 @@ require "openssl"
4
4
  require "securerandom"
5
5
 
6
6
  module Remitmd
7
- # secp256k1 field prime p (constant never changes)
7
+ # secp256k1 field prime p (constant - never changes)
8
8
  SECP256K1_P = OpenSSL::BN.new(
9
9
  "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16
10
10
  ).freeze
11
11
 
12
- # Precomputed (p + 1) / 4 the modular square root exponent for p ≡ 3 (mod 4).
12
+ # Precomputed (p + 1) / 4 - the modular square root exponent for p ≡ 3 (mod 4).
13
13
  # Avoids BN division at runtime (which returns Integer on some OpenSSL versions).
14
14
  SECP256K1_SQRT_EXP = OpenSSL::BN.new(
15
15
  "3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFF0C", 16
@@ -66,7 +66,7 @@ module Remitmd
66
66
  group = @key.group
67
67
  n = group.order
68
68
 
69
- # ECDSA sign dsa_sign_asn1 uses the input directly as the hash (no pre-hashing)
69
+ # ECDSA sign - dsa_sign_asn1 uses the input directly as the hash (no pre-hashing)
70
70
  der = @key.dsa_sign_asn1(digest_bytes)
71
71
  asn1 = OpenSSL::ASN1.decode(der)
72
72
  bn_r = asn1.value[0].value
@@ -104,7 +104,7 @@ module Remitmd
104
104
  break
105
105
  end
106
106
  end
107
- raise "Could not determine recovery ID key or hash may be invalid" if v.nil?
107
+ raise "Could not determine recovery ID - key or hash may be invalid" if v.nil?
108
108
 
109
109
  # Build 65-byte Ethereum signature: r (32) || s (32) || v (1)
110
110
  r_bytes = [bn_r.to_s(16).rjust(64, "0")].pack("H*")
@@ -129,13 +129,13 @@ module Remitmd
129
129
  def recover_r_point(group, bn_r, parity)
130
130
  p = SECP256K1_P
131
131
  x = bn_r
132
- # y² = x³ + 7 (mod p) secp256k1 curve equation
132
+ # y² = x³ + 7 (mod p) - secp256k1 curve equation
133
133
  x3 = x.mod_exp(OpenSSL::BN.new("3"), p)
134
134
  rhs = x3 + OpenSSL::BN.new("7")
135
135
  y_squared = rhs % p
136
136
  # Tonelli–Shanks: since p ≡ 3 mod 4, sqrt = y²^((p+1)/4) mod p
137
137
  y = y_squared.mod_exp(SECP256K1_SQRT_EXP, p)
138
- # Verify that y² ≡ y_squared (mod p) i.e., a square root exists
138
+ # Verify that y² ≡ y_squared (mod p) - i.e., a square root exists
139
139
  return nil unless y.mod_mul(y, p) == y_squared
140
140
 
141
141
  y = p - y if (y.to_i & 1) != parity
@@ -148,7 +148,7 @@ module Remitmd
148
148
  end
149
149
 
150
150
  def derive_address(public_key)
151
- # Uncompressed public key: 04 || x (32) || y (32) skip the 0x04 prefix
151
+ # Uncompressed public key: 04 || x (32) || y (32) - skip the 0x04 prefix
152
152
  pub_bytes = [public_key.to_octet_string(:uncompressed).unpack1("H*")[2..]].pack("H*")
153
153
  keccak = keccak256_hex(pub_bytes)
154
154
  "0x#{keccak[-40..]}"
@@ -28,7 +28,7 @@ module Remitmd
28
28
 
29
29
  # @param private_key [String, nil] 0x-prefixed hex private key
30
30
  # @param signer [Signer, nil] custom signer (pass instead of private_key)
31
- # @param chain [String] chain name "base", "base_sepolia"
31
+ # @param chain [String] chain name - "base", "base_sepolia"
32
32
  # @param api_url [String, nil] override API base URL
33
33
  # @param transport [Object, nil] inject mock transport (used by MockRemit)
34
34
  def initialize(private_key: nil, signer: nil, chain: "base", api_url: nil, router_address: nil, transport: nil)
@@ -67,18 +67,31 @@ module Remitmd
67
67
  end
68
68
 
69
69
  # Build a RemitWallet from environment variables.
70
- # Reads: REMITMD_KEY (primary) or REMITMD_PRIVATE_KEY (deprecated fallback),
71
- # REMITMD_CHAIN, REMITMD_API_URL, REMITMD_ROUTER_ADDRESS.
70
+ # Reads: REMIT_SIGNER_URL + REMIT_SIGNER_TOKEN (preferred, uses HttpSigner),
71
+ # or REMITMD_KEY (primary) / REMITMD_PRIVATE_KEY (deprecated fallback).
72
+ # Also reads: REMITMD_CHAIN, REMITMD_API_URL, REMITMD_ROUTER_ADDRESS.
72
73
  def self.from_env
74
+ chain = ENV.fetch("REMITMD_CHAIN", "base")
75
+ api_url = ENV["REMITMD_API_URL"]
76
+ router_address = ENV["REMITMD_ROUTER_ADDRESS"]
77
+
78
+ # Priority 1: HTTP signer server
79
+ signer_url = ENV["REMIT_SIGNER_URL"]
80
+ if signer_url
81
+ signer_token = ENV["REMIT_SIGNER_TOKEN"]
82
+ raise ArgumentError, "REMIT_SIGNER_TOKEN must be set when REMIT_SIGNER_URL is set" unless signer_token
83
+
84
+ signer = HttpSigner.new(url: signer_url, token: signer_token)
85
+ return new(signer: signer, chain: chain, api_url: api_url, router_address: router_address)
86
+ end
87
+
88
+ # Priority 2: raw private key
73
89
  key = ENV["REMITMD_KEY"] || ENV["REMITMD_PRIVATE_KEY"]
74
90
  if ENV["REMITMD_PRIVATE_KEY"] && !ENV["REMITMD_KEY"]
75
91
  warn "[remitmd] REMITMD_PRIVATE_KEY is deprecated, use REMITMD_KEY instead"
76
92
  end
77
- raise ArgumentError, "REMITMD_KEY not set" unless key
93
+ raise ArgumentError, "REMITMD_KEY not set (or set REMIT_SIGNER_URL + REMIT_SIGNER_TOKEN)" unless key
78
94
 
79
- chain = ENV.fetch("REMITMD_CHAIN", "base")
80
- api_url = ENV["REMITMD_API_URL"]
81
- router_address = ENV["REMITMD_ROUTER_ADDRESS"]
82
95
  new(private_key: key, chain: chain, api_url: api_url, router_address: router_address)
83
96
  end
84
97
 
@@ -144,7 +157,7 @@ module Remitmd
144
157
  # @param to [String] recipient 0x-prefixed address
145
158
  # @param amount [Numeric, BigDecimal] amount in USDC (e.g. 1.50)
146
159
  # @param memo [String, nil] optional note
147
- # @param permit [PermitSignature, nil] EIP-2612 permit auto-signed if nil
160
+ # @param permit [PermitSignature, nil] EIP-2612 permit - auto-signed if nil
148
161
  # @return [Transaction]
149
162
  def pay(to, amount, memo: nil, permit: nil)
150
163
  validate_address!(to)
@@ -163,7 +176,7 @@ module Remitmd
163
176
  # @param amount [Numeric] amount in USDC
164
177
  # @param memo [String, nil] optional note
165
178
  # @param expires_in_secs [Integer, nil] optional expiry in seconds from now
166
- # @param permit [PermitSignature, nil] EIP-2612 permit auto-signed if nil
179
+ # @param permit [PermitSignature, nil] EIP-2612 permit - auto-signed if nil
167
180
  # @return [Escrow]
168
181
  def create_escrow(payee, amount, memo: nil, expires_in_secs: nil, permit: nil)
169
182
  validate_address!(payee)
@@ -224,7 +237,7 @@ module Remitmd
224
237
  # @param limit_amount [Numeric] maximum tab credit in USDC
225
238
  # @param per_unit [Numeric] USDC per API call
226
239
  # @param expires_in_secs [Integer] optional expiry duration in seconds (default: 86400)
227
- # @param permit [PermitSignature, nil] EIP-2612 permit auto-signed if nil
240
+ # @param permit [PermitSignature, nil] EIP-2612 permit - auto-signed if nil
228
241
  # @return [Tab]
229
242
  def create_tab(provider, limit_amount, per_unit = 0.0, expires_in_secs: 86_400, permit: nil)
230
243
  validate_address!(provider)
@@ -417,7 +430,7 @@ module Remitmd
417
430
  # @param payee [String] 0x-prefixed address of the stream recipient
418
431
  # @param rate_per_second [Numeric] USDC per second
419
432
  # @param max_total [Numeric] maximum total USDC for the stream
420
- # @param permit [PermitSignature, nil] EIP-2612 permit auto-signed if nil
433
+ # @param permit [PermitSignature, nil] EIP-2612 permit - auto-signed if nil
421
434
  # @return [Stream]
422
435
  def create_stream(payee, rate_per_second, max_total, permit: nil)
423
436
  validate_address!(payee)
@@ -455,7 +468,7 @@ module Remitmd
455
468
  # @param task_description [String] task description
456
469
  # @param deadline [Integer] deadline as Unix timestamp
457
470
  # @param max_attempts [Integer] maximum submission attempts (default: 10)
458
- # @param permit [PermitSignature, nil] EIP-2612 permit auto-signed if nil
471
+ # @param permit [PermitSignature, nil] EIP-2612 permit - auto-signed if nil
459
472
  # @return [Bounty]
460
473
  def create_bounty(amount, task_description, deadline, max_attempts: 10, permit: nil)
461
474
  validate_amount!(amount)
@@ -509,7 +522,7 @@ module Remitmd
509
522
  # @param provider [String] 0x-prefixed provider address
510
523
  # @param amount [Numeric] amount in USDC
511
524
  # @param expires_in_secs [Integer] expiry duration in seconds (default: 3600)
512
- # @param permit [PermitSignature, nil] EIP-2612 permit auto-signed if nil
525
+ # @param permit [PermitSignature, nil] EIP-2612 permit - auto-signed if nil
513
526
  # @return [Deposit]
514
527
  def place_deposit(provider, amount, expires_in_secs: 3600, permit: nil)
515
528
  validate_address!(provider)
@@ -543,7 +556,7 @@ module Remitmd
543
556
  # Propose a payment intent for counterpart approval before execution.
544
557
  # @param to [String] 0x-prefixed address
545
558
  # @param amount [Numeric] amount in USDC
546
- # @param type [String] payment type "direct", "escrow", "tab"
559
+ # @param type [String] payment type - "direct", "escrow", "tab"
547
560
  # @return [Intent]
548
561
  def propose_intent(to, amount, type: "direct")
549
562
  validate_address!(to)
@@ -570,7 +583,7 @@ module Remitmd
570
583
  # Generate a one-time URL for the operator to fund this wallet.
571
584
  # @param messages [Array<Hash>, nil] chat-style messages (each with :role and :text)
572
585
  # @param agent_name [String, nil] agent display name shown on the funding page
573
- # @param permit [PermitSignature, nil] EIP-2612 permit auto-signed if nil
586
+ # @param permit [PermitSignature, nil] EIP-2612 permit - auto-signed if nil
574
587
  # @return [LinkResponse]
575
588
  def create_fund_link(messages: nil, agent_name: nil, permit: nil)
576
589
  body = {}
@@ -588,7 +601,7 @@ module Remitmd
588
601
  # Generate a one-time URL for the operator to withdraw funds.
589
602
  # @param messages [Array<Hash>, nil] chat-style messages (each with :role and :text)
590
603
  # @param agent_name [String, nil] agent display name shown on the withdraw page
591
- # @param permit [PermitSignature, nil] EIP-2612 permit auto-signed if nil
604
+ # @param permit [PermitSignature, nil] EIP-2612 permit - auto-signed if nil
592
605
  # @return [LinkResponse]
593
606
  def create_withdraw_link(messages: nil, agent_name: nil, permit: nil)
594
607
  body = {}
@@ -697,7 +710,7 @@ module Remitmd
697
710
 
698
711
  # Auto-sign a permit for the given contract type and amount.
699
712
  # Returns nil on failure instead of raising, so callers can proceed without a permit.
700
- # @param contract [String] contract key "router", "escrow", "tab", etc.
713
+ # @param contract [String] contract key - "router", "escrow", "tab", etc.
701
714
  # @param amount [Numeric] amount in USDC
702
715
  # @return [PermitSignature, nil]
703
716
  def auto_permit(contract, amount)
@@ -21,7 +21,7 @@ module Remitmd
21
21
  end
22
22
  end
23
23
 
24
- # x402 client fetch wrapper that auto-pays HTTP 402 Payment Required responses.
24
+ # x402 client - fetch wrapper that auto-pays HTTP 402 Payment Required responses.
25
25
  #
26
26
  # On receiving a 402, the client:
27
27
  # 1. Decodes the PAYMENT-REQUIRED header (base64 JSON)
@@ -6,7 +6,7 @@ require "json"
6
6
  require "base64"
7
7
 
8
8
  module Remitmd
9
- # x402 paywall for service providers gate HTTP endpoints behind payments.
9
+ # x402 paywall for service providers - gate HTTP endpoints behind payments.
10
10
  #
11
11
  # Providers use this class to:
12
12
  # - Return HTTP 402 responses with properly formatted PAYMENT-REQUIRED headers
@@ -29,9 +29,9 @@ module Remitmd
29
29
  # @param facilitator_url [String] base URL of the remit.md facilitator
30
30
  # @param facilitator_token [String] bearer JWT for authenticating calls to /api/v1/x402/verify
31
31
  # @param max_timeout_seconds [Integer] how long the payment authorization remains valid
32
- # @param resource [String, nil] V2 URL or path of the resource being protected
33
- # @param description [String, nil] V2 human-readable description
34
- # @param mime_type [String, nil] V2 MIME type of the resource
32
+ # @param resource [String, nil] V2 - URL or path of the resource being protected
33
+ # @param description [String, nil] V2 - human-readable description
34
+ # @param mime_type [String, nil] V2 - MIME type of the resource
35
35
  def initialize( # rubocop:disable Metrics/ParameterLists
36
36
  wallet_address:,
37
37
  amount_usdc:,
data/lib/remitmd.rb CHANGED
@@ -4,6 +4,7 @@ require_relative "remitmd/errors"
4
4
  require_relative "remitmd/models"
5
5
  require_relative "remitmd/keccak"
6
6
  require_relative "remitmd/signer"
7
+ require_relative "remitmd/http_signer"
7
8
  require_relative "remitmd/http"
8
9
  require_relative "remitmd/wallet"
9
10
  require_relative "remitmd/mock"
@@ -11,7 +12,7 @@ require_relative "remitmd/a2a"
11
12
  require_relative "remitmd/x402_client"
12
13
  require_relative "remitmd/x402_paywall"
13
14
 
14
- # remit.md Ruby SDK universal payment protocol for AI agents.
15
+ # remit.md Ruby SDK - universal payment protocol for AI agents.
15
16
  #
16
17
  # @example Direct payment
17
18
  # wallet = Remitmd::RemitWallet.new(private_key: ENV["REMITMD_PRIVATE_KEY"])
@@ -25,5 +26,5 @@ require_relative "remitmd/x402_paywall"
25
26
  # mock.was_paid?("0x0000000000000000000000000000000000000001", 1.00) # => true
26
27
  #
27
28
  module Remitmd
28
- VERSION = "0.1.7"
29
+ VERSION = "0.1.9"
29
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.1.7
4
+ version: 0.1.9
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-25 00:00:00.000000000 Z
11
+ date: 2026-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -53,6 +53,7 @@ files:
53
53
  - lib/remitmd/a2a.rb
54
54
  - lib/remitmd/errors.rb
55
55
  - lib/remitmd/http.rb
56
+ - lib/remitmd/http_signer.rb
56
57
  - lib/remitmd/keccak.rb
57
58
  - lib/remitmd/mock.rb
58
59
  - lib/remitmd/models.rb