From bff32810f76d599a10a76815ed126e615934ce5b Mon Sep 17 00:00:00 2001 From: Jan Hoffmann Date: Thu, 28 Mar 2024 16:55:00 +0100 Subject: [PATCH] burn implemented Co-authored-by: igor-casper --- Cargo.lock | 8 + execution_engine/src/runtime/mint_internal.rs | 4 + execution_engine/src/runtime/mod.rs | 8 + execution_engine/src/runtime_context/mod.rs | 4 +- .../tests/src/test/system_contracts/mint.rs | 216 ++++++++++++++++++ .../tests/src/test/system_contracts/mod.rs | 1 + resources/local/chainspec.toml.in | 3 +- resources/production/chainspec.toml | 3 +- .../contracts/client/burn/Cargo.toml | 16 ++ .../contracts/client/burn/src/main.rs | 89 ++++++++ storage/src/system/mint.rs | 52 +++-- storage/src/system/mint/detail.rs | 39 ++++ storage/src/system/mint/mint_native.rs | 4 + storage/src/system/mint/runtime_provider.rs | 3 + types/src/chainspec/vm_config/mint_costs.rs | 14 ++ types/src/system/mint/constants.rs | 2 + types/src/system/mint/entry_points.rs | 17 +- 17 files changed, 455 insertions(+), 28 deletions(-) create mode 100644 execution_engine_testing/tests/src/test/system_contracts/mint.rs create mode 100644 smart_contracts/contracts/client/burn/Cargo.toml create mode 100644 smart_contracts/contracts/client/burn/src/main.rs create mode 100644 storage/src/system/mint/detail.rs diff --git a/Cargo.lock b/Cargo.lock index 5207b77f53..6bfd0c03d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,6 +427,14 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "burn" +version = "0.1.0" +dependencies = [ + "casper-contract", + "casper-types", +] + [[package]] name = "bytemuck" version = "1.14.0" diff --git a/execution_engine/src/runtime/mint_internal.rs b/execution_engine/src/runtime/mint_internal.rs index 8607a6eed4..c1c7e4c659 100644 --- a/execution_engine/src/runtime/mint_internal.rs +++ b/execution_engine/src/runtime/mint_internal.rs @@ -97,6 +97,10 @@ where ProviderError::AddressableEntityByAccountHash(account_hash) }) } + + fn is_valid_uref(&self, uref: &URef) -> bool { + self.context.access_rights().has_access_rights_to_uref(uref) + } } // TODO: update Mint + StorageProvider to better handle errors diff --git a/execution_engine/src/runtime/mod.rs b/execution_engine/src/runtime/mod.rs index 7a2899f18f..34ff95494e 100644 --- a/execution_engine/src/runtime/mod.rs +++ b/execution_engine/src/runtime/mod.rs @@ -609,6 +609,14 @@ where let result: Result<(), mint::Error> = mint_runtime.reduce_total_supply(amount); CLValue::from_t(result).map_err(Self::reverter) })(), + mint::METHOD_BURN => (|| { + mint_runtime.charge_system_contract_call(mint_costs.burn)?; + + let purse: URef = Self::get_named_argument(runtime_args, mint::ARG_PURSE)?; + let amount: U512 = Self::get_named_argument(runtime_args, mint::ARG_AMOUNT)?; + let result: Result<(), mint::Error> = mint_runtime.burn(purse, amount); + CLValue::from_t(result).map_err(Self::reverter) + })(), // Type: `fn create() -> URef` mint::METHOD_CREATE => (|| { mint_runtime.charge_system_contract_call(mint_costs.create)?; diff --git a/execution_engine/src/runtime_context/mod.rs b/execution_engine/src/runtime_context/mod.rs index d3b88098f5..0ac96eaa5a 100644 --- a/execution_engine/src/runtime_context/mod.rs +++ b/execution_engine/src/runtime_context/mod.rs @@ -669,7 +669,7 @@ where } /// Validates whether keys used in the `value` are not forged. - fn validate_value(&self, value: &StoredValue) -> Result<(), ExecError> { + pub(crate) fn validate_value(&self, value: &StoredValue) -> Result<(), ExecError> { match value { StoredValue::CLValue(cl_value) => self.validate_cl_value(cl_value), StoredValue::Account(_) => Ok(()), @@ -743,7 +743,7 @@ where } /// Validates if a [`Key`] refers to a [`URef`] and has a write bit set. - fn validate_writeable(&self, key: &Key) -> Result<(), ExecError> { + pub(crate) fn validate_writeable(&self, key: &Key) -> Result<(), ExecError> { if self.is_writeable(key) { Ok(()) } else { diff --git a/execution_engine_testing/tests/src/test/system_contracts/mint.rs b/execution_engine_testing/tests/src/test/system_contracts/mint.rs new file mode 100644 index 0000000000..521d90f0ed --- /dev/null +++ b/execution_engine_testing/tests/src/test/system_contracts/mint.rs @@ -0,0 +1,216 @@ +use casper_engine_test_support::{ + auction, ExecuteRequestBuilder, LmdbWasmTestBuilder, DEFAULT_ACCOUNT_ADDR, +}; +use casper_types::{runtime_args, ProtocolVersion, URef, U512}; + +use tempfile::TempDir; + +const TEST_DELEGATOR_INITIAL_ACCOUNT_BALANCE: u64 = 1_000_000 * 1_000_000_000; + +const CONTRACT_BURN: &str = "burn.wasm"; +const CONTRACT_TRANSFER_TO_NAMED_PURSE: &str = "transfer_to_named_purse.wasm"; + +const ARG_AMOUNT: &str = "amount"; + +const ARG_PURSE_NAME: &str = "purse_name"; + +#[ignore] +#[test] +fn should_empty_purse_when_burning_above_balance() { + let data_dir = TempDir::new().expect("should create temp dir"); + let mut builder = LmdbWasmTestBuilder::new(data_dir.as_ref()); + let source = *DEFAULT_ACCOUNT_ADDR; + + let delegator_keys = auction::generate_public_keys(1); + let validator_keys = auction::generate_public_keys(1); + + auction::run_genesis_and_create_initial_accounts( + &mut builder, + &validator_keys, + delegator_keys + .iter() + .map(|public_key| public_key.to_account_hash()) + .collect::>(), + U512::from(TEST_DELEGATOR_INITIAL_ACCOUNT_BALANCE), + ); + + let initial_supply = builder.total_supply(None); + let purse_name = "purse"; + let purse_amount = U512::from(10_000_000_000u64); + + // Create purse and transfer tokens to it + let exec_request = ExecuteRequestBuilder::standard( + source, + CONTRACT_TRANSFER_TO_NAMED_PURSE, + runtime_args! { + ARG_PURSE_NAME => purse_name, + ARG_AMOUNT => purse_amount, + }, + ) + .build(); + + builder.exec(exec_request).expect_success().commit(); + + let account = builder + .get_entity_with_named_keys_by_account_hash(source) + .expect("should have account"); + + let purse_uref: URef = account + .named_keys() + .get(purse_name) + .unwrap() + .into_uref() + .expect("should be uref"); + + assert_eq!( + builder + .get_purse_balance_result(ProtocolVersion::V2_0_0, purse_uref) + .motes() + .cloned() + .unwrap(), + purse_amount + ); + + // Burn part of tokens in a purse + let num_of_tokens_to_burn = U512::from(2_000_000_000u64); + let num_of_tokens_after_burn = U512::from(8_000_000_000u64); + + let exec_request = ExecuteRequestBuilder::standard( + source, + CONTRACT_BURN, + runtime_args! { + ARG_PURSE_NAME => purse_name, + ARG_AMOUNT => num_of_tokens_to_burn, + }, + ) + .build(); + + builder.exec(exec_request).expect_success().commit(); + + assert_eq!( + builder + .get_purse_balance_result(ProtocolVersion::V2_0_0, purse_uref) + .motes() + .cloned() + .unwrap(), + num_of_tokens_after_burn + ); + + // Burn rest of tokens in a purse + let num_of_tokens_to_burn = U512::from(8_000_000_000u64); + let num_of_tokens_after_burn = U512::zero(); + + let exec_request = ExecuteRequestBuilder::standard( + source, + CONTRACT_BURN, + runtime_args! { + ARG_PURSE_NAME => purse_name, + ARG_AMOUNT => num_of_tokens_to_burn, + }, + ) + .build(); + + builder.exec(exec_request).expect_success().commit(); + + assert_eq!( + builder + .get_purse_balance_result(ProtocolVersion::V2_0_0, purse_uref) + .motes() + .cloned() + .unwrap(), + num_of_tokens_after_burn + ); + + let supply_after_burns = builder.total_supply(None); + let expected_supply_after_burns = initial_supply - U512::from(10_000_000_000u64); + + assert_eq!(supply_after_burns, expected_supply_after_burns); +} + +#[ignore] +#[test] +fn should_not_burn_excess_tokens() { + let data_dir = TempDir::new().expect("should create temp dir"); + let mut builder = LmdbWasmTestBuilder::new(data_dir.as_ref()); + let source = *DEFAULT_ACCOUNT_ADDR; + + let delegator_keys = auction::generate_public_keys(1); + let validator_keys = auction::generate_public_keys(1); + + auction::run_genesis_and_create_initial_accounts( + &mut builder, + &validator_keys, + delegator_keys + .iter() + .map(|public_key| public_key.to_account_hash()) + .collect::>(), + U512::from(TEST_DELEGATOR_INITIAL_ACCOUNT_BALANCE), + ); + + let initial_supply = builder.total_supply(None); + let purse_name = "purse"; + let purse_amount = U512::from(10_000_000_000u64); + + // Create purse and transfer tokens to it + let exec_request = ExecuteRequestBuilder::standard( + source, + CONTRACT_TRANSFER_TO_NAMED_PURSE, + runtime_args! { + ARG_PURSE_NAME => purse_name, + ARG_AMOUNT => purse_amount, + }, + ) + .build(); + + builder.exec(exec_request).expect_success().commit(); + + let account = builder + .get_entity_with_named_keys_by_account_hash(source) + .expect("should have account"); + + let purse_uref: URef = account + .named_keys() + .get(purse_name) + .unwrap() + .into_uref() + .expect("should be uref"); + + assert_eq!( + builder + .get_purse_balance_result(ProtocolVersion::V2_0_0, purse_uref) + .motes() + .cloned() + .unwrap(), + purse_amount + ); + + // Try to burn more then in a purse + let num_of_tokens_to_burn = U512::MAX; + let num_of_tokens_after_burn = U512::zero(); + + let exec_request = ExecuteRequestBuilder::standard( + source, + CONTRACT_BURN, + runtime_args! { + ARG_PURSE_NAME => purse_name, + ARG_AMOUNT => num_of_tokens_to_burn, + }, + ) + .build(); + + builder.exec(exec_request).expect_success().commit(); + + assert_eq!( + builder + .get_purse_balance_result(ProtocolVersion::V2_0_0, purse_uref) + .motes() + .cloned() + .unwrap(), + num_of_tokens_after_burn, + ); + + let supply_after_burns = builder.total_supply(None); + let expected_supply_after_burns = initial_supply - U512::from(10_000_000_000u64); + + assert_eq!(supply_after_burns, expected_supply_after_burns); +} diff --git a/execution_engine_testing/tests/src/test/system_contracts/mod.rs b/execution_engine_testing/tests/src/test/system_contracts/mod.rs index 9a75a324de..a2fe0ef6ef 100644 --- a/execution_engine_testing/tests/src/test/system_contracts/mod.rs +++ b/execution_engine_testing/tests/src/test/system_contracts/mod.rs @@ -2,5 +2,6 @@ mod auction; mod auction_bidding; mod genesis; mod handle_payment; +mod mint; mod standard_payment; mod upgrade; diff --git a/resources/local/chainspec.toml.in b/resources/local/chainspec.toml.in index 3de356c90e..22039595e0 100644 --- a/resources/local/chainspec.toml.in +++ b/resources/local/chainspec.toml.in @@ -295,6 +295,7 @@ mint = 2_500_000_000 reduce_total_supply = 10_000 create = 2_500_000_000 balance = 10_000 +burn = 10_000 transfer = 10_000 read_base_round_reward = 10_000 mint_into_existing_purse = 2_500_000_000 @@ -313,4 +314,4 @@ pay = 10_000 upper_threshold = 90 lower_threshold = 50 max_gas_price = 3 -min_gas_price = 1 \ No newline at end of file +min_gas_price = 1 diff --git a/resources/production/chainspec.toml b/resources/production/chainspec.toml index d7dfe953ca..f20590c526 100644 --- a/resources/production/chainspec.toml +++ b/resources/production/chainspec.toml @@ -306,6 +306,7 @@ mint = 2_500_000_000 reduce_total_supply = 10_000 create = 2_500_000_000 balance = 10_000 +burn = 10_000 transfer = 10_000 read_base_round_reward = 10_000 mint_into_existing_purse = 2_500_000_000 @@ -324,4 +325,4 @@ pay = 10_000 upper_threshold = 90 lower_threshold = 50 max_gas_price = 3 -min_gas_price = 1 \ No newline at end of file +min_gas_price = 1 diff --git a/smart_contracts/contracts/client/burn/Cargo.toml b/smart_contracts/contracts/client/burn/Cargo.toml new file mode 100644 index 0000000000..f9949db688 --- /dev/null +++ b/smart_contracts/contracts/client/burn/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "burn" +version = "0.1.0" +authors = ["Igor Bunar ", "Jan Hoffmann "] +edition = "2021" + +[[bin]] +name = "burn" +path = "src/main.rs" +bench = false +doctest = false +test = false + +[dependencies] +casper-contract = { path = "../../../contract" } +casper-types = { path = "../../../../types" } diff --git a/smart_contracts/contracts/client/burn/src/main.rs b/smart_contracts/contracts/client/burn/src/main.rs new file mode 100644 index 0000000000..545fedafd3 --- /dev/null +++ b/smart_contracts/contracts/client/burn/src/main.rs @@ -0,0 +1,89 @@ +#![no_std] +#![no_main] + +extern crate alloc; +use alloc::{string::String, vec::Vec}; + +use casper_contract::{ + contract_api::{account, alloc_bytes, runtime, system}, + ext_ffi, + unwrap_or_revert::UnwrapOrRevert, +}; +use casper_types::{api_error, bytesrepr, runtime_args, system::mint, ApiError, Key, URef, U512}; + +const ARG_PURSE_NAME: &str = "purse_name"; + +fn burn(uref: URef, amount: U512) -> Result<(), mint::Error> { + let contract_hash = system::get_mint(); + let args = runtime_args! { + mint::ARG_PURSE => uref, + mint::ARG_AMOUNT => amount, + }; + runtime::call_contract(contract_hash, mint::METHOD_BURN, args) +} + +#[no_mangle] +pub extern "C" fn call() { + let purse_uref = match get_named_arg_option::(ARG_PURSE_NAME) { + Some(name) => { + // if a key was provided and there is no value under it we revert + // to prevent user from accidentaly burning tokens from the main purse + // eg. if they make a typo + let Some(Key::URef(purse_uref)) = runtime::get_key(&name) else { + runtime::revert(ApiError::InvalidPurseName) + }; + purse_uref + } + None => account::get_main_purse(), + }; + let amount: U512 = runtime::get_named_arg(mint::ARG_AMOUNT); + + burn(purse_uref, amount).unwrap_or_revert(); +} + +fn get_named_arg_size(name: &str) -> Option { + let mut arg_size: usize = 0; + let ret = unsafe { + ext_ffi::casper_get_named_arg_size( + name.as_bytes().as_ptr(), + name.len(), + &mut arg_size as *mut usize, + ) + }; + match api_error::result_from(ret) { + Ok(_) => Some(arg_size), + Err(ApiError::MissingArgument) => None, + Err(e) => runtime::revert(e), + } +} + +fn get_named_arg_option(name: &str) -> Option { + let arg_size = get_named_arg_size(name).unwrap_or_revert_with(ApiError::MissingArgument); + let arg_bytes = if arg_size > 0 { + let res = { + let data_non_null_ptr = alloc_bytes(arg_size); + let ret = unsafe { + ext_ffi::casper_get_named_arg( + name.as_bytes().as_ptr(), + name.len(), + data_non_null_ptr.as_ptr(), + arg_size, + ) + }; + let data = + unsafe { Vec::from_raw_parts(data_non_null_ptr.as_ptr(), arg_size, arg_size) }; + if ret != 0 { + return None; + } + data + }; + res + } else { + // Avoids allocation with 0 bytes and a call to get_named_arg + Vec::new() + }; + + let deserialized_data = + bytesrepr::deserialize(arg_bytes).unwrap_or_revert_with(ApiError::InvalidArgument); + Some(deserialized_data) +} diff --git a/storage/src/system/mint.rs b/storage/src/system/mint.rs index 71c7ff331f..e97ea36eaf 100644 --- a/storage/src/system/mint.rs +++ b/storage/src/system/mint.rs @@ -1,3 +1,4 @@ +pub(crate) mod detail; mod mint_native; pub mod runtime_provider; pub mod storage_provider; @@ -51,6 +52,33 @@ pub trait Mint: RuntimeProvider + StorageProvider + SystemProvider { Ok(purse_uref) } + /// Burns native tokens. + fn burn(&mut self, purse: URef, amount: U512) -> Result<(), Error> { + if !purse.is_writeable() { + return Err(Error::ForgedReference); + } + if !self.is_valid_uref(&purse) { + return Err(Error::ForgedReference); + } + + let source_balance: U512 = match self.read_balance(purse)? { + Some(source_balance) => source_balance, + None => return Err(Error::PurseNotFound), + }; + + let new_balance = match source_balance.checked_sub(amount) { + Some(value) => value, + None => U512::zero(), + }; + + // source_balance is >= than new_balance + // this should block user from reducing totaly supply beyond what they own + let burned_amount = source_balance - new_balance; + + self.write_balance(purse, new_balance)?; + detail::reduce_total_supply_unsafe(self, burned_amount) + } + /// Reduce total supply by `amount`. Returns unit on success, otherwise /// an error. fn reduce_total_supply(&mut self, amount: U512) -> Result<(), Error> { @@ -60,29 +88,7 @@ pub trait Mint: RuntimeProvider + StorageProvider + SystemProvider { return Err(Error::InvalidTotalSupplyReductionAttempt); } - if amount.is_zero() { - return Ok(()); // no change to supply - } - - // get total supply or error - let total_supply_uref = match self.get_key(TOTAL_SUPPLY_KEY) { - Some(Key::URef(uref)) => uref, - Some(_) => return Err(Error::MissingKey), // TODO - None => return Err(Error::MissingKey), - }; - let total_supply: U512 = self - .read(total_supply_uref)? - .ok_or(Error::TotalSupplyNotFound)?; - - // decrease total supply - let reduced_total_supply = total_supply - .checked_sub(amount) - .ok_or(Error::ArithmeticOverflow)?; - - // update total supply - self.write_amount(total_supply_uref, reduced_total_supply)?; - - Ok(()) + detail::reduce_total_supply_unsafe(self, amount) } /// Read balance of given `purse`. diff --git a/storage/src/system/mint/detail.rs b/storage/src/system/mint/detail.rs new file mode 100644 index 0000000000..cc102629e2 --- /dev/null +++ b/storage/src/system/mint/detail.rs @@ -0,0 +1,39 @@ +use casper_types::{ + system::{ + mint, + mint::{Error, TOTAL_SUPPLY_KEY}, + }, + Key, U512, +}; + +use crate::system::mint::Mint; + +// Please do not expose this to the user! +pub(crate) fn reduce_total_supply_unsafe

(mint: &mut P, amount: U512) -> Result<(), mint::Error> +where + P: Mint + ?Sized, +{ + if amount.is_zero() { + return Ok(()); // no change to supply + } + + // get total supply or error + let total_supply_uref = match mint.get_key(TOTAL_SUPPLY_KEY) { + Some(Key::URef(uref)) => uref, + Some(_) => return Err(Error::MissingKey), // TODO + None => return Err(Error::MissingKey), + }; + let total_supply: U512 = mint + .read(total_supply_uref)? + .ok_or(Error::TotalSupplyNotFound)?; + + // decrease total supply + let reduced_total_supply = total_supply + .checked_sub(amount) + .ok_or(Error::ArithmeticOverflow)?; + + // update total supply + mint.write_amount(total_supply_uref, reduced_total_supply)?; + + Ok(()) +} diff --git a/storage/src/system/mint/mint_native.rs b/storage/src/system/mint/mint_native.rs index 85b648b3c3..5d1dab3bb8 100644 --- a/storage/src/system/mint/mint_native.rs +++ b/storage/src/system/mint/mint_native.rs @@ -102,6 +102,10 @@ where fn allow_unrestricted_transfers(&self) -> bool { self.transfer_config().allow_unrestricted_transfers() } + + fn is_valid_uref(&self, uref: &URef) -> bool { + self.access_rights().has_access_rights_to_uref(uref) + } } impl StorageProvider for RuntimeNative diff --git a/storage/src/system/mint/runtime_provider.rs b/storage/src/system/mint/runtime_provider.rs index 8ade0171ec..57b45b6d51 100644 --- a/storage/src/system/mint/runtime_provider.rs +++ b/storage/src/system/mint/runtime_provider.rs @@ -43,4 +43,7 @@ pub trait RuntimeProvider { /// Checks if users can perform unrestricted transfers. This option is valid only for private /// chains. fn allow_unrestricted_transfers(&self) -> bool; + + /// Validate URef against context access rights. + fn is_valid_uref(&self, uref: &URef) -> bool; } diff --git a/types/src/chainspec/vm_config/mint_costs.rs b/types/src/chainspec/vm_config/mint_costs.rs index 4997468b5f..6039813268 100644 --- a/types/src/chainspec/vm_config/mint_costs.rs +++ b/types/src/chainspec/vm_config/mint_costs.rs @@ -14,6 +14,8 @@ use crate::bytesrepr::{self, FromBytes, ToBytes}; pub const DEFAULT_MINT_COST: u32 = 2_500_000_000; /// Default cost of the `reduce_total_supply` mint entry point. pub const DEFAULT_REDUCE_TOTAL_SUPPLY_COST: u32 = 10_000; +/// Default cost of the `burn` mint entry point. +pub const DEFAULT_BURN_COST: u32 = 10_000; /// Default cost of the `create` mint entry point. pub const DEFAULT_CREATE_COST: u32 = 2_500_000_000; /// Default cost of the `balance` mint entry point. @@ -35,6 +37,8 @@ pub struct MintCosts { pub mint: u32, /// Cost of calling the `reduce_total_supply` entry point. pub reduce_total_supply: u32, + /// Cost of calling the `burn` entry point. + pub burn: u32, /// Cost of calling the `create` entry point. pub create: u32, /// Cost of calling the `balance` entry point. @@ -52,6 +56,7 @@ impl Default for MintCosts { Self { mint: DEFAULT_MINT_COST, reduce_total_supply: DEFAULT_REDUCE_TOTAL_SUPPLY_COST, + burn: DEFAULT_BURN_COST, create: DEFAULT_CREATE_COST, balance: DEFAULT_BALANCE_COST, transfer: DEFAULT_TRANSFER_COST, @@ -68,6 +73,7 @@ impl ToBytes for MintCosts { let Self { mint, reduce_total_supply, + burn, create, balance, transfer, @@ -82,6 +88,7 @@ impl ToBytes for MintCosts { ret.append(&mut transfer.to_bytes()?); ret.append(&mut read_base_round_reward.to_bytes()?); ret.append(&mut mint_into_existing_purse.to_bytes()?); + ret.append(&mut burn.to_bytes()?); Ok(ret) } @@ -90,6 +97,7 @@ impl ToBytes for MintCosts { let Self { mint, reduce_total_supply, + burn, create, balance, transfer, @@ -99,6 +107,7 @@ impl ToBytes for MintCosts { mint.serialized_length() + reduce_total_supply.serialized_length() + + burn.serialized_length() + create.serialized_length() + balance.serialized_length() + transfer.serialized_length() @@ -116,11 +125,13 @@ impl FromBytes for MintCosts { let (transfer, rem) = FromBytes::from_bytes(rem)?; let (read_base_round_reward, rem) = FromBytes::from_bytes(rem)?; let (mint_into_existing_purse, rem) = FromBytes::from_bytes(rem)?; + let (burn, rem) = FromBytes::from_bytes(rem)?; Ok(( Self { mint, reduce_total_supply, + burn, create, balance, transfer, @@ -137,6 +148,7 @@ impl Distribution for Standard { fn sample(&self, rng: &mut R) -> MintCosts { MintCosts { mint: rng.gen(), + burn: rng.gen(), reduce_total_supply: rng.gen(), create: rng.gen(), balance: rng.gen(), @@ -158,6 +170,7 @@ pub mod gens { pub fn mint_costs_arb()( mint in num::u32::ANY, reduce_total_supply in num::u32::ANY, + burn in num::u32::ANY, create in num::u32::ANY, balance in num::u32::ANY, transfer in num::u32::ANY, @@ -167,6 +180,7 @@ pub mod gens { MintCosts { mint, reduce_total_supply, + burn, create, balance, transfer, diff --git a/types/src/system/mint/constants.rs b/types/src/system/mint/constants.rs index cffada448e..b49ab5c94f 100644 --- a/types/src/system/mint/constants.rs +++ b/types/src/system/mint/constants.rs @@ -17,6 +17,8 @@ pub const ARG_ROUND_SEIGNIORAGE_RATE: &str = "round_seigniorage_rate"; pub const METHOD_MINT: &str = "mint"; /// Named constant for method `reduce_total_supply`. pub const METHOD_REDUCE_TOTAL_SUPPLY: &str = "reduce_total_supply"; +/// Named constant for method `burn`. +pub const METHOD_BURN: &str = "burn"; /// Named constant for (synthetic) method `create` pub const METHOD_CREATE: &str = "create"; /// Named constant for method `balance`. diff --git a/types/src/system/mint/entry_points.rs b/types/src/system/mint/entry_points.rs index 6002b3383b..b5a5b6508d 100644 --- a/types/src/system/mint/entry_points.rs +++ b/types/src/system/mint/entry_points.rs @@ -3,7 +3,7 @@ use alloc::boxed::Box; use crate::{ addressable_entity::Parameters, system::mint::{ - ARG_AMOUNT, ARG_ID, ARG_PURSE, ARG_SOURCE, ARG_TARGET, ARG_TO, METHOD_BALANCE, + ARG_AMOUNT, ARG_ID, ARG_PURSE, ARG_SOURCE, ARG_TARGET, ARG_TO, METHOD_BALANCE, METHOD_BURN, METHOD_CREATE, METHOD_MINT, METHOD_MINT_INTO_EXISTING_PURSE, METHOD_READ_BASE_ROUND_REWARD, METHOD_REDUCE_TOTAL_SUPPLY, METHOD_TRANSFER, }, @@ -38,6 +38,21 @@ pub fn mint_entry_points() -> EntryPoints { ); entry_points.add_entry_point(entry_point); + let entry_point = EntryPoint::new( + METHOD_BURN, + vec![ + Parameter::new(ARG_PURSE, CLType::URef), + Parameter::new(ARG_AMOUNT, CLType::U512), + ], + CLType::Result { + ok: Box::new(CLType::Unit), + err: Box::new(CLType::U8), + }, + EntryPointAccess::Public, + EntryPointType::AddressableEntity, + ); + entry_points.add_entry_point(entry_point); + let entry_point = EntryPoint::new( METHOD_CREATE, Parameters::new(),