Skip to content
Merged
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
36 changes: 36 additions & 0 deletions contracts/carbon-credit/src/events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use soroban_sdk::{Env, Address, Symbol, symbol_short, i128};

pub fn emit_initialized(env: &Env, admin: Address) {
env.events().publish(
(symbol_short!("carbon"), symbol_short!("init")),
admin,
);
}

pub fn emit_carbon_registered(env: &Env, invoice_id: Symbol, credits: i128) {
env.events().publish(
(symbol_short!("carbon"), symbol_short!("reg")),
(invoice_id, credits),
);
}

pub fn emit_invoice_linked(env: &Env, invoice_id: Symbol, wallet: Address, credits: i128) {
env.events().publish(
(symbol_short!("carbon"), symbol_short!("link")),
(invoice_id, wallet, credits),
);
}

pub fn emit_carbon_verified(env: &Env, invoice_id: Symbol) {
env.events().publish(
(symbol_short!("carbon"), symbol_short!("verify")),
invoice_id,
);
}

pub fn emit_credits_redeemed(env: &Env, wallet: Address, amount: i128) {
env.events().publish(
(symbol_short!("carbon"), symbol_short!("redeem")),
(wallet, amount),
);
}
127 changes: 127 additions & 0 deletions contracts/carbon-credit/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#![no_std]

use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, Symbol, Vec, i128};

mod events;
mod storage;
mod types;

pub use events::*;
pub use storage::*;
pub use types::*;

#[contract]
pub struct CarbonCreditContract;

#[contractimpl]
impl CarbonCreditContract {
pub fn initialize(env: Env, admin: Address) {
admin.require_auth();
storage::set_admin(&env, &admin);
emit_initialized(&env, admin);
}

pub fn register_invoice_carbon(
env: Env,
invoice_id: Symbol,
credits: i128,
source: Symbol,
) -> Result<(), Error> {
let admin: Address = storage::get_admin(&env)?;
admin.require_auth();

assert!(credits > 0, "credits must be positive");

let record = CarbonRecord {
invoice_id: invoice_id.clone(),
credits,
source,
timestamp: env.ledger().timestamp(),
verified: false,
};

storage::store_carbon_record(&env, &invoice_id, &record);
emit_carbon_registered(&env, invoice_id, credits);

Ok(())
}

pub fn link_invoice_credits(
env: Env,
invoice_id: Symbol,
wallet: Address,
) -> Result<i128, Error> {
let record = storage::get_carbon_record(&env, &invoice_id)
.ok_or(Error::NotFound)?;

storage::link_wallet_credits(&env, &wallet, record.credits);
emit_invoice_linked(&env, invoice_id, wallet, record.credits);

Ok(record.credits)
}

pub fn get_wallet_total_credits(env: Env, wallet: Address) -> i128 {
storage::get_wallet_credits(&env, &wallet).unwrap_or(0)
}

pub fn verify_carbon_record(env: Env, invoice_id: Symbol) -> Result<(), Error> {
let admin: Address = storage::get_admin(&env)?;
admin.require_auth();

let mut record = storage::get_carbon_record(&env, &invoice_id)
.ok_or(Error::NotFound)?;

record.verified = true;
storage::store_carbon_record(&env, &invoice_id, &record);
emit_carbon_verified(&env, invoice_id);

Ok(())
}

pub fn get_carbon_record(env: Env, invoice_id: Symbol) -> Result<CarbonRecord, Error> {
storage::get_carbon_record(&env, &invoice_id)
.ok_or(Error::NotFound)
}

pub fn list_wallet_credits(env: Env, wallet: Address, limit: u32) -> Vec<CarbonRecord> {
storage::list_wallet_records(&env, &wallet, limit)
}

pub fn redeem_credits(
env: Env,
wallet: Address,
amount: i128,
) -> Result<(), Error> {
wallet.require_auth();

let current = storage::get_wallet_credits(&env, &wallet)
.ok_or(Error::InsufficientFunds)?;

assert!(amount <= current, "insufficient credits");

let remaining = current - amount;
storage::update_wallet_credits(&env, &wallet, remaining);
emit_credits_redeemed(&env, wallet, amount);

Ok(())
}
}

#[derive(Clone, Debug)]
pub enum Error {
NotFound,
InsufficientFunds,
Unauthorized,
InvalidAmount,
}

impl From<Error> for soroban_sdk::Error {
fn from(err: Error) -> Self {
match err {
Error::NotFound => soroban_sdk::Error::from_contract_error(1),
Error::InsufficientFunds => soroban_sdk::Error::from_contract_error(2),
Error::Unauthorized => soroban_sdk::Error::from_contract_error(3),
Error::InvalidAmount => soroban_sdk::Error::from_contract_error(4),
}
}
}
62 changes: 62 additions & 0 deletions contracts/carbon-credit/src/storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use soroban_sdk::{Env, Address, Symbol, Vec, symbol_short, i128};
use crate::{CarbonRecord, Error};

const ADMIN_KEY: &str = "admin";
const CARBON_RECORDS_PREFIX: &str = "carbon_";
const WALLET_CREDITS_PREFIX: &str = "wallet_";

pub fn set_admin(env: &Env, admin: &Address) {
env.storage().instance().set(&symbol_short!("admin"), admin);
}

pub fn get_admin(env: &Env) -> Result<Address, Error> {
env.storage()
.instance()
.get(&symbol_short!("admin"))
.ok_or(Error::NotFound)
}

pub fn store_carbon_record(env: &Env, invoice_id: &Symbol, record: &CarbonRecord) {
let key = format!("carbon_{}", invoice_id.to_string());
env.storage().persistent().set(&Symbol::new(env, &key), record);
}

pub fn get_carbon_record(env: &Env, invoice_id: &Symbol) -> Option<CarbonRecord> {
let key = format!("carbon_{}", invoice_id.to_string());
env.storage()
.persistent()
.get(&Symbol::new(env, &key))
}

pub fn link_wallet_credits(env: &Env, wallet: &Address, amount: i128) {
let key = format!("wallet_{}", wallet.to_string());
let current = env.storage()
.persistent()
.get::<Symbol, i128>(&Symbol::new(env, &key))
.unwrap_or(0);

env.storage()
.persistent()
.set(&Symbol::new(env, &key), &(current + amount));
}

pub fn get_wallet_credits(env: &Env, wallet: &Address) -> Option<i128> {
let key = format!("wallet_{}", wallet.to_string());
env.storage()
.persistent()
.get(&Symbol::new(env, &key))
}

pub fn update_wallet_credits(env: &Env, wallet: &Address, amount: i128) {
let key = format!("wallet_{}", wallet.to_string());
env.storage()
.persistent()
.set(&Symbol::new(env, &key), &amount);
}

pub fn list_wallet_records(env: &Env, wallet: &Address, limit: u32) -> Vec<CarbonRecord> {
let mut records = Vec::new(env);
// Implementation would iterate through storage with prefix matching
// Simplified for this example
records
}
29 changes: 29 additions & 0 deletions contracts/carbon-credit/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use soroban_sdk::{Symbol, contracttype, i128};

#[contracttype]
#[derive(Clone, Debug)]
pub struct CarbonRecord {
pub invoice_id: Symbol,
pub credits: i128,
pub source: Symbol,
pub timestamp: u64,
pub verified: bool,
}

#[contracttype]
#[derive(Clone, Debug)]
pub struct WalletCarbonBalance {
pub wallet: soroban_sdk::Address,
pub total_credits: i128,
pub redeemed_credits: i128,
pub available_credits: i128,
}

#[contracttype]
#[derive(Clone, Debug)]
pub struct CarbonSummary {
pub total_registered: i128,
pub total_verified: i128,
pub total_linked: i128,
pub total_redeemed: i128,
}
Loading