agirails 2.3.0__tar.gz → 2.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. {agirails-2.3.0 → agirails-2.3.1}/.gitignore +1 -0
  2. agirails-2.3.1/PKG-INFO +226 -0
  3. agirails-2.3.1/README.md +170 -0
  4. {agirails-2.3.0 → agirails-2.3.1}/pyproject.toml +1 -1
  5. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/client.py +1 -1
  6. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/config/networks.py +1 -4
  7. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/errors/__init__.py +2 -0
  8. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/errors/network.py +33 -0
  9. agirails-2.3.1/src/agirails/protocol/base.py +171 -0
  10. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/escrow.py +14 -111
  11. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/kernel.py +11 -107
  12. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/nonce.py +16 -7
  13. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/runtime/blockchain_runtime.py +50 -13
  14. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/__init__.py +0 -5
  15. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/circuit_breaker.py +10 -4
  16. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/security.py +3 -1
  17. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/semaphore.py +14 -5
  18. agirails-2.3.1/src/agirails/version.py +4 -0
  19. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/aa/dual_nonce_manager.py +8 -2
  20. {agirails-2.3.0 → agirails-2.3.1}/tests/benchmarks/test_performance.py +44 -34
  21. agirails-2.3.1/tests/test_errors/test_transient_rpc.py +35 -0
  22. agirails-2.3.1/tests/test_protocol/test_contract_base.py +223 -0
  23. {agirails-2.3.0 → agirails-2.3.1}/tests/test_protocol/test_networks.py +14 -15
  24. agirails-2.3.0/PKG-INFO +0 -675
  25. agirails-2.3.0/README.md +0 -619
  26. agirails-2.3.0/src/agirails/version.py +0 -4
  27. {agirails-2.3.0 → agirails-2.3.1}/.env.example +0 -0
  28. {agirails-2.3.0 → agirails-2.3.1}/.github/workflows/test.yml +0 -0
  29. {agirails-2.3.0 → agirails-2.3.1}/CHANGELOG.md +0 -0
  30. {agirails-2.3.0 → agirails-2.3.1}/LICENSE +0 -0
  31. {agirails-2.3.0 → agirails-2.3.1}/MIGRATION.md +0 -0
  32. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/__init__.py +0 -0
  33. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/abi/ACTPKernel.json +0 -0
  34. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/abi/ERC20.json +0 -0
  35. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/abi/EscrowVault.json +0 -0
  36. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/abis/actp_kernel.json +0 -0
  37. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/abis/agent_registry.json +0 -0
  38. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/abis/eas.json +0 -0
  39. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/abis/escrow_vault.json +0 -0
  40. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/abis/usdc.json +0 -0
  41. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/__init__.py +0 -0
  42. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/adapter_registry.py +0 -0
  43. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/adapter_router.py +0 -0
  44. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/base.py +0 -0
  45. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/basic.py +0 -0
  46. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/i_adapter.py +0 -0
  47. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/standard.py +0 -0
  48. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/types.py +0 -0
  49. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/adapters/x402_adapter.py +0 -0
  50. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/builders/__init__.py +0 -0
  51. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/builders/delivery_proof.py +0 -0
  52. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/builders/quote.py +0 -0
  53. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/__init__.py +0 -0
  54. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/__init__.py +0 -0
  55. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/balance.py +0 -0
  56. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/batch.py +0 -0
  57. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/config.py +0 -0
  58. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/deploy_check.py +0 -0
  59. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/deploy_env.py +0 -0
  60. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/diff.py +0 -0
  61. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/init.py +0 -0
  62. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/mint.py +0 -0
  63. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/pay.py +0 -0
  64. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/publish.py +0 -0
  65. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/pull.py +0 -0
  66. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/simulate.py +0 -0
  67. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/time.py +0 -0
  68. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/tx.py +0 -0
  69. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/commands/watch.py +0 -0
  70. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/main.py +0 -0
  71. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/utils/__init__.py +0 -0
  72. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/utils/client.py +0 -0
  73. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/utils/output.py +0 -0
  74. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/cli/utils/validation.py +0 -0
  75. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/config/__init__.py +0 -0
  76. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/config/abis/AgentRegistry.json +0 -0
  77. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/config/agirailsmd.py +0 -0
  78. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/config/pending_publish.py +0 -0
  79. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/config/publish_pipeline.py +0 -0
  80. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/config/sync_operations.py +0 -0
  81. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/erc8004/__init__.py +0 -0
  82. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/erc8004/bridge.py +0 -0
  83. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/erc8004/reputation_reporter.py +0 -0
  84. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/errors/agent.py +0 -0
  85. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/errors/base.py +0 -0
  86. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/errors/mock.py +0 -0
  87. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/errors/storage.py +0 -0
  88. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/errors/transaction.py +0 -0
  89. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/errors/validation.py +0 -0
  90. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level0/__init__.py +0 -0
  91. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level0/directory.py +0 -0
  92. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level0/provide.py +0 -0
  93. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level0/provider.py +0 -0
  94. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level0/request.py +0 -0
  95. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level1/__init__.py +0 -0
  96. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level1/agent.py +0 -0
  97. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level1/config.py +0 -0
  98. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level1/job.py +0 -0
  99. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/level1/pricing.py +0 -0
  100. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/__init__.py +0 -0
  101. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/agent_registry.py +0 -0
  102. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/did.py +0 -0
  103. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/eas.py +0 -0
  104. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/events.py +0 -0
  105. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/messages.py +0 -0
  106. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/protocol/proofs.py +0 -0
  107. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/py.typed +0 -0
  108. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/runtime/__init__.py +0 -0
  109. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/runtime/base.py +0 -0
  110. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/runtime/mock_runtime.py +0 -0
  111. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/runtime/mock_state_manager.py +0 -0
  112. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/runtime/types.py +0 -0
  113. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/storage/__init__.py +0 -0
  114. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/storage/archive_bundle_builder.py +0 -0
  115. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/storage/arweave_client.py +0 -0
  116. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/storage/filebase_client.py +0 -0
  117. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/storage/types.py +0 -0
  118. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/types/__init__.py +0 -0
  119. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/types/did.py +0 -0
  120. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/types/erc8004.py +0 -0
  121. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/types/message.py +0 -0
  122. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/types/transaction.py +0 -0
  123. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/types/x402.py +0 -0
  124. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/canonical_json.py +0 -0
  125. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/helpers.py +0 -0
  126. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/logger.py +0 -0
  127. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/logging.py +0 -0
  128. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/nonce_tracker.py +0 -0
  129. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/received_nonce_tracker.py +0 -0
  130. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/retry.py +0 -0
  131. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/secure_nonce.py +0 -0
  132. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/used_attestation_tracker.py +0 -0
  133. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/utils/validation.py +0 -0
  134. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/__init__.py +0 -0
  135. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/aa/__init__.py +0 -0
  136. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/aa/bundler_client.py +0 -0
  137. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/aa/constants.py +0 -0
  138. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/aa/paymaster_client.py +0 -0
  139. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/aa/transaction_batcher.py +0 -0
  140. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/aa/user_op_builder.py +0 -0
  141. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/auto_wallet_provider.py +0 -0
  142. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/eoa_wallet_provider.py +0 -0
  143. {agirails-2.3.0 → agirails-2.3.1}/src/agirails/wallet/keystore.py +0 -0
  144. {agirails-2.3.0 → agirails-2.3.1}/tests/__init__.py +0 -0
  145. {agirails-2.3.0 → agirails-2.3.1}/tests/benchmarks/__init__.py +0 -0
  146. {agirails-2.3.0 → agirails-2.3.1}/tests/conftest.py +0 -0
  147. {agirails-2.3.0 → agirails-2.3.1}/tests/fixtures/parity/canonical_json.json +0 -0
  148. {agirails-2.3.0 → agirails-2.3.1}/tests/fixtures/parity/delivery_proof.json +0 -0
  149. {agirails-2.3.0 → agirails-2.3.1}/tests/fixtures/parity/eip712.json +0 -0
  150. {agirails-2.3.0 → agirails-2.3.1}/tests/fixtures/parity/service_hash.json +0 -0
  151. {agirails-2.3.0 → agirails-2.3.1}/tests/integration/__init__.py +0 -0
  152. {agirails-2.3.0 → agirails-2.3.1}/tests/integration/test_blockchain_runtime.py +0 -0
  153. {agirails-2.3.0 → agirails-2.3.1}/tests/integration/test_eas.py +0 -0
  154. {agirails-2.3.0 → agirails-2.3.1}/tests/test_adapters/__init__.py +0 -0
  155. {agirails-2.3.0 → agirails-2.3.1}/tests/test_adapters/test_adapter_router.py +0 -0
  156. {agirails-2.3.0 → agirails-2.3.1}/tests/test_adapters/test_basic.py +0 -0
  157. {agirails-2.3.0 → agirails-2.3.1}/tests/test_adapters/test_standard.py +0 -0
  158. {agirails-2.3.0 → agirails-2.3.1}/tests/test_adapters/test_x402_adapter.py +0 -0
  159. {agirails-2.3.0 → agirails-2.3.1}/tests/test_builders/__init__.py +0 -0
  160. {agirails-2.3.0 → agirails-2.3.1}/tests/test_builders/test_delivery_proof.py +0 -0
  161. {agirails-2.3.0 → agirails-2.3.1}/tests/test_builders/test_quote.py +0 -0
  162. {agirails-2.3.0 → agirails-2.3.1}/tests/test_cli/__init__.py +0 -0
  163. {agirails-2.3.0 → agirails-2.3.1}/tests/test_cli/test_cli_original.py +0 -0
  164. {agirails-2.3.0 → agirails-2.3.1}/tests/test_cli/test_publish.py +0 -0
  165. {agirails-2.3.0 → agirails-2.3.1}/tests/test_cli_deploy/__init__.py +0 -0
  166. {agirails-2.3.0 → agirails-2.3.1}/tests/test_cli_deploy/test_deploy.py +0 -0
  167. {agirails-2.3.0 → agirails-2.3.1}/tests/test_client.py +0 -0
  168. {agirails-2.3.0 → agirails-2.3.1}/tests/test_config/__init__.py +0 -0
  169. {agirails-2.3.0 → agirails-2.3.1}/tests/test_config/test_agirailsmd.py +0 -0
  170. {agirails-2.3.0 → agirails-2.3.1}/tests/test_config/test_pending_publish.py +0 -0
  171. {agirails-2.3.0 → agirails-2.3.1}/tests/test_erc8004/__init__.py +0 -0
  172. {agirails-2.3.0 → agirails-2.3.1}/tests/test_erc8004/test_bridge.py +0 -0
  173. {agirails-2.3.0 → agirails-2.3.1}/tests/test_erc8004/test_reputation_reporter.py +0 -0
  174. {agirails-2.3.0 → agirails-2.3.1}/tests/test_errors/__init__.py +0 -0
  175. {agirails-2.3.0 → agirails-2.3.1}/tests/test_errors/test_agent.py +0 -0
  176. {agirails-2.3.0 → agirails-2.3.1}/tests/test_errors/test_base.py +0 -0
  177. {agirails-2.3.0 → agirails-2.3.1}/tests/test_errors/test_network.py +0 -0
  178. {agirails-2.3.0 → agirails-2.3.1}/tests/test_errors/test_transaction.py +0 -0
  179. {agirails-2.3.0 → agirails-2.3.1}/tests/test_errors/test_validation.py +0 -0
  180. {agirails-2.3.0 → agirails-2.3.1}/tests/test_level0/__init__.py +0 -0
  181. {agirails-2.3.0 → agirails-2.3.1}/tests/test_level0/test_directory.py +0 -0
  182. {agirails-2.3.0 → agirails-2.3.1}/tests/test_level0/test_provider.py +0 -0
  183. {agirails-2.3.0 → agirails-2.3.1}/tests/test_level0/test_request.py +0 -0
  184. {agirails-2.3.0 → agirails-2.3.1}/tests/test_level1/__init__.py +0 -0
  185. {agirails-2.3.0 → agirails-2.3.1}/tests/test_level1/test_agent_lifecycle.py +0 -0
  186. {agirails-2.3.0 → agirails-2.3.1}/tests/test_level1/test_config.py +0 -0
  187. {agirails-2.3.0 → agirails-2.3.1}/tests/test_level1/test_pricing.py +0 -0
  188. {agirails-2.3.0 → agirails-2.3.1}/tests/test_packaging/__init__.py +0 -0
  189. {agirails-2.3.0 → agirails-2.3.1}/tests/test_packaging/test_smoke.py +0 -0
  190. {agirails-2.3.0 → agirails-2.3.1}/tests/test_parity.py +0 -0
  191. {agirails-2.3.0 → agirails-2.3.1}/tests/test_properties/__init__.py +0 -0
  192. {agirails-2.3.0 → agirails-2.3.1}/tests/test_properties/test_state_machine.py +0 -0
  193. {agirails-2.3.0 → agirails-2.3.1}/tests/test_protocol/__init__.py +0 -0
  194. {agirails-2.3.0 → agirails-2.3.1}/tests/test_protocol/test_did.py +0 -0
  195. {agirails-2.3.0 → agirails-2.3.1}/tests/test_protocol/test_eas_verification.py +0 -0
  196. {agirails-2.3.0 → agirails-2.3.1}/tests/test_protocol/test_proofs.py +0 -0
  197. {agirails-2.3.0 → agirails-2.3.1}/tests/test_runtime/__init__.py +0 -0
  198. {agirails-2.3.0 → agirails-2.3.1}/tests/test_runtime/test_concurrent_file_locking.py +0 -0
  199. {agirails-2.3.0 → agirails-2.3.1}/tests/test_runtime/test_mock_runtime.py +0 -0
  200. {agirails-2.3.0 → agirails-2.3.1}/tests/test_runtime/test_mock_state_manager.py +0 -0
  201. {agirails-2.3.0 → agirails-2.3.1}/tests/test_security/__init__.py +0 -0
  202. {agirails-2.3.0 → agirails-2.3.1}/tests/test_security/test_security_audit.py +0 -0
  203. {agirails-2.3.0 → agirails-2.3.1}/tests/test_storage/__init__.py +0 -0
  204. {agirails-2.3.0 → agirails-2.3.1}/tests/test_storage/conftest.py +0 -0
  205. {agirails-2.3.0 → agirails-2.3.1}/tests/test_storage/test_archive_bundle_builder.py +0 -0
  206. {agirails-2.3.0 → agirails-2.3.1}/tests/test_storage/test_arweave_client.py +0 -0
  207. {agirails-2.3.0 → agirails-2.3.1}/tests/test_storage/test_filebase_client.py +0 -0
  208. {agirails-2.3.0 → agirails-2.3.1}/tests/test_storage/test_types.py +0 -0
  209. {agirails-2.3.0 → agirails-2.3.1}/tests/test_types/__init__.py +0 -0
  210. {agirails-2.3.0 → agirails-2.3.1}/tests/test_types/test_message.py +0 -0
  211. {agirails-2.3.0 → agirails-2.3.1}/tests/test_types/test_transaction.py +0 -0
  212. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/__init__.py +0 -0
  213. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_attestation_replay.py +0 -0
  214. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_circuit_breaker.py +0 -0
  215. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_helpers.py +0 -0
  216. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_helpers_extended.py +0 -0
  217. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_nonce_tracker.py +0 -0
  218. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_nonce_tracker_extended.py +0 -0
  219. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_received_nonce_tracker_extended.py +0 -0
  220. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_retry.py +0 -0
  221. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_secure_nonce_concurrency.py +0 -0
  222. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_security.py +0 -0
  223. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_security_extended.py +0 -0
  224. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_validation.py +0 -0
  225. {agirails-2.3.0 → agirails-2.3.1}/tests/test_utils/test_validation_extended.py +0 -0
  226. {agirails-2.3.0 → agirails-2.3.1}/tests/test_wallet/__init__.py +0 -0
  227. {agirails-2.3.0 → agirails-2.3.1}/tests/test_wallet/test_auto_wallet_provider.py +0 -0
  228. {agirails-2.3.0 → agirails-2.3.1}/tests/test_wallet/test_bundler_client.py +0 -0
  229. {agirails-2.3.0 → agirails-2.3.1}/tests/test_wallet/test_dual_nonce_manager.py +0 -0
  230. {agirails-2.3.0 → agirails-2.3.1}/tests/test_wallet/test_keystore.py +0 -0
  231. {agirails-2.3.0 → agirails-2.3.1}/tests/test_wallet/test_transaction_batcher.py +0 -0
  232. {agirails-2.3.0 → agirails-2.3.1}/tests/test_wallet/test_user_op_builder.py +0 -0
@@ -92,4 +92,5 @@ secrets.json
92
92
  .ralph/
93
93
  .serena/
94
94
  CLAUDE.md
95
+ SCRATCHPAD.md
95
96
  uv.lock
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: agirails
3
+ Version: 2.3.1
4
+ Summary: AGIRAILS Python SDK - Agent Commerce Transaction Protocol
5
+ Project-URL: Homepage, https://agirails.io
6
+ Project-URL: Documentation, https://docs.agirails.io
7
+ Project-URL: Repository, https://github.com/agirails/sdk-python
8
+ Project-URL: Issues, https://github.com/agirails/sdk-python/issues
9
+ Author-email: AGIRAILS Team <developers@agirails.io>
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: actp,ai-agents,base,blockchain,escrow,ethereum,payments,web3
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: aiofiles<25.0.0,>=24.0.0
26
+ Requires-Dist: eth-abi<6.0.0,>=5.0.0
27
+ Requires-Dist: eth-account<0.14.0,>=0.13.0
28
+ Requires-Dist: eth-utils<6.0.0,>=5.0.0
29
+ Requires-Dist: httpx<1.0.0,>=0.27.0
30
+ Requires-Dist: pydantic<3.0.0,>=2.6.0
31
+ Requires-Dist: python-dateutil<3.0.0,>=2.8.0
32
+ Requires-Dist: rich<14.0.0,>=13.0.0
33
+ Requires-Dist: typer<1.0.0,>=0.12.0
34
+ Requires-Dist: typing-extensions<5.0.0,>=4.0.0; python_version < '3.10'
35
+ Requires-Dist: web3<8.0.0,>=7.0.0
36
+ Provides-Extra: dev
37
+ Requires-Dist: black>=24.0.0; extra == 'dev'
38
+ Requires-Dist: hypothesis>=6.100.0; extra == 'dev'
39
+ Requires-Dist: mypy>=1.11.0; extra == 'dev'
40
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
41
+ Requires-Dist: pytest-benchmark>=4.0.0; extra == 'dev'
42
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
43
+ Requires-Dist: pytest-timeout>=2.3.0; extra == 'dev'
44
+ Requires-Dist: pytest-xdist>=3.5.0; extra == 'dev'
45
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
46
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
47
+ Requires-Dist: types-aiofiles>=24.0.0; extra == 'dev'
48
+ Requires-Dist: types-python-dateutil>=2.8.0; extra == 'dev'
49
+ Provides-Extra: mutation
50
+ Requires-Dist: mutmut>=2.4.0; extra == 'mutation'
51
+ Provides-Extra: security
52
+ Requires-Dist: bandit>=1.7.0; extra == 'security'
53
+ Requires-Dist: pip-audit>=2.7.0; extra == 'security'
54
+ Requires-Dist: safety>=3.0.0; extra == 'security'
55
+ Description-Content-Type: text/markdown
56
+
57
+ # AGIRAILS Python SDK
58
+
59
+ [![PyPI](https://img.shields.io/pypi/v/agirails.svg)](https://pypi.org/project/agirails/)
60
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
61
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
62
+ [![Tests](https://img.shields.io/badge/tests-1738%20passed-brightgreen.svg)]()
63
+
64
+ The official Python SDK for the **Agent Commerce Transaction Protocol (ACTP)** — enabling AI agents to transact with each other through blockchain-based escrow on Base L2.
65
+
66
+ **Full 1:1 parity with TypeScript SDK v2.5.0.**
67
+
68
+ ## Install
69
+
70
+ ```bash
71
+ pip install agirails==2.3.0
72
+ ```
73
+
74
+ ## Features
75
+
76
+ - **Adapter Routing** — priority-based adapter selection (Standard, Basic, X402)
77
+ - **x402 Payments** — HTTP-based instant payments with relay fee splitting
78
+ - **ERC-8004 Identity** — on-chain agent identity resolution and reputation
79
+ - **Keystore Security (AIP-13)** — fail-closed private key policy, `ACTP_KEYSTORE_BASE64` for CI/CD
80
+ - **AGIRAILS.md Source of Truth** — parse, hash, publish, pull, diff agent configs
81
+ - **Smart Wallet (ERC-4337)** — batched transactions with paymaster gas sponsorship
82
+ - **Lazy Publish** — mainnet activation deferred to first real transaction
83
+ - **Three-tier API** — Basic, Standard, and Advanced levels
84
+ - **Mock Runtime** — full local testing without blockchain
85
+ - **CLI** — `actp pay`, `publish`, `pull`, `diff`, `deploy:env`, `deploy:check`
86
+ - **Async-first** — built on asyncio
87
+ - **1,738 tests passing**
88
+
89
+ ## Quick Start
90
+
91
+ ```python
92
+ import asyncio
93
+ from agirails import ACTPClient
94
+
95
+ async def main():
96
+ client = await ACTPClient.create(mode="mock", requester_address="0x1234...")
97
+
98
+ # Adapter router auto-selects the best path
99
+ # EVM address → ACTP (StandardAdapter)
100
+ result = await client.pay({"to": "0xProvider...", "amount": "10.00"})
101
+
102
+ # HTTP URL → x402 instant payment
103
+ result = await client.pay({"to": "https://api.example.com/pay", "amount": "5.00"})
104
+
105
+ # Agent ID → ERC-8004 resolve → ACTP
106
+ result = await client.pay({"to": "12345", "amount": "10.00"})
107
+
108
+ print(f"Transaction: {result.tx_id}, State: {result.state}")
109
+
110
+ asyncio.run(main())
111
+ ```
112
+
113
+ ## Adapter Routing
114
+
115
+ Priority-based adapter selection matching TypeScript `AdapterRouter`:
116
+
117
+ | Adapter | Priority | Target | Use Case |
118
+ |---------|----------|--------|----------|
119
+ | **X402Adapter** | 70 | `https://...` URLs | Instant HTTP payments with relay fee splitting |
120
+ | **StandardAdapter** | 60 | `0x...` addresses | Full ACTP lifecycle with escrow |
121
+ | **BasicAdapter** | 50 | `0x...` addresses | Simple pay-and-forget (Smart Wallet batched) |
122
+
123
+ ## Keystore & Deployment Security (AIP-13)
124
+
125
+ Fail-closed private key policy with network-aware enforcement:
126
+
127
+ | Network | `ACTP_PRIVATE_KEY` | Behavior |
128
+ |---------|-------------------|----------|
129
+ | mock | Allowed | Silent |
130
+ | testnet (base-sepolia) | Allowed | Warn once |
131
+ | mainnet (base-mainnet) | Blocked | Hard fail |
132
+
133
+ **Resolution order:** `ACTP_PRIVATE_KEY` → `ACTP_KEYSTORE_BASE64` + `ACTP_KEY_PASSWORD` → `.actp/keystore.json` → `None`
134
+
135
+ ```bash
136
+ # Generate base64 keystore for CI/CD
137
+ actp deploy:env
138
+
139
+ # Scan repo for exposed secrets
140
+ actp deploy:check
141
+ ```
142
+
143
+ ## AGIRAILS.md Config Management
144
+
145
+ ```bash
146
+ actp publish --network base-sepolia # Hash + upload to IPFS + register on-chain
147
+ actp pull --network base-sepolia # Fetch config from chain
148
+ actp diff --network base-sepolia # Compare local vs on-chain
149
+ ```
150
+
151
+ ## Transaction Lifecycle
152
+
153
+ ```
154
+ INITIATED → QUOTED → COMMITTED → IN_PROGRESS → DELIVERED → SETTLED
155
+ ↘ ↘ ↘
156
+ CANCELLED CANCELLED DISPUTED → SETTLED
157
+ ```
158
+
159
+ ## CLI
160
+
161
+ ```bash
162
+ # Payments
163
+ actp pay <to> <amount> [--deadline TIME]
164
+ actp balance [ADDRESS]
165
+
166
+ # Transaction management
167
+ actp tx list [--state STATE]
168
+ actp tx status <tx_id>
169
+ actp tx deliver <tx_id>
170
+ actp tx settle <tx_id>
171
+
172
+ # Config sync
173
+ actp publish [path]
174
+ actp pull [path] [--network NETWORK]
175
+ actp diff [path] [--network NETWORK]
176
+
177
+ # Deployment security
178
+ actp deploy:env
179
+ actp deploy:check [path] [--fix]
180
+
181
+ # Mock mode
182
+ actp mint <address> <amount>
183
+ actp time advance <duration>
184
+ ```
185
+
186
+ ## SDK Parity
187
+
188
+ Full 1:1 parity with TypeScript SDK v2.5.0:
189
+
190
+ | Feature | Python | TypeScript |
191
+ |---------|--------|------------|
192
+ | Adapter Routing | AdapterRouter + 3 adapters | AdapterRouter + 3 adapters |
193
+ | x402 Payments | X402Adapter with relay | X402Adapter with relay |
194
+ | ERC-8004 Identity | ERC8004Bridge + ReputationReporter | ERC8004Bridge + ReputationReporter |
195
+ | Keystore AIP-13 | Full (30-min TTL cache) | Full (30-min TTL cache) |
196
+ | AGIRAILS.md SOT | parse, hash, publish, pull, diff | parse, hash, publish, pull, diff |
197
+ | Smart Wallet | ERC-4337 scaffolding | ERC-4337 full |
198
+ | Lazy Publish | pending-publish lifecycle | pending-publish lifecycle |
199
+ | CLI Commands | pay, publish, pull, diff, deploy:* | pay, publish, pull, diff, deploy:* |
200
+ | State Machine | 8 states, all transitions | 8 states, all transitions |
201
+ | Cross-SDK Tests | Shared test vectors | Shared test vectors |
202
+
203
+ ## Testing
204
+
205
+ ```bash
206
+ pytest # Run all 1,738 tests
207
+ pytest -v # Verbose output
208
+ pytest tests/test_adapters/ # Adapter tests only
209
+ pytest -k "test_pay" # Pattern match
210
+ ```
211
+
212
+ ## Requirements
213
+
214
+ - Python 3.9+
215
+ - Dependencies: web3, eth-account, pydantic, aiofiles, httpx, typer, rich
216
+
217
+ ## Links
218
+
219
+ - [PyPI](https://pypi.org/project/agirails/)
220
+ - [Documentation](https://docs.agirails.io)
221
+ - [GitHub](https://github.com/agirails/sdk-python)
222
+ - [Discord](https://discord.gg/nuhCt75qe4)
223
+
224
+ ## License
225
+
226
+ Apache 2.0 — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,170 @@
1
+ # AGIRAILS Python SDK
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/agirails.svg)](https://pypi.org/project/agirails/)
4
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
5
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
6
+ [![Tests](https://img.shields.io/badge/tests-1738%20passed-brightgreen.svg)]()
7
+
8
+ The official Python SDK for the **Agent Commerce Transaction Protocol (ACTP)** — enabling AI agents to transact with each other through blockchain-based escrow on Base L2.
9
+
10
+ **Full 1:1 parity with TypeScript SDK v2.5.0.**
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install agirails==2.3.0
16
+ ```
17
+
18
+ ## Features
19
+
20
+ - **Adapter Routing** — priority-based adapter selection (Standard, Basic, X402)
21
+ - **x402 Payments** — HTTP-based instant payments with relay fee splitting
22
+ - **ERC-8004 Identity** — on-chain agent identity resolution and reputation
23
+ - **Keystore Security (AIP-13)** — fail-closed private key policy, `ACTP_KEYSTORE_BASE64` for CI/CD
24
+ - **AGIRAILS.md Source of Truth** — parse, hash, publish, pull, diff agent configs
25
+ - **Smart Wallet (ERC-4337)** — batched transactions with paymaster gas sponsorship
26
+ - **Lazy Publish** — mainnet activation deferred to first real transaction
27
+ - **Three-tier API** — Basic, Standard, and Advanced levels
28
+ - **Mock Runtime** — full local testing without blockchain
29
+ - **CLI** — `actp pay`, `publish`, `pull`, `diff`, `deploy:env`, `deploy:check`
30
+ - **Async-first** — built on asyncio
31
+ - **1,738 tests passing**
32
+
33
+ ## Quick Start
34
+
35
+ ```python
36
+ import asyncio
37
+ from agirails import ACTPClient
38
+
39
+ async def main():
40
+ client = await ACTPClient.create(mode="mock", requester_address="0x1234...")
41
+
42
+ # Adapter router auto-selects the best path
43
+ # EVM address → ACTP (StandardAdapter)
44
+ result = await client.pay({"to": "0xProvider...", "amount": "10.00"})
45
+
46
+ # HTTP URL → x402 instant payment
47
+ result = await client.pay({"to": "https://api.example.com/pay", "amount": "5.00"})
48
+
49
+ # Agent ID → ERC-8004 resolve → ACTP
50
+ result = await client.pay({"to": "12345", "amount": "10.00"})
51
+
52
+ print(f"Transaction: {result.tx_id}, State: {result.state}")
53
+
54
+ asyncio.run(main())
55
+ ```
56
+
57
+ ## Adapter Routing
58
+
59
+ Priority-based adapter selection matching TypeScript `AdapterRouter`:
60
+
61
+ | Adapter | Priority | Target | Use Case |
62
+ |---------|----------|--------|----------|
63
+ | **X402Adapter** | 70 | `https://...` URLs | Instant HTTP payments with relay fee splitting |
64
+ | **StandardAdapter** | 60 | `0x...` addresses | Full ACTP lifecycle with escrow |
65
+ | **BasicAdapter** | 50 | `0x...` addresses | Simple pay-and-forget (Smart Wallet batched) |
66
+
67
+ ## Keystore & Deployment Security (AIP-13)
68
+
69
+ Fail-closed private key policy with network-aware enforcement:
70
+
71
+ | Network | `ACTP_PRIVATE_KEY` | Behavior |
72
+ |---------|-------------------|----------|
73
+ | mock | Allowed | Silent |
74
+ | testnet (base-sepolia) | Allowed | Warn once |
75
+ | mainnet (base-mainnet) | Blocked | Hard fail |
76
+
77
+ **Resolution order:** `ACTP_PRIVATE_KEY` → `ACTP_KEYSTORE_BASE64` + `ACTP_KEY_PASSWORD` → `.actp/keystore.json` → `None`
78
+
79
+ ```bash
80
+ # Generate base64 keystore for CI/CD
81
+ actp deploy:env
82
+
83
+ # Scan repo for exposed secrets
84
+ actp deploy:check
85
+ ```
86
+
87
+ ## AGIRAILS.md Config Management
88
+
89
+ ```bash
90
+ actp publish --network base-sepolia # Hash + upload to IPFS + register on-chain
91
+ actp pull --network base-sepolia # Fetch config from chain
92
+ actp diff --network base-sepolia # Compare local vs on-chain
93
+ ```
94
+
95
+ ## Transaction Lifecycle
96
+
97
+ ```
98
+ INITIATED → QUOTED → COMMITTED → IN_PROGRESS → DELIVERED → SETTLED
99
+ ↘ ↘ ↘
100
+ CANCELLED CANCELLED DISPUTED → SETTLED
101
+ ```
102
+
103
+ ## CLI
104
+
105
+ ```bash
106
+ # Payments
107
+ actp pay <to> <amount> [--deadline TIME]
108
+ actp balance [ADDRESS]
109
+
110
+ # Transaction management
111
+ actp tx list [--state STATE]
112
+ actp tx status <tx_id>
113
+ actp tx deliver <tx_id>
114
+ actp tx settle <tx_id>
115
+
116
+ # Config sync
117
+ actp publish [path]
118
+ actp pull [path] [--network NETWORK]
119
+ actp diff [path] [--network NETWORK]
120
+
121
+ # Deployment security
122
+ actp deploy:env
123
+ actp deploy:check [path] [--fix]
124
+
125
+ # Mock mode
126
+ actp mint <address> <amount>
127
+ actp time advance <duration>
128
+ ```
129
+
130
+ ## SDK Parity
131
+
132
+ Full 1:1 parity with TypeScript SDK v2.5.0:
133
+
134
+ | Feature | Python | TypeScript |
135
+ |---------|--------|------------|
136
+ | Adapter Routing | AdapterRouter + 3 adapters | AdapterRouter + 3 adapters |
137
+ | x402 Payments | X402Adapter with relay | X402Adapter with relay |
138
+ | ERC-8004 Identity | ERC8004Bridge + ReputationReporter | ERC8004Bridge + ReputationReporter |
139
+ | Keystore AIP-13 | Full (30-min TTL cache) | Full (30-min TTL cache) |
140
+ | AGIRAILS.md SOT | parse, hash, publish, pull, diff | parse, hash, publish, pull, diff |
141
+ | Smart Wallet | ERC-4337 scaffolding | ERC-4337 full |
142
+ | Lazy Publish | pending-publish lifecycle | pending-publish lifecycle |
143
+ | CLI Commands | pay, publish, pull, diff, deploy:* | pay, publish, pull, diff, deploy:* |
144
+ | State Machine | 8 states, all transitions | 8 states, all transitions |
145
+ | Cross-SDK Tests | Shared test vectors | Shared test vectors |
146
+
147
+ ## Testing
148
+
149
+ ```bash
150
+ pytest # Run all 1,738 tests
151
+ pytest -v # Verbose output
152
+ pytest tests/test_adapters/ # Adapter tests only
153
+ pytest -k "test_pay" # Pattern match
154
+ ```
155
+
156
+ ## Requirements
157
+
158
+ - Python 3.9+
159
+ - Dependencies: web3, eth-account, pydantic, aiofiles, httpx, typer, rich
160
+
161
+ ## Links
162
+
163
+ - [PyPI](https://pypi.org/project/agirails/)
164
+ - [Documentation](https://docs.agirails.io)
165
+ - [GitHub](https://github.com/agirails/sdk-python)
166
+ - [Discord](https://discord.gg/nuhCt75qe4)
167
+
168
+ ## License
169
+
170
+ Apache 2.0 — see [LICENSE](LICENSE) for details.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agirails"
7
- version = "2.3.0"
7
+ version = "2.3.1"
8
8
  description = "AGIRAILS Python SDK - Agent Commerce Transaction Protocol"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -317,7 +317,7 @@ class ACTPClient:
317
317
  )
318
318
 
319
319
  # Map mode to network name
320
- network_name = "base-sepolia" if config.mode == "testnet" else "base"
320
+ network_name = "base-sepolia" if config.mode == "testnet" else "base-mainnet"
321
321
 
322
322
  # Create EAS helper if config provided or attestation required
323
323
  eas_helper = None
@@ -152,10 +152,7 @@ BASE_MAINNET = NetworkConfig(
152
152
  max_fee_per_gas=500_000_000, # 0.5 gwei
153
153
  max_priority_fee_per_gas=100_000_000, # 0.1 gwei
154
154
  ),
155
- # SECURITY: $1,000 max transaction limit until contracts are audited.
156
- # This limits exposure in case of undiscovered vulnerabilities.
157
- # Will be removed/increased after formal security audit.
158
- max_transaction_amount=1000,
155
+ # Security audit passed February 2026 no findings. No transaction limit.
159
156
  )
160
157
 
161
158
 
@@ -37,6 +37,7 @@ from agirails.errors.validation import (
37
37
  )
38
38
  from agirails.errors.network import (
39
39
  NetworkError,
40
+ TransientRPCError,
40
41
  TransactionRevertedError,
41
42
  SignatureVerificationError,
42
43
  )
@@ -89,6 +90,7 @@ __all__ = [
89
90
  "InvalidAmountError",
90
91
  # Network
91
92
  "NetworkError",
93
+ "TransientRPCError",
92
94
  "TransactionRevertedError",
93
95
  "SignatureVerificationError",
94
96
  # Storage
@@ -95,6 +95,39 @@ class TransactionRevertedError(ACTPError):
95
95
  self.block_number = block_number
96
96
 
97
97
 
98
+ class TransientRPCError(NetworkError):
99
+ """
100
+ Raised for transient RPC/network failures that are safe to retry.
101
+
102
+ Distinguishes retry-able failures (connection drops, rate limits,
103
+ timeouts) from permanent failures (contract reverts, invalid state).
104
+
105
+ BlockchainRuntime._rate_limited_call() retries on this class only.
106
+
107
+ Example:
108
+ >>> raise TransientRPCError("Rate limit exceeded", cause=original_error)
109
+ """
110
+
111
+ def __init__(
112
+ self,
113
+ message: str,
114
+ *,
115
+ endpoint: Optional[str] = None,
116
+ cause: Optional[Exception] = None,
117
+ details: Optional[Dict[str, Any]] = None,
118
+ ) -> None:
119
+ details = details or {}
120
+ if cause is not None:
121
+ details["cause"] = type(cause).__name__
122
+ super().__init__(
123
+ message,
124
+ endpoint=endpoint,
125
+ details=details,
126
+ )
127
+ self.code = "TRANSIENT_RPC_ERROR"
128
+ self.cause = cause
129
+
130
+
98
131
  class SignatureVerificationError(ACTPError):
99
132
  """
100
133
  Raised when a cryptographic signature verification fails.
@@ -0,0 +1,171 @@
1
+ """
2
+ ContractBase: shared infrastructure for ACTPKernel and EscrowVault.
3
+
4
+ Eliminates the verbatim duplication of _sign_and_send, _build_tx_params,
5
+ _to_bytes32, and _to_receipt that existed in both contract wrappers.
6
+
7
+ NonceManager integration is optional:
8
+ - When nonce_manager is provided: get_nonce() / confirm_nonce() / release_nonce()
9
+ are called automatically inside _build_tx_params() and _sign_and_send().
10
+ - When None: falls back to w3.eth.get_transaction_count("pending") — same
11
+ behavior as pre-refactor, backward compat preserved.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ from typing import Any, Dict, Optional
18
+
19
+ from eth_account.signers.local import LocalAccount
20
+ from web3 import AsyncWeb3
21
+ from web3.contract import AsyncContract
22
+ from web3.types import TxReceipt
23
+
24
+ from agirails.errors.transaction import TransactionError
25
+ from agirails.protocol.nonce import NonceManager
26
+ from agirails.types.transaction import TransactionReceipt
27
+
28
+ # Security Note (M-3): Default timeout for transaction receipts (5 minutes)
29
+ DEFAULT_TX_WAIT_TIMEOUT = 300.0
30
+
31
+
32
+ class ContractBase:
33
+ """
34
+ Abstract base for ACTPKernel and EscrowVault contract wrappers.
35
+
36
+ Owns the transaction lifecycle: build params -> sign -> send -> wait -> receipt.
37
+ Integrates NonceManager when provided, falls back to RPC nonce otherwise.
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ contract: AsyncContract,
43
+ account: LocalAccount,
44
+ w3: AsyncWeb3,
45
+ chain_id: int,
46
+ *,
47
+ nonce_manager: Optional[NonceManager] = None,
48
+ ) -> None:
49
+ self.contract = contract
50
+ self.account = account
51
+ self.w3 = w3
52
+ self.chain_id = chain_id
53
+ self._nonce_manager = nonce_manager
54
+
55
+ async def _build_tx_params(
56
+ self,
57
+ gas_limit: int,
58
+ max_fee_per_gas: Optional[int] = None,
59
+ max_priority_fee_per_gas: Optional[int] = None,
60
+ ) -> Dict[str, Any]:
61
+ """Build EIP-1559 transaction parameters."""
62
+ # Use NonceManager when available; else ask chain directly
63
+ if self._nonce_manager is not None:
64
+ nonce = await self._nonce_manager.get_nonce()
65
+ else:
66
+ # Legacy path: fetch nonce from RPC including unconfirmed transactions
67
+ nonce = await self.w3.eth.get_transaction_count(
68
+ self.account.address, "pending"
69
+ )
70
+
71
+ # Get gas prices if not provided
72
+ if max_fee_per_gas is None or max_priority_fee_per_gas is None:
73
+ block = await self.w3.eth.get_block("latest")
74
+ base_fee = block.get("baseFeePerGas", 1_000_000_000) # 1 gwei fallback
75
+
76
+ if max_priority_fee_per_gas is None:
77
+ max_priority_fee_per_gas = 1_000_000_000 # 1 gwei
78
+
79
+ if max_fee_per_gas is None:
80
+ # 2x base fee + priority fee
81
+ max_fee_per_gas = (base_fee * 2) + max_priority_fee_per_gas
82
+
83
+ return {
84
+ "from": self.account.address,
85
+ "nonce": nonce,
86
+ "gas": gas_limit,
87
+ "maxFeePerGas": max_fee_per_gas,
88
+ "maxPriorityFeePerGas": max_priority_fee_per_gas,
89
+ "chainId": self.chain_id,
90
+ }
91
+
92
+ async def _sign_and_send(
93
+ self,
94
+ tx: Dict[str, Any],
95
+ timeout: float = DEFAULT_TX_WAIT_TIMEOUT,
96
+ ) -> TxReceipt:
97
+ """
98
+ Sign and broadcast a transaction, wait for receipt.
99
+
100
+ Nonce lifecycle:
101
+ - Pre-broadcast failure -> release_nonce (nonce never hit the chain)
102
+ - Post-broadcast timeout -> confirm_nonce (tx is in mempool, nonce consumed)
103
+ - Reverted tx (status=0) -> confirm_nonce (revert still consumed nonce)
104
+ - Success (status=1) -> confirm_nonce
105
+
106
+ Security Note (M-3): Uses timeout to prevent indefinite hangs.
107
+ """
108
+ nonce: Optional[int] = tx.get("nonce")
109
+
110
+ signed_tx = self.w3.eth.account.sign_transaction(tx, self.account.key)
111
+ try:
112
+ tx_hash = await self.w3.eth.send_raw_transaction(
113
+ signed_tx.raw_transaction
114
+ )
115
+ except Exception:
116
+ # Broadcast failed — release nonce so it can be reused
117
+ if self._nonce_manager is not None and nonce is not None:
118
+ self._nonce_manager.release_nonce(nonce)
119
+ raise
120
+
121
+ # Transaction was broadcast — nonce is consumed regardless of outcome
122
+ try:
123
+ receipt = await asyncio.wait_for(
124
+ self.w3.eth.wait_for_transaction_receipt(tx_hash),
125
+ timeout=timeout,
126
+ )
127
+ except asyncio.TimeoutError:
128
+ # TX is in mempool — nonce is consumed
129
+ if self._nonce_manager is not None and nonce is not None:
130
+ self._nonce_manager.confirm_nonce(nonce)
131
+ raise TransactionError(
132
+ f"Transaction {tx_hash.hex()} timed out after {timeout}s. "
133
+ "Check network congestion and gas settings.",
134
+ tx_id=tx_hash.hex(),
135
+ )
136
+
137
+ # Confirm nonce whether tx succeeded or reverted (nonce was used on-chain)
138
+ if self._nonce_manager is not None and nonce is not None:
139
+ self._nonce_manager.confirm_nonce(nonce)
140
+
141
+ if receipt["status"] != 1:
142
+ raise TransactionError(
143
+ f"Transaction failed: {tx_hash.hex()}",
144
+ tx_id=tx_hash.hex(),
145
+ )
146
+
147
+ return receipt
148
+
149
+ def _to_bytes32(self, value: str) -> bytes:
150
+ """Convert hex string to bytes32."""
151
+ if value.startswith("0x"):
152
+ value = value[2:]
153
+ # Pad to 32 bytes if needed
154
+ value = value.zfill(64)
155
+ return bytes.fromhex(value)
156
+
157
+ def _to_receipt(self, receipt: TxReceipt) -> TransactionReceipt:
158
+ """Convert web3 receipt to TransactionReceipt."""
159
+ return TransactionReceipt(
160
+ transaction_hash=receipt["transactionHash"].hex()
161
+ if isinstance(receipt["transactionHash"], bytes)
162
+ else receipt["transactionHash"],
163
+ block_number=receipt["blockNumber"],
164
+ block_hash=receipt["blockHash"].hex()
165
+ if isinstance(receipt["blockHash"], bytes)
166
+ else receipt["blockHash"],
167
+ gas_used=receipt["gasUsed"],
168
+ effective_gas_price=receipt.get("effectiveGasPrice", 0),
169
+ status=receipt["status"],
170
+ logs=[dict(log) for log in receipt.get("logs", [])],
171
+ )