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

Collections: changes after merge #8

Merged
merged 25 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9db7549
refactor as per PR
gangov Aug 14, 2024
f8a9be8
refactors the key used to store the balance of nfts
gangov Aug 14, 2024
9071b64
adds upgrade entrypoint
gangov Aug 14, 2024
ed7f96a
update
gangov Aug 14, 2024
fadde09
adds is_initialized check and tests for it
gangov Aug 15, 2024
2222893
refactor as per PR comments
gangov Aug 15, 2024
7f1e3ab
failing path for batch_balance_of
gangov Aug 15, 2024
ff8dc8c
failing test case for when the user tries to authorize himself
gangov Aug 15, 2024
29320e1
adds tests for when the user doesn't have enough to spend
gangov Aug 15, 2024
8d73640
adds a test for safe_transfer
gangov Aug 15, 2024
cd380b2
removes a test and refactors the logic on how we handle transfer
gangov Aug 15, 2024
80d7efa
safe batch transfer test
gangov Aug 15, 2024
dd84a14
more tests for the failing path when dealing with safe_batch_transfer
gangov Aug 15, 2024
9ffe070
adds a failing test case for when the user is not authorized to mint
gangov Aug 15, 2024
250dbf5
more tests for failing test case when batch_minting
gangov Aug 15, 2024
263c4fe
test for when burn fails
gangov Aug 15, 2024
7f0aedd
more tests for when batch_burn fails
gangov Aug 15, 2024
15e6afc
test for when set_uri fails
gangov Aug 15, 2024
ada922a
test for set collection uri
gangov Aug 15, 2024
bc06a71
test case for when looking for a uri when such is not set
gangov Aug 15, 2024
8b6f972
adds a few tarpaulin macros to ignore test cases which we cannot cover
gangov Aug 15, 2024
55a32ed
adds events
gangov Aug 16, 2024
08603cc
removes unnecessary comment
gangov Aug 16, 2024
10bb2dc
adds README.md
gangov Aug 16, 2024
f131f66
changes to a test
gangov Aug 19, 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
2 changes: 1 addition & 1 deletion contracts/auctions/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![no_std]

use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env, Vec};
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, Vec};

#[derive(Clone)]
#[contracttype]
Expand Down
128 changes: 111 additions & 17 deletions contracts/collections/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use soroban_sdk::{contract, contractimpl, log, vec, Address, Bytes, Env, String, Vec};
use soroban_sdk::{contract, contractimpl, log, vec, Address, Bytes, BytesN, Env, String, Vec};

use crate::{
error::ContractError,
storage::{
utils::{get_admin, get_balance_of, save_admin, save_config, update_balance_of},
utils::{
get_admin, get_balance_of, is_initialized, save_admin, save_config, set_initialized,
update_balance_of,
},
Config, DataKey, OperatorApprovalKey, URIValue,
},
};
Expand All @@ -19,13 +22,28 @@ impl Collections {
env: Env,
admin: Address,
name: String,
image: URIValue,
symbol: String,
) -> Result<(), ContractError> {
let config = Config { name, image };
let config = Config {
name: name.clone(),
symbol: symbol.clone(),
};

if is_initialized(&env) {
log!(&env, "Collections: Initialize: Already initialized");
return Err(ContractError::AlreadyInitialized);
}

save_config(&env, config)?;
save_admin(&env, &admin)?;

set_initialized(&env);

env.events()
.publish(("initialize", "collection name: "), name);
env.events()
.publish(("initialize", "collectoin symbol: "), symbol);

Ok(())
}

Expand All @@ -46,7 +64,6 @@ impl Collections {
log!(&env, "Collections: Balance of batch: length missmatch");
return Err(ContractError::AccountsIdsLengthMissmatch);
}

let mut batch_balances: Vec<u64> = vec![&env];

// we verified that the length of both `accounts` and `ids` is the same
Expand Down Expand Up @@ -83,12 +100,21 @@ impl Collections {

env.storage().persistent().set(
&DataKey::OperatorApproval(OperatorApprovalKey {
owner: sender,
operator,
owner: sender.clone(),
operator: operator.clone(),
}),
&approved,
);

env.events()
.publish(("Set approval for", "Sender: "), sender);
env.events().publish(
("Set approval for", "Set approval for operator: "),
operator,
);
env.events()
.publish(("Set approval for", "New approval: "), approved);

Ok(())
}

Expand Down Expand Up @@ -119,7 +145,6 @@ impl Collections {
to: Address,
id: u64,
transfer_amount: u64,
_data: Bytes, // we don't have onERC1155Received in Stellar/Soroban
) -> Result<(), ContractError> {
from.require_auth();
// TODO: check if `to` is not zero address
Expand All @@ -139,6 +164,12 @@ impl Collections {
// next we incrase the recipient's `to` balance
update_balance_of(&env, &to, id, rcpt_balance + transfer_amount)?;

env.events().publish(("safe transfer from", "from: "), from);
env.events().publish(("safe transfer from", "to: "), to);
env.events().publish(("safe transfer from", "id: "), id);
env.events()
.publish(("safe transfer from", "transfer amount: "), transfer_amount);

Ok(())
}

Expand All @@ -150,7 +181,6 @@ impl Collections {
to: Address,
ids: Vec<u64>,
amounts: Vec<u64>,
_data: Bytes, // we don't have onERC1155Received in Stellar/Soroban
) -> Result<(), ContractError> {
from.require_auth();
// TODO: check if `to` is not zero address
Expand Down Expand Up @@ -185,6 +215,15 @@ impl Collections {
update_balance_of(&env, &to, id, rcpt_balance + amount)?;
}

env.events()
.publish(("safe batch transfer from", "from: "), from);
env.events()
.publish(("safe batch transfer from", "to: "), to);
env.events()
.publish(("safe batch transfer from", "ids: "), ids);
env.events()
.publish(("safe batch transfer from", "amounts: "), amounts);

Ok(())
}

Expand All @@ -202,13 +241,16 @@ impl Collections {

let admin = get_admin(&env)?;
if admin != sender {
log!(&env, "Collections: Set uri: Unauthorized");
log!(&env, "Collections: Mint: Unauthorized");
return Err(ContractError::Unauthorized);
}

let current_balance = get_balance_of(&env, &to, id)?;
//TODO: check for overflow?
update_balance_of(&env, &to, id, current_balance + amount)?;
update_balance_of(&env, &to, id, amount)?;

env.events().publish(("mint", "sender: "), sender);
env.events().publish(("mint", "to: "), to);
env.events().publish(("mint", "id: "), id);
env.events().publish(("mint", "amount: "), amount);

Ok(())
}
Expand All @@ -226,7 +268,7 @@ impl Collections {

let admin = get_admin(&env)?;
if admin != sender {
log!(&env, "Collections: Set uri: Unauthorized");
log!(&env, "Collections: Mint batch: Unauthorized");
return Err(ContractError::Unauthorized);
}

Expand All @@ -239,11 +281,15 @@ impl Collections {
let id = ids.get(idx).unwrap();
let amount = amounts.get(idx).unwrap();

let current_balance = get_balance_of(&env, &to, id)?;
//TODO: check for overflow?
update_balance_of(&env, &to, id, current_balance + amount)?;
update_balance_of(&env, &to, id, amount)?;
}

env.events().publish(("mint batch", "sender: "), sender);
env.events().publish(("mint batch", "to: "), to);
env.events().publish(("mint batch", "ids: "), ids);
env.events().publish(("mint batch", "amounts: "), amounts);

Ok(())
}

Expand All @@ -262,6 +308,10 @@ impl Collections {

update_balance_of(&env, &from, id, current_balance - amount)?;

env.events().publish(("burn", "from: "), from);
env.events().publish(("burn", "id: "), id);
env.events().publish(("burn", "amount: "), amount);

Ok(())
}

Expand Down Expand Up @@ -293,6 +343,10 @@ impl Collections {
update_balance_of(&env, &from, id, current_balance - amount)?;
}

env.events().publish(("burn batch", "from: "), from);
env.events().publish(("burn batch", "ids: "), ids);
env.events().publish(("burn batch", "amounts: "), amounts);

Ok(())
}

Expand All @@ -308,7 +362,25 @@ impl Collections {

env.storage()
.persistent()
.set(&DataKey::Uri(id), &URIValue { uri });
.set(&DataKey::Uri(id), &URIValue { uri: uri.clone() });

env.events().publish(("set uri", "sender: "), sender);
env.events().publish(("set uri", "id: "), id);
env.events().publish(("set uri", "uri: "), uri);

Ok(())
}

// Sets the main image(logo) for the collection
#[allow(dead_code)]
pub fn set_collection_uri(env: Env, uri: Bytes) -> Result<(), ContractError> {
get_admin(&env)?.require_auth();

env.storage()
.persistent()
.set(&DataKey::CollectionUri, &URIValue { uri: uri.clone() });

env.events().publish(("set collection uri", "uri: "), uri);

Ok(())
}
Expand All @@ -324,6 +396,28 @@ impl Collections {
}
}

// Returns the URI for a token type `id`
#[allow(dead_code)]
pub fn collection_uri(env: Env) -> Result<URIValue, ContractError> {
if let Some(uri) = env.storage().persistent().get(&DataKey::CollectionUri) {
Ok(uri)
} else {
log!(&env, "Collections: Uri: No collection uri set");
Err(ContractError::NoUriSet)
}
}

#[allow(dead_code)]
#[cfg(not(tarpaulin_include))]
pub fn upgrade(env: Env, new_wasm_hash: BytesN<32>) -> Result<(), ContractError> {
let admin: Address = get_admin(&env)?;
admin.require_auth();

env.deployer().update_current_contract_wasm(new_wasm_hash);

Ok(())
}

#[cfg(test)]
#[allow(dead_code)]
pub fn show_admin(env: &Env) -> Result<Address, ContractError> {
Expand Down
3 changes: 3 additions & 0 deletions contracts/collections/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ pub enum ContractError {
Unauthorized = 7,
InvalidAccountIndex = 8,
InvalidIdIndex = 9,
NftIdNotFound = 10,
AlreadyInitialized = 11,
EntryDoesNotExist = 12,
}
67 changes: 41 additions & 26 deletions contracts/collections/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,8 @@ use soroban_sdk::{contracttype, symbol_short, Address, Bytes, String, Symbol};
//pub(crate) const BALANCE_LIFETIME_THRESHOLD: u32 = BALANCE_BUMP_AMOUNT - DAY_IN_LEDGERS;

type NftId = u64;

// Struct to represent a token balance for a specific address and token ID
#[derive(Clone)]
#[contracttype]
pub struct BalanceDataKey {
pub token_id: u64,
pub owner: Address,
}
type TokenId = u64;
type Balance = u64;

// Struct to represent the operator approval status
#[derive(Clone)]
Expand All @@ -29,10 +23,12 @@ pub struct OperatorApprovalKey {
#[derive(Clone)]
#[contracttype]
pub enum DataKey {
Balance(BalanceDataKey),
Balance(Address),
OperatorApproval(OperatorApprovalKey),
Uri(NftId),
CollectionUri,
Config,
IsInitialized,
}

// Struct to represent token URI
Expand All @@ -46,29 +42,30 @@ pub struct URIValue {
#[contracttype]
pub struct Config {
pub name: String,
pub image: URIValue,
pub symbol: String,
}

pub const ADMIN: Symbol = symbol_short!("admin");

pub mod utils {
use soroban_sdk::{log, Address, Env};
use soroban_sdk::{log, Address, Env, Map};

use crate::error::ContractError;

use super::{Config, DataKey, ADMIN};
use super::{Balance, Config, DataKey, TokenId, ADMIN};

pub fn get_balance_of(env: &Env, owner: &Address, id: u64) -> Result<u64, ContractError> {
let result = env
let balance_map: Map<TokenId, Balance> = env
.storage()
.persistent()
.get(&DataKey::Balance(crate::storage::BalanceDataKey {
token_id: id,
owner: owner.clone(),
}))
.unwrap_or(0u64);
.get(&DataKey::Balance(owner.clone()))
.unwrap_or(Map::new(env));

Ok(result)
if let Some(balance) = balance_map.get(id) {
Ok(balance)
} else {
Ok(0u64)
}
}

pub fn update_balance_of(
Expand All @@ -77,13 +74,17 @@ pub mod utils {
id: u64,
new_amount: u64,
) -> Result<(), ContractError> {
env.storage().persistent().set(
&DataKey::Balance(crate::storage::BalanceDataKey {
token_id: id,
owner: owner.clone(),
}),
&new_amount,
);
let mut balance_map: Map<TokenId, Balance> = env
.storage()
.persistent()
.get(&DataKey::Balance(owner.clone()))
.unwrap_or(Map::new(env));

balance_map.set(id, new_amount);

env.storage()
.persistent()
.set(&DataKey::Balance(owner.clone()), &balance_map);

Ok(())
}
Expand All @@ -95,6 +96,7 @@ pub mod utils {
}

#[allow(dead_code)]
#[cfg(not(tarpaulin_include))]
pub fn get_config(env: &Env) -> Result<Config, ContractError> {
if let Some(config) = env.storage().persistent().get(&DataKey::Config) {
Ok(config)
Expand All @@ -110,6 +112,7 @@ pub mod utils {
Ok(())
}

#[cfg(not(tarpaulin_include))]
pub fn get_admin(env: &Env) -> Result<Address, ContractError> {
if let Some(admin) = env.storage().persistent().get(&ADMIN) {
Ok(admin)
Expand All @@ -118,4 +121,16 @@ pub mod utils {
Err(ContractError::AdminNotSet)
}
}
pub fn is_initialized(env: &Env) -> bool {
env.storage()
.persistent()
.get(&DataKey::IsInitialized)
.unwrap_or(false)
}

pub fn set_initialized(env: &Env) {
env.storage()
.persistent()
.set(&DataKey::IsInitialized, &true);
}
}
Loading
Loading