From 67a4151be31d0e0e12372fcb78df3c75a08f3517 Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Thu, 14 May 2026 07:33:06 -0700 Subject: [PATCH 1/2] feat: add hash_operator_token helper Canonical sha256 hex digest for plaintext operator tokens. Merchants hash them before storing in DB columns and before comparing against persisted hashes, so plaintext tokens never land in durable storage. Tests lock 6 fixtures with hardcoded digests as the cross-language contract with the Node sibling at @agent-score/commerce. Parametrized so multiple drifts surface independently rather than short-circuiting. Co-Authored-By: Claude Opus 4.7 (1M context) --- agentscore_commerce/identity/__init__.py | 2 + agentscore_commerce/identity/tokens.py | 20 ++++++++ tests/test_tokens.py | 59 ++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 agentscore_commerce/identity/tokens.py create mode 100644 tests/test_tokens.py diff --git a/agentscore_commerce/identity/__init__.py b/agentscore_commerce/identity/__init__.py index 5e6fb78..9a4f7b6 100644 --- a/agentscore_commerce/identity/__init__.py +++ b/agentscore_commerce/identity/__init__.py @@ -35,6 +35,7 @@ shipping_state_allowed, ) from agentscore_commerce.identity.signer import extract_x402_signer +from agentscore_commerce.identity.tokens import hash_operator_token from agentscore_commerce.identity.types import ( AgentIdentity, AgentMemoryHint, @@ -138,6 +139,7 @@ def _load_asgi_middleware() -> tuple[Any, Any]: "denial_reason_to_body", "extract_x402_signer", "generate_ucp_signing_key", + "hash_operator_token", "is_fixable_denial", "mpp_payment_handler", "run_gate_with_enforcement", diff --git a/agentscore_commerce/identity/tokens.py b/agentscore_commerce/identity/tokens.py new file mode 100644 index 0000000..4b57e60 --- /dev/null +++ b/agentscore_commerce/identity/tokens.py @@ -0,0 +1,20 @@ +"""Operator-token hashing. + +Plaintext operator tokens (``opc_...``) never persist on disk. Merchants hash +them before storing in DB columns and before comparing against persisted hashes. +This helper exposes the canonical hash so every consumer agrees on the shape. +""" + +from __future__ import annotations + +import hashlib + + +def hash_operator_token(plaintext: str) -> str: + """sha256 hex digest of a plaintext operator token. + + Use at every persistence boundary (INSERT) AND every comparison boundary + (SELECT WHERE operator_token_id = ...) so plaintext tokens never land in + durable storage. + """ + return hashlib.sha256(plaintext.encode("utf-8")).hexdigest() diff --git a/tests/test_tokens.py b/tests/test_tokens.py new file mode 100644 index 0000000..0bd66c9 --- /dev/null +++ b/tests/test_tokens.py @@ -0,0 +1,59 @@ +"""Tests for ``agentscore_commerce.identity.tokens.hash_operator_token``. + +The expected digests below are hardcoded — locked as the cross-language +contract with the Node sibling at ``node-commerce/tests/identity/tokens.test.ts``. +Both files reference the same fixture inputs and the same expected output bytes. +A drift in either language (algorithm swap, encoding change, accidental truncation) +fails that language's test against the locked digest. +""" + +from __future__ import annotations + +import pytest + +from agentscore_commerce.identity import hash_operator_token + +# Cross-language fixture inputs + expected digests. The digests are +# sha256(.encode("utf-8")).hexdigest() computed once and locked here so +# the Python and Node sibling tests assert against identical bytes. +# +# Parametrized so each fixture gets its own test invocation: if multiple +# fixtures drift simultaneously, every failure is reported (a for-loop inside +# one test would short-circuit on the first failure). +_FIXTURES = [ + ("opc_test", "97c30e2a512b5968772c2930705bdafff4831d672556dce26c92b83f7e58508d"), + ("opc_cross_lang_fixture", "96690dd2659bc1e33227e943d5f8a526c7c95a0ede5775a1573abab6578ca8ec"), + ("opc_anything", "e6ba517ac96ee39190c4d703b2d968fec96e87827374e56095a2f443d870730d"), + ("opc_42", "731985dd676ea0702b3e6f6cbb107eaf467319e2801e6f953f08cbcc7dd71684"), + # Non-ASCII fixture — UTF-8 encoding of "é" is 0xC3 0xA9; locks the encoding + # contract so a future implementation that drops the explicit "utf-8" arg + # still produces the same bytes. + ("opc_é", "c1dba11d60cbfc1264d115e07a74a0355b6a66ded4ee3f930024a1733ba6942f"), + # Empty-string sha256 is a canonical value documented in many specs; locking + # it here catches an implementation that silently rejects or transforms "". + ("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), +] + + +@pytest.mark.parametrize(("plaintext", "expected"), _FIXTURES, ids=[repr(p) for p, _ in _FIXTURES]) +def test_known_digest_locked(plaintext: str, expected: str) -> None: + """Each fixture input maps to the locked cross-language digest.""" + assert hash_operator_token(plaintext) == expected + + +def test_output_is_64_char_lowercase_hex() -> None: + """sha256 hex digests are always 64 characters of lowercase hex.""" + out = hash_operator_token("opc_anything") + assert len(out) == 64 + assert out == out.lower() + assert all(c in "0123456789abcdef" for c in out) + + +def test_deterministic_across_calls() -> None: + """Same input always yields the same digest (no salt, no nonce).""" + assert hash_operator_token("opc_42") == hash_operator_token("opc_42") + + +def test_distinct_inputs_distinct_outputs() -> None: + """Different plaintexts produce different digests.""" + assert hash_operator_token("opc_a") != hash_operator_token("opc_b") From 7ed121c831c3c9fe9fe180c515afa08424de17cd Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Thu, 14 May 2026 07:37:42 -0700 Subject: [PATCH 2/2] chore: bump deps (x402 2.9 -> 2.10, ruff, cdp-sdk, solana, requests patches) uv lock --upgrade picked up: - x402 2.9.0 -> 2.10.0 (minor) - cdp-sdk 1.45.0 -> 1.45.1 - ruff 0.15.12 -> 0.15.13 - solana 0.36.11 -> 0.36.12 - requests 2.34.0 -> 2.34.1 Co-Authored-By: Claude Opus 4.7 (1M context) --- uv.lock | 66 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/uv.lock b/uv.lock index c37f3d0..01471ff 100644 --- a/uv.lock +++ b/uv.lock @@ -424,7 +424,7 @@ wheels = [ [[package]] name = "cdp-sdk" -version = "1.45.0" +version = "1.45.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -439,9 +439,9 @@ dependencies = [ { name = "urllib3" }, { name = "web3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b0/f6/5f59af94b88520799f6d900df9edaf31907bd44e63309f379d616af67186/cdp_sdk-1.45.0.tar.gz", hash = "sha256:2cb3742ce40ca0b0572445914e5d656d6b05b00f8332de430d6b9e3ee1726067", size = 408683, upload-time = "2026-05-11T22:44:50.401Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/a5/181e5c9cac892c0e395de6a72b4b92ada35c68bf761469a280fc843fef4d/cdp_sdk-1.45.1.tar.gz", hash = "sha256:3b6f5b6c0cb7295907c5e4ffaf588702bb8f0f074c8555e4f4c05235a868a42b", size = 417309, upload-time = "2026-05-13T17:52:13.387Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/12/bb86d7ed976fa9c64931c73bcd68123c061cd01181913478af68df392f17/cdp_sdk-1.45.0-py3-none-any.whl", hash = "sha256:5db4a4615a158b33861b464980778d71dd80d631876b98693e027075c8b63170", size = 1065028, upload-time = "2026-05-11T22:44:48.154Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ce/2718b05cfb4f94522e8a0eb3502e7d1535d1d980cc53d921c0857c9ae0b6/cdp_sdk-1.45.1-py3-none-any.whl", hash = "sha256:29cf91e472ee4bd39d3d5631905caecd78d03460dafeb0dd26f72f9595906426", size = 1099212, upload-time = "2026-05-13T17:52:11.244Z" }, ] [[package]] @@ -2526,7 +2526,7 @@ wheels = [ [[package]] name = "requests" -version = "2.34.0" +version = "2.34.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -2534,9 +2534,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/43/b8/7a707d60fea4c49094e40262cc0e2ca6c768cca21587e34d3f705afec47e/requests-2.34.0.tar.gz", hash = "sha256:7d62fe92f50eb82c529b0916bb445afa1531a566fc8f35ffdc64446e771b856a", size = 142436, upload-time = "2026-05-11T19:29:51.717Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/36/7180e7f077c38108945dbbdf60fe04db681c3feb6e96419f8c6dc8723741/requests-2.34.1.tar.gz", hash = "sha256:0fc5669f2b69704449fe1552360bd2a73a54512dfd03e65529157f1513322beb", size = 142783, upload-time = "2026-05-13T19:20:24.662Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/e6/e300fce5fe83c30520607a015dabd985df3251e188d234bfe9492e17a389/requests-2.34.0-py3-none-any.whl", hash = "sha256:917520a21b767485ce7c588f4ebb917c436b24a31231b44228715eaeb5a52c60", size = 73021, upload-time = "2026-05-11T19:29:49.923Z" }, + { url = "https://files.pythonhosted.org/packages/15/5a/4a949d170476de3c04ac036b5466422fbcbf348a917d8042eedf2cac7d1b/requests-2.34.1-py3-none-any.whl", hash = "sha256:bf38a3ff993960d3dd819c08862c40b3c703306eb7c744fcd9f4ddbb95b548f0", size = 73085, upload-time = "2026-05-13T19:20:22.827Z" }, ] [[package]] @@ -2687,27 +2687,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" }, - { url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" }, - { url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" }, - { url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" }, - { url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" }, - { url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" }, - { url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" }, - { url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" }, - { url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" }, - { url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" }, - { url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" }, +version = "0.15.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/21/a7d5c126d5b557715ef81098f3db2fe20f622a039ff2e626af28d674ab80/ruff-0.15.13.tar.gz", hash = "sha256:f9d89f17f7ba7fb2ed42921f0df75da797a9a5d71bc39049e2c687cf2baf44b7", size = 4678180, upload-time = "2026-05-14T13:44:37.869Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/61/11d458dc6ac22504fd8e237b29dfd40504c7fbbcc8930402cfe51a8e63ed/ruff-0.15.13-py3-none-linux_armv6l.whl", hash = "sha256:444b580fc72fd6887e650acd3e575e18cdc79dbcf42fb4030b491057921f61f8", size = 10738279, upload-time = "2026-05-14T13:44:18.7Z" }, + { url = "https://files.pythonhosted.org/packages/86/ca/caa871ee7be718c45256fada4e16a218ee3e33f0c4a46b729a60a24912e6/ruff-0.15.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6590d009e7cb7ebf36f83dbdd44a3fa48a0994ff6f1cdc1b08006abe58f98dc7", size = 11124798, upload-time = "2026-05-14T13:44:06.427Z" }, + { url = "https://files.pythonhosted.org/packages/d3/19/43f5f2e568dddde567fc41f8471f9432c09563e19d3e617a48cfa52f8f0a/ruff-0.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1c26d2f66163deeb6e08d8b39fbbe983ce3c71cea06a6d7591cfd1421793c629", size = 10460761, upload-time = "2026-05-14T13:44:04.375Z" }, + { url = "https://files.pythonhosted.org/packages/99/df/cf938cd6de3003178f03ad7c1ea2a6c099468c03a35037985070b37e76be/ruff-0.15.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbd6f94b434f896308e4d57fb7bfde0d02b99f7a64b3bdab0fdfa6a864203a5", size = 10804451, upload-time = "2026-05-14T13:44:25.221Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7d/5d0973129b154ded2225729169d7068f26b467760b146493fde138415f23/ruff-0.15.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3259f3be4d181bda591da5db2571aed6853c6a048157756448020bc6c5cd22", size = 10534285, upload-time = "2026-05-14T13:44:08.888Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e3/6b999bbc66cd51e5f073842bc2a3995e99c5e0e72e16b15e7261f7abf57a/ruff-0.15.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae9c17e5eb4430c154e76abc25d79a318190f5a997f38fb6b114416c5319ffc9", size = 11312063, upload-time = "2026-05-14T13:44:11.274Z" }, + { url = "https://files.pythonhosted.org/packages/af/5a/642639e9f5db04f1e97fbd6e091c6fd20725bdf072fb114d00eefb9e6eb8/ruff-0.15.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e2e39bff6c341f4b577a21b801326fab0b11847f48fcaa83f00a113c9b3cb55", size = 12183079, upload-time = "2026-05-14T13:44:01.634Z" }, + { url = "https://files.pythonhosted.org/packages/19/4c/7585735f6b53b0f12de13618b2f7d250a844f018822efc899df2e7b8295f/ruff-0.15.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8d9a8e08013542e94d3220bc5b62cc3e5ef87c5f74bff367d3fac14fab013e6", size = 11440833, upload-time = "2026-05-14T13:43:59.043Z" }, + { url = "https://files.pythonhosted.org/packages/e8/31/bf1a0803d077e679cfeee5f2f67290a0fa79c7385b5d9a8c17b9db2c48f0/ruff-0.15.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc411dfebe5eebe55ce041c6ae080eb7668955e866daa2fbb16692a784f1c4ca", size = 11434486, upload-time = "2026-05-14T13:44:27.761Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4e/62c9b999875d4f14db80f277c030578f5e249c9852d65b7ac7ad0b43c041/ruff-0.15.13-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:768494eb08b9cee54e2fd27969966f74db5a57f6eaa7a90fcb3306af34dfc4bd", size = 11385189, upload-time = "2026-05-14T13:44:13.704Z" }, + { url = "https://files.pythonhosted.org/packages/fc/89/7e959047a104df3eb12863447c110140191fc5b6c4f379ea2e803fcdb0e4/ruff-0.15.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fb75f9a3a7e42ffe117d734494e6c5e5cb3565d66e12612cb63d0e572a41a5b6", size = 10781380, upload-time = "2026-05-14T13:43:56.734Z" }, + { url = "https://files.pythonhosted.org/packages/ff/52/5fd18f3b88cab63e88aa11516b3b4e1e5f720e5c330f8dbe5c26210f41f8/ruff-0.15.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8cb74dd33bb2f6613faf7fc03b660053b5ac4f80e706d5788c6335e2a8048d51", size = 10540605, upload-time = "2026-05-14T13:44:20.748Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e0/9e35f338990d3e41a82875ff7053ffe97541dae81c9d02143177f381d572/ruff-0.15.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:7ef823f817fcd191dc934e984be9cf4094f808effa16f2542ad8e821ba02bbf2", size = 11036554, upload-time = "2026-05-14T13:44:16.256Z" }, + { url = "https://files.pythonhosted.org/packages/c2/13/070fb048c24080fba188f66371e2a92785be257ad02242066dc7255ac6e9/ruff-0.15.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f345a13937bd7f09f6f5d19fa0721b0c103e00e7f62bc67089a8e5e037719e0b", size = 11528133, upload-time = "2026-05-14T13:44:22.808Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8c/b1e1666aef7fc6555094d73ae6cd981701781ae85b97ceefc0eebd0b4668/ruff-0.15.13-py3-none-win32.whl", hash = "sha256:4044f94208b3b05ba0fc4a4abd0558cf4d6459bd18325eead7fd8cc66f909b41", size = 10721455, upload-time = "2026-05-14T13:44:35.697Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a6/870a3e8a50590bb92be184ad928c2922f088b00d9dc5c5ec7b924ee08c22/ruff-0.15.13-py3-none-win_amd64.whl", hash = "sha256:7064884d442b7d477b4e7473d12da7f08851d2b1982763c5d3f388a19468a1a4", size = 11900409, upload-time = "2026-05-14T13:44:30.389Z" }, + { url = "https://files.pythonhosted.org/packages/9b/36/9c015cd052fca743dae8cb2aeb16b551444787467db42ceab0fc968865af/ruff-0.15.13-py3-none-win_arm64.whl", hash = "sha256:2471da9bd1068c8c064b5fd9c0c4b6dddffd6369cb1cd68b29993b1709ff1b21", size = 11179336, upload-time = "2026-05-14T13:44:33.026Z" }, ] [[package]] @@ -2804,7 +2804,7 @@ wheels = [ [[package]] name = "solana" -version = "0.36.11" +version = "0.36.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "construct-typing" }, @@ -2813,9 +2813,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/66/b8cd6e4d95bfe46798942ace31935e7799005a4e2180869dc7bac6b75be9/solana-0.36.11.tar.gz", hash = "sha256:2fdcf483674f4b88fe6510524bf3234a5837d19fe1815aa5a285f2739d28b3a3", size = 54516, upload-time = "2026-01-03T02:11:52.243Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/66/708f3f4ec285773bf1bd785afa442d8315e8471054cce9817ee9d3a17301/solana-0.36.12.tar.gz", hash = "sha256:3f103f5b72f19d44622254c618793fd72ae683776f9676b906e2f1d2f5cda292", size = 54656, upload-time = "2026-05-14T03:45:09.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/8d/807eebf0560759ad90464060e0d1d87ff5409beb6ed56104c553a83a976a/solana-0.36.11-py3-none-any.whl", hash = "sha256:1d659decc67a40ee1e9b5ded373a076b87cf3b4bd0645e120d16d9348c2025ba", size = 64786, upload-time = "2026-01-03T02:11:50.811Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8d/70e07b289a46a1e39611bb45b9af8646566612c3a62f639f6a2c00e7af00/solana-0.36.12-py3-none-any.whl", hash = "sha256:37de51e21a81f5a90e9bb0ede393c70e70011626fd903d82d2c678b050c94c94", size = 64870, upload-time = "2026-05-14T03:45:07.913Z" }, ] [[package]] @@ -3359,16 +3359,16 @@ wheels = [ [[package]] name = "x402" -version = "2.9.0" +version = "2.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nest-asyncio" }, { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/d1/72fc451fe9e5fe999966f8c6d483cdf08ac931c04b499db8a9126cf07845/x402-2.9.0.tar.gz", hash = "sha256:2f4dad28f2010bf796e8f3057f019cc3e46616c54342f913192aaabe9affcb0b", size = 1867212, upload-time = "2026-04-27T17:14:44.276Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/61/b6e69aefdbb507c70139d13739bc4640656100671ddc395323cc566d3a86/x402-2.10.0.tar.gz", hash = "sha256:db14d9cc61b8db5a2c1372447ec42245b7889a86611c48304fe5be22d5f66b29", size = 1991574, upload-time = "2026-05-13T21:02:30.648Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/37/f0daae6f14cda3d1c74f73ebad3a0614c18add942a6928378d085e2ef931/x402-2.9.0-py3-none-any.whl", hash = "sha256:76c5e3ecb59cece7bb33f435aaef42c7e9ee66341a4963961c11152408fffc8a", size = 1942609, upload-time = "2026-04-27T17:14:42.328Z" }, + { url = "https://files.pythonhosted.org/packages/64/45/731542691157dbc1267c164a9430baa517d2c260e7e9b2bbea0279c3d4d0/x402-2.10.0-py3-none-any.whl", hash = "sha256:7e387e6d8c7fa706ffd35754da28e75b8493775d81bc6888cad0d9439106295d", size = 2080428, upload-time = "2026-05-13T21:02:28.561Z" }, ] [package.optional-dependencies]