Build and deploy smart contracts for autonomous commerce operations on the StateSet Network
curl -L https://stateset.network/install | sh
)Install Development Tools
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install cargo-generate for templates
cargo install cargo-generate
# Install StateSet CLI
curl -L https://stateset.network/install | sh
# Verify installation
stateset version
Create Your First Contract
# Generate from template
cargo generate --git https://github.com/stateset/contract-template
cd my-first-contract
# Build the contract
cargo wasm
# Run tests
cargo test
Deploy to Testnet
# Store the contract code
stateset tx wasm store target/wasm32-unknown-unknown/release/my_contract.wasm \
--from wallet \
--chain-id stateset-testnet \
--gas auto \
--gas-adjustment 1.3
# Instantiate the contract
stateset tx wasm instantiate CODE_ID '{"admin":"stateset1..."}' \
--from wallet \
--label "my-contract" \
--chain-id stateset-testnet
use cosmwasm_std::{
entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
};
use cw2::set_contract_version;
const CONTRACT_NAME: &str = "stateset:order-manager";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Order {
pub id: String,
pub buyer: Addr,
pub seller: Addr,
pub items: Vec<OrderItem>,
pub total: Uint128,
pub status: OrderStatus,
pub payment_status: PaymentStatus,
pub created_at: Timestamp,
pub fulfilled_at: Option<Timestamp>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum OrderStatus {
Pending,
Confirmed,
Processing,
Shipped,
Delivered,
Cancelled,
Refunded,
}
#[entry_point]
pub fn execute(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::CreateOrder { items, shipping } => {
create_order(deps, env, info, items, shipping)
}
ExecuteMsg::ConfirmPayment { order_id } => {
confirm_payment(deps, env, info, order_id)
}
ExecuteMsg::ShipOrder { order_id, tracking } => {
ship_order(deps, env, info, order_id, tracking)
}
ExecuteMsg::ConfirmDelivery { order_id } => {
confirm_delivery(deps, env, info, order_id)
}
ExecuteMsg::InitiateRefund { order_id, reason } => {
initiate_refund(deps, env, info, order_id, reason)
}
}
}
fn create_order(
deps: DepsMut,
env: Env,
info: MessageInfo,
items: Vec<OrderItem>,
shipping: ShippingInfo,
) -> Result<Response, ContractError> {
// Validate items and calculate total
let total = calculate_order_total(&items)?;
// Generate unique order ID
let order_id = generate_order_id(&env, &info.sender)?;
// Create order object
let order = Order {
id: order_id.clone(),
buyer: info.sender.clone(),
seller: SELLER.load(deps.storage)?,
items,
total,
status: OrderStatus::Pending,
payment_status: PaymentStatus::Pending,
created_at: env.block.time,
fulfilled_at: None,
};
// Store order
ORDERS.save(deps.storage, &order_id, &order)?;
// Emit event
Ok(Response::new()
.add_attribute("action", "create_order")
.add_attribute("order_id", order_id)
.add_attribute("buyer", info.sender)
.add_attribute("total", total))
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct SupplyChainEvent {
pub product_id: String,
pub event_type: EventType,
pub location: String,
pub handler: Addr,
pub timestamp: Timestamp,
pub temperature: Option<i32>,
pub humidity: Option<u32>,
pub notes: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum EventType {
Manufactured,
QualityChecked,
Packaged,
ShippedFromFactory,
ArrivedAtWarehouse,
ShippedToRetailer,
ReceivedByRetailer,
SoldToCustomer,
}
pub fn track_product(
deps: DepsMut,
env: Env,
info: MessageInfo,
product_id: String,
event_type: EventType,
location: String,
metadata: Option<EventMetadata>,
) -> Result<Response, ContractError> {
// Verify handler authorization
verify_handler_authorization(&deps, &info.sender, &event_type)?;
// Create supply chain event
let event = SupplyChainEvent {
product_id: product_id.clone(),
event_type: event_type.clone(),
location,
handler: info.sender.clone(),
timestamp: env.block.time,
temperature: metadata.as_ref().and_then(|m| m.temperature),
humidity: metadata.as_ref().and_then(|m| m.humidity),
notes: metadata.and_then(|m| m.notes),
};
// Store event in chain
let mut product_history = PRODUCT_HISTORY
.load(deps.storage, &product_id)
.unwrap_or_default();
product_history.push(event.clone());
PRODUCT_HISTORY.save(deps.storage, &product_id, &product_history)?;
// Update product status
update_product_status(deps.storage, &product_id, &event_type)?;
Ok(Response::new()
.add_attribute("action", "track_product")
.add_attribute("product_id", product_id)
.add_attribute("event_type", format!("{:?}", event_type))
.add_attribute("handler", info.sender))
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct LetterOfCredit {
pub id: String,
pub importer: Addr,
pub exporter: Addr,
pub issuing_bank: Addr,
pub advising_bank: Addr,
pub amount: Coin,
pub documents_required: Vec<RequiredDocument>,
pub expiry_date: Timestamp,
pub status: LCStatus,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub enum LCStatus {
Issued,
Confirmed,
DocumentsSubmitted,
DocumentsApproved,
PaymentReleased,
Completed,
Expired,
}
pub fn submit_documents(
deps: DepsMut,
env: Env,
info: MessageInfo,
lc_id: String,
documents: Vec<Document>,
) -> Result<Response, ContractError> {
let mut lc = LETTERS_OF_CREDIT.load(deps.storage, &lc_id)?;
// Verify submitter is the exporter
if info.sender != lc.exporter {
return Err(ContractError::Unauthorized {});
}
// Verify LC is in correct status
if lc.status != LCStatus::Confirmed {
return Err(ContractError::InvalidStatus {});
}
// Validate all required documents are submitted
validate_documents(&lc.documents_required, &documents)?;
// Store documents
SUBMITTED_DOCUMENTS.save(deps.storage, &lc_id, &documents)?;
// Update LC status
lc.status = LCStatus::DocumentsSubmitted;
LETTERS_OF_CREDIT.save(deps.storage, &lc_id, &lc)?;
Ok(Response::new()
.add_attribute("action", "submit_documents")
.add_attribute("lc_id", lc_id)
.add_attribute("exporter", info.sender))
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InventoryItem {
pub sku: String,
pub name: String,
pub quantity: u64,
pub reserved: u64,
pub reorder_point: u64,
pub reorder_quantity: u64,
pub supplier: Addr,
pub last_restocked: Timestamp,
}
pub fn update_inventory(
deps: DepsMut,
env: Env,
info: MessageInfo,
updates: Vec<InventoryUpdate>,
) -> Result<Response, ContractError> {
let mut messages = vec![];
let mut attributes = vec![("action", "update_inventory")];
for update in updates {
let mut item = INVENTORY.load(deps.storage, &update.sku)?;
match update.operation {
Operation::Add => {
item.quantity = item.quantity.saturating_add(update.quantity);
item.last_restocked = env.block.time;
}
Operation::Remove => {
if item.quantity < update.quantity {
return Err(ContractError::InsufficientInventory {
sku: update.sku,
available: item.quantity,
requested: update.quantity,
});
}
item.quantity = item.quantity.saturating_sub(update.quantity);
}
Operation::Reserve => {
let available = item.quantity.saturating_sub(item.reserved);
if available < update.quantity {
return Err(ContractError::InsufficientInventory {
sku: update.sku,
available,
requested: update.quantity,
});
}
item.reserved = item.reserved.saturating_add(update.quantity);
}
}
// Check if reorder needed
if item.quantity <= item.reorder_point && item.quantity > 0 {
let reorder_msg = create_reorder_message(
&item.supplier,
&update.sku,
item.reorder_quantity,
)?;
messages.push(reorder_msg);
attributes.push(("reorder_triggered", &update.sku));
}
INVENTORY.save(deps.storage, &update.sku, &item)?;
}
Ok(Response::new()
.add_messages(messages)
.add_attributes(attributes))
}
// Call another contract
pub fn execute_cross_contract_order(
deps: DepsMut,
env: Env,
info: MessageInfo,
order_details: OrderDetails,
) -> Result<Response, ContractError> {
// Prepare order for order management contract
let order_msg = WasmMsg::Execute {
contract_addr: ORDER_CONTRACT.load(deps.storage)?.to_string(),
msg: to_binary(&OrderExecuteMsg::CreateOrder {
items: order_details.items,
shipping: order_details.shipping,
})?,
funds: info.funds,
};
// Update inventory contract
let inventory_msg = WasmMsg::Execute {
contract_addr: INVENTORY_CONTRACT.load(deps.storage)?.to_string(),
msg: to_binary(&InventoryExecuteMsg::ReserveItems {
items: order_details.items.clone(),
})?,
funds: vec![],
};
Ok(Response::new()
.add_message(order_msg)
.add_message(inventory_msg)
.add_attribute("action", "cross_contract_order"))
}
// Emit custom events
pub fn emit_order_event(
order_id: String,
event_type: OrderEventType,
) -> Event {
Event::new("order_event")
.add_attribute("order_id", order_id)
.add_attribute("event_type", format!("{:?}", event_type))
.add_attribute("timestamp", Timestamp::now().to_string())
}
// Subscribe to events in another contract
pub fn handle_order_event(
deps: DepsMut,
env: Env,
event: OrderEvent,
) -> Result<Response, ContractError> {
match event.event_type {
OrderEventType::PaymentConfirmed => {
// Trigger fulfillment workflow
start_fulfillment_process(deps, env, event.order_id)?
}
OrderEventType::Shipped => {
// Update tracking system
update_tracking_info(deps, env, event.order_id, event.tracking_info)?
}
OrderEventType::Delivered => {
// Release payment to seller
release_payment(deps, env, event.order_id)?
}
}
Ok(Response::new())
}
// Migration entry point
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(
deps: DepsMut,
_env: Env,
msg: MigrateMsg,
) -> Result<Response, ContractError> {
let ver = cw2::get_contract_version(deps.storage)?;
// Ensure we are migrating from an allowed version
if ver.contract != CONTRACT_NAME {
return Err(ContractError::CannotMigrate {
previous_contract: ver.contract,
});
}
if ver.version > CONTRACT_VERSION {
return Err(ContractError::CannotMigrateVersion {
previous_version: ver.version,
new_version: CONTRACT_VERSION.to_string(),
});
}
// Perform migration logic
match msg {
MigrateMsg::UpdateConfig { new_config } => {
CONFIG.save(deps.storage, &new_config)?;
}
MigrateMsg::AddFeature { feature } => {
enable_new_feature(deps.storage, feature)?;
}
}
// Update contract version
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
Ok(Response::new()
.add_attribute("action", "migrate")
.add_attribute("version", CONTRACT_VERSION))
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
#[test]
fn test_create_order() {
let mut deps = mock_dependencies();
let env = mock_env();
let info = mock_info("buyer", &coins(1000, "ustate"));
// Initialize contract
let msg = InstantiateMsg { admin: None };
let _res = instantiate(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
// Create order
let create_msg = ExecuteMsg::CreateOrder {
items: vec![
OrderItem {
sku: "PROD-001".to_string(),
quantity: 2,
price: Uint128::new(100),
}
],
shipping: ShippingInfo {
address: "123 Main St".to_string(),
method: ShippingMethod::Standard,
},
};
let res = execute(deps.as_mut(), env, info, create_msg).unwrap();
// Verify response
assert_eq!(res.attributes.len(), 4);
assert_eq!(res.attributes[0].key, "action");
assert_eq!(res.attributes[0].value, "create_order");
}
}
use stateset_integration_tests::{Contract, ContractWrapper};
#[test]
fn test_order_fulfillment_flow() {
let mut app = mock_app();
// Deploy contracts
let order_contract = deploy_order_contract(&mut app);
let inventory_contract = deploy_inventory_contract(&mut app);
let payment_contract = deploy_payment_contract(&mut app);
// Test complete order flow
let order_id = create_test_order(&mut app, &order_contract);
confirm_payment(&mut app, &payment_contract, &order_id);
verify_inventory_reserved(&mut app, &inventory_contract, &order_id);
ship_order(&mut app, &order_contract, &order_id);
confirm_delivery(&mut app, &order_contract, &order_id);
verify_payment_released(&mut app, &payment_contract, &order_id);
}
# Query contract state
stateset query wasm contract-state smart CONTRACT_ADDR \
'{"get_order":{"order_id":"ORD-12345"}}' \
--chain-id stateset-mainnet
# Monitor contract events
stateset query txs --events 'wasm.contract_address=CONTRACT_ADDR' \
--chain-id stateset-mainnet
# Check contract metrics
stateset query wasm contract-state smart CONTRACT_ADDR \
'{"get_metrics":{}}' \
--chain-id stateset-mainnet