4Mica Rust SDK
The official Rust SDK for interacting with the 4Mica payment network.
Overview
4Mica is a payment network that enables cryptographically-enforced lines of credit for autonomous payments. The 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 when payments aren't fulfilled
- X402 Flow Helper: Generate X-PAYMENT headers for 402-protected HTTP resources via an X402-compatible service
- Guarantee V2 Support: Build and verify validation-gated guarantees bound to ERC-8004 validation policy fields
Installation
Add the SDK to your Cargo.toml:
[]
= "0.6.0"
Guarantee Versions
The SDK supports both V1 and V2 guarantee flows.
- V1 is the original signed payment-intent flow.
- V2 adds ERC-8004 validation policy fields and gates
remunerate()on on-chain validation. - Core advertises the accepted guarantee versions and trusted validation registries through
/core/public-params. GUARANTEE_REQUEST_VERSIONon the core service controls which versions core accepts by default. It does not force the output certificate version. The issued version is derived from the request payload.
For callers there are three common paths:
user.sign_payment(...)signs explicit V1 claims.user.sign_payment_v2(...)signs explicit V2 claims.user.sign_payment_auto(...)chooses V1 or V2 from core metadata plus optionalPaymentGuaranteeValidationInput.
Initialization and Configuration
The SDK requires a signer and can use sensible defaults for the rest:
signer(required): Anyalloy::signers::Signer(typicallyPrivateKeySignerfrom a hex private key). On-chain methods require a signer that also implementsTxSigner<Signature>.rpc_url(optional): URL of the 4Mica RPC server. Defaults tohttps://api.4mica.xyz/; override for local development.
The following parameters are optional and will be automatically fetched from the server if not provided.
ethereum_http_rpc_url: URL of the Ethereum JSON-RPC endpoint (optional)contract_address: Address of the deployed Core4Mica smart contract (optional)
Note: You normally don't need to provide
ethereum_http_rpc_urlandcontract_addressas the SDK will fetch these from the server automatically. Only override these if you need to use different values than the server's defaults.The Ethereum
chain_idis fetched from the core service and validated against the connected Ethereum provider automatically.
Configuration Methods
1. Using ConfigBuilder
use ;
use ;
use FromStr;
async
2. Using Environment Variables (ConfigBuilder::from_env)
ConfigBuilder::from_env() reads these keys:
4MICA_WALLET_PRIVATE_KEY(required)4MICA_RPC_URL(optional; defaults tohttps://api.4mica.xyz/)4MICA_ETHEREUM_HTTP_RPC_URL(optional)4MICA_CONTRACT_ADDRESS(optional)4MICA_BEARER_TOKEN(optional)4MICA_AUTH_URL(optional)4MICA_AUTH_REFRESH_MARGIN_SECS(optional)
Because these variable names start with a digit, use a .env file (or set process env programmatically) instead of export in a shell.
Example .env:
4MICA_WALLET_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
4MICA_RPC_URL=http://localhost:3000
4MICA_ETHEREUM_HTTP_RPC_URL=http://localhost:8545
4MICA_CONTRACT_ADDRESS=0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0
Then in your code:
use ;
async
Add dotenv = "0.15" to your app dependencies if you load .env files this way.
Usage
The SDK provides client interfaces for both sides of the payment flow plus a helper to bridge HTTP 402 resources:
UserClient: payer controls collateral and signs paymentsRecipientClient: payment recipient creates tabs, verify guarantees, and claims collateralX402Flow: builds X-PAYMENT headers for X402-protected HTTP endpoints
X402 flow (HTTP 402)
The X402 helper turns the paymentRequirements emitted by a 402 Payment Required response into an X-PAYMENT header (and optional /settle call) that the facilitator will accept. The examples in https://github.com/4mica-Network/x402-4mica/examples model the expected flow: the client speaks to the resource server, and the resource server talks to the facilitator for /tabs, /verify, and /settle.
What the SDK expects from paymentRequirements
sdk-4mica accepts the canonical X402 JSON (camelCase). At minimum you need:
schemeandnetwork:schememust contain4mica(e.g.4mica-credit)X402Flowwill refresh the tab by callingextra.tabEndpointbefore signing.
End-to-end client flow
X402 Version 1
Version 1 returns payment requirements in the JSON response body:
- GET the protected endpoint; parse the JSON body to get the response with
acceptsarray. - Select a payment option from
acceptsarray. - Call
X402Flow::sign_paymentto get the base64X-PAYMENTheader. - Retry the protected endpoint with
X-PAYMENT; the resource server will call the facilitator/verifyand/settle.
use ;
use ;
use PaymentRequirements;
use Deserialize;
use FromStr;
async
X402 Version 2
Version 2 uses the PAYMENT-REQUIRED header (base64-encoded) instead of a JSON response body:
- GET the protected endpoint; extract and decode the
payment-requiredheader to getX402PaymentRequiredV2. - Select a payment option from
acceptsarray. - Call
X402Flow::sign_payment_v2to get the base64PAYMENT-SIGNATUREheader. - Retry the protected endpoint with
PAYMENT-SIGNATURE; the resource server will call the facilitator/verifyand/settle.
use PrivateKeySigner;
use ;
use ;
use ;
use FromStr;
async
Resource server / facilitator side
If your resource server proxies to the facilitator (the pattern used in examples/server/mock_paid_api.py), you can reuse the SDK to settle after verifying:
use PrivateKeySigner;
use ;
use PaymentRequirements;
use FromStr;
async
Notes:
sign_paymentandsign_payment_v2always use EIP-712 signing and will error if the scheme is not 4mica.settle_paymentonly hits/settle; resource servers should still call the facilitator/verifyfirst when enforcing access (see the Python example for the end-to-end pattern).
API Methods Summary
UserClient Methods
approve_erc20(token: String, amount: U256) -> Result<TransactionReceipt, ApproveErc20Error>: Approve the 4Mica contract to spend ERC20 tokens on behalf of the userdeposit(amount: U256, erc20_token: Option<String>) -> Result<TransactionReceipt, DepositError>: Deposit collateral in ETH or ERC20 tokenget_user() -> Result<Vec<UserInfo>, GetUserError>: Get current user information for all assetsget_tab_payment_status(tab_id: U256) -> Result<TabPaymentStatus, TabPaymentStatusError>: Get payment status for a tabsign_payment(claims: PaymentGuaranteeRequestClaims, scheme: SigningScheme) -> Result<PaymentSignature, SignPaymentError>: Sign a V1 payment (PaymentGuaranteeRequestClaimsis the SDK alias for V1 claims)sign_payment_v2(claims: PaymentGuaranteeRequestClaimsV2, scheme: SigningScheme) -> Result<PaymentSignature, SignPaymentError>: Sign a V2 payment with validation policy fieldssign_payment_auto(intent: PaymentGuaranteeIntent, validation: Option<PaymentGuaranteeValidationInput>, scheme: SigningScheme) -> Result<PreparedPaymentGuaranteeRequest, SignPaymentError>: Build and sign either V1 or V2 claims using core metadatapay_tab(tab_id: U256, req_id: U256, amount: U256, recipient_address: String, erc20_token: Option<String>) -> Result<TransactionReceipt, PayTabError>: Pay a tab directly on-chain in ETH or ERC20 tokenrequest_withdrawal(amount: U256, erc20_token: Option<String>) -> Result<TransactionReceipt, RequestWithdrawalError>: Request withdrawal of collateral in ETH or ERC20 tokencancel_withdrawal(erc20_token: Option<String>) -> Result<TransactionReceipt, CancelWithdrawalError>: Cancel pending withdrawalfinalize_withdrawal(erc20_token: Option<String>) -> Result<TransactionReceipt, FinalizeWithdrawalError>: Finalize withdrawal after waiting period
RecipientClient Methods
create_tab(user_address: String, recipient_address: String, erc20_token: Option<String>, ttl: Option<u64>) -> Result<U256, CreateTabError>: Create a new payment tab in ETH or ERC20 tokenget_tab_payment_status(tab_id: U256) -> Result<TabPaymentStatus, TabPaymentStatusError>: Get payment status for a tabverify_payment_guarantee(cert: &BLSCert) -> Result<PaymentGuaranteeClaims, VerifyGuaranteeError>: Verify a BLS certificate and extract claimsissue_payment_guarantee(claims: PaymentGuaranteeRequestClaims, signature: String, scheme: SigningScheme) -> Result<BLSCert, IssuePaymentGuaranteeError>: Issue a payment guaranteeissue_payment_guarantee_v2(claims: PaymentGuaranteeRequestClaimsV2, signature: String, scheme: SigningScheme) -> Result<BLSCert, IssuePaymentGuaranteeError>: Issue a V2 payment guaranteeissue_prepared_payment_guarantee(request: PreparedPaymentGuaranteeRequest) -> Result<BLSCert, IssuePaymentGuaranteeError>: Issue a guarantee from the output ofsign_payment_autoremunerate(cert: BLSCert) -> Result<TransactionReceipt, RemunerateError>: Claim from user collateral using BLS certificatelist_settled_tabs() -> Result<Vec<TabInfo>, RecipientQueryError>: List all settled tabs for the recipientlist_pending_remunerations() -> Result<Vec<PendingRemunerationInfo>, RecipientQueryError>: List pending remunerations for the recipientget_tab(tab_id: U256) -> Result<Option<TabInfo>, RecipientQueryError>: Get tab information by IDlist_recipient_tabs(settlement_statuses: Option<Vec<String>>) -> Result<Vec<TabInfo>, RecipientQueryError>: List tabs for the recipient with optional status filterget_tab_guarantees(tab_id: U256) -> Result<Vec<GuaranteeInfo>, RecipientQueryError>: Get all guarantees for a tabget_latest_guarantee(tab_id: U256) -> Result<Option<GuaranteeInfo>, RecipientQueryError>: Get the latest guarantee for a tabget_guarantee(tab_id: U256, req_id: U256) -> Result<Option<GuaranteeInfo>, RecipientQueryError>: Get a specific guarantee by tab ID and request IDlist_recipient_payments() -> Result<Vec<RecipientPaymentInfo>, RecipientQueryError>: List all payments for the recipientget_collateral_events_for_tab(tab_id: U256) -> Result<Vec<CollateralEventInfo>, RecipientQueryError>: Get collateral events for a specific tabget_user_asset_balance(user_address: String, asset_address: String) -> Result<Option<AssetBalanceInfo>, RecipientQueryError>: Get user's asset balance
Note:
BLSCertnow exposes typed claims and signatures. Usecert.claims().to_hex()orcert.signature().to_hex()when you need hex strings.
Note: Each method returns a specific error type that provides detailed information about what went wrong. See the Error Handling section for comprehensive documentation and examples.
User Client (Payer)
The user client allows you to manage your collateral and sign payments in ETH or ERC20 tokens.
Approve ERC20 Token (Required before depositing or paying with ERC20)
use U256;
// Approve the 4Mica contract to spend 1000 USDC on your behalf
let token_address = "0x1234567890123456789012345678901234567890".to_string;
let amount = U256from; // 1000 USDC (6 decimals)
match client.user.approve_erc20.await
Deposit Collateral
use U256;
// Deposit 1 ETH as collateral
let amount = U256from; // 1 ETH in wei
match client.user.deposit.await
// Or deposit 1000 USDC (make sure to approve first!)
let token_address = "0x1234567890123456789012345678901234567890".to_string;
let amount = U256from;
let receipt = client.user.deposit.await?;
println!;
Get User Info
// Get information about the current user for all assets
let user_assets = client.user.get_user.await?;
for user_info in user_assets
Get Tab Payment Status
use U256;
let tab_id = U256from;
let status = client.user.get_tab_payment_status.await?;
println!;
println!;
println!;
Sign a Payment
use ;
// Create payment claims for ETH payment
let claims = new;
// Sign using EIP-712 (recommended)
match client.user.sign_payment.await
// Or use EIP-191 (personal_sign)
let payment_sig = client.user.sign_payment.await?;
// For ERC20 token payment, pass the token address
let usdc_token = "0x1234567890123456789012345678901234567890".to_string;
let claims_usdc = new;
let payment_sig_usdc = client.user.sign_payment.await?;
Pay a Tab
use U256;
// Pay 1 ETH to a tab
let tab_id = U256from;
// Use the req_id that came back with the guarantee certificate (placeholder value shown)
let req_id = U256from;
let amount = U256from;
let recipient_address = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC".to_string;
let receipt = client.user.pay_tab.await?;
println!;
// Or pay 1000 USDC to a tab (make sure to approve the 4Mica contract first!)
let token_address = "0x1234567890123456789012345678901234567890".to_string;
let amount_usdc = U256from;
let receipt = client.user.pay_tab.await?;
println!;
Request Withdrawal
use U256;
// Request to withdraw 0.5 ETH
let amount = U256from;
let receipt = client.user.request_withdrawal.await?;
println!;
// Or request to withdraw 500 USDC
let token_address = "0x1234567890123456789012345678901234567890".to_string;
let amount_usdc = U256from;
let receipt = client.user.request_withdrawal.await?;
println!;
Cancel Withdrawal
// Cancel a pending ETH withdrawal request
let receipt = client.user.cancel_withdrawal.await?;
println!;
// Cancel a pending USDC withdrawal request
let token_address = "0x1234567890123456789012345678901234567890".to_string;
let receipt = client.user.cancel_withdrawal.await?;
println!;
Finalize Withdrawal
// Finalize ETH withdrawal (after the waiting period)
let receipt = client.user.finalize_withdrawal.await?;
println!;
// Finalize USDC withdrawal (after the waiting period)
let token_address = "0x1234567890123456789012345678901234567890".to_string;
let receipt = client.user.finalize_withdrawal.await?;
println!;
Recipient Client
The recipient client allows you to create payment tabs, issue payment guarantees, and claim from user collateral when payments aren't fulfilled.
Create Payment Tab
use U256;
// Create a new payment tab for ETH
let user_address = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_string;
let recipient_address = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC".to_string;
let ttl = Some; // Tab expires in 1 hour (optional)
let tab_id = client.recipient.create_tab.await?;
println!;
// Create a new payment tab for USDC
let token_address = "0x1234567890123456789012345678901234567890".to_string;
let tab_id_usdc = client.recipient.create_tab.await?;
println!;
Get Tab Payment Status
use U256;
let tab_id = U256from;
let status = client.recipient.get_tab_payment_status.await?;
println!;
println!;
println!;
Issue Payment Guarantee
use ;
// First, the user signs the payment (see User Client example above)
let claims = new;
let payment_sig = client.user.sign_payment.await?;
let bls_cert = client.recipient.issue_payment_guarantee.await?;
println!;
Remunerate (Claim from Collateral)
// If the user doesn't fulfill the payment guarantee,
// the recipient can claim from the user's collateral on-chain
let receipt = client.recipient.remunerate.await?;
println!;
println!;
Complete Example
Here's a complete example showing a payment flow with ETH:
use PrivateKeySigner;
use ;
use FromStr;
async
Complete Example with ERC20 Token (USDC)
Here's a complete example showing a payment flow with an ERC20 token:
use PrivateKeySigner;
use ;
use FromStr;
async
Error Handling
The SDK provides comprehensive, type-safe error handling with specific error types for each operation. All errors are strongly typed and provide detailed context about what went wrong.
Importing
// Import specific error types when needed
use ;
Error Types
Configuration Errors
ConfigError
InvalidValue(String): Invalid configuration valueMissing(String): Required configuration parameter is missing
Client Errors
ClientError
Rpc(String): RPC connection errorProvider(String): Provider initialization errorInitialization(String): Client initialization error
Payment Signing Errors
SignPaymentError
AddressMismatch { signer: Address, claims: String }: Signer address doesn't match user address in claimsInvalidUserAddress: User address in claims is invalidInvalidRecipientAddress: Recipient address in claims is invalidFailed(String): Failed to sign the payment (includes digest computation and signing errors)Rpc(rpc::ApiClientError): RPC communication error
Deposit Errors
ApproveErc20Error
InvalidParams(String): Invalid parameters provided (e.g., invalid token address)Client(ClientError): Client initialization or provider error while preparing the transactionUnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
DepositError
InvalidParams(String): Invalid parameters provided (e.g., invalid token address)AmountZero: Cannot deposit zero amountClient(ClientError): Client initialization or provider error while preparing the transactionUnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
Withdrawal Errors
RequestWithdrawalError
InvalidParams(String): Invalid parameters provided (e.g., invalid token address)AmountZero: Cannot withdraw zero amountInsufficientAvailable: Not enough available balance to withdrawClient(ClientError): Client initialization or provider error while preparing the transactionUnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
CancelWithdrawalError
InvalidParams(String): Invalid parameters provided (e.g., invalid token address)NoWithdrawalRequested: No withdrawal request exists to cancelClient(ClientError): Client initialization or provider error while preparing the transactionUnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
FinalizeWithdrawalError
InvalidParams(String): Invalid parameters provided (e.g., invalid token address)NoWithdrawalRequested: No withdrawal request exists to finalizeGracePeriodNotElapsed: Grace period has not elapsed yetTransferFailed: Transfer of funds failedClient(ClientError): Client initialization or provider error while preparing the transactionUnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
Tab Payment Errors
CreateTabError
InvalidParams(String): Invalid parameters (e.g., signer address mismatch)Rpc(rpc::ApiClientError): RPC communication error
PayTabError
InvalidParams(String): Invalid parameters providedInvalidAsset: Asset does not match the tab assetClient(ClientError): Client initialization or provider error while preparing the transactionUnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
TabPaymentStatusError
UnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
Payment Guarantee Errors
IssuePaymentGuaranteeError
InvalidParams(String): Invalid parameters (e.g., signer address mismatch)Rpc(rpc::ApiClientError): RPC communication error
VerifyGuaranteeError
InvalidCertificate(anyhow::Error): Invalid BLS certificateCertificateMismatch: Certificate signature mismatchGuaranteeDomainMismatch: Guarantee domain mismatchUnsupportedGuaranteeVersion(u64): Unsupported guarantee version
X402Error
InvalidScheme(String):paymentRequirements.schememust include4micaInvalidFacilitatorUrl(String): Invalid facilitator/settlebase URLTabResolutionFailed(String)/InvalidExtra(String): Issues resolving or parsingpaymentRequirements.extraInvalidNumber { field, source }/UserMismatch { found, expected }: Invalid numeric fields or wrong user in requirementsEncodeEnvelope(String): Failed to encode the X-PAYMENT envelopeSettlementFailed { status, body }: Facilitator/settlereturned a non-success statusSigning(SignPaymentError)/Http(reqwest::Error): Errors while signing or making HTTP requests
RecipientQueryError
Rpc(rpc::ApiClientError): RPC communication error
RemunerateError
InvalidParams(String): Invalid parameters providedClaimsHex(anyhow::Error): Failed to decode the hex-encoded guarantee claims blobClaimsDecode(anyhow::Error): Failed to deserialize guarantee claims after decodingGuaranteeConversion(anyhow::Error): Failed to convert decoded claims into the contract call typeSignatureHex(FromHexError): Failed to decode the hex-encoded BLS signatureSignatureDecode(anyhow::Error): Failed to parse the decoded BLS signature bytesTabNotYetOverdue: Tab has not reached its due date yetTabExpired: Tab has expired and can no longer be remuneratedTabPreviouslyRemunerated: Tab has already been remuneratedTabAlreadyPaid: Tab has already been paid by userInvalidSignature: BLS signature verification failedDoubleSpendingDetected: Attempt to spend same guarantee twiceInvalidRecipient: Caller is not the recipient of this tabAmountZero: Guarantee amount is zeroTransferFailed: Transfer of funds failedCertificateInvalid(anyhow::Error): Certificate verification failedCertificateMismatch: Certificate signature mismatch before submissionGuaranteeDomainMismatch: Guarantee domain mismatchUnsupportedGuaranteeVersion(u64): Unsupported guarantee versionClient(ClientError): Client initialization or provider error while preparing the transactionUnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
GetUserError
UnknownRevert { selector: u32, data: Vec<u8> }: Unknown contract revertTransport(String): Provider or transport error
Development
Running Tests
Building
Security Considerations
- Never commit private keys: Always use environment variables or secure key management systems
- Validate addresses: The SDK validates addresses automatically and returns
SignPaymentError::AddressMismatchif the signer doesn't match the claims - Signature verification: The SDK ensures the signer address matches the claims user address before signing
- Use EIP-712: Prefer EIP-712 signing over EIP-191 for better security and structured data hashing
- Handle errors properly: Always handle errors explicitly. The SDK provides specific error types for each failure scenario to help you build robust applications
- Check signer addresses: For
RecipientClientoperations, ensure your signer address matches the recipient address. The SDK will returnInvalidParamserrors for mismatches - Validate amounts: The SDK prevents zero-amount transactions at the contract level, but you should validate amounts in your application for better UX
- ERC20 Approvals: Always approve the 4Mica contract before depositing or paying with ERC20 tokens. Approve only the amount you need to minimize risk
- Asset Matching: When paying a tab or creating payment claims, ensure the asset (ETH or ERC20 token) matches the tab's asset. The contract will reject mismatched assets
- Multi-Asset Management: Each asset (ETH and each ERC20 token) has its own collateral balance and withdrawal request. Use
get_user()to view all your asset balances
License
This project is licensed under the Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0).
Support
- Website: https://4mica.xyz
- Documentation: https://docs.4mica.xyz
- GitHub: https://github.com/4mica-Network/4mica-core