Class: Blockchain0xX402::Client
- Inherits:
-
Object
- Object
- Blockchain0xX402::Client
- Defined in:
- lib/blockchain0x_x402/client.rb
Constant Summary collapse
- DEFAULT_CONFIRM_TIMEOUT_SECONDS =
30- DEFAULT_CONFIRM_POLL_SECONDS =
1.0
Instance Method Summary collapse
-
#initialize(sdk:, agent_id:, max_amount_wei: nil, allowed_pay_to: nil, confirm_timeout_seconds: DEFAULT_CONFIRM_TIMEOUT_SECONDS, confirm_poll_seconds: DEFAULT_CONFIRM_POLL_SECONDS, connection: nil, sleep_proc: nil, now_proc: nil) ⇒ Client
constructor
A new instance of Client.
-
#request(method, url, body: nil, headers: {}) ⇒ Faraday::Response
Perform an HTTP request that handles 402 automatically.
Constructor Details
#initialize(sdk:, agent_id:, max_amount_wei: nil, allowed_pay_to: nil, confirm_timeout_seconds: DEFAULT_CONFIRM_TIMEOUT_SECONDS, confirm_poll_seconds: DEFAULT_CONFIRM_POLL_SECONDS, connection: nil, sleep_proc: nil, now_proc: nil) ⇒ Client
Returns a new instance of Client.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/blockchain0x_x402/client.rb', line 60 def initialize( sdk:, agent_id:, max_amount_wei: nil, allowed_pay_to: nil, confirm_timeout_seconds: DEFAULT_CONFIRM_TIMEOUT_SECONDS, confirm_poll_seconds: DEFAULT_CONFIRM_POLL_SECONDS, connection: nil, sleep_proc: nil, now_proc: nil ) if !max_amount_wei.nil? && !max_amount_wei.to_s.match?(/\A[0-9]+\z/) raise ArgumentError, 'max_amount_wei must be an integer string of 6-dp USDC base units' end @sdk = sdk @agent_id = agent_id @max_amount_wei = max_amount_wei&.to_s @allowed_pay_to = allowed_pay_to&.map(&:downcase) @confirm_timeout = confirm_timeout_seconds @confirm_poll = confirm_poll_seconds @conn = connection || Faraday.new @sleep = sleep_proc || Kernel.method(:sleep) @now = now_proc || method(:monotonic_now) end |
Instance Method Details
#request(method, url, body: nil, headers: {}) ⇒ Faraday::Response
Perform an HTTP request that handles 402 automatically.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/blockchain0x_x402/client.rb', line 92 def request(method, url, body: nil, headers: {}) first = perform(method, url, body, headers) return first unless first.status == 402 spec = Wire.parse_402_response(first) challenge_at = @now.call requirement = pick_requirement(spec.accepts) # 27.1 A6: the 402 is attacker-controlled input - enforce the # caller's spend policy BEFORE any payment is created. enforce_spend_policy(requirement) payment = @sdk.payments_create( agent_id: @agent_id, to: requirement.pay_to_address, amount_wei: requirement.amount_wei_usdc, ) payment_id = payment.is_a?(Hash) ? (payment['id'] || payment[:id]) : payment.id raise ClientError.new('chain_failed', 'payments_create did not return an id.') if payment_id.nil? confirmed = wait_for_confirmation(payment_id) # 27.1 A6: a proof that confirmed after the requirement's # maxAgeSeconds window is refused client-side, not presented. if requirement.max_age_seconds elapsed = @now.call - challenge_at if elapsed > requirement.max_age_seconds raise ClientError.new( 'stale_challenge', "challenge expired: confirmation took #{elapsed.round}s, " \ "maxAgeSeconds is #{requirement.max_age_seconds}.", ) end end header = Wire.build_payment_header( Wire::ExactUsdcPayment.new( scheme: 'exact-usdc', version: 1, payment_request_id: requirement.payment_request_id, tx_hash: tx_field(confirmed, :tx_hash, 'txHash').to_s, payer_address: tx_field(confirmed, :from_address, 'fromAddress').to_s, amount_usdc: wei_to_usdc(requirement.amount_wei_usdc), network: requirement.network, ), ) perform(method, url, body, headers.merge('X-Payment' => header)) end |