Skip to content

Commit cf32874

Browse files
authored
feat: enable adding examples (#38)
Main things that changed: - Syntax for conditional compilation was wrong, so fixed that. `#[cfg(erc20)]` -> `#[cfg(feature = "erc20")]`. - Added a `tests` flag as a workaround to rust-lang/cargo#13595 - Moved to using workspace dependencies. - Added example erc20. - `alloy-primitives` did not need to be a dependency of `lib/crypto`. As a result of setting features properly, we now have a missing docs warning that we cannot fix. See alloy-rs/core#588
1 parent 484dbd9 commit cf32874

File tree

11 files changed

+127
-51
lines changed

11 files changed

+127
-51
lines changed

Cargo.lock

+11-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
[workspace]
2-
members = ["contracts", "lib/crypto", "lib/grip", "lib/grip-proc"]
2+
members = [
3+
"contracts",
4+
"lib/crypto",
5+
"lib/grip",
6+
"lib/grip-proc",
7+
"examples/erc20",
8+
]
39
# Explicitly set the resolver to version 2, which is the default for packages
410
# with edition >= 2021.
511
# https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html
@@ -12,6 +18,13 @@ license = "MIT"
1218
keywords = ["arbitrum", "ethereum", "stylus"]
1319
repository = "https://github.com/OpenZeppelin/rust-contracts-stylus"
1420

21+
[workspace.dependencies]
22+
alloy-primitives = { version = "0.3.1", default-features = false }
23+
alloy-sol-types = { version = "0.3.1", default-features = false }
24+
stylus-sdk = { version = "0.4.3", default-features = false }
25+
stylus-proc = { version = "0.4.3", default-features = false }
26+
mini-alloc = "0.4.2"
27+
1528
[profile.release]
1629
codegen-units = 1
1730
panic = "abort"

contracts/Cargo.toml

+8-27
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ repository.workspace = true
1010
version = "0.1.0"
1111

1212
[dependencies]
13-
alloy-primitives = { version = "0.3.1", default-features = false }
14-
alloy-sol-types = { version = "0.3.1", default-features = false }
15-
stylus-sdk = { version = "0.4.3", default-features = false }
16-
stylus-proc = { version = "0.4.3", default-features = false }
17-
mini-alloc = "0.4.2"
13+
alloy-primitives.workspace = true
14+
alloy-sol-types.workspace = true
15+
stylus-sdk.workspace = true
16+
stylus-proc.workspace = true
17+
mini-alloc.workspace = true
1818
derive_more = "0.99.17"
19+
cfg-if = "1.0"
1920

2021
[dev-dependencies]
2122
grip = { path = "../lib/grip" }
@@ -24,30 +25,10 @@ once_cell = "1.19.0"
2425

2526
[features]
2627
default = []
28+
tests = []
2729
erc20 = []
2830
erc20_metadata = ["erc20"]
2931
erc721 = []
3032

3133
[lib]
32-
# The Stylus team sets new crates with the following types:
33-
# `lib` - The default, which gets turned to `rlib` by cargo and is needed to
34-
# link the crate as a dependency of binaries.
35-
# `cdylib` - A dynamic system library to be loaded from `wasm`.
36-
#
37-
# See <https://doc.rust-lang.org/reference/linkage.html>
38-
#
39-
# This means our crate would be built twice: once as a rust library and once as
40-
# a dynamic library. When running `cargo test`, cargo invokes rustc twice, but
41-
# when it build the rust library, it doesn't set the `test` feature flag, so we
42-
# can't use it.
43-
#
44-
# The reason to add `lib` is to be able to use the `export-abi` feature of the
45-
# SDK. We don't set `lib` here because our contracts are meant to be used as an
46-
# addition to other contracts. For this use case, the abi of those contracts
47-
# will contain ours.
48-
#
49-
# The trade-off is being able to run `cargo test` with conditional compilation
50-
# vs being able to run `cargo stylus export-abi`.
51-
#
52-
# This may change in the future, so this behavior should not be relied upon.
53-
crate-type = ["cdylib"]
34+
crate-type = ["lib", "cdylib"]

contracts/src/erc20/extensions/metadata.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl Metadata {
3636
/// * `name` - The name of the token.
3737
/// * `symbol` - The symbol of the token.
3838
pub fn constructor(&mut self, name: String, symbol: String) {
39-
if self._initialized.get() == true {
39+
if self._initialized.get() {
4040
return;
4141
}
4242

@@ -87,7 +87,7 @@ impl Metadata {
8787
}
8888
}
8989

90-
#[cfg(test)]
90+
#[cfg(all(test, feature = "tests"))]
9191
mod tests {
9292
use alloy_primitives::U256;
9393
use stylus_sdk::storage::{StorageBool, StorageString, StorageType};

contracts/src/erc20/extensions/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
#[cfg(any(test, erc20_metadata))]
2-
pub mod metadata;
1+
//! Common extensions to the ERC-20 standard.
2+
3+
cfg_if::cfg_if! {
4+
if #[cfg(any(test, feature = "erc20_metadata"))] {
5+
pub mod metadata;
6+
pub use metadata::Metadata;
7+
}
8+
}

contracts/src/erc20/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ impl ERC20 {
343343
}
344344
}
345345

346-
#[cfg(test)]
346+
#[cfg(all(test, feature = "tests"))]
347347
mod tests {
348348
use alloy_primitives::{address, Address, U256};
349349
use stylus_sdk::{

contracts/src/erc721/mod.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! Implementation of the ERC-721 token standard.
2+
use alloc::vec;
3+
14
use alloy_primitives::{fixed_bytes, Address, FixedBytes, U128, U256};
25
use derive_more::From;
36
use stylus_sdk::{
@@ -88,13 +91,26 @@ sol! {
8891
/// [ERC-6093]: https://eips.ethereum.org/EIPS/eip-6093
8992
#[derive(SolidityError, Debug, From)]
9093
pub enum Error {
94+
/// Indicates that an address can't be an owner.
95+
/// For example, `address(0)` is a forbidden owner in ERC-721. Used in
96+
/// balance queries.
9197
InvalidOwner(ERC721InvalidOwner),
98+
/// Indicates a `tokenId` whose `owner` is the zero address.
9299
NonexistentToken(ERC721NonexistentToken),
100+
/// Indicates an error related to the ownership over a particular token.
101+
/// Used in transfers.
93102
IncorrectOwner(ERC721IncorrectOwner),
103+
/// Indicates a failure with the token `sender`. Used in transfers.
94104
InvalidSender(ERC721InvalidSender),
105+
/// Indicates a failure with the token `receiver`. Used in transfers.
95106
InvalidReceiver(ERC721InvalidReceiver),
107+
/// Indicates a failure with the `operator`’s approval. Used in transfers.
96108
InsufficientApproval(ERC721InsufficientApproval),
109+
/// Indicates a failure with the `approver` of a token to be approved. Used
110+
/// in approvals.
97111
InvalidApprover(ERC721InvalidApprover),
112+
/// Indicates a failure with the `operator` to be approved. Used in
113+
/// approvals.
98114
InvalidOperator(ERC721InvalidOperator),
99115
}
100116

@@ -120,13 +136,15 @@ sol_interface! {
120136
}
121137

122138
sol_storage! {
139+
/// State of an ERC-721 token.
123140
pub struct ERC721 {
141+
/// Maps tokens to owners.
124142
mapping(uint256 => address) _owners;
125-
143+
/// Maps users to balances.
126144
mapping(address => uint256) _balances;
127-
145+
/// Maps tokens to approvals.
128146
mapping(uint256 => address) _token_approvals;
129-
147+
/// Maps owners to a mapping of operator approvals.
130148
mapping(address => mapping(address => bool)) _operator_approvals;
131149
}
132150
}
@@ -272,7 +290,7 @@ impl ERC721 {
272290
data: Bytes,
273291
) -> Result<(), Error> {
274292
self.transfer_from(from, to, token_id)?;
275-
self._check_on_erc721_received(msg::sender(), from, to, token_id, data)
293+
self._check_on_erc721_received(msg::sender(), from, to, token_id, &data)
276294
}
277295

278296
/// Transfers `token_id` token from `from` to `to`.
@@ -706,7 +724,7 @@ impl ERC721 {
706724
Address::ZERO,
707725
to,
708726
token_id,
709-
data,
727+
&data,
710728
)
711729
}
712730

@@ -847,7 +865,7 @@ impl ERC721 {
847865
data: Bytes,
848866
) -> Result<(), Error> {
849867
self._transfer(from, to, token_id)?;
850-
self._check_on_erc721_received(msg::sender(), from, to, token_id, data)
868+
self._check_on_erc721_received(msg::sender(), from, to, token_id, &data)
851869
}
852870

853871
/// Variant of `approve_inner` with an optional flag to enable or disable
@@ -989,7 +1007,7 @@ impl ERC721 {
9891007
from: Address,
9901008
to: Address,
9911009
token_id: U256,
992-
data: Bytes,
1010+
data: &Bytes,
9931011
) -> Result<(), Error> {
9941012
const IERC721RECEIVER_INTERFACE_ID: FixedBytes<4> =
9951013
fixed_bytes!("150b7a02");
@@ -1017,7 +1035,7 @@ impl ERC721 {
10171035
}
10181036
}
10191037

1020-
#[cfg(test)]
1038+
#[cfg(all(test, feature = "tests"))]
10211039
mod tests {
10221040
use alloy_primitives::address;
10231041
use once_cell::sync::Lazy;

contracts/src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
#![doc = include_str!("../../README.md")]
22
#![warn(missing_docs, unreachable_pub, rust_2021_compatibility)]
33
#![warn(clippy::all, clippy::pedantic)]
4-
#![cfg_attr(not(test), no_std, no_main)]
4+
#![cfg_attr(not(feature = "tests"), no_std, no_main)]
55
extern crate alloc;
66

77
#[global_allocator]
88
static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
99

1010
mod arithmetic;
11-
#[cfg(any(test, erc20))]
11+
#[cfg(any(feature = "tests", feature = "erc20"))]
1212
pub mod erc20;
13-
#[cfg(any(test, erc721))]
13+
#[cfg(any(feature = "tests", feature = "erc721"))]
1414
pub mod erc721;
1515

16-
#[cfg(not(any(test, target_arch = "wasm32-unknown-unknown")))]
16+
#[cfg(not(any(feature = "tests", target_arch = "wasm32-unknown-unknown")))]
1717
#[panic_handler]
1818
fn panic(_info: &core::panic::PanicInfo) -> ! {
1919
loop {}

examples/erc20/Cargo.toml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "erc20-example"
3+
edition.workspace = true
4+
license.workspace = true
5+
repository.workspace = true
6+
publish = false
7+
version = "0.0.0"
8+
9+
[dependencies]
10+
contracts = { path = "../../contracts", features = ["erc20", "erc20_metadata"] }
11+
stylus-sdk.workspace = true
12+
stylus-proc.workspace = true
13+
mini-alloc.workspace = true
14+
15+
[lib]
16+
crate-type = ["lib", "cdylib"]

examples/erc20/src/lib.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![cfg_attr(not(test), no_main, no_std)]
2+
extern crate alloc;
3+
4+
use alloc::string::String;
5+
6+
use contracts::erc20::{extensions::Metadata, ERC20};
7+
use stylus_sdk::prelude::{entrypoint, external, sol_storage};
8+
9+
const DECIMALS: u8 = 10;
10+
11+
sol_storage! {
12+
#[entrypoint]
13+
struct Token {
14+
#[borrow]
15+
ERC20 erc20;
16+
#[borrow]
17+
Metadata metadata;
18+
}
19+
}
20+
21+
#[external]
22+
#[inherit(ERC20, Metadata)]
23+
impl Token {
24+
pub fn constructor(&mut self, name: String, symbol: String) {
25+
self.metadata.constructor(name, symbol);
26+
}
27+
28+
// Overrides the default [`Metadata::decimals`], and sets it to `10`.
29+
//
30+
// If you don't provide this method in the `entrypoint` contract, it will
31+
// default to `18`.
32+
pub fn decimals(&self) -> u8 {
33+
DECIMALS
34+
}
35+
}

lib/crypto/Cargo.toml

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ license.workspace = true
88
repository.workspace = true
99
version = "0.1.0"
1010

11-
[dependencies]
12-
alloy-primitives = { version = "0.6.4", default-features = false }
13-
1411
[dev-dependencies]
15-
const-hex = "1.11.1"
12+
alloy-primitives = { version = "0.6.4", default-features = false }
13+
const-hex = { version = "1.11.1", default-features = false }
1614
rand = "0.8.5"
1715

1816
[features]

0 commit comments

Comments
 (0)