Skip to content

Commit

Permalink
refactor generically typed checkpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
qalisander committed Oct 4, 2024
1 parent e502d12 commit 111aa36
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 83 deletions.
27 changes: 13 additions & 14 deletions contracts/src/token/erc721/extensions/consecutive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,20 @@ use crate::{
structs::{
bitmap::BitMap,
checkpoints,
checkpoints::{Trace160, U96},
checkpoints::{trace::Trace, Size, S160},
},
},
};

type U96 = <S160 as Size>::Key;

sol_storage! {
/// State of an [`Erc721Consecutive`] token.
pub struct Erc721Consecutive {
/// Erc721 contract storage.
Erc721 erc721;
/// Checkpoint library contract for sequential ownership.
Trace160 _sequential_ownership;
Trace<S160> _sequential_ownership;
/// BitMap library contract for sequential burn of tokens.
BitMap _sequential_burn;
/// Used to offset the first token id in
Expand Down Expand Up @@ -113,8 +115,8 @@ sol! {
pub enum Error {
/// Error type from [`Erc721`] contract [`erc721::Error`].
Erc721(erc721::Error),
/// Error type from checkpoint contract [`checkpoints::Error`].
Checkpoints(checkpoints::Error),
/// Error type from checkpoint contract [`checkpoints::trace::Error`].
Checkpoints(checkpoints::trace::Error),
/// Batch mint is restricted to the constructor.
/// Any batch mint not emitting the [`Transfer`] event outside of
/// the constructor is non ERC-721 compliant.
Expand Down Expand Up @@ -793,18 +795,15 @@ mod tests {
use alloy_primitives::{address, uint, Address, U256};
use stylus_sdk::msg;

use crate::{
token::{
erc721,
erc721::{
extensions::consecutive::{
ERC721ExceededMaxBatchMint, Erc721Consecutive, Error,
},
tests::random_token_id,
ERC721InvalidReceiver, ERC721NonexistentToken, IErc721,
use crate::token::{
erc721,
erc721::{
extensions::consecutive::{
ERC721ExceededMaxBatchMint, Erc721Consecutive, Error, U96,
},
tests::random_token_id,
ERC721InvalidReceiver, ERC721NonexistentToken, IErc721,
},
utils::structs::checkpoints::U96,
};

const BOB: Address = address!("F4EaCDAbEf3c8f1EdE91b6f2A6840bc2E4DD3526");
Expand Down
97 changes: 97 additions & 0 deletions contracts/src/utils/structs/checkpoints/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! Contract for checkpointing values as they change at different points in
//! time, to looking up past values by block number later.
//!
//! To create a history of checkpoints, define a variable type [`trace::Trace`]
//! in your contract.
//! Types [`S160`], [`S160`] and [`S160`] can be used to
//! define sizes for key and value.
//! Then store a new checkpoint for the current
//! transaction block using the [`trace::Trace::push`] function.
pub mod trace;

use core::ops::{Add, Div, Mul, Sub};

use alloy_primitives::Uint;
use stylus_sdk::prelude::*;

/// Trait that associates types of specific size for checkpoints key and value.
pub trait Size {
/// Type of the key in abi.
type Key: Num;

/// Type of the key in storage.
type KeyStorage: for<'a> StorageType<Wraps<'a> = Self::Key>
+ Accessor<Wrap = Self::Key>;

/// Type of the value in abi.
type Value: Num;

/// Type of the value in storage.
type ValueStorage: for<'a> StorageType<Wraps<'a> = Self::Value>
+ Accessor<Wrap = Self::Value>;
}

/// Size of checkpoint storage contract corresponding to the size of 96 bits of
/// the key and size 160 bits of the value.
pub type S160 = SpecificSize<96, 2, 160, 3>;

/// Size of checkpoint storage contract corresponding to the size of 32 bits of
/// the key and size 224 bits of the value.
pub type S224 = SpecificSize<32, 1, 224, 4>;

/// Size of checkpoint storage contract corresponding to the size of 48 bits of
/// the key and size 208 bits of the value.
pub type S208 = SpecificSize<48, 1, 208, 4>;

/// Contains the size of checkpoint's key and value in bits.
pub struct SpecificSize<
const KEY_BITS: usize,
const KEY_LIMBS: usize,
const VALUE_BITS: usize,
const VALUE_LIMBS: usize,
>;

impl<const KB: usize, const KL: usize, const VB: usize, const VL: usize> Size
for SpecificSize<KB, KL, VB, VL>
{
type Key = Uint<KB, KL>;
type KeyStorage = stylus_sdk::storage::StorageUint<KB, KL>;
type Value = Uint<VB, VL>;
type ValueStorage = stylus_sdk::storage::StorageUint<VB, VL>;
}

/// Abstracts number inside the checkpoint contract.
pub trait Num: Add + Sub + Mul + Div + Ord + Sized + Copy {
/// Zero value of the number.
const ZERO: Self;
}

impl<const B: usize, const L: usize> Num for Uint<B, L> {
const ZERO: Self = Self::ZERO;
}

/// Abstracts accessor inside the checkpoint contract
pub trait Accessor {
/// Type of the number associated with the storage type.
type Wrap: Num;

/// Gets underlying element [`Self::Wrap`] from persistent storage.
fn get(&self) -> Self::Wrap;

/// Sets underlying element [`Self::Wrap`] in persistent storage.
fn set(&mut self, value: Self::Wrap);
}

impl<const B: usize, const L: usize> Accessor
for stylus_sdk::storage::StorageUint<B, L>
{
type Wrap = Uint<B, L>;

fn get(&self) -> Self::Wrap {
self.get()
}

fn set(&mut self, value: Self::Wrap) {
self.set(value);
}
}
Loading

0 comments on commit 111aa36

Please sign in to comment.