Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6a1b8eb
Add entry point management for smart contracts
kpob Oct 31, 2025
966bcf9
wip
kpob Oct 31, 2025
1c83f0a
wip
kpob Nov 3, 2025
e168476
Merge branch 'fix/factory-module' into feature/factory-upgrade
kpob Nov 3, 2025
00f11a8
Merge branch 'fix/factory-module' into feature/factory-upgrade
kpob Nov 3, 2025
f01dd47
factory upgrade
kpob Nov 5, 2025
7804184
Rename factory to new_contract
kpob Nov 5, 2025
f4d60d2
wip
kpob Nov 7, 2025
d6756b8
wip
kpob Nov 12, 2025
a28f1ee
Refactor batch_upgrade_child_contract to use Bytes for names_to_upgra…
kpob Nov 13, 2025
749cb40
Refactor entry point access control to use groups for factory and upg…
kpob Nov 13, 2025
a930e9a
Add tests for factory upgrade functionality and error handling
kpob Nov 13, 2025
27372d9
Remove FactoryUpgradeArgs struct and related implementations from hos…
kpob Nov 13, 2025
1c24f30
Add FToken and FTokenFactory modules; update Odra.toml and host funct…
kpob Nov 13, 2025
6d863e9
Merge branch 'release/2.4.1' into feature/factory-upgrade
kpob Nov 13, 2025
9cf9265
Add ignore attribute to tests that do not work on odra vm
kpob Nov 13, 2025
1070884
Remove unnecessary runtime arguments from factory item tests
kpob Nov 14, 2025
0b9d0d0
Refactor FToken struct documentation and change upgrader_args visibil…
kpob Nov 14, 2025
6d10a9b
Add new entry points and schemas for contract upgrades
kpob Nov 14, 2025
3a056e3
update `batch_upgrade_child_contract` function to accept one argument
kpob Nov 19, 2025
7002831
Remove ignore attribute from factory module test
kpob Nov 19, 2025
167564f
Uncomment ignore attribute for tests that do not work on odra vm
kpob Nov 20, 2025
03ba014
Rename `is_reentrant` to `is_non_reentrant` in entry point definition…
kpob Nov 20, 2025
77014b1
Update BatchUpgradeArgs to use a map for runtime arguments and clarif…
kpob Nov 20, 2025
0eda282
Merge branch 'release/2.4.1' into feature/factory-upgrade
kpob Nov 20, 2025
e4ddbfe
Refactor contract address type to use `odra::prelude::Address` for co…
kpob Nov 20, 2025
fb35623
Refactor batch upgrade contract functions to use BTreeMap for argumen…
kpob Nov 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions core/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,48 @@ impl<T: CLTyped + ToBytes> EntrypointArgument for T {
}
}

/// A type representing arguments for batch upgrading child contracts.
pub struct BatchUpgradeArgs<T: Into<casper_types::RuntimeArgs>>(BTreeMap<String, T>);

impl<T: Into<casper_types::RuntimeArgs>> From<BTreeMap<String, T>> for BatchUpgradeArgs<T> {
fn from(val: BTreeMap<String, T>) -> Self {
BatchUpgradeArgs(val)
}
}

impl<T: Into<casper_types::RuntimeArgs>> EntrypointArgument for BatchUpgradeArgs<T> {
fn is_required() -> bool {
true
}

fn cl_type() -> CLType {
CLType::Map {
key: Box::new(CLType::String),
value: Box::new(CLType::List(Box::new(CLType::U8)))
}
}

fn insert_runtime_arg(self, name: &str, args: &mut RuntimeArgs) {
let mut args_map: BTreeMap<String, casper_types::bytesrepr::Bytes> = Default::default();
for (contract, v) in self.0 {
let rt: RuntimeArgs = v.into();
let bytes = ToBytes::to_bytes(&rt).unwrap();
args_map.insert(
contract.to_string(),
casper_types::bytesrepr::Bytes::from(bytes)
);
}
let _ = args.insert(name, args_map);
}

fn unwrap(value: Option<Self>, env: &ContractEnv) -> Self {
match value {
Some(v) => v,
None => env.revert(ExecutionError::UnwrapError)
}
}
}

/// Returns a Casper entrypoint argument representation.
/// If the parameter is not required, it returns `None`.
pub fn parameter<T: EntrypointArgument>(name: &str) -> Option<Parameter> {
Expand Down
3 changes: 3 additions & 0 deletions core/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ pub const CREATE_UPGRADE_GROUP: &str = "odra_cfg_create_upgrade_group";
/// The arg name for telling the installer that the contract is being upgraded.
pub const IS_UPGRADE_ARG: &str = "odra_cfg_is_upgrade";

/// The arg name for telling the installer that the contract is performing a factory upgrade.
pub const IS_FACTORY_UPGRADE_ARG: &str = "odra_cfg_is_factory_upgrade";

/// The arg name for telling the installer the previous contract version.
pub const PACKAGE_HASH_TO_UPGRADE_ARG: &str = "odra_cfg_package_hash_to_upgrade";

Expand Down
248 changes: 248 additions & 0 deletions core/src/entry_point.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
//! This module defines various types of entry points for smart contracts
use alloc::boxed::Box;
use alloc::{vec, vec::Vec};
use casper_types::{CLType, CLTyped, Parameter};

/// Represents different kinds of entry points in a contract.
pub enum EntryPoint {
/// A regular entry point.
Regular {
/// The name of the entry point.
name: &'static str,
/// The arguments for the entry point.
args: Vec<Option<Parameter>>,
/// The return type of the entry point.
ret_ty: CLType,
/// Indicates if the entry point is non-reentrant.
is_non_reentrant: bool,
/// Indicates if the entry point is payable.
is_payable: bool
},
/// A constructor entry point.
Constructor {
/// The arguments for the constructor.
args: Vec<Option<Parameter>>
},
/// A template entry point, used in a factory pattern.
Template {
/// The name of the entry point.
name: &'static str,
/// The arguments for the entry point.
args: Vec<Option<Parameter>>,
/// The return type of the entry point.
ret_ty: CLType
},
/// A factory entry point.
Factory {
/// The arguments for the factory.
args: Vec<Option<Parameter>>
},
/// A factory upgrade entry point.
FactoryUpgrade {
/// The arguments for the factory.
args: Vec<Option<Parameter>>
},
/// A factory batch upgrade entry point.
FactoryBatchUpgrade,
/// An upgrader entry point.
Upgrader {
/// The arguments for the upgrader entry point.
args: Vec<Option<Parameter>>
}
}

/// Extension trait for adding entry points to `casper_types::EntryPoints`.
pub trait EntityEntryPointsExt {
/// Adds an entry point to the collection.
fn add<T: Into<casper_types::EntityEntryPoint>>(&mut self, ep: T);
}

impl EntityEntryPointsExt for casper_types::EntryPoints {
fn add<T: Into<casper_types::EntityEntryPoint>>(&mut self, ep: T) {
self.add_entry_point(ep.into());
}
}

impl From<EntryPoint> for casper_types::EntityEntryPoint {
fn from(val: EntryPoint) -> Self {
match val {
EntryPoint::Regular {
name, args, ret_ty, ..
} => entry_point(name, args, ret_ty),
EntryPoint::Constructor { args } => constructor(args),
EntryPoint::Template { name, args, ret_ty } => template(name, args, ret_ty),
EntryPoint::Factory { args } => factory(args),
EntryPoint::Upgrader { args } => upgrader(args),
EntryPoint::FactoryUpgrade { args } => factory_upgrade(args),
EntryPoint::FactoryBatchUpgrade => factory_batch_upgrade()
}
}
}

impl TryFrom<EntryPoint> for crate::contract_def::Entrypoint {
type Error = &'static str;

fn try_from(val: EntryPoint) -> Result<Self, Self::Error> {
match val {
EntryPoint::Regular { name, args, ret_ty, is_non_reentrant, is_payable } => Ok(crate::contract_def::Entrypoint {
name: crate::prelude::string::String::from(name),
args: convert_args(args),
is_mutable: true,
return_ty: ret_ty,
ty: crate::contract_def::EntrypointType::Public,
attributes: match (is_non_reentrant, is_payable) {
(true, true) => vec![
crate::contract_def::EntrypointAttribute::NonReentrant,
crate::contract_def::EntrypointAttribute::Payable
],
(true, false) => vec![
crate::contract_def::EntrypointAttribute::NonReentrant
],
(false, true) => vec![
crate::contract_def::EntrypointAttribute::Payable
],
(false, false) => vec![]
}
}),
EntryPoint::Constructor { args } => Ok(crate::contract_def::Entrypoint {
name: crate::prelude::String::from("init"),
args: convert_args(args),
is_mutable: true,
return_ty: <() as crate::casper_types::CLTyped>::cl_type(),
ty: crate::contract_def::EntrypointType::Constructor,
attributes: crate::prelude::vec![]
}),
EntryPoint::Factory { args } => Ok(crate::contract_def::Entrypoint {
name: crate::prelude::string::String::from("new_contract"),
args: convert_args(args),
is_mutable: true,
return_ty: <(crate::prelude::Address, crate::casper_types::URef) as crate::casper_types::CLTyped>::cl_type(),
ty: crate::contract_def::EntrypointType::Public,
attributes: crate::prelude::vec![]
}),
EntryPoint::FactoryUpgrade { args } => Ok(crate::contract_def::Entrypoint {
name: crate::prelude::String::from("upgrade_child_contract"),
args: convert_args(args),
is_mutable: true,
return_ty: <() as crate::casper_types::CLTyped>::cl_type(),
ty: crate::contract_def::EntrypointType::Public,
attributes: crate::prelude::vec![]
}),
EntryPoint::FactoryBatchUpgrade => Ok(crate::contract_def::Entrypoint {
name: crate::prelude::String::from("batch_upgrade_child_contract"),
args: crate::prelude::vec![
crate::contract_def::Argument {
name: crate::prelude::String::from("args"),
ty: Vec::<u8>::cl_type(),
is_ref: false,
is_slice: false,
is_required: true
}
],
is_mutable: true,
return_ty: <() as crate::casper_types::CLTyped>::cl_type(),
ty: crate::contract_def::EntrypointType::Public,
attributes: crate::prelude::vec![]
}),
_ => Err("Conversion not implemented for this entry point type"),
}
}
}

fn convert_args(args: Vec<Option<Parameter>>) -> Vec<crate::contract_def::Argument> {
args.into_iter()
.flatten()
.map(|p| crate::contract_def::Argument {
name: crate::prelude::string::String::from(p.name()),
ty: p.cl_type().clone(),
is_ref: false,
is_slice: false,
is_required: true
})
.collect()
}

fn entry_point(
name: &str,
args: Vec<Option<Parameter>>,
ret_ty: CLType
) -> casper_types::EntityEntryPoint {
casper_types::EntityEntryPoint::new(
name,
args.into_iter().flatten().collect(),
ret_ty,
casper_types::EntryPointAccess::Public,
casper_types::EntryPointType::Called,
casper_types::EntryPointPayment::Caller
)
}

fn constructor(args: Vec<Option<Parameter>>) -> casper_types::EntityEntryPoint {
casper_types::EntityEntryPoint::new(
"init",
args.into_iter().flatten().collect(),
CLType::Unit,
casper_types::EntryPointAccess::Groups(vec![casper_types::Group::new("constructor_group")]),
casper_types::EntryPointType::Called,
casper_types::EntryPointPayment::Caller
)
}

fn template(
name: &str,
args: Vec<Option<Parameter>>,
ret_ty: CLType
) -> casper_types::EntityEntryPoint {
casper_types::EntityEntryPoint::new(
name,
args.into_iter().flatten().collect(),
ret_ty,
casper_types::EntryPointAccess::Template,
casper_types::EntryPointType::Called,
casper_types::EntryPointPayment::Caller
)
}

fn factory(args: Vec<Option<Parameter>>) -> casper_types::EntityEntryPoint {
casper_types::EntityEntryPoint::new(
"new_contract",
args.into_iter().flatten().collect(),
CLType::Tuple2([Box::new(CLType::Key), Box::new(CLType::URef)]),
casper_types::EntryPointAccess::Public,
casper_types::EntryPointType::Factory,
casper_types::EntryPointPayment::Caller
)
}

fn factory_upgrade(args: Vec<Option<Parameter>>) -> casper_types::EntityEntryPoint {
casper_types::EntityEntryPoint::new(
"upgrade_child_contract",
args.into_iter().flatten().collect(),
CLType::Unit,
casper_types::EntryPointAccess::Groups(vec![casper_types::Group::new("factory_group")]),
casper_types::EntryPointType::Called,
casper_types::EntryPointPayment::Caller
)
}

fn factory_batch_upgrade() -> casper_types::EntityEntryPoint {
casper_types::EntityEntryPoint::new(
"batch_upgrade_child_contract",
vec![casper_types::Parameter::new("args", Vec::<u8>::cl_type())],
CLType::Unit,
casper_types::EntryPointAccess::Groups(vec![casper_types::Group::new("factory_group")]),
casper_types::EntryPointType::Called,
casper_types::EntryPointPayment::Caller
)
}

fn upgrader(args: Vec<Option<Parameter>>) -> casper_types::EntityEntryPoint {
casper_types::EntityEntryPoint::new(
"upgrade",
args.into_iter().flatten().collect(),
CLType::Unit,
casper_types::EntryPointAccess::Groups(vec![casper_types::Group::new("upgrader_group")]),
casper_types::EntryPointType::Called,
casper_types::EntryPointPayment::Caller
)
}
1 change: 1 addition & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod contract_def;
mod contract_env;
mod contract_register;
pub mod crypto;
pub mod entry_point;
pub mod entry_point_callback;
mod error;
mod external;
Expand Down
3 changes: 3 additions & 0 deletions examples/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[build]
target-dir = "../target"

[alias]
rl = "run --features=livenet"
11 changes: 10 additions & 1 deletion examples/Odra.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,13 @@ fqn = "features::upgrade::CounterV2"
fqn = "factory::CounterFactory"

[[contracts]]
fqn = "factory::Counter"
fqn = "factory::BetterCounterFactory"

[[contracts]]
fqn = "factory::Counter"

[[contracts]]
fqn = "factory::token::FTokenFactory"

[[contracts]]
fqn = "factory::token::FToken"
Loading