What are Deals?
Deals are the foundation of the IOTA Repstation system. They represent agreements between two parties (Party A and Party B) that enable mutual rating after completion. Every rating in the system must be tied to a specific deal, ensuring accountability and preventing spam ratings.
Deal Lifecycle
Deals follow a strict three-state lifecycle:
PENDING
Deal is created by Party A but not yet accepted by Party B.
- Only Party A can modify or cancel
- No ratings allowed
- Party B can accept to move to ACTIVE
ACTIVE
Both parties have agreed to the deal terms.
- Deal is locked and cannot be modified
- No ratings allowed yet
- Either party can close the deal
CLOSED
Deal has been completed and closed.
- Both parties can now rate each other
- Deal cannot be reopened
- Ratings can be updated or revoked
Deal Components
Participants
Each deal has exactly two participants:
- Party A: The deal initiator (usually the service requester)
- Party B: The deal acceptor (usually the service provider)
Subject Reference
Every deal includes a subject reference that identifies what the deal is about:
interface SubjectReference {
type: string; // e.g., "NFT", "SERVICE", "ORDER"
id: string; // e.g., "nft-123", "order-456"
}
Common Subject Types:
NFT - Non-fungible token transactions
SERVICE - Service agreements
ORDER - Purchase orders
LOAN - Lending agreements
ESCROW - Escrow services
Deal Amount
The monetary value associated with the deal, stored as a string to handle large numbers:
// Examples
amount: "1000" // 1000 units
amount: "250000" // 250k units
amount: "0" // Free service
Creating Deals
Admin-Created Deals
Applications with admin capabilities can create deals directly:
const { deal_id } = await client.openDealAdmin({
signer: adminWallet,
admin_cap_id: adminCapability,
party_b: customerWallet,
subject_ref: {
type: "ORDER",
id: "ORD-2024-001"
},
amount: "15000"
});
Client-Created Deals
Users with client capabilities can create deals:
const { deal_id } = await client.openDealClient({
signer: userWallet,
client_cap_id: userCapability,
party_b: providerWallet,
subject_ref: {
type: "SERVICE",
id: "web-design-project"
},
amount: "50000"
});
Deal Acceptance
Only Party B can accept a pending deal:
// Party B accepts the deal
await client.acceptDeal({
signer: partyBWallet,
deal_id: dealId
});
Once a deal is accepted, it cannot be modified. Ensure all terms are agreed upon before acceptance.
Deal Completion
Either party can close an active deal:
// Close the deal when work is completed
await client.closeDeal({
signer: partyWallet, // Can be either party
deal_id: dealId
});
Deal States and Permissions
| State | Party A Can | Party B Can | Ratings Allowed |
|---|
| PENDING | Cancel, Close | Accept | ❌ |
| ACTIVE | Close | Close | ❌ |
| CLOSED | Rate Party B | Rate Party A | ✅ |
Best Practices
1. Clear Subject References
Use descriptive and unique subject references:
// Good
subject_ref: {
type: "NFT_MARKETPLACE_SALE",
id: "cryptopunk-7804-sale-2024"
}
// Avoid
subject_ref: {
type: "SALE",
id: "123"
}
2. Accurate Amounts
Always specify the correct deal amount:
// For a $150 service (assuming token has 2 decimals)
amount: "15000" // 150.00 in smallest units
3. Timely Deal Closure
Close deals promptly when work is completed to enable rating:
// In your application logic
async function markOrderDelivered(orderId: string) {
// Update your database
await db.orders.update(orderId, { status: 'delivered' });
// Close the reputation deal
const order = await db.orders.findById(orderId);
await reputationClient.closeDeal({
signer: adminWallet,
deal_id: order.reputationDealId
});
}
Error Handling
Common deal-related errors and solutions:
try {
await client.acceptDeal({ signer, deal_id });
} catch (error) {
switch (error.code) {
case 'DEAL_NOT_FOUND':
console.error('Deal ID does not exist');
break;
case 'NOT_PARTY_B':
console.error('Only Party B can accept this deal');
break;
case 'DEAL_ALREADY_ACCEPTED':
console.error('Deal is already in ACTIVE state');
break;
case 'DEAL_ALREADY_CLOSED':
console.error('Deal has been closed');
break;
}
}
Integration Patterns
E-commerce Integration
class OrderService {
async createOrder(buyerId: string, sellerId: string, productId: string, amount: number) {
// Create order in your system
const order = await this.db.orders.create({
buyerId, sellerId, productId, amount,
status: 'pending'
});
// Create reputation deal
const dealResult = await this.reputation.openDealAdmin({
signer: this.adminWallet,
admin_cap_id: this.adminCap,
party_b: buyerId,
subject_ref: {
type: 'ECOMMERCE_ORDER',
id: order.id
},
amount: amount.toString()
});
// Link deal to order
await this.db.orders.update(order.id, {
reputationDealId: dealResult.deal_id
});
return order;
}
}
class ServiceBooking {
async bookService(clientId: string, providerId: string, serviceType: string, fee: number) {
// Create booking
const booking = await this.createBooking({
clientId, providerId, serviceType, fee
});
// Auto-accept deal for service bookings
const dealResult = await this.reputation.openDealClient({
signer: clientId,
client_cap_id: await this.getClientCap(clientId),
party_b: providerId,
subject_ref: {
type: 'SERVICE_BOOKING',
id: booking.id
},
amount: fee.toString()
});
// Auto-accept if provider is online
if (await this.isProviderOnline(providerId)) {
await this.reputation.acceptDeal({
signer: providerId,
deal_id: dealResult.deal_id
});
}
return booking;
}
}
Next Steps