Skip to content

Commit

Permalink
Update examples ERC-20 and ERC-721
Browse files Browse the repository at this point in the history
  • Loading branch information
TucksonDev committed Jun 14, 2024
1 parent b1f3971 commit 16c61ea
Show file tree
Hide file tree
Showing 12 changed files with 1,542 additions and 244 deletions.
File renamed without changes.
267 changes: 108 additions & 159 deletions examples/erc20/Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions examples/erc20/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mini-alloc = { path = "../../mini-alloc" }
[features]
export-abi = ["stylus-sdk/export-abi"]

[lib]
crate-type = ["lib", "cdylib"]

[profile.release]
codegen-units = 1
strip = true
Expand Down
101 changes: 81 additions & 20 deletions examples/erc20/src/erc20.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
//! Implementation of the ERC-20 standard
//!
//! The eponymous [`Erc20`] type provides all the standard methods,
//! and is intended to be inherited by other contract types.
//!
//! You can configure the behavior of [`Erc20`] via the [`Erc20Params`] trait,
//! which allows specifying the name, symbol, and decimals of the token.
//!
//! Note that this code is unaudited and not fit for production use.

// Imported packages
use alloc::string::String;
use alloy_primitives::{Address, U256};
use alloy_sol_types::sol;
use core::marker::PhantomData;
use stylus_sdk::{
alloy_primitives::{Address, U256},
alloy_sol_types::sol,
evm, msg,
evm,
msg,
prelude::*,
};

pub trait Erc20Params {
/// Immutable token name
const NAME: &'static str;

/// Immutable token symbol
const SYMBOL: &'static str;

/// Immutable token decimals
const DECIMALS: u8;
}

Expand All @@ -36,21 +53,26 @@ sol! {
error InsufficientAllowance(address owner, address spender, uint256 have, uint256 want);
}

/// Represents the ways methods may fail.
#[derive(SolidityError)]
pub enum Erc20Error {
InsufficientBalance(InsufficientBalance),
InsufficientAllowance(InsufficientAllowance),
}

// These methods aren't exposed to other contracts
// Methods marked as "pub" here are usable outside of the erc20 module (i.e. they're callable from lib.rs)
// Note: modifying storage will become much prettier soon
impl<T: Erc20Params> Erc20<T> {
pub fn transfer_impl(
/// Movement of funds between 2 accounts
/// (invoked by the external transfer() and transfer_from() functions )
pub fn _transfer(
&mut self,
from: Address,
to: Address,
value: U256,
) -> Result<(), Erc20Error> {
// Decreasing sender balance
let mut sender_balance = self.balances.setter(from);
let old_sender_balance = sender_balance.get();
if old_sender_balance < value {
Expand All @@ -61,26 +83,40 @@ impl<T: Erc20Params> Erc20<T> {
}));
}
sender_balance.set(old_sender_balance - value);

// Increasing receiver balance
let mut to_balance = self.balances.setter(to);
let new_to_balance = to_balance.get() + value;
to_balance.set(new_to_balance);

// Emitting the transfer event
evm::log(Transfer { from, to, value });
Ok(())
}

pub fn mint(&mut self, address: Address, value: U256) {
/// Mints `value` tokens to `address`
pub fn mint(&mut self, address: Address, value: U256) -> Result<(), Erc20Error> {
// Increasing balance
let mut balance = self.balances.setter(address);
let new_balance = balance.get() + value;
balance.set(new_balance);

// Increasing total supply
self.total_supply.set(self.total_supply.get() + value);

// Emitting the transfer event
evm::log(Transfer {
from: Address::ZERO,
to: address,
value,
});

Ok(())
}

/// Burns `value` tokens from `address`
pub fn burn(&mut self, address: Address, value: U256) -> Result<(), Erc20Error> {
// Decreasing balance
let mut balance = self.balances.setter(address);
let old_balance = balance.get();
if old_balance < value {
Expand All @@ -91,12 +127,17 @@ impl<T: Erc20Params> Erc20<T> {
}));
}
balance.set(old_balance - value);

// Decreasing the total supply
self.total_supply.set(self.total_supply.get() - value);

// Emitting the transfer event
evm::log(Transfer {
from: address,
to: Address::ZERO,
value,
});

Ok(())
}
}
Expand All @@ -105,43 +146,46 @@ impl<T: Erc20Params> Erc20<T> {
// Note: modifying storage will become much prettier soon
#[external]
impl<T: Erc20Params> Erc20<T> {
/// Immutable token name
pub fn name() -> String {
T::NAME.into()
}

/// Immutable token symbol
pub fn symbol() -> String {
T::SYMBOL.into()
}

/// Immutable token decimals
pub fn decimals() -> u8 {
T::DECIMALS
}

pub fn balance_of(&self, address: Address) -> U256 {
self.balances.get(address)
/// Total supply of tokens
pub fn total_supply(&self) -> U256 {
self.total_supply.get()
}

pub fn transfer(&mut self, to: Address, value: U256) -> Result<bool, Erc20Error> {
self.transfer_impl(msg::sender(), to, value)?;
Ok(true)
/// Balance of `address`
pub fn balance_of(&self, owner: Address) -> U256 {
self.balances.get(owner)
}

pub fn approve(&mut self, spender: Address, value: U256) -> bool {
self.allowances.setter(msg::sender()).insert(spender, value);
evm::log(Approval {
owner: msg::sender(),
spender,
value,
});
true
/// Transfers `value` tokens from msg::sender() to `to`
pub fn transfer(&mut self, to: Address, value: U256) -> Result<bool, Erc20Error> {
self._transfer(msg::sender(), to, value)?;
Ok(true)
}

/// Transfers `value` tokens from `from` to `to`
/// (msg::sender() must be able to spend at least `value` tokens from `from`)
pub fn transfer_from(
&mut self,
from: Address,
to: Address,
value: U256,
) -> Result<bool, Erc20Error> {
// Check msg::sender() allowance
let mut sender_allowances = self.allowances.setter(from);
let mut allowance = sender_allowances.setter(msg::sender());
let old_allowance = allowance.get();
Expand All @@ -153,12 +197,29 @@ impl<T: Erc20Params> Erc20<T> {
want: value,
}));
}

// Decreases allowance
allowance.set(old_allowance - value);
self.transfer_impl(from, to, value)?;

// Calls the internal transfer function
self._transfer(from, to, value)?;

Ok(true)
}

/// Approves the spenditure of `value` tokens of msg::sender() to `spender`
pub fn approve(&mut self, spender: Address, value: U256) -> bool {
self.allowances.setter(msg::sender()).insert(spender, value);
evm::log(Approval {
owner: msg::sender(),
spender,
value,
});
true
}

/// Returns the allowance of `spender` on `owner`'s tokens
pub fn allowance(&self, owner: Address, spender: Address) -> U256 {
self.allowances.getter(owner).get(spender)
}
}
}
59 changes: 59 additions & 0 deletions examples/erc20/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Only run this as a WASM if the export-abi feature is not set.
#![cfg_attr(not(any(feature = "export-abi", test)), no_main)]
extern crate alloc;

// Modules and imports
mod erc20;

use alloy_primitives::{Address, U256};
use stylus_sdk::{
msg,
prelude::*
};
use crate::erc20::{Erc20, Erc20Params, Erc20Error};

/// Initializes a custom, global allocator for Rust programs compiled to WASM.
#[global_allocator]
static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;

/// Immutable definitions
struct StylusTestTokenParams;
impl Erc20Params for StylusTestTokenParams {
const NAME: &'static str = "StylusTestToken";
const SYMBOL: &'static str = "STTK";
const DECIMALS: u8 = 18;
}

// Define the entrypoint as a Solidity storage object. The sol_storage! macro
// will generate Rust-equivalent structs with all fields mapped to Solidity-equivalent
// storage slots and types.
sol_storage! {
#[entrypoint]
struct StylusTestToken {
// Allows erc20 to access StylusTestToken's storage and make calls
#[borrow]
Erc20<StylusTestTokenParams> erc20;
}
}

#[external]
#[inherit(Erc20<StylusTestTokenParams>)]
impl StylusTestToken {
/// Mints tokens
pub fn mint(&mut self, value: U256) -> Result<(), Erc20Error> {
self.erc20.mint(msg::sender(), value)?;
Ok(())
}

/// Mints tokens to another address
pub fn mint_to(&mut self, to: Address, value: U256) -> Result<(), Erc20Error> {
self.erc20.mint(to, value)?;
Ok(())
}

/// Burns tokens
pub fn burn(&mut self, value: U256) -> Result<(), Erc20Error> {
self.erc20.burn(msg::sender(), value)?;
Ok(())
}
}
69 changes: 4 additions & 65 deletions examples/erc20/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,4 @@
#![cfg_attr(not(feature = "export-abi"), no_main, no_std)]
extern crate alloc;

use crate::erc20::{Erc20, Erc20Params};
use alloc::{string::String, vec::Vec};
use stylus_sdk::{alloy_primitives::U256, call, msg, prelude::*};

#[cfg(target_arch = "wasm32")]
#[global_allocator]
static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;

mod erc20;

struct WethParams;

/// Immutable definitions
impl Erc20Params for WethParams {
const NAME: &'static str = "Wrapped Ether Example";
const SYMBOL: &'static str = "WETH";
const DECIMALS: u8 = 18;
}

// The contract
sol_storage! {
#[entrypoint] // Makes Weth the entrypoint
struct Weth {
#[borrow] // Allows erc20 to access Weth's storage and make calls
Erc20<WethParams> erc20;
}
}

// Another contract we'd like to call
sol_interface! {
interface IMath {
function sum(uint256[] values) pure returns (string, uint256);
}
}

#[external]
#[inherit(Erc20<WethParams>)]
impl Weth {
#[payable]
pub fn deposit(&mut self) {
self.erc20.mint(msg::sender(), msg::value());
}

pub fn withdraw(&mut self, amount: U256) -> Result<(), Vec<u8>> {
self.erc20.burn(msg::sender(), amount)?;

// send the user their funds
call::transfer_eth(msg::sender(), amount)
}

// sums numbers
pub fn sum(values: Vec<U256>) -> (String, U256) {
("sum".into(), values.iter().sum())
}

// calls the sum() method from the interface
pub fn sum_with_helper(&self, helper: IMath, values: Vec<U256>) -> Result<U256, Vec<u8>> {
let (text, sum) = helper.sum(self, values)?;
assert_eq!(&text, "sum");
Ok(sum)
}
}
#[cfg(feature = "export-abi")]
fn main() {
erc20::print_abi("MIT-OR-APACHE-2.0", "pragma solidity ^0.8.23;");
}
7 changes: 7 additions & 0 deletions examples/erc721/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[build]
target = "wasm32-unknown-unknown"

[target.wasm32-unknown-unknown]
rustflags = [
"-C", "link-arg=-zstack-size=32768",
]
Loading

0 comments on commit 16c61ea

Please sign in to comment.