Python SDK for interacting with the 4Mica payment network.
Project description
4Mica Python SDK
The official Python SDK for interacting with the 4Mica payment network.
Overview
This SDK provides:
- User Client: deposit collateral, sign payments, and manage withdrawals in ETH or ERC20 tokens
- Recipient Client: create payment tabs, verify payment guarantees, and claim from user collateral
- X402 Flow Helper: generate X-PAYMENT headers for 402-protected HTTP resources via an X402-compatible service
- Admin RPCs: manage user suspension and admin API keys (when authorized)
Installation
pip install sdk-4mica
Latest major release:
pip install "sdk-4mica>=1.0.0"
Install BLS support for on-chain remuneration:
pip install 'sdk-4mica[bls]'
Python 3.9+ is required.
Networks
| Shorthand | CAIP-2 | Core API URL |
|---|---|---|
ethereum-sepolia |
eip155:11155111 |
https://ethereum.sepolia.api.4mica.xyz/ |
base-sepolia |
eip155:84532 |
https://base.sepolia.api.4mica.xyz/ |
The default network is Ethereum Sepolia. Use .network() or the 4MICA_NETWORK environment
variable to switch networks.
from fourmica_sdk import NETWORKS
print(NETWORKS["base-sepolia"].caip2) # "eip155:84532"
print(NETWORKS["base-sepolia"].rpc_url) # "https://base.sepolia.api.4mica.xyz/"
Initialization and Configuration
The SDK requires a signing key and can use sensible defaults for the rest:
wallet_private_key(required): private key used for on-chain gateway operationsevm_signer(optional): custom signer implementingEvmSignerfor payment/auth signingnetwork(optional): select a network by shorthand or CAIP-2 id. Defaults toethereum-sepolia.rpc_url(optional): override the core API URL directly (for self-hosted deployments).ethereum_http_rpc_url(optional): Ethereum JSON-RPC endpoint; fetched from core if omittedcontract_address(optional): Core4Mica contract address; fetched from core if omittedbearer_token(optional): static bearer token for authauth_urlandauth_refresh_margin_secs(optional): SIWE auth config. Only used when auth is enabled viaenable_auth()or when set via env (defaults torpc_urland 60 seconds).
Note: wallet_private_key is currently required even when you supply evm_signer, because the
client always initializes the on-chain gateway.
1) Using ConfigBuilder
import asyncio
from fourmica_sdk import Client, ConfigBuilder
async def main() -> None:
cfg = (
ConfigBuilder()
.network("base-sepolia") # or "ethereum-sepolia" (default)
.wallet_private_key("0x...")
.build()
)
client = await Client.new(cfg)
try:
# use client.user, client.recipient, client.rpc
pass
finally:
await client.aclose()
if __name__ == "__main__":
asyncio.run(main())
2) Using Environment Variables
Set environment variables (example .env):
4MICA_WALLET_PRIVATE_KEY="0x..."
4MICA_NETWORK="base-sepolia" # shorthand or CAIP-2 id
# or override URL directly:
# 4MICA_RPC_URL="https://base.sepolia.api.4mica.xyz/"
4MICA_ETHEREUM_HTTP_RPC_URL="http://localhost:8545"
4MICA_CONTRACT_ADDRESS="0x..."
4MICA_BEARER_TOKEN="Bearer <access_token>"
4MICA_AUTH_URL="https://ethereum.sepolia.api.4mica.xyz/"
4MICA_AUTH_REFRESH_MARGIN_SECS="60"
If you want to set them inline for a single command, use env since most shells do not allow
variable names that start with a digit:
env 4MICA_WALLET_PRIVATE_KEY="0x..." 4MICA_NETWORK="base-sepolia" python app.py
Then in code:
from fourmica_sdk import Client, ConfigBuilder
cfg = ConfigBuilder().from_env().build()
client = await Client.new(cfg)
3) Using a Custom Signer
Provide a signer that implements EvmSigner (must expose address, sign_typed_data, and
sign_message). The private key is still required for on-chain operations.
from fourmica_sdk import Client, ConfigBuilder, LocalAccountSigner
signer = LocalAccountSigner("0x...")
cfg = ConfigBuilder().wallet_private_key("0x...").evm_signer(signer).build()
client = await Client.new(cfg)
SIWE Auth (Optional)
Enable automatic SIWE auth refresh, or pass a static bearer token:
from fourmica_sdk import Client, ConfigBuilder
cfg = (
ConfigBuilder()
.wallet_private_key("0x...")
.rpc_url("https://api.4mica.xyz/")
.enable_auth()
.build()
)
client = await Client.new(cfg)
await client.login() # optional: first RPC call also triggers auth
Or use a static token:
cfg = (
ConfigBuilder()
.wallet_private_key("0x...")
.bearer_token("Bearer <access_token>")
.build()
)
Env vars: 4MICA_BEARER_TOKEN, 4MICA_AUTH_URL, 4MICA_AUTH_REFRESH_MARGIN_SECS.
Usage
The SDK exposes three main entry points:
client.user: payer-side operations (collateral, signing, withdrawals)client.recipient: recipient-side operations (tabs, guarantees, remuneration)X402Flow: helper for 402-protected HTTP resources
Low-level admin RPCs are available under client.rpc (requires an admin API key):
client.rpc.with_admin_api_key("ak_...")
await client.rpc.update_user_suspension("0xUser", True)
Direct SDK Quick Start
This direct flow talks to 4mica core and shows the four core actions: deposit, open a tab, request
a guarantee, and settle (remunerate) it on-chain. The payer and recipient roles are separate in
production, so the SDK uses different keys. The snippets below are copied from the runnable scripts
in examples/recipient_quickstart.py and examples/payer_quickstart.py.
Key requirements:
- Payer flow uses the private key of the user
PAYER_KEYandRECIPIENT_ADDRESS.
Four-step direct flow:
- Deposit collateral (payer). For ETH, call
payer_client.user.deposit(amount). For ERC20, callpayer_client.user.approve_erc20(token, amount)first, thenpayer_client.user.deposit(amount, token).tokenis the contract address of the USDC/USDT. - Get
tab_idandreq_id(recipient). Callrecipient_client.recipient.create_tab(...)which hits core/core/payment-tabs. The SDK returnstab_id; compute the nextreq_idby callinglatest = await recipient_client.recipient.get_latest_guarantee(tab_id)and usingreq_id = 0 if latest is None else latest.req_id + 1. - Sign the guarantee (payer). Build
PaymentGuaranteeRequestClaimsfor V1, orPaymentGuaranteeRequestClaimsV2for validation-gated V2 usingcompute_validation_subject_hash(...)andcompute_validation_request_hash(...), then callsignature = await payer_client.user.sign_payment(claims, SigningScheme.EIP712). - Settle (recipient). Call
cert = await recipient_client.recipient.issue_payment_guarantee(...)with the claims + payer signature, thenawait recipient_client.recipient.remunerate(cert)to settle on chain.
Recipient (resource server) quick start
Recipient is the service provider that accepts a payer's credit. Each recipient can open a tab
with a user, a configurable line of credit that enables instant, trustless fair exchange between
payer and merchant. Run this script first to open a tab, compute the next REQ_ID, and print the
tab's asset address plus the amount the payer should sign.
export RECIPIENT_KEY=0x..
export USER_ADDRESS=0x.. # payer address
export AMOUNT_WEI=100000000000000000
import asyncio
import os
from eth_account import Account
from fourmica_sdk import Client, ConfigBuilder
RECIPIENT_KEY = os.environ["RECIPIENT_KEY"]
USER_ADDRESS = os.environ["USER_ADDRESS"]
AMOUNT_WEI = int(os.getenv("AMOUNT_WEI", "100000000000000000"), 0)
GUARANTEE_VERSION = int(os.getenv("GUARANTEE_VERSION", "1"), 10)
async def main() -> None:
recipient_cfg = ConfigBuilder().from_env().wallet_private_key(RECIPIENT_KEY).build()
recipient_client = await Client.new(recipient_cfg)
try:
recipient_address = Account.from_key(RECIPIENT_KEY).address
tab_id = await recipient_client.recipient.create_tab(
user_address=USER_ADDRESS,
recipient_address=recipient_address,
erc20_token=None,
ttl=None,
guarantee_version=GUARANTEE_VERSION,
)
latest = await recipient_client.recipient.get_latest_guarantee(tab_id)
req_id = latest.req_id + 1 if latest else 0
tab = await recipient_client.recipient.get_tab(tab_id)
asset_address = tab.asset_address if tab else None
print("TAB_ID=", tab_id)
print("REQ_ID=", req_id)
print("AMOUNT_WEI=", AMOUNT_WEI)
print("ASSET_ADDRESS=", asset_address)
print(
"ACCEPTED_GUARANTEE_VERSIONS=",
recipient_client.params.accepted_guarantee_versions_or_default(),
)
print(
"TRUSTED_VALIDATION_REGISTRIES=",
recipient_client.params.trusted_validation_registries,
)
print("GUARANTEE_VERSION=", GUARANTEE_VERSION)
finally:
await recipient_client.aclose()
if __name__ == "__main__":
asyncio.run(main())
Payer (client) quick start
Run this after you have a TAB_ID and REQ_ID from the recipient. It signs the guarantee and
optionally issues a certificate if you also pass RECIPIENT_KEY. For V2, provide the validation
policy env vars as well.
export PAYER_KEY=0x..
export RECIPIENT_ADDRESS=0x..
export TAB_ID=<tab_id>
export REQ_ID=<req_id>
export AMOUNT_WEI=100000000000000000
export ASSET_ADDRESS=0x0000000000000000000000000000000000000000
export RECIPIENT_KEY=0x.. # optional
export VALIDATION_REGISTRY_ADDRESS=0x.. # optional, enables V2 when set
export VALIDATION_CHAIN_ID=137 # optional, defaults to client.params.chain_id
export VALIDATOR_ADDRESS=0x.. # required for V2
export VALIDATOR_AGENT_ID=7 # required for V2
export MIN_VALIDATION_SCORE=80 # required for V2
export REQUIRED_VALIDATION_TAG=hard-finality # optional
import asyncio
import json
import os
import time
from eth_account import Account
from fourmica_sdk import (
AssetBalanceInfo,
Client,
ConfigBuilder,
PaymentGuaranteeRequestClaims,
PaymentGuaranteeRequestClaimsV2,
SigningScheme,
compute_validation_request_hash,
compute_validation_subject_hash,
)
PAYER_KEY = os.environ["PAYER_KEY"]
RECIPIENT_ADDRESS = os.environ["RECIPIENT_ADDRESS"]
TAB_ID = int(os.environ["TAB_ID"], 0)
REQ_ID = int(os.environ["REQ_ID"], 0)
DEFAULT_ASSET_ADDRESS = "0x0000000000000000000000000000000000000000"
REQUESTED_AMOUNT_WEI = int(os.getenv("AMOUNT_WEI", "100000000000000000"), 0)
ASSET_ADDRESS = os.getenv("ASSET_ADDRESS") or DEFAULT_ASSET_ADDRESS
RECIPIENT_KEY = os.getenv("RECIPIENT_KEY")
VALIDATION_REGISTRY_ADDRESS = os.getenv("VALIDATION_REGISTRY_ADDRESS")
VALIDATION_CHAIN_ID = os.getenv("VALIDATION_CHAIN_ID")
VALIDATOR_ADDRESS = os.getenv("VALIDATOR_ADDRESS")
VALIDATOR_AGENT_ID = os.getenv("VALIDATOR_AGENT_ID")
MIN_VALIDATION_SCORE = os.getenv("MIN_VALIDATION_SCORE")
REQUIRED_VALIDATION_TAG = os.getenv("REQUIRED_VALIDATION_TAG", "")
JOB_HASH = os.getenv("JOB_HASH")
def build_claims(
payer_client: Client,
user_address: str,
amount_wei: int,
timestamp: int,
) -> PaymentGuaranteeRequestClaims | PaymentGuaranteeRequestClaimsV2:
base_claims = PaymentGuaranteeRequestClaims.new(
user_address=user_address,
recipient_address=RECIPIENT_ADDRESS,
tab_id=TAB_ID,
req_id=REQ_ID,
amount=amount_wei,
timestamp=timestamp,
erc20_token=ASSET_ADDRESS,
)
wants_v2 = any(
value is not None
for value in (
VALIDATION_REGISTRY_ADDRESS,
VALIDATION_CHAIN_ID,
VALIDATOR_ADDRESS,
VALIDATOR_AGENT_ID,
MIN_VALIDATION_SCORE,
JOB_HASH,
)
)
if not wants_v2:
return base_claims
validation_chain_id = (
int(VALIDATION_CHAIN_ID, 0)
if VALIDATION_CHAIN_ID is not None
else int(payer_client.params.chain_id)
)
validation_subject_hash = compute_validation_subject_hash(base_claims)
partial_claims = PaymentGuaranteeRequestClaimsV2.new(
user_address=base_claims.user_address,
recipient_address=base_claims.recipient_address,
tab_id=base_claims.tab_id,
req_id=base_claims.req_id,
amount=base_claims.amount,
timestamp=base_claims.timestamp,
erc20_token=base_claims.asset_address,
validation_registry_address=VALIDATION_REGISTRY_ADDRESS,
validation_request_hash="0x" + "00" * 32,
validation_chain_id=validation_chain_id,
validator_address=VALIDATOR_ADDRESS,
validator_agent_id=int(VALIDATOR_AGENT_ID, 0),
min_validation_score=int(MIN_VALIDATION_SCORE, 0),
validation_subject_hash=validation_subject_hash,
required_validation_tag=REQUIRED_VALIDATION_TAG,
job_hash=JOB_HASH,
)
return PaymentGuaranteeRequestClaimsV2.new(
user_address=partial_claims.user_address,
recipient_address=partial_claims.recipient_address,
tab_id=partial_claims.tab_id,
req_id=partial_claims.req_id,
amount=partial_claims.amount,
timestamp=partial_claims.timestamp,
erc20_token=partial_claims.asset_address,
validation_registry_address=partial_claims.validation_registry_address,
validation_request_hash=compute_validation_request_hash(partial_claims),
validation_chain_id=partial_claims.validation_chain_id,
validator_address=partial_claims.validator_address,
validator_agent_id=partial_claims.validator_agent_id,
min_validation_score=partial_claims.min_validation_score,
validation_subject_hash=partial_claims.validation_subject_hash,
required_validation_tag=partial_claims.required_validation_tag,
job_hash=partial_claims.job_hash,
)
async def main() -> None:
payer_cfg = ConfigBuilder().from_env().wallet_private_key(PAYER_KEY).build()
payer_client = await Client.new(payer_cfg)
recipient_client = None
try:
user_address = Account.from_key(PAYER_KEY).address
amount_wei = REQUESTED_AMOUNT_WEI
balance_raw = await payer_client.rpc.get_user_asset_balance(
user_address, ASSET_ADDRESS
)
if balance_raw:
balance = AssetBalanceInfo.from_rpc(balance_raw)
available = max(balance.total - balance.locked, 0)
print("COLLATERAL_TOTAL=", balance.total)
print("COLLATERAL_LOCKED=", balance.locked)
print("COLLATERAL_AVAILABLE=", available)
if available <= 0:
raise SystemExit("No available collateral for this asset.")
if amount_wei > available:
amount_wei = available
if amount_wei <= 0:
raise SystemExit("Requested amount exceeds available collateral.")
else:
print("COLLATERAL_TOTAL= <unknown>")
print("COLLATERAL_LOCKED= <unknown>")
print("COLLATERAL_AVAILABLE= <unknown>")
print("AMOUNT_WEI=", amount_wei)
claims = build_claims(
payer_client, user_address, amount_wei, timestamp=int(time.time())
)
signature = await payer_client.user.sign_payment(claims, SigningScheme.EIP712)
print("PAYER_SIGNATURE=", signature.signature)
print("CLAIMS_JSON=", json.dumps(claims.__dict__))
if RECIPIENT_KEY:
recipient_cfg = (
ConfigBuilder().from_env().wallet_private_key(RECIPIENT_KEY).build()
)
recipient_client = await Client.new(recipient_cfg)
cert = await recipient_client.recipient.issue_payment_guarantee(
claims, signature.signature, SigningScheme.EIP712
)
print("CERT_CLAIMS=", cert.claims)
print("CERT_SIGNATURE=", cert.signature)
finally:
if recipient_client is not None:
await recipient_client.aclose()
await payer_client.aclose()
if __name__ == "__main__":
asyncio.run(main())
For V2 x402 flows, include the following fields under paymentRequirements.extra:
validationRegistryAddress, validatorAddress, validatorAgentId,
minValidationScore, jobHash, and optional requiredValidationTag.
validationChainId is optional; when omitted, the SDK derives the expected chain id from the
CAIP-2 network value (for example, eip155:1).
X402 Flow (HTTP 402)
The X402 helper turns paymentRequirements from a 402 Payment Required response into an
X-PAYMENT header (and optional /settle call) that the facilitator will accept.
What the SDK expects from paymentRequirements
At minimum you need:
schemeandnetwork(scheme must include4mica, e.g.4mica-credit)extra.tabEndpointfor tab resolution
X402Flow will refresh the tab by calling extra.tabEndpoint before signing.
X402 Version 1
Version 1 returns payment requirements in the JSON response body:
import asyncio
from fourmica_sdk import Client, ConfigBuilder, X402Flow
from fourmica_sdk import PaymentRequirementsV1
async def main() -> None:
cfg = ConfigBuilder().wallet_private_key("0x...").build()
client = await Client.new(cfg)
flow = X402Flow.from_client(client)
# 1) GET the protected endpoint and parse JSON body
res = fetch_resource_somehow()
requirements = PaymentRequirementsV1.from_raw(res["accepts"][0])
# 2) Build the X-PAYMENT header with the SDK
payment = await flow.sign_payment(requirements, "0xUser")
# 3) Call the protected resource with the header
headers = {"X-PAYMENT": payment.header}
await call_resource_somehow(headers)
await client.aclose()
if __name__ == "__main__":
asyncio.run(main())
X402 Version 2
Version 2 uses the payment-required header (base64-encoded) instead of a JSON response body:
import asyncio
import base64
import json
from fourmica_sdk import Client, ConfigBuilder, X402Flow
from fourmica_sdk import X402PaymentRequired, PaymentRequirementsV2
async def main() -> None:
cfg = ConfigBuilder().wallet_private_key("0x...").build()
client = await Client.new(cfg)
flow = X402Flow.from_client(client)
# 1) GET the protected endpoint and extract payment-required header
res = fetch_resource_somehow()
header = res.headers.get("payment-required")
if not header:
raise RuntimeError("Missing payment-required header")
# 2) Decode the header
decoded = base64.b64decode(header).decode("utf8")
payment_required = X402PaymentRequired.from_raw(json.loads(decoded))
# 3) Select a payment option
accepted = payment_required.accepts[0]
# 4) Build the PAYMENT-SIGNATURE header with the SDK
signed = await flow.sign_payment_v2(payment_required, accepted, "0xUser")
# 5) Call the protected resource with the header
headers = {"PAYMENT-SIGNATURE": signed.header}
await call_resource_somehow(headers)
await client.aclose()
if __name__ == "__main__":
asyncio.run(main())
Resource server / facilitator side
If your resource server proxies to the facilitator, you can reuse the SDK to settle after verifying:
from fourmica_sdk import Client, ConfigBuilder, X402Flow
from fourmica_sdk import PaymentRequirementsV1, X402SignedPayment
async def settle(
facilitator_url: str,
payment_requirements: PaymentRequirementsV1,
payment: X402SignedPayment,
) -> None:
core = await Client.new(
ConfigBuilder().wallet_private_key("0x...").build()
)
flow = X402Flow.from_client(core)
settled = await flow.settle_payment(payment, payment_requirements, facilitator_url)
print("settlement result:", settled.settlement)
await core.aclose()
Notes:
sign_paymentandsign_payment_v2always use EIP-712 signing and will error if the scheme is not 4mica.UserClient.sign_paymentsupportsSigningScheme.EIP712(default) andSigningScheme.EIP191.settle_paymentonly hits/settle; resource servers should still call/verifyfirst when enforcing access.
Concepts
- Tabs are per
(user, recipient, asset, guarantee_version)credit ledgers. Core reuses an existing active tab for that exact identity if it is still valid; otherwise it creates a new opaquetab_id. - Tab lifecycle: tabs start
Pending; the first valid guarantee opens the tab and setsstart_timestampto the claim timestamp. Guarantees must be within[start_timestamp, start_timestamp + ttl]and are rejected if the tab is closed or expired. - Request ids:
req_idis per-tab and strictly sequential. The first guarantee usesreq_id = 0; each new guarantee must belast_req_id + 1. The facilitator returnsnextReqIdin/tabs. - Guarantee request claims (v1) are the signed payload:
{ user_address, recipient_address, tab_id, req_id, amount, asset_address, timestamp }.asset_addressis the zero address for ETH if omitted.timestampis seconds since epoch and is validated by core. - Guarantee request claims (v2) extend V1 with validation policy fields:
validation_registry_address,validation_request_hash,validation_chain_id,validator_address,validator_agent_id,min_validation_score,validation_subject_hash, andrequired_validation_tag. - Guarantee certificates are BLS signatures over
PaymentGuaranteeClaims(core addsdomain,total_amountwhich is the running sum for the tab, andversion). The SDK models this asBLSCert { claims, signature }, whereclaimsis ABI-encoded hex.
X-PAYMENT header schema
X-PAYMENT is a base64-encoded JSON envelope:
{
"x402Version": 1,
"scheme": "4mica-credit",
"network": "polygon-amoy",
"payload": {
"claims": {
"user_address": "<0x-prefixed checksum string>",
"recipient_address": "<0x-prefixed checksum string>",
"tab_id": "<decimal or 0x value>",
"req_id": "<decimal or 0x value>",
"amount": "<decimal or 0x value>",
"asset_address": "<0x-prefixed checksum string>",
"timestamp": 1716500000,
"version": "v1"
},
"signature": "<0x-prefixed wallet signature>",
"scheme": "eip712"
}
}
Facilitators enforce that scheme / network match /supported, payTo matches
recipient_address in the claims, and asset / maxAmountRequired equal the signed amount.
For V2, payload.claims.version is "v2" and the validation policy fields are included in the
same claims object. The signed validation_chain_id is derived from paymentRequirements.network
(eip155:<chainId>) and must match any optional extra.validationChainId value if provided.
Testing
By default, pytest runs unit tests only:
./venv/bin/pytest -q
Integration tests are marked with @pytest.mark.integration and require a live core/auth service.
Run them explicitly when the environment is available:
./venv/bin/pytest -q -m integration
End-to-end credit flow (x402)
- Resource sends
402 Payment Requiredwith(scheme, network)and a tab endpoint. - Client calls the tab endpoint with
{ userAddress, erc20Token?, ttlSeconds?, x402Version? }; the recipient calls/tabsand returns tab metadata. - Client signs a guarantee with the SDK and wraps it into
X-PAYMENT. - Client retries the protected call with
X-PAYMENT: <base64>. - Recipient optionally calls
/verifywith{ x402Version, paymentHeader, paymentRequirements }. - Recipient calls
/settleto obtain the certificate, then relays repayment details to the payer.
API Methods Summary
UserClient Methods
approve_erc20(token, amount)deposit(amount, erc20_token=None)get_user()get_tab_payment_status(tab_id)sign_payment(claims, scheme=SigningScheme.EIP712)pay_tab(tab_id, req_id, amount, recipient_address, erc20_token=None)request_withdrawal(amount, erc20_token=None)cancel_withdrawal(erc20_token=None)finalize_withdrawal(erc20_token=None)
RecipientClient Methods
create_tab(user_address, recipient_address, erc20_token, ttl, guarantee_version=1)get_tab_payment_status(tab_id)issue_payment_guarantee(claims, signature, scheme)verify_payment_guarantee(cert)remunerate(cert)list_settled_tabs()list_pending_remunerations()get_tab(tab_id)list_recipient_tabs(settlement_statuses=None)get_tab_guarantees(tab_id)get_latest_guarantee(tab_id)get_guarantee(tab_id, req_id)list_recipient_payments()get_collateral_events_for_tab(tab_id)get_user_asset_balance(user_address, asset_address)
Admin / RPC Methods
Available under client.rpc (requires an admin API key):
update_user_suspension(user_address, suspended)create_admin_api_key({"name": ..., "scopes": [...]})list_admin_api_keys()revoke_admin_api_key(key_id)
Error Handling
All SDK errors extend FourMicaError. Common error types include ConfigError, RpcError,
ClientInitializationError, ContractError, SigningError, VerificationError, X402Error, and
AuthError.
Notes
- All methods are
async; useasyncio.runor your event loop of choice. - Remuneration requires
py-ecc(pip install 'sdk-4mica[bls]') to expand BLS signatures into the on-chain format. - Numeric values accept
intor hex/decimal strings and are serialized to0x-prefixed hex when sent to the facilitator.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sdk_4mica-1.2.0.tar.gz.
File metadata
- Download URL: sdk_4mica-1.2.0.tar.gz
- Upload date:
- Size: 67.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5295da7e2a53f75c3e0de52243e2adeb04dd16dd679be9d5568e476dbb73b0c
|
|
| MD5 |
224406452e4f939ae14872600578ef4e
|
|
| BLAKE2b-256 |
6f8299b2f12dbf60391deb587791d04895e4b4b76ea7742e7e51e0f35d806770
|
File details
Details for the file sdk_4mica-1.2.0-py3-none-any.whl.
File metadata
- Download URL: sdk_4mica-1.2.0-py3-none-any.whl
- Upload date:
- Size: 44.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3416a009d6c4aa5eb36dee7029d368950adfa6a661aeeda3048a022e00959809
|
|
| MD5 |
38d107feebb9f045c8724a4cecc9b991
|
|
| BLAKE2b-256 |
f78520a701c37dc589c05eace4761c827bd32e8ad252c2313ab52389c85c19ac
|