Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions docusaurus/docs/advanced/03-attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ a plain `impl` block of the module (not in a trait impl, since the trait is also
functions and non-mutating entry points of other contracts. Other contracts cannot call it: it does
not exist on chain, so it is not on the `ContractRef`.

Inside it, `caller()` is the account that called it, and a getter it calls sees the offchain
function's contract as its caller, with the whole call stack, as on chain. A revert comes back as
the error, so `try_all_balances()` returns `Err` instead of panicking.

## Mixing attributes

A function can accept more than one attribute, with one exception: a constructor cannot be payable.
Expand Down
19 changes: 12 additions & 7 deletions docusaurus/docs/advanced/09-factory.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,20 @@ The generated factory contract exposes a standardized set of management entry po

## Testing the Factory

:::caution
Factory deployments are not supported by OdraVM. Any test that calls `new_contract` must be run against
the Casper backend:
Factories work on both test backends, so the tests below run with `cargo odra test` (OdraVM) as well
as `cargo odra test -b casper`. OdraVM mimics Casper: a child's `init` and `upgrade` see the caller of
the factory as their caller, only the account that deployed the factory can upgrade its children, the
events of the children are tracked, and a child whose `init` or `upgrade` reverts leaves nothing
behind.

```bash
cargo odra test -b casper
```
:::note
A few details are still simplified on OdraVM:

Tests that only use the child contract as a standalone module still run fine on OdraVM.
- The access `URef` returned by `new_contract` is a stand-in derived from the child's address.
- The upgrade permission check compares the direct caller with the deployer of the factory, where
Casper checks group access.
- A factory opened with `HostRefLoader::load` has no recorded deployer, so its children cannot be
upgraded there.
:::

Remember to register both the child contract and the generated factory module in `Odra.toml`, otherwise
Expand Down
2 changes: 1 addition & 1 deletion docusaurus/docs/advanced/10-wasm-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ does not meet out of the box:
members = ["counter"]

[workspace.dependencies]
odra = { version = "2.9.0", features = [], default-features = false }
odra = { version = "2.10.0", features = [], default-features = false }
```

```toml title="counter/Cargo.toml"
Expand Down
16 changes: 15 additions & 1 deletion docusaurus/docs/backends/02-odra-vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,18 @@ graph TD;
id1[[Odra code]]-->id2[Contract Container];
id2[Contract Container]-->id3((Contract Registry))
id3((Contract Registry))-->id4[(OdraVM Execution)]
```
```

## Reverts

A call that reverts is rolled back as a whole, as on Casper: the storage and balances it changed,
contracts it deployed (also factory children), the new code of a failed `upgrade` - the old version
keeps running - and the events and native events it emitted, including those of nested calls. Event
counts, `get_event` indices and `last_call()` never see them.

Two things are still simpler than on Casper: a plain Rust panic in a contract (an `unwrap()` on
`None`, not a revert) rolls nothing back, and a reverted deploy still uses up an address.

The output of a revert is compact - the error and the call stack that led to it. Any other panic, an
assertion failure in a test or an `unwrap()` in contract code, prints the standard Rust message
(with `RUST_BACKTRACE` support), and a panic hook you install yourself keeps working.
6 changes: 3 additions & 3 deletions docusaurus/docs/backends/03-casper.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,10 @@ graph TD;
[deploy]: https://docs.rs/casper-execution-engine/latest/casper_execution_engine/core/engine_state/deploy_item/struct.DeployItem.html
[Casper Event Standard]: https://github.com/make-software/casper-event-standard
[Casper's 'Writing On-Chain Code']: https://docs.casper.network/writing-contracts/
[proxy_caller.wasm]: https://github.com/odradev/odra/blob/release/2.9.0/odra-casper/test-vm/resources/proxy_caller.wasm
[proxy_caller.wasm]: https://github.com/odradev/odra/blob/release/2.10.0/odra-casper/test-vm/resources/proxy_caller.wasm
[CasperPackageHash]: https://docs.rs/casper-types/latest/casper_types/contracts/struct.ContractPackageHash.html
[RuntimeArgs]: https://docs.rs/casper-types/latest/casper_types/runtime_args/struct.RuntimeArgs.html
[Bytes]: https://docs.rs/casper-types/latest/casper_types/bytesrepr/struct.Bytes.html
[ERC721]: https://github.com/odradev/odra/blob/release/2.9.0/modules/src/erc721_token.rs
[ERC1155]: https://github.com/odradev/odra/blob/release/2.9.0/modules/src/erc1155_token.rs
[ERC721]: https://github.com/odradev/odra/blob/release/2.10.0/modules/src/erc721_token.rs
[ERC1155]: https://github.com/odradev/odra/blob/release/2.10.0/modules/src/erc1155_token.rs
[Tutorial section]: ../tutorials/using-proxy-caller
74 changes: 71 additions & 3 deletions docusaurus/docs/backends/04-livenet.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ add it as an **optional** dependency and put it behind a `livenet` feature:

```toml title="Cargo.toml"
[dependencies]
odra-casper-livenet-env = { version = "2.9.0", optional = true }
odra-casper-livenet-env = { version = "2.10.0", optional = true }

[features]
default = []
Expand Down Expand Up @@ -340,7 +340,61 @@ node is the current state. This is why the `balance_of` call was almost instant
Basically, if the entrypoint function is not mutable or does not make a call to an unknown external contract
(see [Cross Calls](../basics/10-cross-calls.md)), it is executed offline and
node is used for the state query only. However, the Livenet needs to know the connection between the contracts
and the code, so make sure to deploy or load already deployed contracts
and the code, so make sure to deploy or load already deployed contracts.

A function executed this way behaves as it does in tests:

- `caller()` is the calling account (the `HostEnv` caller), and `call_stack()` starts with it. A
getter called from another one sees the calling contract as its caller, like on chain.
- If it reverts, its `try_*` call returns the error (`token.try_balance_of(..)` gives `Err`), and the
plain call panics with it. A revert in a nested getter returns the error of the inner contract, and
any other panic becomes `VmError::Panic`.

The same applies to [`#[odra(offchain)]`](../advanced/03-attributes.md#offchain) functions, which
always run this way.

## Errors

The node reports a failed transaction only as a number (`User error: 2`). The Livenet backend turns
it back into an `OdraError`: Odra's own errors are recognised by their code, and the errors your
contracts define are looked up by name in the contract schemas, in
`resources/casper_contract_schemas/` of the current directory and of its parents up to the project
root. The schema of the contract that was called is searched first - contracts usually number their
errors 1, 2, 3, so the same code can mean something else in another contract - and the others only
if it has no such error (a contract it called reverted).

Keep the schemas present and up to date with `cargo odra schema`. A code that no schema knows (or an
Odra error code from a newer Odra version) comes back as `VmError::Other` with the node's message.

A deploy or an upgrade whose `init` or `upgrade` reverts returns the contract's error too, named after
the schema of the contract being deployed, and running out of gas returns `OutOfGas`. The
`ContractDeploymentError` is left for a deployment that did not get to run the contract: the wasm
file was not found, the gas was not set or the node could not be reached.

## Gas used

`env.gas_report()` and `env.last_call_gas_cost()` work on Livenet like in the test VMs. The gas
comes from the node's execution result of each transaction the environment sends:

```rust
let mut token = Erc20::deploy(&env, init_args);
token.transfer(&recipient, &U256::from(10));
println!("{}", env.gas_report());
// Wasm deploy: Erc20.wasm - 259.888293669 CSPR
// Contract call: transfer - 0.50742048 CSPR
println!("{}", env.last_call_gas_cost()); // 507420480
```

- The figure is the gas the transaction consumed, not the payment you set with `env.set_gas` -
Casper charges the whole payment limit, so the two differ.
- Failed transactions are reported as well. A CSPR transfer updates `last_call_gas_cost` but is not
listed in the report.
- Getters run on your machine and send no transaction, so they don't change either value.
- Each worker environment of [`HostEnv::concurrently`](#doing-several-things-at-once) has its own
report.

A getter that reads a validator's bid with `self.env().get_validator_info(..)` works on Livenet too:
the bid is read from the node, and a validator without a bid gives `None`.

## Native events

Expand Down Expand Up @@ -378,6 +432,20 @@ so a test written this way needs no livenet.
`cargo run --bin odra_cli --features livenet -- scenario concurrent` runs this against your node and
prints how long the reads take one after another and concurrently.

At most 8 worker threads run at once. A public node may limit how many connections one client keeps
open, a local one may take more; set `ODRA_MAX_THREADS` to change the limit for every `concurrently`
call, or pass it to a single call with `concurrently_with`:

```rust
// A single worker thread: one request at a time, for a node that allows one connection.
let supplies = env.concurrently_with(1, addresses, |env, address| {
Erc20::load(env, address).total_supply()
});
```

`0` counts as `1`, and an `ODRA_MAX_THREADS` that is not a number panics rather than falling back
to the default. On OdraVM and CasperVM the limit changes nothing.

## Async code

The livenet `HostEnv` is synchronous, like every other backend. Underneath, `CasperClient` in
Expand Down Expand Up @@ -413,5 +481,5 @@ ODRA_CASPER_LIVENET_ENV=integration cargo run --bin odra_cli --features=livenet

To sum up - this command will firstly load the `integration.env` file and then load the missing values from `.env` file.

[.env.sample]: https://github.com/odradev/odra/blob/release/2.9.0/examples/.env.sample
[.env.sample]: https://github.com/odradev/odra/blob/release/2.10.0/examples/.env.sample
[odra_cli.rs]: https://github.com/odradev/odra/blob/release/2.10.0/examples/bin/odra_cli.rs
4 changes: 1 addition & 3 deletions docusaurus/docs/basics/01-cargo-odra.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,10 @@ cargo odra schema
```

This generates JSON schema files for all your contracts and places them in the `resources` folder.
If the `resources` folder does not exist, it creates the folder for you. Each contract gets two files,
in two subfolders:
If the `resources` folder does not exist, it creates the folder for you. Each contract gets one file:

```
resources/casper_contract_schemas/flipper_schema.json
resources/legacy/flipper_schema.json
```

Like with the `build` command, you can use the `-c` option to generate a schema for a specific contract.
Expand Down
10 changes: 5 additions & 5 deletions docusaurus/docs/basics/02-directory-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ version = "0.1.0"
edition = "2021"

[dependencies]
odra = { version = "2.9.0", features = [], default-features = false }
odra = { version = "2.10.0", features = [], default-features = false }

[dev-dependencies]
odra-test = { version = "2.9.0", features = [], default-features = false }
odra-test = { version = "2.10.0", features = [], default-features = false }

[build-dependencies]
odra-build = { version = "2.9.0", features = [], default-features = false }
odra-build = { version = "2.10.0", features = [], default-features = false }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
odra-build = { version = "2.9.0", features = [], default-features = false }
odra-cli = { version = "2.9.0", features = [], default-features = false }
odra-build = { version = "2.10.0", features = [], default-features = false }
odra-cli = { version = "2.10.0", features = [], default-features = false }

[[bin]]
name = "sample_build_contract"
Expand Down
15 changes: 13 additions & 2 deletions docusaurus/docs/basics/04-flipper-internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ The function named `init` is the constructor of the contract. This function will
to a single call, all further calls to it will result in an error. The `init` function is optional,
if your contract does not need any initialization, you can skip it.

:::note
A few names are taken by the code Odra generates. An entry point (a `pub` function of a
`#[odra::module]` impl block, or any function of a trait impl) cannot be named `new`, `env`,
`address`, `new_contract`, `upgrade_child_contract` or `batch_upgrade_child_contract`, and its
arguments cannot be named `gas` or `attached_value`. Private helper functions may use any name. Any
other name works, also ones like `contract`, `env` or `result`.

`#[odra(..)]` accepts `payable`, `non_reentrant` and `offchain`. Anything else - a typo such as
`#[odra(paybale)]`, a repeated flag or a value like `payable = true` - is a compile error.
:::

```rust title="flipper.rs"
...
/// Replaces the current value with the passed argument.
Expand Down Expand Up @@ -112,5 +123,5 @@ The contract will be deployed on the VM you chose while running `cargo odra test
## What's next
Now let's take a look at the different types of storage that Odra provides and how to use them.

[`Deployer::deploy`]: https://docs.rs/odra/2.9.0/odra/host/trait.Deployer.html#tymethod.deploy
[`InitArgs`]: https://docs.rs/odra/2.9.0/odra/host/trait.InitArgs.html
[`Deployer::deploy`]: https://docs.rs/odra/2.10.0/odra/host/trait.Deployer.html#tymethod.deploy
[`InitArgs`]: https://docs.rs/odra/2.10.0/odra/host/trait.InitArgs.html
15 changes: 11 additions & 4 deletions docusaurus/docs/basics/05-storage-interaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,17 @@ A `u128` reaches roughly `3.4 * 10^38`, comfortably beyond any realistic token s
fills the low half of the `U256` and the high half stays zero. Values above that range still need a
runtime conversion.

:::note
There is no `u512` counterpart. CSPR amounts are `U512`, but they generally arrive from the host at
runtime rather than being written as constants.
:::
`u512` does the same for `U512`, the type of CSPR amounts. Together with `cspr!` from `odra-cli`,
which converts a literal amount of CSPR to motes at compile time, a fixed CSPR amount can be a constant
too:

```rust
use odra::casper_types::U512;
use odra::prelude::*;

const MIN_DEPOSIT: U512 = u512(1_000_000_000); // 1 CSPR in motes
const STAKE: U512 = u512(odra_cli::cspr!(500) as u128);
```

## What's next
In the next article, we'll see how to query the host for information about the world and our contract.
7 changes: 4 additions & 3 deletions docusaurus/docs/basics/06-communicating-with-host.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ impl CallStackProbe {

Called by an account directly, `inspect` answers `(account, None, [account, probe])`. Called
through another contract, it answers `(relay, Some(account), [account, relay, probe])`. The same on
OdraVM, CasperVM and livenet.
OdraVM, CasperVM and livenet, including getters and `#[odra(offchain)]` functions, which livenet runs
locally: the account at the bottom of the stack is the `HostEnv` caller.

:::caution
Trusting an address further up the stack has the usual `tx.origin` caveats: a user can be tricked
Expand Down Expand Up @@ -114,6 +115,6 @@ You will learn more functions that Odra exposes from host and types it uses in f
In the next article, we'll dive into testing your contracts with Odra, so you can check that the code
we presented in fact works!

[`Module::env()`]: https://docs.rs/odra/2.9.0/odra/module/trait.Module.html#tymethod.env
[`ContractEnv`]: https://docs.rs/odra/2.9.0/odra/struct.ContractEnv.html
[`Module::env()`]: https://docs.rs/odra/2.10.0/odra/module/trait.Module.html#tymethod.env
[`ContractEnv`]: https://docs.rs/odra/2.10.0/odra/struct.ContractEnv.html
[`Backend section`]: ../backends/01-what-is-a-backend.md#contract-env
23 changes: 10 additions & 13 deletions docusaurus/docs/basics/07-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,6 @@ the function we are calling inside the contract.
it back, see [Snapshots](#snapshots) below
- `fn concurrently<T, R>(&self, items: Vec<T>, f: impl Fn(&HostEnv, T) -> R) -> Vec<R>` - runs `f` once
per item; one after another here, on worker threads on [livenet](../backends/04-livenet.md#doing-several-things-at-once)
- `fn enable_addressable_entity(&self) -> bool` - switches the backend from legacy mode to
addressable-entity mode, migrating the chain state like a real network upgrade would; returns
`false` if the backend does not support the switch or already runs in that mode (see the
[v3.0.0 migration guide](../migrations/to-3.0.0.md))

Full list of functions can be found in the [`HostEnv`] documentation.

Expand Down Expand Up @@ -164,11 +160,12 @@ assert_eq!(token.balance_of(&alice), U256::zero());
// Scenario B starts from the same point; the snapshot can be restored again and again.
```

A snapshot covers the contract storage, CSPR balances, events and the block time. Only the last
snapshot is kept: taking a new one replaces it. The caller chosen with `set_caller` and the gas
report are not part of it. Snapshots work on OdraVM and on CasperVM (where restoring is only a
pointer back to an older state root); on livenet the state lives on a real chain, so
`take_snapshot` panics there.
A snapshot covers the contract storage, CSPR balances, events and the block time, as well as the
contracts themselves: a contract deployed or upgraded after the snapshot is gone (or back to its old
code) once it is restored, and the event counts follow. Only the last snapshot is kept: taking a new
one replaces it. The caller chosen with `set_caller` and the gas report are not part of it.
Snapshots work on OdraVM and on CasperVM (where restoring is only a pointer back to an older state
root); on livenet the state lives on a real chain, so `take_snapshot` panics there.

## Choosing the backend

Expand Down Expand Up @@ -197,7 +194,7 @@ fn odra_vm_only() {
## What's next
We take a look at how Odra handles errors!

[`HostRef`]: https://docs.rs/odra/2.9.0/odra/host/trait.HostRef.html
[`InitArgs`]: https://docs.rs/odra/2.9.0/odra/host/trait.InitArgs.html
[`HostEnv`]: https://docs.rs/odra/2.9.0/odra/host/struct.HostEnv.html
[`Deployer`]: https://docs.rs/odra/2.9.0/odra/host/trait.Deployer.html
[`HostRef`]: https://docs.rs/odra/2.10.0/odra/host/trait.HostRef.html
[`InitArgs`]: https://docs.rs/odra/2.10.0/odra/host/trait.InitArgs.html
[`HostEnv`]: https://docs.rs/odra/2.10.0/odra/host/struct.HostEnv.html
[`Deployer`]: https://docs.rs/odra/2.10.0/odra/host/trait.Deployer.html
10 changes: 8 additions & 2 deletions docusaurus/docs/basics/08-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,15 @@ In our example, we are calling `try_change_name` and expecting an error to be th
For assertions, we are using a standard `assert_eq!` macro. As the contract call returns an `OdraError`,
we need to convert our custom error to `OdraError` using `Into::into()`.

:::note
On livenet the node reports only the error code; Odra names it using the
[contract schemas](./casper-contract-schema), so keep them up to date with `cargo odra schema`. See
[Livenet errors](../backends/04-livenet.md#errors).
:::

## What's next
We will learn how to emit and test events using Odra.

[`OdraResult`]: https://docs.rs/odra/2.9.0/odra/type.OdraResult.html
[`OdraError`]: https://docs.rs/odra/2.9.0/odra/enum.OdraError.html
[`OdraResult`]: https://docs.rs/odra/2.10.0/odra/type.OdraResult.html
[`OdraError`]: https://docs.rs/odra/2.10.0/odra/enum.OdraError.html
[`schema`]: ./casper-contract-schema
Loading
Loading