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
684 changes: 462 additions & 222 deletions Cargo.lock

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,17 @@ categories = ["no-std", "wasm"]
version = "0.4.1"

[workspace.dependencies]
soroban-sdk = "22.0.8"
soroban-sdk = { git = "https://github.com/ahalabs/rs-soroban-sdk", rev = "dfddbff7ef3e4f8f83f4b8a48a4e5c19be685dd9" }
#
proc-macro2 = "1.0"
proptest = "1"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }
soroban-test-helpers = "0.2.3"
hex-literal = "0.4.1"
ed25519-dalek = "2.1.1"
k256 = "0.13.4"
p256 = "0.13.2"
hex-literal = "1.0.0"
ed25519-dalek = "2.0.0"

# members
stellar-access = { path = "packages/access" }
Expand Down
2 changes: 1 addition & 1 deletion examples/fungible-allowlist/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ doctest = false

[dependencies]
soroban-sdk = { workspace = true }
stellar-access = { workspace = true }
stellar-macros = { workspace = true }
stellar-tokens = { workspace = true }
stellar-access = { workspace = true }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
52 changes: 26 additions & 26 deletions examples/fungible-allowlist/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,66 +6,66 @@
//! accounts.
use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, String};
use stellar_access::access_control::{self as access_control, AccessControl};
use stellar_macros::{default_impl, only_role};
use stellar_access::{AccessControl, AccessController};
use stellar_macros::only_role;
use stellar_tokens::fungible::{
allowlist::{AllowList, FungibleAllowList},
burnable::FungibleBurnable,
Base, FungibleToken,
FungibleToken,
};

#[contract]
pub struct ExampleContract;

#[contractimpl]
impl ExampleContract {
pub fn __constructor(e: &Env, admin: Address, manager: Address, initial_supply: i128) {
Base::set_metadata(
pub fn __constructor(e: &Env, admin: &Address, manager: &Address, initial_supply: i128) {
Self::set_metadata(
e,
18,
String::from_str(e, "AllowList Token"),
String::from_str(e, "ALT"),
);

access_control::set_admin(e, &admin);
Self::init_admin(e, admin);

// create a role "manager" and grant it to `manager`
access_control::grant_role_no_auth(e, &admin, &manager, &symbol_short!("manager"));
Self::grant_role_no_auth(e, admin, manager, &symbol_short!("manager"));

// Allow the admin to transfer tokens
AllowList::allow_user(e, &admin);
Self::allow_user_no_auth(e, admin);

// Mint initial supply to the admin
Base::mint(e, &admin, initial_supply);
Self::internal_mint(e, admin, initial_supply);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me:

internal_mint is a new addition?

Willem:

mint was public but added an internal_mint to the FT trait with #[internal]. This way if you you wanted to swap of the default implementation it would be guaranteed to have it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I fully get it, could you elaborate on that a bit more @willemneal ?

}
}

#[default_impl]
#[contractimpl]
impl FungibleToken for ExampleContract {
type ContractType = AllowList;
type Impl = AllowList;
Comment on lines -46 to +45
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me:

ContractType is changed to Impl. We decided to keep it as ContractType in our previous discussion

Willem:

My branch of the contracttraits adds the default impl redirect, but uses Impl . What I wanted was to be consistent with contracttraits which used the associated type.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think ContractType is more self-explanatory than an abstract name such as Impl. I'd prefer to keep it as ContractType

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about ContractImpl? Type is also broad and it already is a type. What it is is the implementation of the contract so ContractImpl seems appropriate to me.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is already a type

Yeah I give you that :D

I don't think Impl keyword fits in here.

What it is is the implementation of the contract so ContractImpl seems appropriate to me.

I disagree. It is the associated type. The implementation of the contract is happening elsewhere, this is just the indicator type. I can go with Contract alone, agree that Type is redundant, but I don't think Impl fits here.

}

#[contractimpl]
impl FungibleBurnable for ExampleContract {
type Impl = AllowList;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me:

Why have we added ContractType to every trait? It was not required back then for FungibleAllowList, FungibleBurnable, AccessControl, etc.

Willem:

The idea was that I wanted an actual default implementation for each. This way devs could replace it with their own and opens up to the trait extensions which I use an another branch.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are specific reasons that we didn't want to provide default implementations for some modules, like AccessControl. This was a deliberate choice because the functions that will be implemented for the contract are critical w.r.t security, and providing defaults will effectively result in people using the default rather than carefully thinking about their business logic and how to configure AccessControl accordingly.

The reason for the existence of the ContractType trait, is to provide overrides across different types of contracts. For Allowlist, Burnable, AccessControl, there are no overrides needed for their interface.

I'd say this change is contradicting with 2 critical design choices of our library.

}

#[contractimpl]
impl AccessControl for ExampleContract {
type Impl = AccessController;
}

#[contractimpl]
impl FungibleAllowList for ExampleContract {
fn allowed(e: &Env, account: Address) -> bool {
AllowList::allowed(e, &account)
}
type Impl = AllowList;

#[only_role(operator, "manager")]
fn allow_user(e: &Env, user: Address, operator: Address) {
AllowList::allow_user(e, &user)
fn allow_user(e: &Env, user: &Address, operator: &Address) {
Self::Impl::allow_user(e, user, operator)
}

#[only_role(operator, "manager")]
fn disallow_user(e: &Env, user: Address, operator: Address) {
AllowList::disallow_user(e, &user)
fn disallow_user(e: &Env, user: &Address, operator: &Address) {
Self::Impl::disallow_user(e, user, operator)
}
}

#[default_impl]
#[contractimpl]
impl AccessControl for ExampleContract {}

#[default_impl]
#[contractimpl]
impl FungibleBurnable for ExampleContract {}
52 changes: 20 additions & 32 deletions examples/fungible-blocklist/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,68 +5,56 @@
//! controlled token transfers by an admin who can block or unblock specific
//! accounts.

use soroban_sdk::{contract, contracterror, contractimpl, symbol_short, Address, Env, String};
use stellar_access::access_control::{self as access_control, AccessControl};
use stellar_macros::{default_impl, only_role};
use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, String};
use stellar_access::{AccessControl, AccessController};
use stellar_macros::only_role;
use stellar_tokens::fungible::{
blocklist::{BlockList, FungibleBlockList},
Base, FungibleToken,
FungibleToken,
};

#[contract]
pub struct ExampleContract;

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum ExampleContractError {
Unauthorized = 1,
}

#[contractimpl]
impl ExampleContract {
pub fn __constructor(e: &Env, admin: Address, manager: Address, initial_supply: i128) {
Base::set_metadata(
pub fn __constructor(e: &Env, admin: &Address, manager: &Address, initial_supply: i128) {
Self::set_metadata(
e,
18,
String::from_str(e, "BlockList Token"),
String::from_str(e, "BLT"),
);

access_control::set_admin(e, &admin);

Self::init_admin(e, admin);
// create a role "manager" and grant it to `manager`

access_control::grant_role_no_auth(e, &admin, &manager, &symbol_short!("manager"));

Self::grant_role_no_auth(e, admin, manager, &symbol_short!("manager"));
// Mint initial supply to the admin
Base::mint(e, &admin, initial_supply);
Self::internal_mint(e, admin, initial_supply);
}
}

#[default_impl]
#[contractimpl]
impl FungibleToken for ExampleContract {
type ContractType = BlockList;
type Impl = BlockList;
}

#[contractimpl]
impl AccessControl for ExampleContract {
type Impl = AccessController;
}

#[contractimpl]
impl FungibleBlockList for ExampleContract {
fn blocked(e: &Env, account: Address) -> bool {
BlockList::blocked(e, &account)
}
type Impl = BlockList;

#[only_role(operator, "manager")]
fn block_user(e: &Env, user: Address, operator: Address) {
BlockList::block_user(e, &user)
fn block_user(e: &Env, user: &Address, operator: &Address) {
Self::Impl::block_user(e, user, operator)
}

#[only_role(operator, "manager")]
fn unblock_user(e: &Env, user: Address, operator: Address) {
BlockList::unblock_user(e, &user)
fn unblock_user(e: &Env, user: &Address, operator: &Address) {
Self::Impl::unblock_user(e, user, operator)
}
}

#[default_impl]
#[contractimpl]
impl AccessControl for ExampleContract {}
4 changes: 2 additions & 2 deletions examples/fungible-blocklist/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ fn blocklist_approve_override_works() {
let user1 = Address::generate(&e);
let user2 = Address::generate(&e);
let initial_supply = 1_000_000;
e.mock_all_auths();
let client = create_client(&e, &admin, &manager, &initial_supply);
let transfer_amount = 1000;

e.mock_all_auths();
let transfer_amount = 1000;

// Verify initial state - no users are blocked
assert!(!client.blocked(&user1));
Expand Down
44 changes: 4 additions & 40 deletions examples/fungible-capped/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
//! **IMPORTANT**: this example is for demonstration purposes, and authorization
//! is not taken into consideration

use soroban_sdk::{contract, contractimpl, Address, Env, String};
use soroban_sdk::{contract, contractimpl, Address, Env};
use stellar_tokens::fungible::{
capped::{check_cap, set_cap},
Base, FungibleToken,
FTBase, FungibleToken,
};

#[contract]
Expand All @@ -24,47 +24,11 @@ impl ExampleContract {

pub fn mint(e: &Env, account: Address, amount: i128) {
check_cap(e, amount);
Base::mint(e, &account, amount);
Self::internal_mint(e, &account, amount);
}
}

#[contractimpl]
impl FungibleToken for ExampleContract {
type ContractType = Base;

fn total_supply(e: &Env) -> i128 {
Self::ContractType::total_supply(e)
}

fn balance(e: &Env, account: Address) -> i128 {
Self::ContractType::balance(e, &account)
}

fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {
Self::ContractType::allowance(e, &owner, &spender)
}

fn transfer(e: &Env, from: Address, to: Address, amount: i128) {
Self::ContractType::transfer(e, &from, &to, amount);
}

fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {
Self::ContractType::transfer_from(e, &spender, &from, &to, amount);
}

fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {
Self::ContractType::approve(e, &owner, &spender, amount, live_until_ledger);
}

fn decimals(e: &Env) -> u32 {
Self::ContractType::decimals(e)
}

fn name(e: &Env) -> String {
Self::ContractType::name(e)
}

fn symbol(e: &Env) -> String {
Self::ContractType::symbol(e)
}
type Impl = FTBase;
}
18 changes: 8 additions & 10 deletions examples/fungible-merkle-airdrop/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,29 @@

use hex_literal::hex;
use soroban_sdk::{contract, contractimpl, testutils::Address as _, vec, Address, BytesN, Env};
use stellar_macros::default_impl;
use stellar_tokens::fungible::{Base, FungibleToken};
use stellar_tokens::fungible::{FTBase, FungibleToken};

use crate::contract::{AirdropContract, AirdropContractClient};

#[contract]
pub struct TokenContract;

#[contractimpl]
impl FungibleToken for TokenContract {
type Impl = FTBase;
}

#[contractimpl]
impl TokenContract {
pub fn __constructor(e: &Env, owner: Address, initial_supply: i128) {
Base::mint(e, &owner, initial_supply);
Self::internal_mint(e, &owner, initial_supply);
}

pub fn mint(e: &Env, to: Address, amount: i128) {
Base::mint(e, &to, amount);
Self::internal_mint(e, &to, amount);
}
}

#[default_impl]
#[contractimpl]
impl FungibleToken for TokenContract {
type ContractType = Base;
}

fn create_token_contract<'a>(e: &Env, owner: &Address) -> TokenContractClient<'a> {
let address = e.register(TokenContract, (owner, 10_000i128));
TokenContractClient::new(e, &address)
Expand Down
1 change: 1 addition & 0 deletions examples/fungible-pausable/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ soroban-sdk = { workspace = true }
stellar-contract-utils = { workspace = true }
stellar-macros = { workspace = true }
stellar-tokens = { workspace = true }
stellar-access = { workspace = true }

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