Attribute Macro concordium_std::receive

source ·
#[receive]
Expand description

Derive the appropriate export for an annotated receive function.

This macro requires the following items to be present

  • contract = "<contract-name>" where <contract-name> is the name of the smart contract.
  • name = "<receive-name>" where <receive-name> is the name of the receive function, or the fallback option. The generated function is exported as <contract-name>.<receive-name>, or if fallback is given, as <contract-name>..Contract name and receive name is required to be unique in the module. In particular, a contract may have only a single fallback method.

The annotated function must be of a specific type, which depends on the enabled attributes. Without any of the optional attributes the function must have a signature of

#[receive(contract = "my_contract", name = "some_receive")]
fn contract_receive(
    ctx: &ReceiveContext,
    host: &Host<MyState>
) -> ReceiveResult<MyReturnValue> {...}

Where the ReceiveContext, Host, and ReceiveResult are from concordium-std and MyState and MyReturnValue are user-defined types.

§Optional attributes

§payable: Make function accept an amount of CCD

Without setting the payable attribute, the function will reject any non-zero amount of CCD, supplied with the transaction. This means we are required to explicitly mark our functions as payable, if they are to accept CCD.

Setting the payable attribute changes the required signature to include an extra argument of type Amount, allowing the function to access the amount of CCD supplied with the transaction.

§Example

#[receive(contract = "my_contract", name = "some_receive", payable)]
fn contract_receive(
    ctx: &ReceiveContext,
    host: &Host<MyState>,
    amount: Amount
) -> ReceiveResult<MyReturnValue> {...}

§mutable: Function can mutate the state

Setting the mutable attribute changes the required signature so the host becomes a mutable reference.

When a receive method is mutable, the state, e.g. MyState, is serialized and stored after each invocation. This means that even if the state does not change semantically, it will be considered as modified by callers. Thus the mutable option should only be used when absolutely necessary.

§Example

#[receive(contract = "my_contract", name = "some_receive", mutable)]
fn contract_receive(
    ctx: &ReceiveContext,
    host: &mut Host<MyState>,
) -> ReceiveResult<MyReturnValue> {...}

§enable_logger: Function can access event logging

Setting the enable_logger attribute changes the required signature to include an extra argument &mut Logger, allowing the function to log events.

§Example

#[receive(contract = "my_contract", name = "some_receive", enable_logger)]
fn contract_receive(
    ctx: &ReceiveContext,
    host: &Host<MyState>,
    logger: &mut Logger,
) -> ReceiveResult<MyReturnValue> {...}

§low_level: Manually deal with the low-level state including writing

bytes Setting the low_level attribute disables the generated code for serializing the contract state. However, the return value is still serialized automatically.

If low_level is set, the &Host<State> in the signature is replaced by &mut LowLevelHost found in concordium-std, which gives access to manipulating the low-level contract state directly via the methods state() and state_mut().

§Example

#[receive(contract = "my_contract", name = "some_receive", low_level)]
fn contract_receive(
    ctx: &ReceiveContext,
    state: &mut LowLevelHost,
) -> ReceiveResult<MyReturnValue> {...}

§parameter="<Param>": Generate schema for parameter

To make schema generation include the parameter for this function, add the attribute parameter and set it equal to a string literal containing the type used for the parameter. The parameter type must implement the SchemaType trait, which for most cases can be derived automatically.

§Example

#[derive(SchemaType)]
struct MyParam { ... }

#[receive(contract = "my_contract", name = "some_receive", parameter = "MyParam")]
fn contract_receive(
    ctx: &ReceiveContext,
    host: &Host<MyState>,
) -> ReceiveResult<A> {...}

§return_value="<ReturnValue>": Generate schema for the return value.

To make schema generation include the return value for this function, add the attribute return_value and set it equal to a string literal containing the type used for the parameter. The parameter type must implement the SchemaType trait, which for most cases can be derived automatically.

§Example

#[derive(SchemaType)]
struct MyReturnValue { ... }

#[receive(contract = "my_contract", name = "some_receive", return_value = "MyReturnValue")]
fn contract_receive(
   ctx: &ReceiveContext,
   host: &Host<MyState>,
) -> ReceiveResult<MyReturnValue> {...}

§error="<Error>": Generate schema for error

To make schema generation include the error for this function, add the attribute error and set it equal to a string literal containing the type used for the error. The error type must implement the SchemaType trait, which for most cases can be derived automatically.

§Example

#[derive(SchemaType)]
enum MyError { ... }

#[receive(contract = "my_contract", name = "some_receive", error = "MyError")]
fn contract_receive(
    ctx: &ReceiveContext,
    host: &Host<MyState>,
) -> Result<A, MyError> {...}

§fallback: Create a fallback entrypoint.

A contract can have a single fallback entrypoint defined. If defined, invocations on missing entrypoint will be redirected to the fallback entrypoint. For fallback entrypoints, the name attribute is not allowed. This is because fallback entrypoints always have the empty string as their name.

To get the name of the entrypoint used for invocation, use ctx.named_entrypoint(). The method is available in all receive methods, but is only useful on fallback entrypoints.

§Example

#[receive(contract = "my_contract", fallback)]
fn contract_receive(
   ctx: &ReceiveContext,
   host: &Host<MyState>,
) -> ReceiveResult<MyReturnValue> {
    let named_entrypoint = ctx.named_entrypoint();
    // ...
}

§crypto_primitives: Function can access cryptographic primitives

Setting the crypto_primitives attribute changes the required signature to include an extra argument &CryptoPrimitives, which provides cryptographic primitives such as verifying signatures and hashing data.

§Example

#[receive(contract = "my_contract", name = "some_receive", crypto_primitives)]
fn some_receive(
    ctx: &ReceiveContext,
    host: &Host<MyState>,
    crypto_primitives: &CryptoPrimitives,
) -> ReceiveResult<MyReturnValue> {...}