Module: X402::Payments
- Defined in:
- lib/x402/payments.rb,
lib/x402/payments.rb,
lib/x402/payments/chains.rb,
lib/x402/payments/version.rb,
lib/x402/payments/networks.rb,
lib/x402/payments/generator.rb,
lib/x402/payments/v1/headers.rb,
lib/x402/payments/v2/headers.rb,
lib/x402/payments/configuration.rb,
lib/x402/payments/solana/generator.rb,
lib/x402/payments/v1/payload_builder.rb,
lib/x402/payments/v2/payload_builder.rb
Defined Under Namespace
Modules: Networks, Solana, V1, V2 Classes: Configuration, ConfigurationError, Error, Generator
Constant Summary collapse
- SUPPORTED_VERSIONS =
[1, 2].freeze
- CHAINS =
Chain configurations for supported networks
{ "base-sepolia" => { chain_id: 84532, rpc_url: "https://clean-snowy-hexagon.base-sepolia.quiknode.pro", usdc_address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", explorer_url: "https://sepolia.basescan.org" }, "base" => { chain_id: 8453, rpc_url: "https://snowy-compatible-ensemble.base-mainnet.quiknode.pro", usdc_address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", explorer_url: "https://basescan.org" }, "avalanche-fuji" => { chain_id: 43113, rpc_url: "https://muddy-sly-field.avalanche-testnet.quiknode.pro/ext/bc/C/rpc", usdc_address: "0x5425890298aed601595a70AB815c96711a31Bc65", explorer_url: "https://testnet.snowtrace.io" }, "avalanche" => { chain_id: 43114, rpc_url: "https://floral-patient-panorama.avalanche-mainnet.quiknode.pro/ext/bc/C/rpc", usdc_address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", explorer_url: "https://snowtrace.io" }, "solana-devnet" => { chain_id: 103, rpc_url: "https://bitter-twilight-vineyard.solana-devnet.quiknode.pro", usdc_address: "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", explorer_url: "https://explorer.solana.com/?cluster=devnet", fee_payer: "CKPKJWNdJEqa81x7CkZ14BVPiY6y16Sxs7owznqtWYp5" }, "solana" => { chain_id: 101, rpc_url: "https://alien-burned-energy.solana-mainnet.quiknode.pro", usdc_address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", explorer_url: "https://explorer.solana.com", fee_payer: "CKPKJWNdJEqa81x7CkZ14BVPiY6y16Sxs7owznqtWYp5" } }.freeze
- SOLANA_CHAINS =
%w[solana solana-devnet].freeze
- CURRENCY_BY_CHAIN =
Currency configurations by chain
{ "base-sepolia" => { symbol: "USDC", decimals: 6, name: "USDC", version: "2" }, "base" => { symbol: "USDC", decimals: 6, name: "USD Coin", version: "2" }, "avalanche-fuji" => { symbol: "USDC", decimals: 6, name: "USD Coin", version: "2" }, "avalanche" => { symbol: "USDC", decimals: 6, name: "USDC", version: "2" }, "solana-devnet" => { symbol: "USDC", decimals: 6, name: "USDC", version: nil }, "solana" => { symbol: "USDC", decimals: 6, name: "USD Coin", version: nil } }.freeze
- VERSION =
"1.0.0"
Class Attribute Summary collapse
Class Method Summary collapse
- .asset_address_for(chain_name, symbol = nil) ⇒ Object
- .caip2_for(chain_name) ⇒ Object
- .chain_config(chain_name) ⇒ Object
- .chain_id_for(chain_name) ⇒ Object
- .configure {|configuration| ... } ⇒ Object
- .currency_config_for_chain(chain_name) ⇒ Object
- .currency_decimals_for_chain(chain_name) ⇒ Object
- .fee_payer_for(chain_name) ⇒ Object
- .generate_header(amount:, resource:, description: nil, network: nil, private_key: nil, pay_to: nil, extra: nil, version: nil, fee_payer: nil) ⇒ Object
- .generate_link(amount:, resource:, description: nil, network: nil, private_key: nil, pay_to: nil, extra: nil, version: nil, fee_payer: nil) ⇒ Object
-
.normalize_version(version) ⇒ Integer?
Normalize version parameter to integer and validate.
- .payment_header_name(version = nil) ⇒ Object
- .reset_configuration! ⇒ Object
- .rpc_url_for(chain_name) ⇒ Object
- .solana_chain?(chain_name) ⇒ Boolean
- .supported_chains ⇒ Object
- .token_config_for(chain_name, symbol = nil) ⇒ Object
- .usdc_address_for(chain_name) ⇒ Object
Class Attribute Details
.configuration ⇒ Object
65 66 67 |
# File 'lib/x402/payments/configuration.rb', line 65 def configuration @configuration ||= Configuration.new end |
Class Method Details
.asset_address_for(chain_name, symbol = nil) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/x402/payments/chains.rb', line 110 def asset_address_for(chain_name, symbol = nil) symbol ||= X402::Payments.configuration.currency custom = X402::Payments.configuration.token_config(chain_name, symbol) return custom[:address] if custom if symbol.upcase == "USDC" builtin = CHAINS[chain_name] return builtin[:usdc_address] if builtin && builtin[:usdc_address] end raise ConfigurationError, "Unknown token #{symbol} for chain #{chain_name}. Register with config.register_token()" end |
.caip2_for(chain_name) ⇒ Object
148 149 150 |
# File 'lib/x402/payments/chains.rb', line 148 def caip2_for(chain_name) Networks.to_caip2(chain_name) end |
.chain_config(chain_name) ⇒ Object
90 91 92 93 94 95 |
# File 'lib/x402/payments/chains.rb', line 90 def chain_config(chain_name) custom = X402::Payments.configuration.chain_config(chain_name) return custom if custom CHAINS[chain_name] || raise(ConfigurationError, "Unsupported chain: #{chain_name}. Register with config.register_chain()") end |
.chain_id_for(chain_name) ⇒ Object
137 138 139 140 141 142 |
# File 'lib/x402/payments/chains.rb', line 137 def chain_id_for(chain_name) custom = X402::Payments.configuration.chain_config(chain_name) return custom[:chain_id] if custom chain_config(chain_name)[:chain_id] end |
.configure {|configuration| ... } ⇒ Object
69 70 71 |
# File 'lib/x402/payments/configuration.rb', line 69 def configure yield(configuration) end |
.currency_config_for_chain(chain_name) ⇒ Object
97 98 99 |
# File 'lib/x402/payments/chains.rb', line 97 def currency_config_for_chain(chain_name) CURRENCY_BY_CHAIN[chain_name] || raise(ConfigurationError, "Unsupported chain for currency: #{chain_name}") end |
.currency_decimals_for_chain(chain_name) ⇒ Object
144 145 146 |
# File 'lib/x402/payments/chains.rb', line 144 def currency_decimals_for_chain(chain_name) currency_config_for_chain(chain_name)[:decimals] end |
.fee_payer_for(chain_name) ⇒ Object
173 174 175 176 177 178 179 180 181 |
# File 'lib/x402/payments/chains.rb', line 173 def fee_payer_for(chain_name) return nil unless solana_chain?(chain_name) # Priority: 1) ENV variable, 2) Default from CHAINS env_fee_payer = ENV["X402_SOLANA_FEE_PAYER"] return env_fee_payer if env_fee_payer && !env_fee_payer.empty? chain_config(chain_name)[:fee_payer] end |
.generate_header(amount:, resource:, description: nil, network: nil, private_key: nil, pay_to: nil, extra: nil, version: nil, fee_payer: nil) ⇒ Object
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 |
# File 'lib/x402/payments.rb', line 38 def generate_header(amount:, resource:, description: nil, network: nil, private_key: nil, pay_to: nil, extra: nil, version: nil, fee_payer: nil) normalized_version = normalize_version(version) chain_name = network || configuration.chain if solana_chain?(chain_name) generator = Solana::Generator.new generator.generate_header( amount: amount, resource: resource, description: description, network: network, private_key: private_key, pay_to: pay_to, version: normalized_version, fee_payer: fee_payer ) else generator = Generator.new generator.generate_header( amount: amount, resource: resource, description: description, network: network, private_key: private_key, pay_to: pay_to, extra: extra, version: normalized_version ) end end |
.generate_link(amount:, resource:, description: nil, network: nil, private_key: nil, pay_to: nil, extra: nil, version: nil, fee_payer: nil) ⇒ Object
69 70 71 72 73 74 75 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 105 |
# File 'lib/x402/payments.rb', line 69 def generate_link(amount:, resource:, description: nil, network: nil, private_key: nil, pay_to: nil, extra: nil, version: nil, fee_payer: nil) normalized_version = normalize_version(version) chain_name = network || configuration.chain protocol_version = normalized_version || configuration.protocol_version header_name = payment_header_name(protocol_version) if solana_chain?(chain_name) generator = Solana::Generator.new header = generator.generate_header( amount: amount, resource: resource, description: description, network: network, private_key: private_key, pay_to: pay_to, version: protocol_version, fee_payer: fee_payer ) { payment_header: header, header_name: header_name, curl_command: "curl -s -H \"#{header_name}: #{header}\" #{resource} | jq ." } else generator = Generator.new generator.generate_link( amount: amount, resource: resource, description: description, network: network, private_key: private_key, pay_to: pay_to, extra: extra, version: version ) end end |
.normalize_version(version) ⇒ Integer?
Normalize version parameter to integer and validate
29 30 31 32 33 34 35 36 37 |
# File 'lib/x402/payments.rb', line 29 def normalize_version(version) return nil if version.nil? normalized = version.to_i unless SUPPORTED_VERSIONS.include?(normalized) raise ArgumentError, "Unsupported protocol version: #{version.inspect}. Supported versions: #{SUPPORTED_VERSIONS.join(', ')}" end normalized end |
.payment_header_name(version = nil) ⇒ Object
107 108 109 110 111 112 113 114 115 |
# File 'lib/x402/payments.rb', line 107 def payment_header_name(version = nil) normalized = normalize_version(version) || configuration.protocol_version case normalized when 2 V2::Headers::PAYMENT_HEADER else V1::Headers::PAYMENT_HEADER end end |
.reset_configuration! ⇒ Object
73 74 75 |
# File 'lib/x402/payments/configuration.rb', line 73 def reset_configuration! @configuration = Configuration.new end |
.rpc_url_for(chain_name) ⇒ Object
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/x402/payments/chains.rb', line 152 def rpc_url_for(chain_name) # Priority: 1) Programmatic config, 2) ENV variable, 3) Default from CHAINS config = X402::Payments.configuration # Check programmatic configuration return config.rpc_urls[chain_name] if config.rpc_urls[chain_name] # Check environment variable env_var_name = "X402_#{chain_name.upcase.gsub('-', '_')}_RPC_URL" env_rpc = ENV[env_var_name] return env_rpc if env_rpc && !env_rpc.empty? # Fall back to default (only for built-in chains) builtin = CHAINS[chain_name] builtin ? builtin[:rpc_url] : nil end |
.solana_chain?(chain_name) ⇒ Boolean
169 170 171 |
# File 'lib/x402/payments/chains.rb', line 169 def solana_chain?(chain_name) SOLANA_CHAINS.include?(chain_name) end |
.supported_chains ⇒ Object
101 102 103 |
# File 'lib/x402/payments/chains.rb', line 101 def supported_chains CHAINS.keys + X402::Payments.configuration.custom_chains.keys end |
.token_config_for(chain_name, symbol = nil) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/x402/payments/chains.rb', line 124 def token_config_for(chain_name, symbol = nil) symbol ||= X402::Payments.configuration.currency custom = X402::Payments.configuration.token_config(chain_name, symbol) return custom if custom if symbol.upcase == "USDC" && CURRENCY_BY_CHAIN[chain_name] currency_config_for_chain(chain_name) else raise ConfigurationError, "Unknown token #{symbol} for chain #{chain_name}. Register with config.register_token()" end end |
.usdc_address_for(chain_name) ⇒ Object
105 106 107 108 |
# File 'lib/x402/payments/chains.rb', line 105 def usdc_address_for(chain_name) builtin = CHAINS[chain_name] builtin ? builtin[:usdc_address] : nil end |