Skip to content

Commit

Permalink
Allow supply-capped mints (ordinals#3365)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey authored and vuviettai committed Mar 28, 2024
1 parent 5d42929 commit aec5ec0
Show file tree
Hide file tree
Showing 26 changed files with 967 additions and 359 deletions.
2 changes: 1 addition & 1 deletion src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ mod updater;
#[cfg(test)]
pub(crate) mod testing;

const SCHEMA_VERSION: u64 = 22;
const SCHEMA_VERSION: u64 = 23;

macro_rules! define_table {
($name:ident, $key:ty, $value:ty) => {
Expand Down
212 changes: 195 additions & 17 deletions src/index/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,34 +34,51 @@ pub struct RuneEntry {
pub divisibility: u8,
pub etching: Txid,
pub mint: Option<MintEntry>,
pub mints: u64,
pub mints: u128,
pub number: u64,
pub premine: u128,
pub spaced_rune: SpacedRune,
pub supply: u128,
pub symbol: Option<char>,
pub timestamp: u32,
}

impl RuneEntry {
pub fn mintable(&self, block_height: Height, block_time: u32) -> Result<u128, MintError> {
let Some(mint) = self.mint else {
return Err(MintError::Unmintable(self.spaced_rune.rune));
return Err(MintError::Unmintable);
};

if let Some(end) = mint.end {
if block_height.0 >= end {
return Err(MintError::End((self.spaced_rune.rune, end)));
return Err(MintError::End(end));
}
}

if let Some(deadline) = mint.deadline {
if block_time >= deadline {
return Err(MintError::Deadline((self.spaced_rune.rune, deadline)));
return Err(MintError::Deadline(deadline));
}
}

Ok(mint.limit.unwrap_or(runes::MAX_LIMIT))
let cap = mint.cap.unwrap_or_default();

if self.mints >= cap {
return Err(MintError::Cap(cap));
}

Ok(mint.limit.unwrap_or_default())
}

pub fn supply(&self) -> u128 {
self.premine + self.mints * self.mint.and_then(|mint| mint.limit).unwrap_or_default()
}

pub fn pile(&self, amount: u128) -> Pile {
Pile {
amount,
divisibility: self.divisibility,
symbol: self.symbol,
}
}
}

Expand All @@ -70,23 +87,24 @@ pub(super) type RuneEntryValue = (
u8, // divisibility
(u128, u128), // etching
Option<MintEntryValue>, // mint parameters
u64, // mints
u128, // mints
u64, // number
u128, // premine
(u128, u32), // spaced rune
u128, // supply
Option<char>, // symbol
u32, // timestamp
);

#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize, Default)]
pub struct MintEntry {
pub cap: Option<u128>, // mint cap
pub deadline: Option<u32>, // unix timestamp
pub end: Option<u32>, // block height
pub limit: Option<u128>, // claim amount
}

type MintEntryValue = (
Option<u128>, // cap
Option<u32>, // deadline
Option<u32>, // end
Option<u128>, // limit
Expand All @@ -103,7 +121,6 @@ impl Default for RuneEntry {
number: 0,
premine: 0,
spaced_rune: SpacedRune::default(),
supply: 0,
symbol: None,
timestamp: 0,
}
Expand All @@ -123,7 +140,6 @@ impl Entry for RuneEntry {
number,
premine,
(rune, spacers),
supply,
symbol,
timestamp,
): RuneEntryValue,
Expand All @@ -141,7 +157,8 @@ impl Entry for RuneEntry {
high[14], high[15],
])
},
mint: mint.map(|(deadline, end, limit)| MintEntry {
mint: mint.map(|(cap, deadline, end, limit)| MintEntry {
cap,
deadline,
end,
limit,
Expand All @@ -153,7 +170,6 @@ impl Entry for RuneEntry {
rune: Rune(rune),
spacers,
},
supply,
symbol,
timestamp,
}
Expand All @@ -178,16 +194,16 @@ impl Entry for RuneEntry {
},
self.mint.map(
|MintEntry {
cap,
deadline,
end,
limit,
}| (deadline, end, limit),
}| (cap, deadline, end, limit),
),
self.mints,
self.number,
self.premine,
(self.spaced_rune.rune.0, self.spaced_rune.spacers),
self.supply,
self.symbol,
self.timestamp,
)
Expand Down Expand Up @@ -492,6 +508,7 @@ mod tests {
0x1E, 0x1F,
]),
mint: Some(MintEntry {
cap: Some(1),
deadline: Some(2),
end: Some(4),
limit: Some(5),
Expand All @@ -503,7 +520,6 @@ mod tests {
rune: Rune(7),
spacers: 8,
},
supply: 9,
symbol: Some('a'),
timestamp: 10,
};
Expand All @@ -515,12 +531,11 @@ mod tests {
0x0F0E0D0C0B0A09080706050403020100,
0x1F1E1D1C1B1A19181716151413121110,
),
Some((Some(2), Some(4), Some(5))),
Some((Some(1), Some(2), Some(4), Some(5))),
11,
6,
12,
(7, 8),
9,
Some('a'),
10,
);
Expand Down Expand Up @@ -550,4 +565,167 @@ mod tests {

assert_eq!(actual, expected);
}

#[test]
fn mintable() {
assert_eq!(
RuneEntry::default().mintable(Height(0), 0),
Err(MintError::Unmintable)
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
end: Some(1),
limit: Some(1000),
cap: Some(1),
..default()
}),
..default()
}
.mintable(Height(0), 0),
Ok(1000),
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
end: Some(1),
limit: Some(1000),
cap: Some(1),
..default()
}),
..default()
}
.mintable(Height(1), 0),
Err(MintError::End(1)),
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
deadline: Some(1),
limit: Some(1000),
cap: Some(1),
..default()
}),
..default()
}
.mintable(Height(0), 0),
Ok(1000),
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
deadline: Some(1),
limit: Some(1000),
cap: Some(1),
..default()
}),
..default()
}
.mintable(Height(0), 1),
Err(MintError::Deadline(1)),
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
cap: Some(1),
limit: Some(1000),
..default()
}),
mints: 0,
..default()
}
.mintable(Height(0), 0),
Ok(1000),
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
cap: Some(1),
limit: Some(1000),
..default()
}),
mints: 1,
..default()
}
.mintable(Height(0), 0),
Err(MintError::Cap(1)),
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
cap: None,
limit: Some(1000),
..default()
}),
mints: 0,
..default()
}
.mintable(Height(0), 0),
Err(MintError::Cap(0)),
);
}

#[test]
fn supply() {
assert_eq!(
RuneEntry {
mint: Some(MintEntry {
limit: Some(1000),
..default()
}),
mints: 0,
..default()
}
.supply(),
0
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
limit: Some(1000),
..default()
}),
mints: 1,
..default()
}
.supply(),
1000
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
limit: Some(1000),
..default()
}),
mints: 0,
premine: 1,
..default()
}
.supply(),
1
);

assert_eq!(
RuneEntry {
mint: Some(MintEntry {
limit: Some(1000),
..default()
}),
mints: 1,
premine: 1,
..default()
}
.supply(),
1001
);
}
}
2 changes: 1 addition & 1 deletion src/index/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ impl Context {
for (id, entry) in runes {
pretty_assert_eq!(
outstanding.get(id).copied().unwrap_or_default(),
entry.supply - entry.burned
entry.supply() - entry.burned
);
}
}
Expand Down
18 changes: 3 additions & 15 deletions src/index/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,8 @@ impl<'index> Updater<'index> {
.unwrap_or(0);

let mut rune_updater = RuneUpdater {
block_time: block.header.time,
burned: HashMap::new(),
client: &self.index.client,
height: self.height,
id_to_entry: &mut rune_id_to_rune_entry,
Expand All @@ -611,7 +613,6 @@ impl<'index> Updater<'index> {
runes,
sequence_number_to_rune_id: &mut sequence_number_to_rune_id,
statistic_to_count: &mut statistic_to_count,
block_time: block.header.time,
transaction_id_to_rune: &mut transaction_id_to_rune,
updates: HashMap::new(),
extension: Some(extension),
Expand All @@ -621,20 +622,7 @@ impl<'index> Updater<'index> {
rune_updater.index_runes(u32::try_from(i).unwrap(), tx, *txid)?;
}

for (rune_id, update) in rune_updater.updates {
let mut entry = RuneEntry::load(
rune_id_to_rune_entry
.get(&rune_id.store())?
.unwrap()
.value(),
);

entry.burned += update.burned;
entry.mints += update.mints;
entry.supply += update.supply;

rune_id_to_rune_entry.insert(&rune_id.store(), entry.store())?;
}
rune_updater.update()?;
}

height_to_block_header.insert(&self.height, &block.header.store())?;
Expand Down
Loading

0 comments on commit aec5ec0

Please sign in to comment.