Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changelog/gentle-eagles-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
pympp: patch
---

Defaulted `chain_id` to 4217 (mainnet) in the `tempo()` function, removing the need to pass it explicitly. Updated docs and example code accordingly.
1 change: 0 additions & 1 deletion src/mpp/methods/tempo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

server = Mpp.create(
method=tempo(
chain_id=42431,
intents={"charge": ChargeIntent()},
),
)
Expand Down
4 changes: 0 additions & 4 deletions src/mpp/methods/tempo/_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@
TESTNET_CHAIN_ID = 42431
TESTNET_RPC_URL = "https://rpc.moderato.tempo.xyz"

# Testnet only — the fee payer service sponsors gas on testnet.
# On mainnet, the server itself must pay gas or provide its own fee payer.
DEFAULT_FEE_PAYER_URL = "https://sponsor.moderato.tempo.xyz"

# Chain ID -> default currency mapping
# Mainnet defaults to USDC, testnet defaults to pathUSD
DEFAULT_CURRENCIES: MappingProxyType[int, str] = MappingProxyType(
Expand Down
9 changes: 5 additions & 4 deletions src/mpp/methods/tempo/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from mpp import Challenge, Credential
from mpp.methods.tempo._attribution import encode as encode_attribution
from mpp.methods.tempo._defaults import (
CHAIN_ID,
CHAIN_RPC_URLS,
RPC_URL,
default_currency_for_chain,
Expand Down Expand Up @@ -303,7 +304,7 @@ def tempo(
intents: dict[str, Intent],
account: TempoAccount | None = None,
fee_payer: TempoAccount | None = None,
chain_id: int | None = None,
chain_id: int = CHAIN_ID,
rpc_url: str | None = None,
root_account: str | None = None,
currency: str | None = None,
Expand All @@ -320,8 +321,8 @@ def tempo(
(server-side). When set, the server signs with domain
``0x78`` and broadcasts directly — no external fee payer
service needed.
chain_id: Tempo chain ID (4217 for mainnet, 42431 for testnet).
Resolves the RPC URL automatically from known chains.
chain_id: Tempo chain ID (default: 4217 for mainnet, use 42431
for testnet). Resolves the RPC URL automatically from known chains.
rpc_url: Tempo RPC endpoint URL. Overrides the URL resolved
from ``chain_id``. Defaults to mainnet if neither is set.
root_account: Root account address for access key signing.
Expand Down Expand Up @@ -350,7 +351,7 @@ def tempo(
)
"""
if rpc_url is None:
rpc_url = rpc_url_for_chain(chain_id) if chain_id else RPC_URL
rpc_url = rpc_url_for_chain(chain_id)

if currency is None:
currency = default_currency_for_chain(chain_id)
Expand Down
9 changes: 7 additions & 2 deletions src/mpp/methods/tempo/intents.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from mpp import Credential, Receipt
from mpp.errors import VerificationError
from mpp.methods.tempo._defaults import DEFAULT_FEE_PAYER_URL, PATH_USD, rpc_url_for_chain
from mpp.methods.tempo._defaults import PATH_USD, rpc_url_for_chain
from mpp.methods.tempo.schemas import (
ChargeRequest,
CredentialPayload,
Expand Down Expand Up @@ -366,7 +366,12 @@ async def _verify_transaction(
if self.fee_payer is not None:
raw_tx = self._cosign_as_fee_payer(raw_tx, request.currency, request=request)
else:
fee_payer_url = request.methodDetails.feePayerUrl or DEFAULT_FEE_PAYER_URL
fee_payer_url = request.methodDetails.feePayerUrl
if not fee_payer_url:
raise VerificationError(
"No fee payer configured: set feePayer on the tempo() method "
"or provide a feePayerUrl in methodDetails"
)

sign_response = await client.post(
fee_payer_url,
Expand Down
8 changes: 4 additions & 4 deletions tests/test_mpp_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,12 @@ async def test_charge_override_currency_recipient(self) -> None:
assert result.request["recipient"] == "0xother"

@pytest.mark.asyncio
async def test_charge_defaults_currency_to_pathusd(self) -> None:
"""Currency defaults to pathUSD when chain_id is not set."""
from mpp.methods.tempo import PATH_USD
async def test_charge_defaults_currency_to_usdc(self) -> None:
"""Currency defaults to USDC when chain_id defaults to mainnet (4217)."""
from mpp.methods.tempo import USDC

method = tempo(intents={"charge": ChargeIntent()})
assert method.currency == PATH_USD
assert method.currency == USDC

@pytest.mark.asyncio
async def test_charge_defaults_currency_to_usdc_on_mainnet(self) -> None:
Expand Down
42 changes: 30 additions & 12 deletions tests/test_tempo.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,9 +612,10 @@ async def test_client_builds_sponsored_transaction(self, httpx_mock: HTTPXMock)
intents={"charge": ChargeIntent()},
)

# eth_chainId
httpx_mock.add_response(
url="https://rpc.test",
json={"jsonrpc": "2.0", "result": "0x1", "id": 1},
json={"jsonrpc": "2.0", "result": "0x1079", "id": 1},
)
httpx_mock.add_response(
url="https://rpc.test",
Expand Down Expand Up @@ -1191,10 +1192,10 @@ def test_tempo_factory_stores_chain_id(self) -> None:
method = tempo(chain_id=42431, intents={"charge": ChargeIntent()})
assert method.chain_id == 42431

def test_tempo_factory_chain_id_defaults_none(self) -> None:
"""tempo() without chain_id should default to None."""
def test_tempo_factory_chain_id_defaults_mainnet(self) -> None:
"""tempo() without chain_id should default to 4217 (mainnet)."""
method = tempo(intents={"charge": ChargeIntent()})
assert method.chain_id is None
assert method.chain_id == 4217

def test_tempo_factory_chain_id_resolves_rpc(self) -> None:
"""tempo(chain_id=42431) should resolve testnet RPC URL."""
Expand Down Expand Up @@ -1275,9 +1276,10 @@ async def test_client_falls_back_to_method_rpc_for_unknown_chain(
intents={"charge": ChargeIntent()},
)

# eth_chainId
httpx_mock.add_response(
url="https://rpc.custom",
json={"jsonrpc": "2.0", "result": "0x1", "id": 1},
json={"jsonrpc": "2.0", "result": "0x1079", "id": 1},
)
httpx_mock.add_response(
url="https://rpc.custom",
Expand Down Expand Up @@ -1323,9 +1325,10 @@ async def test_client_ignores_non_numeric_chain_id(self, httpx_mock: HTTPXMock)
intents={"charge": ChargeIntent()},
)

# eth_chainId
httpx_mock.add_response(
url="https://rpc.custom",
json={"jsonrpc": "2.0", "result": "0x1", "id": 1},
json={"jsonrpc": "2.0", "result": "0x1079", "id": 1},
)
httpx_mock.add_response(
url="https://rpc.custom",
Expand Down Expand Up @@ -1415,8 +1418,12 @@ async def test_access_key_builds_keychain_signature(self, httpx_mock: HTTPXMock)
intents={"charge": ChargeIntent()},
)

# Mock RPC: chain_id, nonce, gas_price, estimateGas
for _ in range(4):
# Mock RPC: chain_id (4217=0x1079), nonce, gas_price, estimateGas
httpx_mock.add_response(
url="https://rpc.test",
json={"jsonrpc": "2.0", "result": "0x1079", "id": 1},
)
for _ in range(3):
httpx_mock.add_response(
url="https://rpc.test",
json={"jsonrpc": "2.0", "result": "0x1", "id": 1},
Expand Down Expand Up @@ -1455,9 +1462,15 @@ async def test_access_key_with_fee_payer(self, httpx_mock: HTTPXMock) -> None:
intents={"charge": ChargeIntent()},
)

for _ in range(4):
# Mock RPC: chain_id (4217=0x1079), nonce, gas_price, estimateGas
# Challenge chainId=4217 resolves to rpc.tempo.xyz
httpx_mock.add_response(
url="https://rpc.tempo.xyz",
json={"jsonrpc": "2.0", "result": "0x1079", "id": 1},
)
for _ in range(3):
httpx_mock.add_response(
url="https://rpc.test",
url="https://rpc.tempo.xyz",
json={"jsonrpc": "2.0", "result": "0x1", "id": 1},
)

Expand All @@ -1469,7 +1482,7 @@ async def test_access_key_with_fee_payer(self, httpx_mock: HTTPXMock) -> None:
"amount": "1000000",
"currency": "0x20c0000000000000000000000000000000000000",
"recipient": "0x742d35Cc6634c0532925a3b844bC9e7595F8fE00",
"methodDetails": {"feePayer": True, "chainId": 1},
"methodDetails": {"feePayer": True, "chainId": 4217},
},
realm="test.example.com",
request_b64="e30",
Expand All @@ -1492,7 +1505,12 @@ async def test_no_root_account_uses_regular_signing(self, httpx_mock: HTTPXMock)
intents={"charge": ChargeIntent()},
)

for _ in range(4):
# Mock RPC: chain_id (4217=0x1079), nonce, gas_price, estimateGas
httpx_mock.add_response(
url="https://rpc.test",
json={"jsonrpc": "2.0", "result": "0x1079", "id": 1},
)
for _ in range(3):
httpx_mock.add_response(
url="https://rpc.test",
json={"jsonrpc": "2.0", "result": "0x1", "id": 1},
Expand Down
Loading