Class: Remitmd::RemitWallet

Inherits:
Object
  • Object
show all
Defined in:
lib/remitmd/wallet.rb

Overview

Primary remit.md client. All payment operations are methods on RemitWallet.

Private keys are held only by the Signer and never appear in inspect/to_s.

Examples:

Quickstart

wallet = Remitmd::RemitWallet.new(private_key: ENV["REMITMD_KEY"])
tx = wallet.pay("0xRecipient...", 1.50)
puts tx.tx_hash

From environment

wallet = Remitmd::RemitWallet.from_env

Constant Summary collapse

MIN_AMOUNT =

1 micro-USDC

BigDecimal("0.000001")

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(private_key: nil, signer: nil, chain: "base", api_url: nil, router_address: nil, transport: nil) ⇒ RemitWallet

Returns a new instance of RemitWallet.

Parameters:

  • private_key (String, nil) (defaults to: nil)

    0x-prefixed hex private key

  • signer (Signer, nil) (defaults to: nil)

    custom signer (pass instead of private_key)

  • chain (String) (defaults to: "base")

    chain name - "base", "base_sepolia"

  • api_url (String, nil) (defaults to: nil)

    override API base URL

  • transport (Object, nil) (defaults to: nil)

    inject mock transport (used by MockRemit)



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/remitmd/wallet.rb', line 38

def initialize(private_key: nil, signer: nil, chain: "base", api_url: nil, router_address: nil, transport: nil)
  if transport
    # MockRemit path: transport + signer injected directly
    @signer    = signer
    @transport = transport
    @chain_key = "base-sepolia"
    @chain_id  = ChainId::BASE_SEPOLIA
    @mock_mode = true
    return
  end

  if private_key.nil? && signer.nil?
    raise ArgumentError, "Provide either :private_key or :signer"
  end
  if !private_key.nil? && !signer.nil?
    raise ArgumentError, "Provide :private_key OR :signer, not both"
  end

  @signer = signer || PrivateKeySigner.new(private_key)
  # Normalize chain key (underscore → hyphen). Full chain name is sent in API bodies.
  @chain_key = chain.tr("_", "-")
  cfg = CHAIN_CONFIG.fetch(chain) do
    raise ArgumentError, "Unknown chain: #{chain}. Valid: #{CHAIN_CONFIG.keys.join(", ")}"
  end
  base_url       = api_url || cfg[:url]
  @chain_id      = cfg[:chain_id]
  router_address ||= ""
  @transport = HttpTransport.new(
    base_url:       base_url,
    signer:         @signer,
    chain_id:       @chain_id,
    router_address: router_address
  )
end

Class Method Details

.from_envObject

Build a RemitWallet from environment variables. Priority: CliSigner (if available) > REMITMD_KEY / REMITMD_PRIVATE_KEY. Also reads: REMITMD_CHAIN, REMITMD_API_URL, REMITMD_ROUTER_ADDRESS.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/remitmd/wallet.rb', line 76

def self.from_env
  chain          = ENV.fetch("REMITMD_CHAIN", "base")
  api_url        = ENV["REMITMD_API_URL"]
  router_address = ENV["REMITMD_ROUTER_ADDRESS"]

  # Priority 1: CLI signer (remit binary + keystore + password)
  if CliSigner.available?
    signer = CliSigner.new
    return new(signer: signer, chain: chain, api_url: api_url, router_address: router_address)
  end

  # Priority 2: raw private key
  key = ENV["REMITMD_KEY"] || ENV["REMITMD_PRIVATE_KEY"]
  if ENV["REMITMD_PRIVATE_KEY"] && !ENV["REMITMD_KEY"]
    warn "[remitmd] REMITMD_PRIVATE_KEY is deprecated, use REMITMD_KEY instead"
  end

  unless key
    raise ArgumentError,
      "No signing method available. Either:\n" \
      "  1. Install the remit CLI and set REMIT_SIGNER_KEY:\n" \
      "     macOS:   brew install remit-md/tap/remit\n" \
      "     Windows: winget install remit-md.remit\n" \
      "     Linux:   curl -fsSL https://remit.md/install.sh | sh\n" \
      "  2. Set REMITMD_KEY to a raw private key (hex)"
  end

  new(private_key: key, chain: chain, api_url: api_url, router_address: router_address)
end

Instance Method Details

#addressObject

The Ethereum address associated with this wallet.



107
108
109
# File 'lib/remitmd/wallet.rb', line 107

def address
  @signer.address
end

#award_bounty(bounty_id, submission_id) ⇒ Bounty

Award a bounty to a specific submission.

Parameters:

  • bounty_id (String)
  • submission_id (Integer)

    ID of the winning submission

Returns:



458
459
460
# File 'lib/remitmd/wallet.rb', line 458

def award_bounty(bounty_id, submission_id)
  Bounty.new(@transport.post("/bounties/#{bounty_id}/award", { submission_id: submission_id }))
end

#balanceBalance

Fetch the current USDC balance.

Returns:



129
130
131
# File 'lib/remitmd/wallet.rb', line 129

def balance
  Balance.new(@transport.get("/wallet/balance"))
end

#cancel_escrow(escrow_id) ⇒ Transaction

Cancel an escrow and refund the payer.

Parameters:

  • escrow_id (String)

Returns:



223
224
225
# File 'lib/remitmd/wallet.rb', line 223

def cancel_escrow(escrow_id)
  Transaction.new(@transport.post("/escrows/#{escrow_id}/cancel", {}))
end

#charge_tab(tab_id, amount, cumulative, call_count, provider_sig) ⇒ TabDebit

Charge a tab using an EIP-712 signed provider authorization.

Parameters:

  • tab_id (String)
  • amount (Numeric)

    charge amount in USDC

  • cumulative (Numeric)

    cumulative total charged so far

  • call_count (Integer)

    total number of calls so far

  • provider_sig (String)

    EIP-712 signature from the provider

Returns:



272
273
274
275
276
277
278
279
280
# File 'lib/remitmd/wallet.rb', line 272

def charge_tab(tab_id, amount, cumulative, call_count, provider_sig)
  body = {
    amount: amount.to_f,
    cumulative: cumulative.to_f,
    call_count: call_count,
    provider_sig: provider_sig
  }
  TabDebit.new(@transport.post("/tabs/#{tab_id}/charge", body))
end

#claim_start(escrow_id) ⇒ Escrow

Signal the provider has started work on an escrow.

Parameters:

  • escrow_id (String)

Returns:



230
231
232
# File 'lib/remitmd/wallet.rb', line 230

def claim_start(escrow_id)
  Escrow.new(@transport.post("/escrows/#{escrow_id}/claim-start", {}))
end

#close_stream(stream_id) ⇒ Stream

Close an active payment stream.

Parameters:

  • stream_id (String)

Returns:



412
413
414
# File 'lib/remitmd/wallet.rb', line 412

def close_stream(stream_id)
  Stream.new(@transport.post("/streams/#{stream_id}/close", {}))
end

#close_tab(tab_id, final_amount: nil, provider_sig: nil) ⇒ Tab

Close a tab on-chain, settling the final balance.

Parameters:

  • tab_id (String)
  • final_amount (Numeric, nil) (defaults to: nil)

    final settlement amount in USDC

  • provider_sig (String, nil) (defaults to: nil)

    EIP-712 signature from the provider

Returns:



298
299
300
301
302
303
304
# File 'lib/remitmd/wallet.rb', line 298

def close_tab(tab_id, final_amount: nil, provider_sig: nil)
  body = {
    final_amount: final_amount ? final_amount.to_f : 0,
    provider_sig: provider_sig || "0x"
  }
  Tab.new(@transport.post("/tabs/#{tab_id}/close", body))
end

#create_bounty(amount, task_description, deadline, max_attempts: 10, permit: nil) ⇒ Bounty

Post a bounty for any agent to claim by completing a task.

Parameters:

  • amount (Numeric)

    bounty amount in USDC

  • task_description (String)

    task description

  • deadline (Integer)

    deadline as Unix timestamp

  • max_attempts (Integer) (defaults to: 10)

    maximum submission attempts (default: 10)

  • permit (PermitSignature, nil) (defaults to: nil)

    EIP-2612 permit - auto-signed if nil

Returns:



432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/remitmd/wallet.rb', line 432

def create_bounty(amount, task_description, deadline, max_attempts: 10, permit: nil)
  validate_amount!(amount)
  resolved = permit || auto_permit("bounty", amount.to_f)
  body = {
    chain: @chain_key,
    amount: amount.to_f,
    task_description: task_description,
    deadline: deadline,
    max_attempts: max_attempts,
    permit: resolved&.to_h
  }
  Bounty.new(@transport.post("/bounties", body))
end

#create_escrow(payee, amount, memo: nil, expires_in_secs: nil, permit: nil) ⇒ Escrow

Create a new escrow (funds held until release or cancel).

Parameters:

  • payee (String)

    0x-prefixed payee address

  • amount (Numeric)

    amount in USDC

  • memo (String, nil) (defaults to: nil)

    optional note

  • expires_in_secs (Integer, nil) (defaults to: nil)

    optional expiry in seconds from now

  • permit (PermitSignature, nil) (defaults to: nil)

    EIP-2612 permit - auto-signed if nil

Returns:



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/remitmd/wallet.rb', line 189

def create_escrow(payee, amount, memo: nil, expires_in_secs: nil, permit: nil)
  validate_address!(payee)
  validate_amount!(amount)
  resolved = permit || auto_permit("escrow", amount.to_f)

  # Step 1: create invoice on server.
  invoice_id = SecureRandom.hex(16)
  nonce      = SecureRandom.hex(16)
  inv_body = {
    id: invoice_id, chain: @chain_key,
    from_agent: address.downcase, to_agent: payee.downcase,
    amount: amount.to_f, type: "escrow",
    task: memo || "", nonce: nonce, signature: "0x"
  }
  inv_body[:escrow_timeout] = expires_in_secs if expires_in_secs
  @transport.post("/invoices", inv_body)

  # Step 2: fund the escrow.
  esc_body = { invoice_id: invoice_id, permit: resolved&.to_h }
  Escrow.new(@transport.post("/escrows", esc_body))
end

Generate a one-time URL for the operator to fund this wallet.

Parameters:

  • messages (Array<Hash>, nil) (defaults to: nil)

    chat-style messages (each with :role and :text)

  • agent_name (String, nil) (defaults to: nil)

    agent display name shown on the funding page

  • permit (PermitSignature, nil) (defaults to: nil)

    EIP-2612 permit - auto-signed if nil

Returns:



598
599
600
601
602
603
604
605
606
607
608
609
# File 'lib/remitmd/wallet.rb', line 598

def create_fund_link(messages: nil, agent_name: nil, permit: nil)
  body = {}
  body[:messages] = messages if messages
  body[:agent_name] = agent_name if agent_name
  begin
    resolved = permit || auto_permit("relayer", 999_999_999.0)
    body[:permit] = resolved&.to_h
  rescue StandardError => e
    warn "[remitmd] create_fund_link: auto-permit failed: #{e.message}"
  end
  LinkResponse.new(@transport.post("/links/fund", body))
end

#create_stream(payee, rate_per_second, max_total, permit: nil) ⇒ Stream

Create a real-time payment stream.

Parameters:

  • payee (String)

    0x-prefixed address of the stream recipient

  • rate_per_second (Numeric)

    USDC per second

  • max_total (Numeric)

    maximum total USDC for the stream

  • permit (PermitSignature, nil) (defaults to: nil)

    EIP-2612 permit - auto-signed if nil

Returns:



394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/remitmd/wallet.rb', line 394

def create_stream(payee, rate_per_second, max_total, permit: nil)
  validate_address!(payee)
  validate_amount!(rate_per_second)
  validate_amount!(max_total)
  resolved = permit || auto_permit("stream", max_total.to_f)
  body = {
    chain: @chain_key,
    payee: payee,
    rate_per_second: rate_per_second.to_s,
    max_total: max_total.to_s,
    permit: resolved&.to_h
  }
  Stream.new(@transport.post("/streams", body))
end

#create_tab(provider, limit_amount, per_unit = 0.0, expires_in_secs: 86_400, permit: nil) ⇒ Tab

Open a payment tab for off-chain metered billing.

Parameters:

  • provider (String)

    0x-prefixed provider address

  • limit_amount (Numeric)

    maximum tab credit in USDC

  • per_unit (Numeric) (defaults to: 0.0)

    USDC per API call

  • expires_in_secs (Integer) (defaults to: 86_400)

    optional expiry duration in seconds (default: 86400)

  • permit (PermitSignature, nil) (defaults to: nil)

    EIP-2612 permit - auto-signed if nil

Returns:



250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/remitmd/wallet.rb', line 250

def create_tab(provider, limit_amount, per_unit = 0.0, expires_in_secs: 86_400, permit: nil)
  validate_address!(provider)
  validate_amount!(limit_amount)
  resolved = permit || auto_permit("tab", limit_amount.to_f)
  body = {
    chain: @chain_key,
    provider: provider,
    limit_amount: limit_amount.to_f,
    per_unit: per_unit.to_f,
    expiry: Time.now.to_i + expires_in_secs,
    permit: resolved&.to_h
  }
  Tab.new(@transport.post("/tabs", body))
end

Generate a one-time URL for the operator to withdraw funds.

Parameters:

  • messages (Array<Hash>, nil) (defaults to: nil)

    chat-style messages (each with :role and :text)

  • agent_name (String, nil) (defaults to: nil)

    agent display name shown on the withdraw page

  • permit (PermitSignature, nil) (defaults to: nil)

    EIP-2612 permit - auto-signed if nil

Returns:



616
617
618
619
620
621
622
623
624
625
626
627
# File 'lib/remitmd/wallet.rb', line 616

def create_withdraw_link(messages: nil, agent_name: nil, permit: nil)
  body = {}
  body[:messages] = messages if messages
  body[:agent_name] = agent_name if agent_name
  begin
    resolved = permit || auto_permit("relayer", 999_999_999.0)
    body[:permit] = resolved&.to_h
  rescue StandardError => e
    warn "[remitmd] create_withdraw_link: auto-permit failed: #{e.message}"
  end
  LinkResponse.new(@transport.post("/links/withdraw", body))
end

#debit_tab(tab_id, amount, memo = "") ⇒ TabDebit

Record a debit against an open tab (off-chain, no gas).

Parameters:

  • tab_id (String)
  • amount (Numeric)

    amount in USDC

  • memo (String) (defaults to: "")

    description of this debit

Returns:



287
288
289
290
291
# File 'lib/remitmd/wallet.rb', line 287

def debit_tab(tab_id, amount, memo = "")
  validate_amount!(amount)
  body = { amount: amount.to_s, memo: memo }
  TabDebit.new(@transport.post("/tabs/#{tab_id}/debit", body))
end

#delete_webhook(webhook_id) ⇒ Object

Delete a webhook by ID.



546
547
548
549
# File 'lib/remitmd/wallet.rb', line 546

def delete_webhook(webhook_id)
  @transport.delete("/webhooks/#{webhook_id}")
  nil
end

#forfeit_deposit(deposit_id) ⇒ Transaction

Forfeit a deposit (called by the depositor to surrender their deposit).

Parameters:

  • deposit_id (String)

Returns:



517
518
519
# File 'lib/remitmd/wallet.rb', line 517

def forfeit_deposit(deposit_id)
  Transaction.new(@transport.post("/deposits/#{deposit_id}/forfeit", {}))
end

#get_contractsContractAddresses

Get deployed contract addresses. Cached for the lifetime of this client.

Returns:



121
122
123
# File 'lib/remitmd/wallet.rb', line 121

def get_contracts
  @get_contracts ||= ContractAddresses.new(@transport.get("/contracts"))
end

#get_escrow(escrow_id) ⇒ Escrow

Fetch escrow details.

Parameters:

  • escrow_id (String)

Returns:



237
238
239
# File 'lib/remitmd/wallet.rb', line 237

def get_escrow(escrow_id)
  Escrow.new(@transport.get("/escrows/#{escrow_id}"))
end

#history(limit: 50, offset: 0) ⇒ TransactionList

Fetch transaction history.

Parameters:

  • limit (Integer) (defaults to: 50)

    max results per page

  • offset (Integer) (defaults to: 0)

    pagination offset

Returns:



137
138
139
140
# File 'lib/remitmd/wallet.rb', line 137

def history(limit: 50, offset: 0)
  data = @transport.get("/wallet/history?limit=#{limit}&offset=#{offset}")
  TransactionList.new(data)
end

#inspectObject Also known as: to_s



111
112
113
# File 'lib/remitmd/wallet.rb', line 111

def inspect
  "#<Remitmd::RemitWallet address=#{address}>"
end

#list_bounties(status: "open", poster: nil, submitter: nil, limit: 20) ⇒ Array<Bounty>

List bounties with optional filters.

Parameters:

  • status (String, nil) (defaults to: "open")

    filter by status (open, claimed, awarded, expired)

  • poster (String, nil) (defaults to: nil)

    filter by poster wallet address

  • submitter (String, nil) (defaults to: nil)

    filter by submitter wallet address

  • limit (Integer) (defaults to: 20)

    max results (default 20, max 100)

Returns:



475
476
477
478
479
480
481
482
483
# File 'lib/remitmd/wallet.rb', line 475

def list_bounties(status: "open", poster: nil, submitter: nil, limit: 20)
  params = ["limit=#{limit}"]
  params << "status=#{status}" if status
  params << "poster=#{poster}" if poster
  params << "submitter=#{submitter}" if submitter
  data = @transport.get("/bounties?#{params.join('&')}")
  items = data.is_a?(Hash) ? (data["data"] || []) : data
  items.map { |d| Bounty.new(d) }
end

#list_webhooksObject

List all registered webhooks for this wallet.



541
542
543
# File 'lib/remitmd/wallet.rb', line 541

def list_webhooks
  @transport.get("/webhooks").map { |w| Webhook.new(w) }
end

#lock_deposit(provider, amount, expires_in_secs, permit: nil) ⇒ Object

Deprecated.

Use #place_deposit instead

Lock a security deposit.



523
524
525
# File 'lib/remitmd/wallet.rb', line 523

def lock_deposit(provider, amount, expires_in_secs, permit: nil)
  place_deposit(provider, amount, expires_in_secs: expires_in_secs, permit: permit)
end

#mint(amount) ⇒ Hash

Mint testnet USDC. Max $2,500 per call, once per hour per wallet. Uses unauthenticated HTTP (no EIP-712 auth headers).

Parameters:

  • amount (Numeric)

    amount in USDC

Returns:

  • (Hash)

    { "tx_hash" => "0x...", "balance" => 1234.56 }



635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/remitmd/wallet.rb', line 635

def mint(amount)
  if @mock_mode
    @transport.post("/mint", { wallet: address, amount: amount })
  else
    cfg = CHAIN_CONFIG[@chain_key]
    base_url = cfg ? cfg[:url] : "https://testnet.remit.md/api/v1"
    uri = URI("#{base_url}/mint")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = uri.scheme == "https"
    http.read_timeout = 15
    req = Net::HTTP::Post.new(uri.path)
    req["Content-Type"] = "application/json"
    req.body = { wallet: address, amount: amount }.to_json
    resp = http.request(req)
    JSON.parse(resp.body.to_s)
  end
end

#open_stream(payee, rate_per_second, max_total, permit: nil) ⇒ Object

Canonical name for create_stream.



582
583
584
# File 'lib/remitmd/wallet.rb', line 582

def open_stream(payee, rate_per_second, max_total, permit: nil)
  create_stream(payee, rate_per_second, max_total, permit: permit)
end

#open_tab(provider, limit_amount, per_unit = 0.0, expires_in_secs: 86_400, permit: nil) ⇒ Object

Canonical name for create_tab.



577
578
579
# File 'lib/remitmd/wallet.rb', line 577

def open_tab(provider, limit_amount, per_unit = 0.0, expires_in_secs: 86_400, permit: nil)
  create_tab(provider, limit_amount, per_unit, expires_in_secs: expires_in_secs, permit: permit)
end

#pay(to, amount, memo: nil, permit: nil) ⇒ Transaction

Send USDC directly to another address.

Parameters:

  • to (String)

    recipient 0x-prefixed address

  • amount (Numeric, BigDecimal)

    amount in USDC (e.g. 1.50)

  • memo (String, nil) (defaults to: nil)

    optional note

  • permit (PermitSignature, nil) (defaults to: nil)

    EIP-2612 permit - auto-signed if nil

Returns:



170
171
172
173
174
175
176
177
178
# File 'lib/remitmd/wallet.rb', line 170

def pay(to, amount, memo: nil, permit: nil)
  validate_address!(to)
  validate_amount!(amount)
  resolved = permit || auto_permit("router", amount.to_f)
  nonce = SecureRandom.hex(16)
  body = { to: to, amount: amount.to_f, task: memo || "", chain: @chain_key, nonce: nonce, signature: "0x" }
  body[:permit] = resolved&.to_h
  Transaction.new(@transport.post("/payments/direct", body))
end

#place_deposit(provider, amount, expires_in_secs: 3600, permit: nil) ⇒ Deposit

Place a security deposit with a provider.

Parameters:

  • provider (String)

    0x-prefixed provider address

  • amount (Numeric)

    amount in USDC

  • expires_in_secs (Integer) (defaults to: 3600)

    expiry duration in seconds (default: 3600)

  • permit (PermitSignature, nil) (defaults to: nil)

    EIP-2612 permit - auto-signed if nil

Returns:



493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'lib/remitmd/wallet.rb', line 493

def place_deposit(provider, amount, expires_in_secs: 3600, permit: nil)
  validate_address!(provider)
  validate_amount!(amount)
  resolved = permit || auto_permit("deposit", amount.to_f)
  body = {
    chain: @chain_key,
    provider: provider,
    amount: amount.to_f,
    expiry: Time.now.to_i + expires_in_secs,
    permit: resolved&.to_h
  }
  Deposit.new(@transport.post("/deposits", body))
end

#post_bounty(amount, task_description, deadline, max_attempts: 10, permit: nil) ⇒ Object

Canonical name for create_bounty.



587
588
589
# File 'lib/remitmd/wallet.rb', line 587

def post_bounty(amount, task_description, deadline, max_attempts: 10, permit: nil)
  create_bounty(amount, task_description, deadline, max_attempts: max_attempts, permit: permit)
end

#reclaim_bounty(bounty_id) ⇒ Transaction

Reclaim an expired or cancelled bounty (called by the poster).

Parameters:

  • bounty_id (String)

Returns:



465
466
467
# File 'lib/remitmd/wallet.rb', line 465

def reclaim_bounty(bounty_id)
  Transaction.new(@transport.post("/bounties/#{bounty_id}/reclaim", {}))
end

#register_webhook(url, events, chains: nil) ⇒ Webhook

Register a webhook endpoint to receive event notifications.

Parameters:

  • url (String)

    the HTTPS endpoint that will receive POST notifications

  • events (Array<String>)

    event types to subscribe to (e.g. ["payment.sent", "escrow.funded"])

  • chains (Array<String>, nil) (defaults to: nil)

    optional chain names to filter by

Returns:



534
535
536
537
538
# File 'lib/remitmd/wallet.rb', line 534

def register_webhook(url, events, chains: nil)
  body = { url: url, events: events }
  body[:chains] = chains || [@chain_key]
  Webhook.new(@transport.post("/webhooks", body))
end

#release_escrow(escrow_id, memo: nil) ⇒ Transaction

Release an escrow to the payee.

Parameters:

  • escrow_id (String)
  • memo (String, nil) (defaults to: nil)

Returns:



215
216
217
218
# File 'lib/remitmd/wallet.rb', line 215

def release_escrow(escrow_id, memo: nil)
  body = memo ? { memo: memo } : {}
  Transaction.new(@transport.post("/escrows/#{escrow_id}/release", body))
end

#remaining_budgetBudget

Fetch operator-set budget limits and remaining allowances.

Returns:



158
159
160
# File 'lib/remitmd/wallet.rb', line 158

def remaining_budget
  Budget.new(@transport.get("/wallet/budget"))
end

#reputation(addr) ⇒ Reputation

Fetch the on-chain reputation for an address.

Parameters:

  • addr (String)

    0x-prefixed Ethereum address

Returns:



145
146
147
148
# File 'lib/remitmd/wallet.rb', line 145

def reputation(addr)
  validate_address!(addr)
  Reputation.new(@transport.get("/reputation/#{addr}"))
end

#return_deposit(deposit_id) ⇒ Transaction

Return a deposit to the payer.

Parameters:

  • deposit_id (String)

Returns:



510
511
512
# File 'lib/remitmd/wallet.rb', line 510

def return_deposit(deposit_id)
  Transaction.new(@transport.post("/deposits/#{deposit_id}/return", {}))
end

#settle_tab(tab_id) ⇒ Tab

Deprecated.

Use #close_tab instead

Settle a tab on-chain, paying the net balance.

Parameters:

  • tab_id (String)

Returns:



310
311
312
# File 'lib/remitmd/wallet.rb', line 310

def settle_tab(tab_id)
  close_tab(tab_id)
end

#sign_permit(flow, amount) ⇒ PermitSignature

Sign a USDC permit via the server's /permits/prepare endpoint.

The server computes the EIP-712 hash, manages nonces, and resolves contract addresses. The SDK only signs the hash.

Parameters:

  • flow (String)

    payment flow ("direct", "escrow", "tab", "stream", "bounty", "deposit")

  • amount (Numeric)

    amount in USDC (e.g. 5.0 for $5.00)

Returns:



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/remitmd/wallet.rb', line 363

def sign_permit(flow, amount)
  data = @transport.post("/permits/prepare", {
    flow: flow,
    amount: amount.to_s,
    owner: address
  })

  hash_hex = data["hash"]
  hash_bytes = [hash_hex.delete_prefix("0x")].pack("H*")

  sig = @signer.sign_hash(hash_bytes)
  sig_hex = sig.start_with?("0x") ? sig[2..] : sig
  r = "0x#{sig_hex[0, 64]}"
  s = "0x#{sig_hex[64, 64]}"
  v = sig_hex[128, 2].to_i(16)

  PermitSignature.new(
    value: data["value"].to_i,
    deadline: data["deadline"].to_i,
    v: v, r: r, s: s
  )
end

#sign_tab_charge(tab_contract, tab_id, total_charged_base_units, call_count, chain_id: 84_532) ⇒ String

Sign an EIP-712 TabCharge message as the provider. Domain: RemitTab/1// Type: TabCharge(bytes32 tabId, uint96 totalCharged, uint32 callCount)

Parameters:

  • tab_contract (String)

    tab contract address

  • tab_id (String)

    UUID of the tab

  • total_charged_base_units (Integer)

    total charged in USDC base units (6 decimals)

  • call_count (Integer)

    total call count

  • chain_id (Integer) (defaults to: 84_532)

    chain ID (default: 84532 for Base Sepolia)

Returns:

  • (String)

    0x-prefixed 65-byte hex signature



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/remitmd/wallet.rb', line 323

def sign_tab_charge(tab_contract, tab_id, total_charged_base_units, call_count, chain_id: 84_532)
  # Domain separator for RemitTab
  domain_type_hash = keccak256_raw(
    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
  )
  name_hash = keccak256_raw("RemitTab")
  version_hash = keccak256_raw("1")
  chain_id_enc = abi_uint256(chain_id)
  contract_enc = abi_address(tab_contract)

  domain_data = domain_type_hash + name_hash + version_hash + chain_id_enc + contract_enc
  domain_sep = keccak256_raw(domain_data)

  # TabCharge struct hash
  type_hash = keccak256_raw(
    "TabCharge(bytes32 tabId,uint96 totalCharged,uint32 callCount)"
  )

  # Encode tabId as bytes32: ASCII bytes right-padded with zeroes
  tab_id_bytes = tab_id.b[0, 32].ljust(32, "\x00".b)

  struct_data = type_hash + tab_id_bytes + abi_uint256(total_charged_base_units) + abi_uint256(call_count)
  struct_hash = keccak256_raw(struct_data)

  # EIP-712 digest
  digest = keccak256_raw("\x19\x01".b + domain_sep + struct_hash)

  @signer.sign(digest)
end

#spending_summarySpendingSummary

Fetch monthly spending summary.

Returns:



152
153
154
# File 'lib/remitmd/wallet.rb', line 152

def spending_summary
  SpendingSummary.new(@transport.get("/wallet/spending"))
end

#submit_bounty(bounty_id, evidence_hash) ⇒ BountySubmission

Submit evidence to claim a bounty.

Parameters:

  • bounty_id (String)
  • evidence_hash (String)

    0x-prefixed hash of the evidence

Returns:



450
451
452
# File 'lib/remitmd/wallet.rb', line 450

def submit_bounty(bounty_id, evidence_hash)
  BountySubmission.new(@transport.post("/bounties/#{bounty_id}/submit", { evidence_hash: evidence_hash }))
end

#update_wallet_settings(display_name: nil) ⇒ WalletSettings

Update wallet display settings.

Parameters:

  • display_name (String, nil) (defaults to: nil)

    new display name

Returns:



568
569
570
571
572
# File 'lib/remitmd/wallet.rb', line 568

def update_wallet_settings(display_name: nil)
  body = {}
  body[:display_name] = display_name unless display_name.nil?
  WalletSettings.new(@transport.patch("/wallet/settings", body))
end

#update_webhook(webhook_id, url: nil, events: nil, active: nil) ⇒ Webhook

Update an existing webhook's URL, events, or active status.

Parameters:

  • webhook_id (String)
  • url (String, nil) (defaults to: nil)

    new URL

  • events (Array<String>, nil) (defaults to: nil)

    new event types

  • active (Boolean, nil) (defaults to: nil)

    new active status

Returns:



557
558
559
560
561
562
563
# File 'lib/remitmd/wallet.rb', line 557

def update_webhook(webhook_id, url: nil, events: nil, active: nil)
  body = {}
  body[:url] = url unless url.nil?
  body[:events] = events unless events.nil?
  body[:active] = active unless active.nil?
  Webhook.new(@transport.patch("/webhooks/#{webhook_id}", body))
end

#withdraw_stream(stream_id) ⇒ Transaction

Withdraw accrued funds from a stream.

Parameters:

  • stream_id (String)

Returns:



419
420
421
# File 'lib/remitmd/wallet.rb', line 419

def withdraw_stream(stream_id)
  Transaction.new(@transport.post("/streams/#{stream_id}/withdraw", {}))
end