Skip to content

Commit 44a8efc

Browse files
authored
fix: Replace example OZ contract for fungible, update library dependencies (#118)
* Remove fungible-token-interface and update nft enumerable, and lib deps * Add fungible-allowlist to replace interface contract
1 parent 28f3abe commit 44a8efc

File tree

12 files changed

+306
-589
lines changed

12 files changed

+306
-589
lines changed

Cargo.lock

Lines changed: 58 additions & 307 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ repository = "https://github.com/theahaco/scaffold-stellar"
1010
version = "0.0.1"
1111

1212
[workspace.dependencies.soroban-sdk]
13-
version = "22.0.8"
13+
version = "23.0.2"
1414

15-
[workspace.dependencies.soroban-token-sdk]
16-
version = "22.0.8"
15+
[workspace.dependencies.stellar-access]
16+
git = "https://github.com/OpenZeppelin/stellar-contracts"
17+
tag = "v0.5.0"
1718

1819
[workspace.dependencies.stellar-default-impl-macro]
1920
git = "https://github.com/OpenZeppelin/stellar-contracts"
@@ -23,6 +24,10 @@ tag = "v0.3.0"
2324
git = "https://github.com/OpenZeppelin/stellar-contracts"
2425
tag = "v0.3.0"
2526

27+
[workspace.dependencies.stellar-macros]
28+
git = "https://github.com/OpenZeppelin/stellar-contracts"
29+
tag = "v0.5.0"
30+
2631
[workspace.dependencies.stellar-non-fungible]
2732
git = "https://github.com/OpenZeppelin/stellar-contracts"
2833
tag = "v0.3.0"
@@ -35,6 +40,10 @@ tag = "v0.3.0"
3540
git = "https://github.com/OpenZeppelin/stellar-contracts"
3641
tag = "v0.3.0"
3742

43+
[workspace.dependencies.stellar-tokens]
44+
git = "https://github.com/OpenZeppelin/stellar-contracts"
45+
tag = "v0.5.0"
46+
3847
[profile.release]
3948
opt-level = "z"
4049
debug = false

contracts/fungible-token-interface/Cargo.toml renamed to contracts/fungible-allowlist/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "fungible-token-interface-example"
2+
name = "fungible-allowlist-example"
33
edition.workspace = true
44
license.workspace = true
55
repository.workspace = true
@@ -12,9 +12,9 @@ doctest = false
1212

1313
[dependencies]
1414
soroban-sdk = { workspace = true }
15-
stellar-pausable = { workspace = true }
16-
stellar-pausable-macros = { workspace = true }
17-
stellar-fungible = { workspace = true }
15+
stellar-access = { workspace = true }
16+
stellar-macros = { workspace = true }
17+
stellar-tokens = { workspace = true }
1818

1919
[dev-dependencies]
2020
soroban-sdk = { workspace = true, features = ["testutils"] }
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//! Fungible AllowList Example Contract.
2+
3+
//! This contract showcases how to integrate the AllowList extension with a
4+
//! SEP-41-compliant fungible token. It includes essential features such as
5+
//! controlled token transfers by an admin who can allow or disallow specific
6+
//! accounts.
7+
8+
use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, String};
9+
use stellar_access::access_control::{self as access_control, AccessControl};
10+
use stellar_macros::{default_impl, only_role};
11+
use stellar_tokens::fungible::{
12+
allowlist::{AllowList, FungibleAllowList},
13+
burnable::FungibleBurnable,
14+
Base, FungibleToken,
15+
};
16+
17+
#[contract]
18+
pub struct ExampleContract;
19+
20+
#[contractimpl]
21+
impl ExampleContract {
22+
pub fn __constructor(e: &Env, admin: Address, manager: Address, initial_supply: i128) {
23+
Base::set_metadata(
24+
e,
25+
18,
26+
String::from_str(e, "AllowList Token"),
27+
String::from_str(e, "ALT"),
28+
);
29+
30+
access_control::set_admin(e, &admin);
31+
32+
// create a role "manager" and grant it to `manager`
33+
access_control::grant_role_no_auth(e, &admin, &manager, &symbol_short!("manager"));
34+
35+
// Allow the admin to transfer tokens
36+
AllowList::allow_user(e, &admin);
37+
38+
// Mint initial supply to the admin
39+
Base::mint(e, &admin, initial_supply);
40+
}
41+
}
42+
43+
#[default_impl]
44+
#[contractimpl]
45+
impl FungibleToken for ExampleContract {
46+
type ContractType = AllowList;
47+
}
48+
#[contractimpl]
49+
impl FungibleAllowList for ExampleContract {
50+
fn allowed(e: &Env, account: Address) -> bool {
51+
AllowList::allowed(e, &account)
52+
}
53+
54+
#[only_role(operator, "manager")]
55+
fn allow_user(e: &Env, user: Address, operator: Address) {
56+
AllowList::allow_user(e, &user)
57+
}
58+
59+
#[only_role(operator, "manager")]
60+
fn disallow_user(e: &Env, user: Address, operator: Address) {
61+
AllowList::disallow_user(e, &user)
62+
}
63+
}
64+
65+
#[default_impl]
66+
#[contractimpl]
67+
impl AccessControl for ExampleContract {}
68+
69+
#[default_impl]
70+
#[contractimpl]
71+
impl FungibleBurnable for ExampleContract {}

contracts/fungible-token-interface/src/lib.rs renamed to contracts/fungible-allowlist/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
#![allow(dead_code)]
33

44
mod contract;
5+
6+
#[cfg(test)]
57
mod test;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
extern crate std;
2+
3+
use soroban_sdk::{testutils::Address as _, Address, Env};
4+
5+
use crate::contract::{ExampleContract, ExampleContractClient};
6+
7+
fn create_client<'a>(
8+
e: &Env,
9+
admin: &Address,
10+
manager: &Address,
11+
initial_supply: &i128,
12+
) -> ExampleContractClient<'a> {
13+
let address = e.register(ExampleContract, (admin, manager, initial_supply));
14+
ExampleContractClient::new(e, &address)
15+
}
16+
17+
#[test]
18+
#[should_panic(expected = "Error(Contract, #113)")]
19+
fn cannot_transfer_before_allow() {
20+
let e = Env::default();
21+
let admin = Address::generate(&e);
22+
let manager = Address::generate(&e);
23+
let user1 = Address::generate(&e);
24+
let user2 = Address::generate(&e);
25+
let initial_supply = 1_000_000;
26+
let client = create_client(&e, &admin, &manager, &initial_supply);
27+
let transfer_amount = 1000;
28+
29+
// Verify initial state - admin is allowed, others are not
30+
assert!(client.allowed(&admin));
31+
assert!(!client.allowed(&user1));
32+
assert!(!client.allowed(&user2));
33+
34+
// Admin can't transfer to user1 initially (user1 not allowed)
35+
e.mock_all_auths();
36+
client.transfer(&admin, &user1, &transfer_amount);
37+
}
38+
39+
#[test]
40+
fn transfer_to_allowed_account_works() {
41+
let e = Env::default();
42+
let admin = Address::generate(&e);
43+
let manager = Address::generate(&e);
44+
let user1 = Address::generate(&e);
45+
let user2 = Address::generate(&e);
46+
let initial_supply = 1_000_000;
47+
let client = create_client(&e, &admin, &manager, &initial_supply);
48+
let transfer_amount = 1000;
49+
50+
e.mock_all_auths();
51+
52+
// Verify initial state - admin is allowed, others are not
53+
assert!(client.allowed(&admin));
54+
assert!(!client.allowed(&user1));
55+
assert!(!client.allowed(&user2));
56+
57+
// Allow user1
58+
client.allow_user(&user1, &manager);
59+
assert!(client.allowed(&user1));
60+
61+
// Now admin can transfer to user1
62+
client.transfer(&admin, &user1, &transfer_amount);
63+
assert_eq!(client.balance(&user1), transfer_amount);
64+
}
65+
66+
#[test]
67+
#[should_panic(expected = "Error(Contract, #113)")]
68+
fn cannot_transfer_after_disallow() {
69+
let e = Env::default();
70+
let admin = Address::generate(&e);
71+
let manager = Address::generate(&e);
72+
let user1 = Address::generate(&e);
73+
let user2 = Address::generate(&e);
74+
let initial_supply = 1_000_000;
75+
let client = create_client(&e, &admin, &manager, &initial_supply);
76+
let transfer_amount = 1000;
77+
78+
e.mock_all_auths();
79+
80+
// Verify initial state - admin is allowed, others are not
81+
assert!(client.allowed(&admin));
82+
assert!(!client.allowed(&user1));
83+
assert!(!client.allowed(&user2));
84+
85+
// Allow user1
86+
client.allow_user(&user1, &manager);
87+
assert!(client.allowed(&user1));
88+
89+
// Now admin can transfer to user1
90+
client.transfer(&admin, &user1, &transfer_amount);
91+
assert_eq!(client.balance(&user1), transfer_amount);
92+
93+
// Disallow user1
94+
client.disallow_user(&user1, &manager);
95+
assert!(!client.allowed(&user1));
96+
97+
// Admin can't transfer to user1 after disallowing
98+
client.transfer(&admin, &user1, &100);
99+
}
100+
101+
#[test]
102+
fn allowlist_transfer_from_override_works() {
103+
let e = Env::default();
104+
let admin = Address::generate(&e);
105+
let manager = Address::generate(&e);
106+
let user1 = Address::generate(&e);
107+
let user2 = Address::generate(&e);
108+
let initial_supply = 1_000_000;
109+
let client = create_client(&e, &admin, &manager, &initial_supply);
110+
let transfer_amount = 1000;
111+
112+
e.mock_all_auths();
113+
114+
// Verify initial state - admin is allowed, others are not
115+
assert!(client.allowed(&admin));
116+
assert!(!client.allowed(&user1));
117+
assert!(!client.allowed(&user2));
118+
119+
// Allow user2
120+
client.allow_user(&user2, &manager);
121+
assert!(client.allowed(&user2));
122+
123+
// Now admin can transfer to user1
124+
client.approve(&admin, &user1, &transfer_amount, &1000);
125+
client.transfer_from(&user1, &admin, &user2, &transfer_amount);
126+
assert_eq!(client.balance(&user2), transfer_amount);
127+
}
128+
129+
#[test]
130+
fn allowlist_approve_override_works() {
131+
let e = Env::default();
132+
let admin = Address::generate(&e);
133+
let manager = Address::generate(&e);
134+
let user1 = Address::generate(&e);
135+
let user2 = Address::generate(&e);
136+
let initial_supply = 1_000_000;
137+
let client = create_client(&e, &admin, &manager, &initial_supply);
138+
let transfer_amount = 1000;
139+
140+
e.mock_all_auths();
141+
142+
// Verify initial state - admin is allowed, others are not
143+
assert!(client.allowed(&admin));
144+
assert!(!client.allowed(&user1));
145+
assert!(!client.allowed(&user2));
146+
147+
// Allow user1
148+
client.allow_user(&user1, &manager);
149+
assert!(client.allowed(&user1));
150+
151+
// Approve user2 to transfer from user1
152+
client.approve(&user1, &user2, &transfer_amount, &1000);
153+
assert_eq!(client.allowance(&user1, &user2), transfer_amount);
154+
}

0 commit comments

Comments
 (0)