Skip to content

Commit

Permalink
Update metadate to support payable constructors (#1100)
Browse files Browse the repository at this point in the history
* Add `payable` field to constructor metadata

* Bump metadata version to `V3`

* Update metadata tests for `payable` constructors
  • Loading branch information
HCastano authored Jan 18, 2022
1 parent e646ddd commit 47b1fa3
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 11 deletions.
2 changes: 2 additions & 0 deletions crates/lang/codegen/src/generator/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ impl Metadata<'_> {
.iter()
.filter_map(|attr| attr.extract_docs());
let selector_bytes = constructor.composed_selector().hex_lits();
let is_payable = constructor.is_payable();
let constructor = constructor.callable();
let ident = constructor.ident();
let args = constructor.inputs().map(Self::generate_dispatch_argument);
Expand All @@ -133,6 +134,7 @@ impl Metadata<'_> {
.args([
#( #args ),*
])
.payable(#is_payable)
.docs([
#( #docs ),*
])
Expand Down
6 changes: 4 additions & 2 deletions crates/metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ pub enum MetadataVersioned {
/// Version 1 of the contract metadata.
V1(MetadataVersionDeprecated),
/// Version 2 of the contract metadata.
V2(InkProject),
V2(MetadataVersionDeprecated),
/// Version 3 of the contract metadata.
V3(InkProject),
}

impl From<InkProject> for MetadataVersioned {
fn from(ink_project: InkProject) -> Self {
MetadataVersioned::V2(ink_project)
MetadataVersioned::V3(ink_project)
}
}

Expand Down
46 changes: 37 additions & 9 deletions crates/metadata/src/specs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ pub struct ConstructorSpec<F: Form = MetaForm> {
pub label: F::String,
/// The selector hash of the message.
pub selector: Selector,
/// If the constructor accepts any `value` from the caller.
pub payable: bool,
/// The parameters of the deployment handler.
pub args: Vec<MessageParamSpec<F>>,
/// The deployment handler documentation.
Expand All @@ -240,6 +242,7 @@ impl IntoPortable for ConstructorSpec {
ConstructorSpec {
label: self.label.into_portable(registry),
selector: self.selector,
payable: self.payable,
args: self
.args
.into_iter()
Expand All @@ -266,6 +269,11 @@ where
&self.selector
}

/// Returns if the constructor is payable by the caller.
pub fn payable(&self) -> &bool {
&self.payable
}

/// Returns the parameters of the deployment handler.
pub fn args(&self) -> &[MessageParamSpec<F>] {
&self.args
Expand All @@ -285,20 +293,21 @@ where
/// compile-time instead of at run-time. This is useful to better
/// debug code-gen macros.
#[must_use]
pub struct ConstructorSpecBuilder<Selector> {
pub struct ConstructorSpecBuilder<Selector, IsPayable> {
spec: ConstructorSpec,
marker: PhantomData<fn() -> Selector>,
marker: PhantomData<fn() -> (Selector, IsPayable)>,
}

impl ConstructorSpec {
/// Creates a new constructor spec builder.
pub fn from_label(
label: &'static str,
) -> ConstructorSpecBuilder<Missing<state::Selector>> {
) -> ConstructorSpecBuilder<Missing<state::Selector>, Missing<state::IsPayable>> {
ConstructorSpecBuilder {
spec: Self {
label,
selector: Selector::default(),
payable: Default::default(),
args: Vec::new(),
docs: Vec::new(),
},
Expand All @@ -307,9 +316,12 @@ impl ConstructorSpec {
}
}

impl ConstructorSpecBuilder<Missing<state::Selector>> {
impl<P> ConstructorSpecBuilder<Missing<state::Selector>, P> {
/// Sets the function selector of the message.
pub fn selector(self, selector: [u8; 4]) -> ConstructorSpecBuilder<state::Selector> {
pub fn selector(
self,
selector: [u8; 4],
) -> ConstructorSpecBuilder<state::Selector, P> {
ConstructorSpecBuilder {
spec: ConstructorSpec {
selector: selector.into(),
Expand All @@ -320,7 +332,23 @@ impl ConstructorSpecBuilder<Missing<state::Selector>> {
}
}

impl<S> ConstructorSpecBuilder<S> {
impl<S> ConstructorSpecBuilder<S, Missing<state::IsPayable>> {
/// Sets if the constructor is payable, thus accepting value for the caller.
pub fn payable(
self,
is_payable: bool,
) -> ConstructorSpecBuilder<S, state::IsPayable> {
ConstructorSpecBuilder {
spec: ConstructorSpec {
payable: is_payable,
..self.spec
},
marker: PhantomData,
}
}
}

impl<S, P> ConstructorSpecBuilder<S, P> {
/// Sets the input arguments of the message specification.
pub fn args<A>(self, args: A) -> Self
where
Expand All @@ -344,7 +372,7 @@ impl<S> ConstructorSpecBuilder<S> {
}
}

impl ConstructorSpecBuilder<state::Selector> {
impl ConstructorSpecBuilder<state::Selector, state::IsPayable> {
/// Finishes construction of the constructor.
pub fn done(self) -> ConstructorSpec {
self.spec
Expand All @@ -368,7 +396,7 @@ pub struct MessageSpec<F: Form = MetaForm> {
selector: Selector,
/// If the message is allowed to mutate the contract state.
mutates: bool,
/// If the message is payable by the caller.
/// If the message accepts any `value` from the caller.
payable: bool,
/// The parameters of the message.
args: Vec<MessageParamSpec<F>>,
Expand Down Expand Up @@ -508,7 +536,7 @@ impl<S, P, R> MessageSpecBuilder<S, Missing<state::Mutates>, P, R> {
}

impl<S, M, R> MessageSpecBuilder<S, M, Missing<state::IsPayable>, R> {
/// Sets if the message is mutable, thus taking `&mut self` or not thus taking `&self`.
/// Sets if the message is payable, thus accepting value for the caller.
pub fn payable(
self,
is_payable: bool,
Expand Down
8 changes: 8 additions & 0 deletions crates/metadata/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ fn spec_constructor_selector_must_serialize_to_hex() {
let label = "foo";
let cs = ConstructorSpec::from_label(label)
.selector(123_456_789u32.to_be_bytes())
.payable(true)
.done();
let mut registry = Registry::new();
let portable_spec = cs.into_portable(&mut registry);
Expand All @@ -40,6 +41,7 @@ fn spec_constructor_selector_must_serialize_to_hex() {
json,
json!({
"label": "foo",
"payable": true,
"selector": "0x075bcd15",
"args": [],
"docs": []
Expand All @@ -55,6 +57,7 @@ fn spec_contract_json() {
.constructors(vec![
ConstructorSpec::from_label("new")
.selector([94u8, 189u8, 136u8, 214u8])
.payable(true)
.args(vec![MessageParamSpec::new("init_value")
.of_type(TypeSpec::with_name_segs::<i32, _>(
vec!["i32"].into_iter().map(AsRef::as_ref),
Expand All @@ -64,6 +67,7 @@ fn spec_contract_json() {
.done(),
ConstructorSpec::from_label("default")
.selector([2u8, 34u8, 255u8, 24u8])
.payable(Default::default())
.args(Vec::new())
.docs(Vec::new())
.done(),
Expand Down Expand Up @@ -120,12 +124,14 @@ fn spec_contract_json() {
],
"docs": [],
"label": "new",
"payable": true,
"selector": "0x5ebd88d6"
},
{
"args": [],
"docs": [],
"label": "default",
"payable": false,
"selector": "0x0222ff18"
}
],
Expand Down Expand Up @@ -177,6 +183,7 @@ fn trim_docs() {
let cs = ConstructorSpec::from_label(label)
.selector(123_456_789u32.to_be_bytes())
.docs(vec![" foobar "])
.payable(Default::default())
.done();
let mut registry = Registry::new();
let compact_spec = cs.into_portable(&mut registry);
Expand All @@ -191,6 +198,7 @@ fn trim_docs() {
json,
json!({
"label": "foo",
"payable": false,
"selector": "0x075bcd15",
"args": [],
"docs": ["foobar"]
Expand Down

0 comments on commit 47b1fa3

Please sign in to comment.