Skip to content

Commit 38daada

Browse files
committed
chore: bump version to 0.4.0 and add Monad Mainnet deployment with streamlined documentation
- Bump workspace version from 0.3.1 to 0.4.0 in Cargo.toml - Add Monad Mainnet deployment (chain 143, contract 0xE8c4FFb4A6F7B8040a7AE39F6651290E06A40725) to networks.rs - Refactor Network enum to only include live deployments (remove placeholder networks) - Update README with deployment table, condensed Quick Start showing full lifecycle, and removed Design Principles section - Replace basic.rs example
1 parent b1cd543 commit 38daada

9 files changed

Lines changed: 204 additions & 166 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ target
2121
#.idea/
2222

2323
/contracts
24-
/3rdparty
24+
# /3rdparty

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ default-members = ["erc8183"]
44
resolver = "3"
55

66
[workspace.package]
7-
version = "0.3.1"
7+
version = "0.4.0"
88
edition = "2024"
99
license = "MIT OR Apache-2.0"
1010
repository = "https://github.com/qntx/erc8183"
1111
description = "The Commerce Layer for AI Agents."
1212

1313
[workspace.dependencies]
14-
erc8183 = { version = "0.3", path = "erc8183" }
14+
erc8183 = { version = "0.4", path = "erc8183" }
1515

1616
alloy = { version = "1", features = ["full"] }
1717
serde = { version = "1", features = ["derive"] }

README.md

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
Type-safe Rust SDK for the [ERC-8183](https://eips.ethereum.org/EIPS/eip-8183) Agentic Commerce Protocol.
2727
On-chain job escrow with evaluator attestation for AI agent commerce.
2828

29-
[Quick Start](#quick-start) | [Protocol Reference](#erc-8183-protocol) | [API docs][doc-url]
29+
[Quick Start](#quick-start) | [Protocol](#erc-8183-protocol) | [API docs][doc-url]
3030

3131
</div>
3232

@@ -37,37 +37,55 @@ ERC-8183 enables **trustless commerce between AI agents**: a client locks funds
3737
This SDK provides type-safe Rust bindings for the protocol, built on [alloy](https://github.com/alloy-rs/alloy).
3838

3939
> [!NOTE]
40-
> ERC-8183 is currently a **Draft** EIP. See [SECURITY.md](SECURITY.md) before production use.
41-
>
42-
> Reference implementation: **[qntx/market-contract](https://github.com/qntx/market-contract)**
40+
> Reference implementation: **[qntx/market-contract](https://github.com/qntx/market-contract)**, See [SECURITY.md](SECURITY.md) before production use.
4341
44-
## Quick Start
45-
46-
```rust
47-
use erc8183::{Erc8183, types::CreateJobParams};
42+
## Deployments
4843

49-
// Connect to contract
50-
let job = Erc8183::new(provider).with_address(contract_addr).job()?;
44+
| Network | Chain ID | Contract | Payment Token | Explorer |
45+
| --- | --- | --- | --- | --- |
46+
| **Monad Mainnet** | 143 | [`0xE8c4FFb4A6F7B8040a7AE39F6651290E06A40725`](https://monad.socialscan.io/address/0xE8c4FFb4A6F7B8040a7AE39F6651290E06A40725) | USDC | [Socialscan](https://monad.socialscan.io/address/0xE8c4FFb4A6F7B8040a7AE39F6651290E06A40725) |
5147

52-
// Create → Fund → Submit → Complete
53-
let id = job.create_job(&CreateJobParams::new(provider, evaluator, expires, "task")).await?;
54-
job.fund(id, budget, None).await?;
55-
job.submit(id, deliverable, None).await?;
56-
job.complete(id, reason, None).await?;
48+
## Quick Start
5749

58-
// Query
59-
let data = job.get_job(id).await?;
50+
```rust
51+
use erc8183::{Erc8183, Network, types::CreateJobParams};
52+
use alloy::primitives::{Address, U256};
53+
54+
// Connect to Monad Mainnet
55+
let sdk = Erc8183::new(provider).with_network(Network::MonadMainnet);
56+
let job = sdk.job()?;
57+
58+
// Create a job (caller becomes client)
59+
let params = CreateJobParams::new(
60+
provider_addr, // provider
61+
evaluator_addr, // evaluator
62+
U256::from(1_800_000_000u64), // expiredAt (Unix timestamp)
63+
"Analyze market data", // description
64+
);
65+
let job_id = job.create_job(&params).await?;
66+
67+
// Fund the escrow (requires ERC-20 approval first)
68+
job.set_budget(job_id, U256::from(1_000_000), None).await?;
69+
job.fund(job_id, U256::from(1_000_000), None).await?;
70+
71+
// Provider submits work
72+
job.submit(job_id, deliverable_hash, None).await?;
73+
74+
// Evaluator completes — releases payment to provider
75+
job.complete(job_id, FixedBytes::ZERO, None).await?;
76+
77+
// Query job state
78+
let data = job.get_job(job_id).await?;
79+
println!("Status: {} (terminal: {})", data.status, data.status.is_terminal());
6080
```
6181

62-
The full API mirrors the on-chain functions: `set_provider`, `set_budget`, `reject`, `claim_refund`, plus view/admin methods like `total_jobs`, `payment_token`, `set_platform_fee`, etc.
63-
6482
### Three-Layer Bindings
6583

6684
| Layer | Binding | Scope |
6785
| --- | --- | --- |
68-
| **Standard** | [`IERC8183`](erc8183/src/contracts.rs) | Spec-mandated lifecycle functions and events. Works with **any** ERC-8183 contract. |
69-
| **Implementation** | [`AgenticCommerce`](erc8183/src/contracts.rs) | Full QNTX ABI: `Job` struct, custom errors, admin functions, view getters, `Ownable2Step`. |
70-
| **Hook** | [`IACPHook`](erc8183/src/contracts.rs) | Normative hook interface. `beforeAction` / `afterAction` callbacks. |
86+
| **Standard** | `IERC8183` | Spec-mandated lifecycle functions and events. Works with **any** ERC-8183 contract. |
87+
| **Hook** | `IACPHook` | Normative hook interface. `beforeAction` / `afterAction` callbacks. |
88+
| **Implementation** | `AgenticCommerce` | Full QNTX ABI: `Job` struct, custom errors, admin functions, view getters, `Ownable2Step`. |
7189

7290
Core lifecycle operations (`create_job`, `fund`, `submit`, `complete`, `reject`, `claim_refund`) are sent through `IERC8183` for portability. View and admin operations use `AgenticCommerce`.
7391

@@ -129,16 +147,6 @@ use erc8183::hooks;
129147
assert_eq!(hooks::SEL_FUND, erc8183::contracts::IERC8183::fundCall::SELECTOR.into());
130148
```
131149

132-
## Design Principles
133-
134-
| Principle | Detail |
135-
| --- | --- |
136-
| **Zero `async_trait`** | Pure RPITIT — no trait-object overhead, no heap allocation per call |
137-
| **Inline `sol!` bindings** | Preserves struct names and visibility; no JSON ABI files to manage |
138-
| **Provider-generic** | Works with any alloy transport (HTTP, WebSocket, IPC) and signer |
139-
| **Layered bindings** | `IERC8183` for portability, `AgenticCommerce` for full access |
140-
| **Strict linting** | `clippy::pedantic` + `nursery` + `correctness` (deny) |
141-
142150
## Related Standards
143151

144152
| Standard | Relationship |

erc8183/examples/basic.rs

Lines changed: 0 additions & 59 deletions
This file was deleted.

erc8183/examples/lifecycle.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//! Complete lifecycle on Monad Mainnet: `Open → Funded → Submitted → Completed`.
2+
//!
3+
//! ```sh
4+
//! export RPC_URL=https://rpc.monad.xyz
5+
//! export CLIENT_KEY=0xac0974...
6+
//! export PROVIDER_KEY=0x59c699...
7+
//! export EVALUATOR_KEY=0x5de411...
8+
//! cargo run --example lifecycle
9+
//! ```
10+
11+
use std::env;
12+
13+
use alloy::{
14+
network::EthereumWallet,
15+
primitives::{Address, FixedBytes, U256},
16+
providers::{Provider, ProviderBuilder},
17+
signers::local::PrivateKeySigner,
18+
};
19+
use erc8183::{Erc8183, Network, job::JobHandle, types::CreateJobParams};
20+
21+
fn connect(
22+
rpc: &str,
23+
key_env: &str,
24+
) -> Result<(Erc8183<impl Provider>, Address), Box<dyn std::error::Error>> {
25+
let signer: PrivateKeySigner = env::var(key_env)?.parse()?;
26+
let addr = signer.address();
27+
let sdk = Erc8183::new(
28+
ProviderBuilder::new()
29+
.wallet(EthereumWallet::from(signer))
30+
.connect_http(rpc.parse()?),
31+
)
32+
.with_network(Network::MonadMainnet);
33+
Ok((sdk, addr))
34+
}
35+
36+
/// Client: create job → set budget → fund escrow.
37+
async fn client_create_and_fund<P: Provider>(
38+
job: &JobHandle<P>,
39+
provider: Address,
40+
evaluator: Address,
41+
) -> erc8183::Result<U256> {
42+
let params = CreateJobParams::new(
43+
provider,
44+
evaluator,
45+
U256::from(u64::MAX),
46+
"Analyze market data",
47+
);
48+
let id = job.create_job(&params).await?;
49+
let budget = U256::from(1_000_000u64);
50+
job.set_budget(id, budget, None).await?;
51+
job.fund(id, budget, None).await?;
52+
Ok(id)
53+
}
54+
55+
/// Provider: submit work deliverable.
56+
async fn provider_submit<P: Provider>(job: &JobHandle<P>, id: U256) -> erc8183::Result<()> {
57+
job.submit(id, FixedBytes::from([0xAB; 32]), None).await
58+
}
59+
60+
/// Evaluator: attest completion → release escrow.
61+
async fn evaluator_complete<P: Provider>(job: &JobHandle<P>, id: U256) -> erc8183::Result<()> {
62+
job.complete(id, FixedBytes::ZERO, None).await
63+
}
64+
65+
#[allow(clippy::print_stdout)]
66+
#[tokio::main]
67+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
68+
let rpc = env::var("RPC_URL")?;
69+
70+
let (client_sdk, _) = connect(&rpc, "CLIENT_KEY")?;
71+
let (provider_sdk, provider_addr) = connect(&rpc, "PROVIDER_KEY")?;
72+
let (evaluator_sdk, evaluator_addr) = connect(&rpc, "EVALUATOR_KEY")?;
73+
74+
let client = client_sdk.job()?;
75+
let provider = provider_sdk.job()?;
76+
let evaluator = evaluator_sdk.job()?;
77+
78+
// Open → Funded → Submitted → Completed
79+
let id = client_create_and_fund(&client, provider_addr, evaluator_addr).await?;
80+
println!("[Client] Created & funded job #{id}");
81+
82+
provider_submit(&provider, id).await?;
83+
println!("[Provider] Submitted deliverable");
84+
85+
evaluator_complete(&evaluator, id).await?;
86+
println!("[Evaluator] Completed — payment released");
87+
88+
let job = client.get_job(id).await?;
89+
println!(
90+
"\nJob #{}: {} (terminal: {})",
91+
job.id,
92+
job.status,
93+
job.status.is_terminal()
94+
);
95+
96+
Ok(())
97+
}

erc8183/src/client.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
//! ```rust,no_run
66
//! use alloy::primitives::U256;
77
//! use alloy::providers::ProviderBuilder;
8-
//! use erc8183::Erc8183;
8+
//! use erc8183::{Erc8183, Network};
99
//!
1010
//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1111
//! let provider = ProviderBuilder::new()
12-
//! .connect_http("https://eth.llamarpc.com".parse()?);
12+
//! .connect_http("https://monad-rpc.example.com".parse()?);
13+
//!
14+
//! // Built-in network (Monad Mainnet)
1315
//! let client = Erc8183::new(provider)
14-
//! .with_address("0x1234...".parse()?);
16+
//! .with_network(Network::MonadMainnet);
1517
//!
1618
//! let job = client.job()?.get_job(U256::from(1)).await?;
1719
//! # Ok(())
@@ -23,7 +25,7 @@ use alloy::{primitives::Address, providers::Provider};
2325
use crate::{
2426
error::{Error, Result},
2527
job::JobHandle,
26-
networks::{Network, NetworkAddress},
28+
networks::Network,
2729
};
2830

2931
/// The main client for interacting with the ERC-8183 Agentic Commerce Protocol.
@@ -35,19 +37,34 @@ use crate::{
3537
///
3638
/// # Examples
3739
///
40+
/// Built-in network:
41+
///
3842
/// ```rust,no_run
39-
/// use alloy::primitives::U256;
4043
/// use alloy::providers::ProviderBuilder;
41-
/// use erc8183::Erc8183;
44+
/// use erc8183::{Erc8183, Network};
4245
///
4346
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
44-
/// let provider = ProviderBuilder::new()
45-
/// .connect_http("https://eth.llamarpc.com".parse()?);
47+
/// let client = Erc8183::new(
48+
/// ProviderBuilder::new()
49+
/// .connect_http("https://monad-rpc.example.com".parse()?),
50+
/// )
51+
/// .with_network(Network::MonadMainnet);
52+
/// # Ok(())
53+
/// # }
54+
/// ```
4655
///
47-
/// let client = Erc8183::new(provider)
48-
/// .with_address("0x1234...".parse()?);
56+
/// Custom deployment:
57+
///
58+
/// ```rust,no_run
59+
/// use alloy::providers::ProviderBuilder;
60+
/// use erc8183::Erc8183;
4961
///
50-
/// let job = client.job()?.get_job(U256::from(1)).await?;
62+
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
63+
/// let client = Erc8183::new(
64+
/// ProviderBuilder::new()
65+
/// .connect_http("https://rpc.example.com".parse()?),
66+
/// )
67+
/// .with_address("0x1234...".parse()?);
5168
/// # Ok(())
5269
/// # }
5370
/// ```
@@ -74,19 +91,13 @@ impl<P: Provider> Erc8183<P> {
7491
/// Configure the contract address from a pre-defined [`Network`].
7592
#[must_use]
7693
pub const fn with_network(mut self, network: Network) -> Self {
77-
let addr = network.address();
78-
self.contract_address = Some(addr.agentic_commerce);
79-
self
80-
}
81-
82-
/// Configure the contract address from a [`NetworkAddress`] struct.
83-
#[must_use]
84-
pub const fn with_addresses(mut self, addr: NetworkAddress) -> Self {
85-
self.contract_address = Some(addr.agentic_commerce);
94+
self.contract_address = Some(network.address());
8695
self
8796
}
8897

89-
/// Set a custom Agentic Commerce contract address.
98+
/// Set a custom `AgenticCommerce` contract address.
99+
///
100+
/// Use this for deployments not yet listed in [`Network`].
90101
#[must_use]
91102
pub const fn with_address(mut self, address: Address) -> Self {
92103
self.contract_address = Some(address);

0 commit comments

Comments
 (0)