Tutorial eSealing example and concordium-client

Hi!

I am currently working through the tutorials and are now stuck with the eSealing application.
(From here: concordium-rust-smart-contracts/examples/eSealing at main · Concordium/concordium-rust-smart-contracts · GitHub)

First I try to build/deploy/init the app by following 3 commands (run in order):

cargo concordium build --out dist/module.wasm.v1 --schema-out dist/schema.bin
concordium-client module deploy dist/module.wasm.v1 --sender {concordium_account} --name {contract_name} --grpc-port {grpc_port}
concordium-client contract init {module_id} --sender {concordium_account} --energy 30000 --contract {contract_name} --grpc-port {grpc_port}

These seems to work fine if I set grpc_port == 10001, but if I change it to 20001 as the example suggests, I get following error.
concordium-client: user error (gRPC error: not enough bytes)

So continuing with the port working, I run following:

concordium-client contract update {contract_instance} --entrypoint registerFile --sender {concordium_account} --energy 30000 --parameter-json {file_path} --grpc-port {grpc_port}

Which leads to following error:
Error: Could not parse the json parameter because a no schema was embedded in the module or provided with the --schema flag.

So any suggestions to further progress is welcomed with open arms :slight_smile:

Bonus Question:
In the function definitions:

#[init(contract = "eSealingtest7", event = "Event")]

Is it then possible to use a variable in place for the “eSealingtest7”? To prevent having to change all manually? My attempts so far to create the variable is:

//static CONTRACT_NAME: &str = "eSealing";
//static CONTRACT_NAME_L: &str = CONTRACT_NAME.as_str();

But both cases I get an error stating it expect a string literal? Any way to circumvent?

Thanks in advance and good day! :slight_smile:

  1. The missing schema error is because you did not embed it.
cargo concordium build --out dist/module.wasm.v1 --schema-out dist/schema.bin --schema-embed

should fix that for you (–schema-embed is the relevant flag) if you redeploy the module.

  1. Which version of the client are you using. The latest version needs port 20001, but all previous versions will need 10001 since they work with V1 API.

  2. The name has to be a string literal.

1 Like
  1. Worked perfectly! :slight_smile: But for understanding is this something that should be included normally, or just in specific / this case?

  2. Where can I see which version I am using? Is there a command?

  3. So this has to be a hardcoded string at all positions and not possible to make into a variable defined one place?

Thanks for the insights!

  1. By default the schema is not embedded. You have to choose to embed it every time you build a new contract.

  2. concordium-client --version

  3. Yes, that is correct.

I’ve added schema-embed but still can’t pass parameter on init func, following is my code.


// /// The human-readable description of a voting option.
// pub type VotingOption = String;
/// The voting options are stored in a vector. The vector index is used to refer
/// to a specific voting option.
/// Number of holder.
pub type TotalHolders = u32;
/// single holder amount
pub type TotalHolderAmount = u64;

/// The parameter type for the contract function `init`.
/// Takes a description, the voting options, and the `end_time` to start the
/// election.
#[derive(Serialize, SchemaType)]
pub struct InitParameter {
    pub end_time: Timestamp,
}

/// The contract state
#[derive(Serialize, Clone)]
struct State {
    /// The map connects a voter to the index of the voted-for voting option.
    pub holders: BTreeMap<AccountAddress, Amount>,
    /// The last timestamp that an account can vote.
    /// The election is open from the point in time that this smart contract is
    /// initialized until the `end_time`.
    end_time: Timestamp,
    total_holders: TotalHolders,
}

#[derive(Serialize, SchemaType)]
pub struct VestingView {
    /// The last timestamp that an account can vote.
    /// The election is open from the point in time that this smart contract is
    /// initialized until the `end_time`.
    pub end_time: Timestamp,
    /// The map connects the index of a voting option to the number of votes
    /// it received so far.
    pub holders: BTreeMap<AccountAddress, Amount>,
    pub total_holders: TotalHolders,
    pub current_balance: Amount

}

/// The different errors that the `vote` function can produce.
#[derive(Reject, Serialize, PartialEq, Eq, Debug, SchemaType)]
pub enum VotingError {
    /// Raised when parsing the parameter failed.
    #[from(ParseError)]
    ParsingFailed,
    /// Raised when the vote is placed after the election has ended.
    VestingFinished,
    /// Raised when voting for a voting option that does not exist.
    InvalidVotingOption,
    /// Raised when a smart contract tries to participate in the election. Only
    /// accounts are allowed to vote.
    ContractVoter,
}

pub type VestingResult<T> = Result<T, VotingError>;



// Contract functions

/// Initialize the contract instance and start the vesting.
/// end_time
#[init(contract = "vesting", parameter = "InitParameter")]
fn init<S: HasStateApi>(
    ctx: &impl HasInitContext,
    _state_builder: &mut StateBuilder<S>,
) -> InitResult<State> {
    // Parse the parameter.
    let param: InitParameter = ctx.parameter_cursor().get()?;

    // Set the state.
    Ok(State {
        holders: BTreeMap::new(),
        end_time: param.end_time,
        total_holders: 0,
    })
}

/// Enables accounts to vote for a specific voting option. Each account can
/// change its selected voting option with this function as often as it desires
/// until the `end_time` is reached.
///
/// It rejects if:
/// - It fails to parse the parameter.
/// - A contract tries to vote.
/// - It is past the `end_time`.
#[receive(
    contract = "vesting",
    name = "vest",
    mutable,
    payable,
    error = "VotingError"
)]
fn vest<S: HasStateApi>(
    ctx: &impl HasReceiveContext,
    host: &mut impl HasHost<State, StateApiType = S>,
    _amount: Amount,
) -> VestingResult<()> {
    // Check that the election hasn't finished yet.
    if ctx.metadata().slot_time() > host.state().end_time {
        return Err(VotingError::VestingFinished);
    }
    
    // Ensure that the sender is an account.
    let acc = match ctx.sender() {
        Address::Account(acc) => acc,
        Address::Contract(_) => return Err(VotingError::ContractVoter),
    };

    host.state_mut().total_holders += 1;
    // Insert or replace the vote for the account.
    host.state_mut()
        .holders
        .entry(acc)
        .and_modify(|current_balance| *current_balance += _amount)
        .or_insert(_amount);

    Ok(())
}

#[receive(contract = "vesting", name = "view")]
fn vesting_info<S: HasStateApi>(
    _ctx: &impl HasReceiveContext,
    host: &impl HasHost<State, StateApiType = S>,
) -> ReceiveResult<VestingView> {

    let holders = host.state().holders.clone();
    let total_holders = host.state().total_holders.clone();
    let end_time = host.state().end_time;
    let current_balance = host.self_balance(); // read current balance

    Ok(VestingView {
        holders,
        total_holders,
        end_time,
        current_balance,
    })
}```

So you have

cargo concordium build -e -o my-contract.wasm.v1

Then you have deployed my-contract.wasm.v1

And then initialized a new instance from it?

Can you show the commands you have executed?

@abizjak yes,
the main problem is unable to call the update function from frontend
I’m using concordium-web-sdk, and this code is from piggybank

function contractUpdatePayload(amount: CcdAmount, contract: Info, method: string) {
    return {
        amount,
        address: {
            index: contract.index,
            subindex: BigInt(0),
        },
        receiveName: `${contract.name}.${method}`,
        maxContractExecutionEnergy: MAX_CONTRACT_EXECUTION_ENERGY,
    };
}

export async function submitDeposit(connection: WalletConnection, amount: CcdAmount, account: string, contract: Info) {
    // const buff = toBuffer(RAW_SCHEMA, 'base64');
    // const seerial = serializeTypeValue(params, buff);
    // console.log(JSON.stringify(RAW_SCHEMA));
    // const sch = JSON.stringify(RAW_SCHEMA);
    // const param = JSON.stringify(account);
    const param = { User: '3v6SAaexxQ4zwi5AwAncD4yTLtMSXuB9PC7v4dEwu5arMuRRsW' };
    return connection.signAndSendTransaction(
        account,
        AccountTransactionType.Update,
        contractUpdatePayload(amount, contract, 'vest'),
        {},
        `//8DAQAAAAQAAAB2ZXN0AAIAAAAIAAAAcmV0cmlldmUECxUEAAAADQAAAFBhcnNpbmdGYWlsZWQCDwAAAFZlc3RpbmdGaW5pc2hlZAILAAAASW52YWxpZFVzZXICDQAAAENvbnRyYWN0Vm90ZXICBAAAAHZlc3QECxUEAAAADQAAAFBhcnNpbmdGYWlsZWQCDwAAAFZlc3RpbmdGaW5pc2hlZAILAAAASW52YWxpZFVzZXICDQAAAENvbnRyYWN0Vm90ZXICAA==`
    );

Hi again!

A bit on the same subject, I have the contract deployment working, but now I want to connect the frontend. So to this bit as I understand I have to edit the constant variables in (https://github.com/Concordium/concordium-browser-wallet/blob/main/examples/eSealing/src/constants.ts)
However I am a bit in doubt about the E_SEALING_RAW_SCHEMA variable, how do I create / get this from the deployment process?
I have a feeling it is related to the build schema, but how should I apply it here? :slight_smile:

Thanks in advance!

You can get the schema using the flag to cargo-concordium

    -b, --schema-base64-out <schema-base64-out>
            Builds the contract schema and writes it in base64 format to file at specified location or prints the base64
            schema to the console if the value `-` is used (expected input: `./my/path/base64_schema.b64` or `-`).

That is,

cargo concordium build .... --schema-base64-out ...

What is my input for the --schema-base64?

As for now I use following command to build contract:
cargo concordium build --out dist/module.wasm.v1 --schema-out dist/schema.bin --schema-embed

So how should I append the --schema-base64?

Nevermind I figured it out with:
cargo concordium build --out dist/module.wasm.v1 --schema-out dist/schema.bin --schema-embed --schema-base64 dist/schema.b64

1 Like

So I almost goit it working, the check function works as intended, but when sealing the browser wallet briefly shows the correct screen and then turns blank as seen on the screenshot. I am not sure where to start debugging, does anyone have experience with this behaviour?

Solution: I had just given a “number” as index instead of “bigInt” :slight_smile: