Skip to content

Multi-Option - Let Agents Accept Multiple Payment Currencies - #342

Merged
raahulrahl merged 2 commits into
GetBindu:mainfrom
Prateekiiitg56:Multiple-Payment
Mar 8, 2026
Merged

Multi-Option - Let Agents Accept Multiple Payment Currencies#342
raahulrahl merged 2 commits into
GetBindu:mainfrom
Prateekiiitg56:Multiple-Payment

Conversation

@Prateekiiitg56

Copy link
Copy Markdown
Contributor

Summary

Earlier an agent could only specify one payment option - one amount, one token, on one network.

X402 already allows returning multiple PaymentRequirements in the 402 Payment Required response, which lets the caller choose which currency/network to use. Agents just didn't have a way to expose multiple options.

Now execution_cost can accept a list of payment options.

Example:

@bindufy({
    "execution_cost": [
        {"amount": "1000000", "token": "USDC", "network": "base-sepolia", "pay_to_address": "0xABC..."},
        {"amount": "100000000000000", "token": "ETH", "network": "mainnet", "pay_to_address": "0xABC..."},
    ],
})
async def my_agent(message: str) -> str:
    ...

The existing single-dict format still works.

"execution_cost": {"amount": "1000000", "token": "USDC", ...}

So nothing breaks for existing agents.


Problem

execution_cost previously accepted only a single dictionary.
That meant an agent could only request payment in one currency on one network.

The X402 spec allows multiple payment requirements in a 402 response, but agents had no way to define more than one option.


Changes

bindu/penguin/config_validator.py

Validation now allows either:

  • dict

  • list[dict]

Invalid cases like empty lists, non-dict entries, or wrong types raise validation errors.

Examples that now pass validation:

"execution_cost": {"amount": "1000000", "token": "USDC", ...}

"execution_cost": [
{"amount": "1000000", "token": "USDC", ...},
{"amount": "100000000000000", "token": "ETH", ...},
]


bindu/penguin/bindufy.py

Input is normalized into a list internally.

normalized_costs = [execution_cost] if isinstance(execution_cost, dict) else execution_cost

That list is passed to X402AgentExtension:

x402_extension = X402AgentExtension(
...
payment_options=normalized_costs,
)

bindu/extensions/x402/x402_agent_extension.py

X402AgentExtension now accepts:

payment_options: list[dict] | None

When provided:

  • the full list is stored in self.payment_options

  • the first entry is used to populate the existing attributes (amount, token, network, pay_to_address)

This keeps existing code working without changes.

The old constructor with flat arguments still works.


bindu/server/applications.py

_create_payment_requirements now builds payment requirements from payment_options if it exists.

if getattr(x402_ext, "payment_options", None):
options = list(x402_ext.payment_options)
else:
options = [{"amount": ..., "network": ..., "pay_to_address": ...}]

Each entry becomes a PaymentRequirements object.

Different options can use different pay_to_address values.


Tests

File:

tests/unit/test_execution_cost_multi_option.py

Total: 22 tests

Config validator

  • single dict

  • list with one option

  • list with multiple options

  • None

  • missing field

  • empty list (invalid)

  • non-dict entries (invalid)

  • invalid types

X402AgentExtension

  • single payment option

  • multiple options (primary = index 0)

  • backward compatibility with flat args

  • empty list validation

  • non-dict validation

  • missing pay_to_address

  • required=False behavior

Payment requirement generation

  • no extension → None

  • single option → one requirement

  • multiple options → multiple requirements

  • fallback path without payment_options

  • different pay_to_address values

  • resource URL handling


Backward Compatibility

Case Behavior
single dict unchanged
list of dicts now supported
execution_cost missing unchanged
code using ext.amount, ext.token, ext.network unchanged

Existing agent configurations do not need modification.


Checklist

  • multi-option execution cost supported

  • single-dict configuration still works

  • tests added and passing

  • no breaking API changes

  • optional integration test with X402 middleware

Copilot AI review requested due to automatic review settings March 6, 2026 19:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds unit coverage for multi-option execution_cost configurations and hardens the A2A message/send endpoint to safely attach (or omit) payment context from request state without causing 500s on missing/partial/unserializable state.

Changes:

  • Added a new unit test suite covering multi-option execution_cost validation, extension construction behavior, and payment requirements generation.
  • Extended A2A endpoint tests to cover payment context forwarding and added targeted unit tests for state-object serialization.
  • Introduced _serialize_state_obj() and guarded payment-context attachment in agent_run_endpoint to avoid AttributeError/serialization-related 500s.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
tests/unit/test_execution_cost_multi_option.py New unit tests for multi-option execution cost and multi-requirement generation.
tests/unit/test_a2a_endpoint.py Adds tests ensuring payment context is attached only when complete and serializable.
bindu/server/endpoints/a2a_protocol.py Adds safe serialization helper + more robust guards when attaching payment context.

Comment thread tests/unit/test_execution_cost_multi_option.py
Comment thread tests/unit/test_execution_cost_multi_option.py
@raahulrahl
raahulrahl merged commit b9832c7 into GetBindu:main Mar 8, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants