Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions contracts/waste-management/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "waste-management"
description = "Waste tracking and recycling management contract"
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
version.workspace = true

[package.metadata.stellar]
cargo_inherit = true

[lib]
crate-type = ["cdylib"]
doctest = false

[dependencies]
soroban-sdk.workspace = true

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
98 changes: 98 additions & 0 deletions contracts/waste-management/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Waste Management Smart Contract

A Soroban smart contract for tracking and managing waste recycling on the Stellar blockchain.

## Overview

This contract provides a robust system for recording waste entries with comprehensive tracking capabilities including location data, ownership, and confirmation status.

## Waste Struct

The main `Waste` struct contains the following fields:

| Field | Type | Description |
|-------|------|-------------|
| `waste_id` | `u128` | Unique identifier for the waste entry |
| `waste_type` | `WasteType` | Type of waste (Plastic, Glass, Metal, etc.) |
| `weight` | `u128` | Weight of the waste in grams |
| `current_owner` | `Address` | Current owner of the waste |
| `latitude` | `i128` | Latitude coordinate (multiplied by 10^7 for precision) |
| `longitude` | `i128` | Longitude coordinate (multiplied by 10^7 for precision) |
| `recycled_timestamp` | `u64` | Timestamp when the waste was recycled |
| `is_active` | `bool` | Whether the waste entry is active |
| `is_confirmed` | `bool` | Whether the waste has been confirmed |
| `confirmer` | `Address` | Address of the confirmer |

## Waste Types

The contract supports the following waste types:

- `Plastic`
- `Glass`
- `Metal`
- `Paper`
- `Organic`
- `Electronic`
- `Hazardous`
- `Mixed`

## Builder Pattern

The contract implements a builder pattern for convenient waste creation:

```rust
let waste = WasteBuilder::new()
.waste_id(1)
.waste_type(WasteType::Plastic)
.weight(1000)
.current_owner(owner)
.latitude(404850000) // 40.4850000 * 10^7
.longitude(-740600000) // -74.0600000 * 10^7
.recycled_timestamp(timestamp)
.is_active(true)
.is_confirmed(false)
.confirmer(confirmer)
.build();
```

## Contract Methods

### `create_waste`

Creates a new waste entry with the specified parameters.

**Parameters:**
- `waste_id`: Unique identifier
- `waste_type`: Type of waste
- `weight`: Weight in grams
- `current_owner`: Owner address (requires auth)
- `latitude`: Latitude coordinate
- `longitude`: Longitude coordinate

**Returns:** `Waste` struct

## Location Coordinates

Latitude and longitude values are stored as `i128` integers multiplied by 10^7 for precision:
- Example: 40.4850000° → 404850000
- Example: -74.0600000° → -740600000

## Building

```bash
cargo build --manifest-path contracts/waste-management/Cargo.toml --release --target wasm32-unknown-unknown
```

## Testing

```bash
cargo test --manifest-path contracts/waste-management/Cargo.toml
```

## Acceptance Criteria

✅ Struct compiles and can be stored
✅ All fields are properly typed
✅ Builder pattern works correctly
✅ All tests pass
✅ Contract can be built for WASM deployment
44 changes: 44 additions & 0 deletions contracts/waste-management/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#![no_std]

mod waste;

pub use waste::{Waste, WasteBuilder, WasteType};

use soroban_sdk::{contract, contractimpl, Address, Env};

#[contract]
pub struct WasteManagement;

#[contractimpl]
impl WasteManagement {
/// Create a new waste entry
pub fn create_waste(
env: Env,
waste_id: u128,
waste_type: WasteType,
weight: u128,
current_owner: Address,
latitude: i128,
longitude: i128,
) -> Waste {
current_owner.require_auth();

let waste = WasteBuilder::new()
.waste_id(waste_id)
.waste_type(waste_type)
.weight(weight)
.current_owner(current_owner.clone())
.latitude(latitude)
.longitude(longitude)
.recycled_timestamp(env.ledger().timestamp())
.is_active(true)
.is_confirmed(false)
.confirmer(current_owner)
.build();

waste
}
}

#[cfg(test)]
mod test;
169 changes: 169 additions & 0 deletions contracts/waste-management/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#![cfg(test)]
extern crate std;

use crate::{Waste, WasteBuilder, WasteManagement, WasteManagementClient, WasteType};
use soroban_sdk::{testutils::Address as _, Address, Env};
use std::vec;

#[test]
fn test_waste_struct_creation() {
let env = Env::default();
let owner = Address::generate(&env);
let confirmer = Address::generate(&env);

let waste = Waste {
waste_id: 1,
waste_type: WasteType::Plastic,
weight: 1000,
current_owner: owner.clone(),
latitude: 404850000, // 40.4850000 * 10^7
longitude: -740600000, // -74.0600000 * 10^7
recycled_timestamp: 1234567890,
is_active: true,
is_confirmed: false,
confirmer: confirmer.clone(),
};

assert_eq!(waste.waste_id, 1);
assert_eq!(waste.waste_type, WasteType::Plastic);
assert_eq!(waste.weight, 1000);
assert_eq!(waste.current_owner, owner);
assert_eq!(waste.latitude, 404850000);
assert_eq!(waste.longitude, -740600000);
assert_eq!(waste.recycled_timestamp, 1234567890);
assert_eq!(waste.is_active, true);
assert_eq!(waste.is_confirmed, false);
assert_eq!(waste.confirmer, confirmer);
}

#[test]
fn test_waste_builder_pattern() {
let env = Env::default();
let owner = Address::generate(&env);
let confirmer = Address::generate(&env);

let waste = WasteBuilder::new()
.waste_id(42)
.waste_type(WasteType::Glass)
.weight(2500)
.current_owner(owner.clone())
.latitude(337700000) // 33.7700000 * 10^7
.longitude(-842800000) // -84.2800000 * 10^7
.recycled_timestamp(9876543210)
.is_active(true)
.is_confirmed(true)
.confirmer(confirmer.clone())
.build();

assert_eq!(waste.waste_id, 42);
assert_eq!(waste.waste_type, WasteType::Glass);
assert_eq!(waste.weight, 2500);
assert_eq!(waste.current_owner, owner);
assert_eq!(waste.latitude, 337700000);
assert_eq!(waste.longitude, -842800000);
assert_eq!(waste.recycled_timestamp, 9876543210);
assert_eq!(waste.is_active, true);
assert_eq!(waste.is_confirmed, true);
assert_eq!(waste.confirmer, confirmer);
}

#[test]
fn test_all_waste_types() {
let env = Env::default();
let owner = Address::generate(&env);

let waste_types = vec![
WasteType::Plastic,
WasteType::Glass,
WasteType::Metal,
WasteType::Paper,
WasteType::Organic,
WasteType::Electronic,
WasteType::Hazardous,
WasteType::Mixed,
];

for (i, waste_type) in waste_types.iter().enumerate() {
let waste = WasteBuilder::new()
.waste_id(i as u128)
.waste_type(waste_type.clone())
.weight(1000)
.current_owner(owner.clone())
.latitude(0)
.longitude(0)
.recycled_timestamp(0)
.is_active(true)
.is_confirmed(false)
.confirmer(owner.clone())
.build();

assert_eq!(waste.waste_type, *waste_type);
}
}

#[test]
fn test_contract_create_waste() {
let env = Env::default();
env.mock_all_auths();

let contract_id = env.register(WasteManagement, ());
let client = WasteManagementClient::new(&env, &contract_id);

let owner = Address::generate(&env);

let waste = client.create_waste(
&123,
&WasteType::Metal,
&5000,
&owner,
&515000000, // 51.5000000 * 10^7 (London)
&-1270000, // -0.1270000 * 10^7
);

assert_eq!(waste.waste_id, 123);
assert_eq!(waste.waste_type, WasteType::Metal);
assert_eq!(waste.weight, 5000);
assert_eq!(waste.current_owner, owner);
assert_eq!(waste.latitude, 515000000);
assert_eq!(waste.longitude, -1270000);
assert_eq!(waste.is_active, true);
assert_eq!(waste.is_confirmed, false);
}

#[test]
#[should_panic(expected = "waste_id is required")]
fn test_builder_missing_waste_id() {
let env = Env::default();
let owner = Address::generate(&env);

WasteBuilder::new()
.waste_type(WasteType::Plastic)
.weight(1000)
.current_owner(owner.clone())
.latitude(0)
.longitude(0)
.recycled_timestamp(0)
.is_active(true)
.is_confirmed(false)
.confirmer(owner)
.build();
}

#[test]
#[should_panic(expected = "waste_type is required")]
fn test_builder_missing_waste_type() {
let env = Env::default();
let owner = Address::generate(&env);

WasteBuilder::new()
.waste_id(1)
.weight(1000)
.current_owner(owner.clone())
.latitude(0)
.longitude(0)
.recycled_timestamp(0)
.is_active(true)
.is_confirmed(false)
.confirmer(owner)
.build();
}
Loading
Loading