diff --git a/blockrun_llm/x402.py b/blockrun_llm/x402.py index 36b32a8..4576519 100644 --- a/blockrun_llm/x402.py +++ b/blockrun_llm/x402.py @@ -23,6 +23,30 @@ USDC_BASE_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e" +# BlockRun's x402 builder code — the ERC-8021 Schema 2 service code (`s`) that +# tags every payment this SDK signs as BlockRun-originated for on-chain +# attribution. See https://docs.cdp.coinbase.com/x402/core-concepts/builder-codes +BLOCKRUN_SERVICE_CODE = "blockrun" + + +def with_builder_code_service_code( + extensions: Optional[Dict[str, Any]], +) -> Dict[str, Any]: + """Merge BlockRun's service code (``s``) into the payload's ``builder-code`` + extension, preserving any app code (``a``) the server echoed back in its 402. + + The CDP facilitator reads ``builder-code.info.s`` and encodes it into the + settlement calldata suffix — no CBOR/encoding happens client-side. + """ + merged: Dict[str, Any] = dict(extensions or {}) + existing = dict(merged.get("builder-code") or {}) + info = dict(existing.get("info") or {}) + info["s"] = [BLOCKRUN_SERVICE_CODE] + existing["info"] = info + merged["builder-code"] = existing + return merged + + def get_chain_config(network: str) -> tuple[int, str]: """ Get chain ID and USDC contract address for a given network. @@ -174,7 +198,7 @@ def create_payment_payload( "nonce": nonce, }, }, - "extensions": extensions or {}, + "extensions": with_builder_code_service_code(extensions), } # Encode as base64 diff --git a/tests/unit/test_x402.py b/tests/unit/test_x402.py index 62ca893..7665c3d 100644 --- a/tests/unit/test_x402.py +++ b/tests/unit/test_x402.py @@ -72,6 +72,31 @@ def test_payload_includes_authorization(self): assert "validBefore" in auth assert "nonce" in auth + def test_payload_attaches_builder_code_service_code(self): + """Should tag every payment with the BlockRun service code (s).""" + payload = create_payment_payload( + account=TEST_ACCOUNT, + recipient=TEST_RECIPIENT, + amount="1000000", + ) + + decoded = json.loads(base64.b64decode(payload)) + assert decoded["extensions"]["builder-code"]["info"]["s"] == ["blockrun"] + + def test_payload_preserves_echoed_app_code(self): + """Should keep the server-echoed app code (a) when adding service code (s).""" + payload = create_payment_payload( + account=TEST_ACCOUNT, + recipient=TEST_RECIPIENT, + amount="1000000", + extensions={"builder-code": {"info": {"a": "blockrun"}}}, + ) + + decoded = json.loads(base64.b64decode(payload)) + info = decoded["extensions"]["builder-code"]["info"] + assert info["a"] == "blockrun" + assert info["s"] == ["blockrun"] + def test_payload_includes_resource_info(self): """Should include resource information.""" payload = create_payment_payload(