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/nft example #91

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4849202
Update dev_dependencies -> dev-dependencies in examples
elizabethengelman Jun 26, 2024
8e6d9c9
Add NFT example
elizabethengelman Jun 26, 2024
9cf14ea
Fix build errors
elizabethengelman Jun 27, 2024
e1404c3
Change NFT ids to u32s
elizabethengelman Jun 27, 2024
4f31132
Refactor so that the nft id is generated in mint
elizabethengelman Jun 27, 2024
e647a55
Formatting & clippy
elizabethengelman Jun 28, 2024
a1fb4ff
Fully qualify soroban_sdk types in subcontract traits
elizabethengelman Jul 1, 2024
aabcdfa
Panic on transfer if the NFT id does not exist
elizabethengelman Jul 2, 2024
2c7fefd
Fix owner_id check
elizabethengelman Jul 2, 2024
1bad366
Update examples/soroban/nft/src/nft.rs
elizabethengelman Jul 2, 2024
432c914
Cargo fmt updates
elizabethengelman Jul 2, 2024
abe69e2
Make ft_init fn accept &self instead of &mut self
elizabethengelman Jul 2, 2024
765fbf3
Update examples/soroban/nft/src/nft.rs
elizabethengelman Jul 3, 2024
1aa5b11
Fix owners_to_nft_ids behavior 🙈
elizabethengelman Jul 3, 2024
b46b4c5
Move nft example to loam-cli dir
elizabethengelman Jul 15, 2024
b18d6d6
Use derive_contract for example nft
elizabethengelman Jul 15, 2024
959952a
Refactor owners_to_nft_ids
elizabethengelman Jul 15, 2024
6e41c99
build: 'loam build' changes Cargo.lock
chadoh Jul 15, 2024
95d2161
fix: use doc comments for subcontracts
chadoh Jul 15, 2024
ba7af15
Remove extraneous comments in nft.rs
elizabethengelman Jul 15, 2024
ccaa9ae
Remove loam-soroban-sdk from Cargo.toml as it is re-exported from loa…
elizabethengelman Jul 15, 2024
d3acb1a
Require auth from the admin for minting
elizabethengelman Jul 16, 2024
cc22d8b
Bump total count limit
elizabethengelman Jul 16, 2024
553f401
Remove admin attribute from MyNonFungibleToken
elizabethengelman Jul 19, 2024
ec7df81
WIP: add happy path tests for nft example
elizabethengelman Jul 22, 2024
7e7cfbf
Apply suggestions from code review
elizabethengelman Jul 24, 2024
5655fd8
Cleanup happy path test
elizabethengelman Jul 26, 2024
3d83b38
Add tests for error cases
elizabethengelman Jul 26, 2024
3a6507c
Ensure that panicks are happening for the right reasons
elizabethengelman Jul 26, 2024
63ed1f4
Factor out some commonly use fns in nft test
elizabethengelman Jul 26, 2024
dc9ed7c
Add Metadata type for NFT example
elizabethengelman Jul 29, 2024
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
8 changes: 8 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/loam-cli/examples/soroban/calculator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ loam-sdk = { workspace = true, features = ["loam-soroban-sdk"] }
loam-subcontract-core = { workspace = true }


[dev_dependencies]
[dev-dependencies]
loam-sdk = { workspace = true, features = ["soroban-sdk-testutils"] }
2 changes: 1 addition & 1 deletion crates/loam-cli/examples/soroban/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ loam-subcontract-core = { workspace = true }
loam-soroban-sdk = { workspace = true }


[dev_dependencies]
[dev-dependencies]
loam-sdk = { workspace = true, features = ["soroban-sdk-testutils"] }

[package.metadata.loam]
Expand Down
2 changes: 1 addition & 1 deletion crates/loam-cli/examples/soroban/ft/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ loam-subcontract-core = { workspace = true }
loam-subcontract-ft = { workspace = true }


[dev_dependencies]
[dev-dependencies]
loam-sdk = { workspace = true, features = ["soroban-sdk-testutils"] }

[package.metadata.loam]
Expand Down
2 changes: 1 addition & 1 deletion crates/loam-cli/examples/soroban/ft/src/ft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Default for MyFungibleToken {
}

impl IsInitable for MyFungibleToken {
fn ft_init(&mut self, admin: Address, name: Bytes, symbol: Bytes, decimals: u32) {
fn ft_init(&self, admin: Address, name: Bytes, symbol: Bytes, decimals: u32) {
Contract::admin_get().unwrap().require_auth();
MyFungibleToken::set_lazy(MyFungibleToken::new(admin, name, symbol, decimals));
}
Expand Down
22 changes: 22 additions & 0 deletions crates/loam-cli/examples/soroban/nft/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "example-nft"
version = "0.0.0"
authors = ["Stellar Development Foundation <[email protected]>"]
license = "Apache-2.0"
edition = "2021"
publish = false

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

[dependencies]
loam-sdk = { workspace = true, features = ["loam-soroban-sdk"] }
loam-subcontract-core = { workspace = true }


[dev-dependencies]
loam-sdk = { workspace = true, features = ["soroban-sdk-testutils"] }

[package.metadata.loam]
contract = true
27 changes: 27 additions & 0 deletions crates/loam-cli/examples/soroban/nft/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#![no_std]
use loam_sdk::derive_contract;
use loam_subcontract_core::{admin::Admin, Core};

pub mod nft;
pub mod subcontract;

use nft::MyNonFungibleToken;
use subcontract::{Initable, NonFungible};
use crate::subcontract::Metadata;

#[derive_contract(
Core(Admin),
NonFungible(MyNonFungibleToken),
Initable(MyNonFungibleToken)
)]
pub struct Contract;

impl Contract {
pub(crate) fn require_auth() {
Contract::admin_get()
.expect("No admin! Call 'admin_set' first.")
.require_auth();
}
}

mod test;
116 changes: 116 additions & 0 deletions crates/loam-cli/examples/soroban/nft/src/nft.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use loam_sdk::{
soroban_sdk::{self, contracttype, env, Address, Bytes, Lazy, Map, Vec},
IntoKey,
};

use crate::{subcontract::{IsInitable, IsNonFungible, Metadata}, Contract};

#[contracttype]
#[derive(IntoKey)]
pub struct MyNonFungibleToken {
name: Bytes,
total_count: u128,
owners_to_nft_ids: Map<Address, Map<u128, ()>>, // the owner's collection
nft_ids_to_owners: Map<u128, Address>,
nft_ids_to_metadata: Map<u128, Metadata>,
}

impl MyNonFungibleToken {
#[must_use]
pub fn new(name: Bytes) -> Self {
MyNonFungibleToken {
name,
total_count: 0,
owners_to_nft_ids: Map::new(env()),
nft_ids_to_owners: Map::new(env()),
nft_ids_to_metadata: Map::new(env()),
}
}
}

impl Default for MyNonFungibleToken {
fn default() -> Self {
MyNonFungibleToken::new(Bytes::new(env()))
}
}

impl IsInitable for MyNonFungibleToken {
fn nft_init(&self, name: Bytes) {
Contract::require_auth();
MyNonFungibleToken::set_lazy(MyNonFungibleToken::new(name));
}
}

impl IsNonFungible for MyNonFungibleToken {
fn mint(&mut self, owner: Address, metadata: Metadata) -> u128 {
Contract::require_auth();

let current_count = self.total_count;
let new_id = current_count + 1;

//todo: check that the metadata is unique
self.nft_ids_to_metadata.set(new_id, metadata);
self.nft_ids_to_owners.set(new_id, owner.clone());

let mut owner_collection = self
.owners_to_nft_ids
.get(owner.clone())
.unwrap_or_else(|| Map::new(env()));
owner_collection.set(new_id, ());
self.owners_to_nft_ids.set(owner, owner_collection);

self.total_count = new_id;

new_id
}

fn transfer(&mut self, id: u128, current_owner: Address, new_owner: Address) {
current_owner.require_auth();
let owner_id = self.nft_ids_to_owners.get(id).expect("NFT does not exist");
assert!(
owner_id == current_owner,
"You are not the owner of this NFT"
);

// update the nft_ids_to_owners map with the new owner
self.nft_ids_to_owners.remove(id);
self.nft_ids_to_owners.set(id, new_owner.clone());

// remove the NFT id from the current owner's collection
let mut current_owner_collection = self
.owners_to_nft_ids
.get(current_owner.clone())
.expect("Owner does not have a collection of NFTs");
current_owner_collection.remove(id);

self.owners_to_nft_ids
.set(current_owner, current_owner_collection);

// add the NFT id to the new owner's collection
let mut new_owner_collection = self
.owners_to_nft_ids
.get(new_owner.clone())
.unwrap_or_else(|| Map::new(env()));
new_owner_collection.set(id, ());
self.owners_to_nft_ids.set(new_owner, new_owner_collection);
}

fn get_nft(&self, id: u128) -> Option<Metadata> {
self.nft_ids_to_metadata.get(id)
}

fn get_owner(&self, id: u128) -> Option<Address> {
self.nft_ids_to_owners.get(id)
}

fn get_total_count(&self) -> u128 {
self.total_count
}

fn get_collection_by_owner(&self, owner: Address) -> Vec<u128> {
self.owners_to_nft_ids
.get(owner)
.unwrap_or(Map::new(env()))
.keys()
}
}
47 changes: 47 additions & 0 deletions crates/loam-cli/examples/soroban/nft/src/subcontract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use loam_sdk::{
soroban_sdk::{self, contracttype, Lazy, String},
subcontract,
};

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Metadata {
pub(crate) name: String,
pub(crate) description: String,
pub(crate) url: String,
}

#[subcontract]
pub trait IsNonFungible {
/// Mint a new NFT with the given owner and metadata
fn mint(&mut self, owner: loam_sdk::soroban_sdk::Address, metadata: Metadata) -> u128;

/// Transfer the NFT with the given ID from current_owner to new_owner
fn transfer(
&mut self,
id: u128,
current_owner: loam_sdk::soroban_sdk::Address,
new_owner: loam_sdk::soroban_sdk::Address,
);

/// Get the NFT with the given ID
fn get_nft(&self, id: u128) -> Option<Metadata>;

/// Find the owner of the NFT with the given ID
fn get_owner(&self, id: u128) -> Option<loam_sdk::soroban_sdk::Address>;

/// Get the total count of NFTs
fn get_total_count(&self) -> u128;

/// Get all of the NFTs owned by the given address
fn get_collection_by_owner(
&self,
owner: loam_sdk::soroban_sdk::Address,
) -> loam_sdk::soroban_sdk::Vec<u128>;
}

#[subcontract]
pub trait IsInitable {
/// Initialize the NFT contract with the given admin and name
fn nft_init(&self, name: loam_sdk::soroban_sdk::Bytes);
}
Loading
Loading