Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/access control and ownership verification in nft contract #155

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
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
74 changes: 74 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -1,3 +1,77 @@
# Next.js and Frontend
*.ts linguist-language=TypeScript
*.tsx linguist-language=TypeScript
*.js linguist-language=JavaScript
*.jsx linguist-language=JavaScript
*.css linguist-language=CSS

# Soroban Smart Contracts (Stellar)
*.rs linguist-language=Rust
Cargo.toml linguist-language=TOML
soroban-config.json linguist-language=JSON

# Build and Dependencies
target/ linguist-generated=true
target/ -diff
**/target/ linguist-generated=true
**/target/ -diff
.next/ linguist-generated=true
.next/ -diff
dist/ linguist-generated=true
dist/ -diff

# Soroban Build Artifacts
**/*.wasm linguist-generated=true
.soroban/ linguist-generated=true
.soroban/ -diff

# Documentation
*.md linguist-documentation
docs/ linguist-documentation
README.md linguist-documentation

# Test Files
*_test.rs linguist-language=Rust
*.test.ts linguist-language=TypeScript
*.test.tsx linguist-language=TypeScript
tests/ linguist-language=Rust
__tests__/ linguist-language=TypeScript

# IDE and Editor Files
.vscode/ linguist-vendored
.idea/ linguist-vendored
*.json linguist-language=JSON
.env.* linguist-language=Text

# Package Management
node_modules/ linguist-vendored
node_modules/ -diff
yarn.lock linguist-generated=true
package-lock.json linguist-generated=true

# Ignore binary files
*.wasm binary
*.jpg binary
*.png binary
*.gif binary
*.ico binary
*.pdf binary

# Configuration Files
*.config.js linguist-language=JavaScript
*.config.ts linguist-language=TypeScript
tailwind.config.js linguist-language=JavaScript
next.config.js linguist-language=JavaScript
postcss.config.js linguist-language=JavaScript

# Database
*.sql linguist-language=SQL
prisma/ linguist-language=Prisma
drizzle/ linguist-language=TypeScript

# Security
.env.example linguist-language=Text

You are an expert developer in TypeScript, Node.js, Next.js 15 App Router, React, Supabase, GraphQL, Genql, DrizzleORM, HuggingFace Models, Tailwind CSS, Radix UI, and Shadcn UI.

---
Expand Down
5 changes: 0 additions & 5 deletions apps/contract/.cargo/config.toml

This file was deleted.

14 changes: 7 additions & 7 deletions apps/contract/Cargo.lock

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

3 changes: 2 additions & 1 deletion apps/contract/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
members = ["contracts/auth-contracts/*", "contracts/nft"]
resolver = "2"
members = ["contracts/auth-contracts/*", "contracts/nft"]

[workspace.dependencies]
soroban-sdk = "22.0.6"
Expand All @@ -15,6 +15,7 @@ panic = "abort"
codegen-units = 1
lto = true

# For more information about this profile see https://soroban.stellar.org/docs/basic-tutorials/logging#cargotoml-profile
[profile.release-with-logs]
inherits = "release"
debug-assertions = true
6 changes: 4 additions & 2 deletions apps/contract/contracts/nft/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
[package]
name = "kindfi-nft"
version = "0.1.0"
name = "nft"
version = "0.0.0"
aguilar1x marked this conversation as resolved.
Show resolved Hide resolved
edition = "2021"
publish = false

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

[dependencies]
soroban-sdk = { workspace = true }
Expand Down
56 changes: 0 additions & 56 deletions apps/contract/contracts/nft/src/contract.rs

This file was deleted.

29 changes: 29 additions & 0 deletions apps/contract/contracts/nft/src/distribution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use soroban_sdk::{Address, Env, Symbol, symbol_short};
use crate::contract::NFTContract;
use crate::events::NFTEvents;

impl NFTContract {
pub fn transfer(env: Env, from: Address, to: Address, token_id: u32) {
// Verify ownership
let token_key = symbol_short!(&format!("TOKEN_{}", token_id));
let owner: Address = env.storage().instance().get(&token_key).unwrap_or_else(|| {
panic!("Token not found");
});
aguilar1x marked this conversation as resolved.
Show resolved Hide resolved

if owner != from {
panic!("Not token owner");
}

// Verify authorization
let sender = env.invoker();
if sender != from {
panic!("Not authorized");
}
aguilar1x marked this conversation as resolved.
Show resolved Hide resolved

// Update token ownership
env.storage().instance().set(&token_key, &to);

// Emit transfer event
NFTEvents::transfer(&env, &from, &to, token_id);
}
}
12 changes: 6 additions & 6 deletions apps/contract/contracts/nft/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use soroban_sdk::contracterror;

#[contracterror]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum NFTError {
AlreadyInitialized = 100,
NotAuthorized = 101,
TokenAlreadyExists = 102,
TokenNotFound = 103,
NotTokenOwner = 104,
AlreadyInitialized = 1,
NotAuthorized = 2,
TokenNotFound = 3,
NotTokenOwner = 4,
TokenAlreadyExists = 5,
}
8 changes: 5 additions & 3 deletions apps/contract/contracts/nft/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use soroban_sdk::{Address, Env};
use soroban_sdk::{Address, Env, symbol_short};

pub struct NFTEvents;

impl NFTEvents {
pub fn mint(env: &Env, to: &Address, token_id: u32) {
// Publish the MINT event
let topics = (symbol_short!("mint"), to);
env.events().publish(topics, token_id);
}

pub fn transfer(env: &Env, from: &Address, to: &Address, token_id: u32) {
// Publish the TRANSFER event
let topics = (symbol_short!("transfer"), from, to);
env.events().publish(topics, token_id);
}
}
102 changes: 98 additions & 4 deletions apps/contract/contracts/nft/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,105 @@
#![no_std]

mod contract;
mod errors;
mod events;
mod storage;
mod types;

pub use contract::NFTContract;
pub use errors::*;
pub use types::*;
use soroban_sdk::{
contract, contractimpl, Address, Env, String, Vec,
};

use crate::{errors::NFTError, storage::NFTStorage, types::*, events::NFTEvents};

#[contract]
pub struct NFTContract;

#[contractimpl]
impl NFTContract {
pub fn initialize(env: Env, admin: Address) -> Result<(), NFTError> {
if env.storage().instance().has(&ADMIN_KEY) {
return Err(NFTError::AlreadyInitialized);
}
NFTStorage::set_admin(&env, &admin);
env.storage().instance().set(&COUNTER_KEY, &0u32);
Ok(())
}

pub fn mint(
env: Env,
to: Address,
name: String,
description: String,
attributes: Vec<String>,
) -> Result<(), NFTError> {
// Verify admin access
let admin = NFTStorage::get_admin(&env);
admin.require_auth();

// Get and increment token counter
let token_id: u32 = env.storage().instance().get(&COUNTER_KEY).unwrap();
env.storage().instance().set(&COUNTER_KEY, &(token_id + 1));

// Create metadata
let metadata = NFTMetadata {
name,
description,
attributes,
};

// Store token data
NFTStorage::set_token_owner(&env, &token_id, &to);
NFTStorage::set_token_metadata(&env, &token_id, &metadata);

// Increment balance
NFTStorage::increment_balance(&env, &to);

// Emit mint event
NFTEvents::mint(&env, &to, token_id);

Ok(())
}

pub fn transfer(
env: Env,
from: Address,
to: Address,
token_id: u32,
) -> Result<(), NFTError> {
// Verify ownership
let token_owner = NFTStorage::get_token_owner(&env, &token_id)
.ok_or(NFTError::TokenNotFound)?;

if token_owner != from {
return Err(NFTError::NotTokenOwner);
}

// Verify authorization
from.require_auth();

// Update token ownership
NFTStorage::set_token_owner(&env, &token_id, &to);

// Update balances
NFTStorage::decrement_balance(&env, &from);
NFTStorage::increment_balance(&env, &to);

// Emit transfer event
NFTEvents::transfer(&env, &from, &to, token_id);

Ok(())
}

pub fn token_metadata(env: Env, token_id: u32) -> NFTDetail {
let owner = NFTStorage::get_token_owner(&env, &token_id)
.unwrap_or_else(|| panic!("Token not found"));

let metadata = NFTStorage::get_token_metadata(&env, &token_id)
.unwrap_or_else(|| panic!("Metadata not found"));

NFTDetail { owner, metadata }
}
}

#[cfg(test)]
mod test;
Loading
Loading