Commit 098b5a4d authored by Hero Bird's avatar Hero Bird Committed by GitHub

Implementation of ink! syntax 2.0 (#191)

* [lang2] initial commit of ink! lang2 frontend

This is going to be the frontend for the new ink! syntax.

* [lang2] change version syntax to semantic versioning string

* [lang2] add failure test for invalid semantic version string

* [lang2] fix some unused warnings

* [lang2] change lib name to ink_lang2

* [lang2] add span to ir::data::ItemStorage

* [lang2] modernize env types codegen

* [lang2] re-introduce entry points codegen but without body for now

* [lang2] add initial storage struct codegen

* [lang2] add lang2 flipper contract example for testing purposes

* [core] impl env2::EnvTypes for DefaultSrmlTypes instead of old env::EnvTypes

* [lang2] add `0` body to entry points

* [lang2] mark fn allocate_using impl for storage struct as unsafe

* [lagn2] ignore args in Initialize::initialize impl for storage struct

* [lang2] adjust compiletests

* [core] add emit_event to EnvAccess{Mut}

* [lang2] add codegen for contract functions

* [lang2] add Signature::inputs

* [lang2] adjust and modernize compiletests

* [lang2] apply rustfmt

* [core] add generic Encode param to EmitEventParams

* [cli] bump version 0.1.1 -> 0.1.2

* [core] make it possible to use EnvAccess{Mut} directly in storage

* [core] make Selector::{from_bytes, to_bytes} const fn

* [core] add check to Decode impl for CallData

* [core] add #[inline..] annotations to some public trait impls

* [lang2] move lang2 into lang2_macro and create new lang2 helper crate

* [examples] add initial core/flipper example that acts as codegen prototype

* [core] add some more #[inline..] annotations

* [lang2] split DerefEnv into AccessEnv{Mut} and losen direct trait bounds

* [core] add efficient conversion from DynEnv<EnvAccessMut> to DynEnv<EnvAccess>

* [examples] adjust core::Flipper prototype to newest state

* [core] add env accessors to DynEnv

* [lang2] add AccessEnv{Mut} impls for DynEnv and EnvAccess{Mut}

* [lang2] use double AccessEnv trait indirection in dispatch fns

* [examples] core::flipper: add ink-dyn-alloc crate feature

Enable this to enable dynamic storage allocation for your contract.
Dynamic storage allocation is an optional drop-in feature.

* [lang2] make LICENSE and README.md sym links

* [lang2-macro] replace LICENSE and README.md with sym links

* [lang2] add access_env sub module

* [lang2] simplify Message trait concept for constructors

* [lang2] add more general error codes

* [lang2] add storage sub module

* [lang2] remove old dispatch sub module

* [lang2] implement new dispatcher based on ink_model v2

* [examples] adjust core flipper example for new codegen

* [lang2] remove some commented out code

* [lang2] rename dispatch2 to dispatch_using_mode

* [lang2] fix doc link in DispatchMode docs

* [examples] adjust core::Flipper to better reflect codegen prototype

* [lang2] rename ContractDispatch to DispatchUsingMode and add docs

* [lang2] remove unused ContractInstance trait

* [lang2] remove unneeded Msg and Constr utility types

Those types should solely be provided by the contracts themselves.
They act as namespaces for the respective message type.

* [lang2/macro] add GenerateCodeUsing trait

* [example] add explanation docs to the Flipper::get(&mut self) method

* [lang2] fix unused warning

* [lang2/macro] refactor codegen module

* [lang2/macro] remove commented-out code

* [core] add EnvAccess to allow universal usage of EnvAccess

* [lang2] no longer require separate storage structs

* [examples] adjust core/Flipper to newest ink_lang2 and ink_core changes

* [examples] remove as much direct Flipper usages as possible

* [lang2/macro] temporarily disable dispatch codegen

* [lang2/macro] add MetaInfo::is_dynamic_allocation_enabled stub

* [lang2/macro] implement initial storage codegen

* [core] remove DynEnvAccess and DynEnvAccessMut

No longer needed!

* [core] implement AccessEnv for &'a {mut} DynEnv<EnvAccess{Mut}>

* [examples] fix implementation of ink_lang2::AccesEnv for StorageAndEnv

* [examples] fix ink_core::env2::AccessEnv impl for StorageAndEnv

* [examples] move deploy and call fns into dispatch const block

* [examples] convert docs to comments because they screw up cargo expand

* [lang2/macro] remove ink_model dependency

* [lang2] fix some minor warnings and issues

* [lang2/macro] fix codegen of ink_core::env2::AccessEnv impls

* [lang2/macro] refactor and improve FunctionSelector implementation

* [lang2/macro] Signature::inputs now returns an iterator over IdentType

Was returning an iterator over FnArg before but conversion is trivial.

* [lang2/macro] add dispatch codegen

* [examples] fix DefaultSrmlTypes usage in lang2/Flipper

* [core] prepare env2 to be used with ABI generation purposes

* [examples] add ABI generation to core/Flipper

This will serve as a prototype for ink_lang2 codegen.

* [core, model] remove unneeded warnings/lints

* [lang2] make calls to AccessEnv and Flush more explicit

* [lang2/macro] no longer use no-default-features for dev-dependencies

* [lang2/macro] surpress warnings of unused imports for core::env2::AccessEnv

* [lang2/macro] fix codegen of {ink_lang2/core::env2}::AccessEnv impls

* [lang2/macro] remove unused let binding (was commented out anyways)

* [lang2/macro] fix several codegen bugs

* [examples] remove ink_model and ink_lang dependencies of lang2/Flipper

* [examples] add contract and layout bindings type annotation

* [lang2/macro] adjust some passing tests

* [lang2] add GenerateAbi trait guarded by ink-generate-abi crate feature

* [lang2/macro] move scale dependency away from ink dependencies in Cargo.toml

* [lang2/macro] add Metadata and HasLayout derives to storage structs

* [lang2/macro] insert minor whitespace between bindings and quote

* [lang2/macro] add abi module to generate ABI generating code

* [lang2/macro] add Function::{filter_constructor, filter_message}

* [lang2/macro] fix bug that non-ink! filter for attributes was off

* [lang2/macro] make use of iterative array quoting in Dispatch codegen

* [lang2/macro] add utility to filter doc attributes and parse them to readable string

* [lang2/macro] add codegen for ABI generation

* [example] adjust lang2/Flipper abi-gen package

* [lang2/macro] ink::contract params adjustments

- make `env` param default to ink_core::env2::DefaultSrmlTypes
- add `dynamic_allocations = {true|false}`, default: false
- add `compile_as_dependency = {true|false}`, default: false

* [lang2/macro] adjust passing tests to new contract params

* [examples] adjust lang2/Flipper to new contract params

We enable dynamic_allocations for now since disabling it causes other errors.

* [lang2/macro] add different HasLayout impl for dynamic_allocations {true|false}

* [examples] lang2/Flipper no longer requires dynamic_allocations = true

* [lang2] rename AccessEnv::env to AccessEnv::access_env

This helps to disambiguate certain other `env` methods and trait impls

* [examples] adjust core/Flipper example

* [core] refactor event emitting interfaces in env2

* [examples] core/Flipper: fix env types aliases

* [examples] core/Flipper: adjust to new event emitting

Also:
- messages and constructors no longer can access private fields from StorageAndEnv
- rename __ink_storage to __ink_private
- add __ink_events module in __ink_private defining all events and utilities
- general rust formatting

* [core] add EmitEvent utility trait

This allow to disambiguate the contract's provided EmitEvent trait

* [examples] core/Flipper: make it impossible to ambiguate emitting of events

* [lang2/macro] rename contract_module.rs to contract.rs

* [lang2/macro] generated messages and constructors can no longer access internals of StorageAndEnv

* [lang2/macro] apply rustfmt

* [lang2/macro] add event codegen

- ABI generation codegen for events is still missing

* [lang2/macro] adjust some pass UI tests to new event codegen

* [lang2/macro] re-export the contract on crate root again

This is done for ABI creation since otherwise the abi-gen tool couldn't access the data.

* [lang2/macro] add ABI generation for events

* [lang2/macro] add Erc721 UI pass test

* [lang2/macro] add missing codegen for ABI event args docs

* [lang2/macro] fix a bug that ABI codegen would always impl GenerateAbi for Flipper

* [lang2/macro] add derive(Metadata) to Error enum for Erc721 test

* [core] add missing Initialize::default_value impls

* [core] add Debug impls for some env types

* [examples] core/Flipper: add code to allow for offchain testability

* [examples] core/Flipper: add error handling for test env init

* [lang2/macro] add docs to ABI generation of event args

* [examples] core/Flipper: adjust to improve testability

- No longer requires <Flipper as Testable>::constructor
- packs whole of flipper contract in associated flipper module such as what ink_lang2 does

* [lang2/macro] add filter_ink_attributes and has_ink_attributes to utilities

* [lang2/macro] re-export utils from ir

* [lang2/macro] add ir::utils::is_ink_attribute

* [lang2/macro] allow non-ink! items in the contract module

* [lang2/macro] add test for non-ink! module items

* [lang2/macro] apply rustfmt on UI tests

* [lang2/macro] fix 02 ui fail test

* [lang2/macro] generate code for non-ink! module items

* [lang2/macro] move utilities inside the contract module - because we can!!

* [lang2/macro] adjust compile tests for most recent changes

* [lang2/macro] add compile test for missing messages

* [lang2/macro] fix some minor warnings

* [lang2/macro] add a lint to check if all identifiers respect not starting with __ink

* [lang2/macro] add simple test for __ink starting identifiers lint

* [lang2/macro] update docs of idents_respect_pred

* [lang2] apply rustfmt

* [core] apply rustfmt to env2 module

* [lang2/macro] add ir::utils::filter_non_ink_attributes

* [lang2/macro] make use or ir::utils::filter_non_ink_attributes

* [lang2/macro] re-export contract on crate root only for ABI generation

* [lang2/macro] documented code generation principles

* [examples] add Erc20 token example for lang2

* [lang2/macro] refactor event codegen

* [lang2/macro] move dispatch codegen into __ink_private

This hides `Msg` and `Constr` namespaces into __ink_private.

* [lang2/macro] move ABI generation codegen into __ink_private

* [lang2/macro] add test to forbig accessing generated storage fields

* [lang2/macro] extend test to forbig accessing generated storage fields

* [lang2] implement testable contracts codegen

* [lang2/macro] do not generate dispatch code for tests or if `test-env` is enabled

* [examples] lang2/Flipper: add contract tests

* [lang2/macro] add simple tests to flipper test

* [examples] remove core/Flipper example again

This was decided after we felt that maintaining it was more of a burden than it being a help for further development.

* [lang2/macro] apply rustfmt

* [core] refactor env2 cross-calling infrastructure

* [core] move seals into utils

* [examples] lang2/Flipper: add default_works test

* [lang2/macro] add cross-contract calling

* [lang2/macro] add cross-contract calling test

* [lang2] remove CreateForwarder infrastructure since unneeded

* [lang2/macro] do not generate code for Events when compiled as dependency

* [lang2/macro] remove strange semi-comments in Msg and Constr

* [lang2/macro] add Debug derive to StorageAsDependency

* [examples] add Delegator contract based on ink_lang2

* [core] guard calling CreateBuilder::using_code at compile-time

* [lang2] fix some clippy warnings

* [chores] update README

* [lang2/macro] refactor into_hir: use ir types through ir module

* fix typo
Co-Authored-By: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* fix typo in lang2/macro/src/ir/into_hir.rs
Co-Authored-By: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* fix typo in examples/lang2/delegator/adder/lib.rs
Co-Authored-By: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* fix typo in lang2/macro/src/codegen/cross_calling.rs
Co-Authored-By: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* fix typo in core/src/env2/traits.rs
Co-Authored-By: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* fix typo in lang2/macro/src/codegen/contract.rs
Co-Authored-By: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* [lang2/macro] improve some ink! error messages

* [lang2/macro] remove commented-out code

* fix typo in lang2/macro/src/codegen/dispatch.rs
Co-Authored-By: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* [lang2/macro] remove ancient Cargo.toml artifacts

* [examples] fix typo and enhance delegator docs a bit

* use `#!/usr/bin/env` instead of `#!/bin/bash`
Co-Authored-By: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* [lang2] apply rustfmt

* [core] env2: apply rustfmt

* [cli] adjust CLI to generate ink_lang2 contract upon new command

* [lang2/macro] generate some #[inline] annotations on some generated cross-calling interfaces

* [examples] improve Delegator example slightly

* [core] fix bug that SrmlEnv tried to impl traits for TestEnv

* [examples] fix bug that .ink/abi_gen was using incorrect crate features

* [cli,examples] fix all abi_gen/Cargo.toml files

* [examples] fix bugs in some examples

* [lang2/macro] suppress some unused_parens warnings

* [examples] add incrementer as lang2 example

* [lang2/macro] make codegen more precise when using traits

* [lang2/macro] "fix" a bug in cross-calling codegen

The fix is actually a hack that we should remove again as soon as possible by a proper solution.

* [core] rename ext_create to ext_instantiate

* [examples] lang2/Delegator: fix some warnings and added Delegator::get

* [examples] lang2/Erc20: add Erc20::allowance

* [core] fix bug in executing ext::scratch_read causing panic

* [core] remove unused println statements in tests

* [examples] delegator: minor improvements
parent b748cc16
Pipeline #56351 failed with stages
in 2 minutes and 4 seconds
......@@ -5,6 +5,7 @@ members = [
"core",
"model",
"lang",
"lang2",
"cli",
"abi",
]
......
......@@ -29,6 +29,8 @@
ink! is an [eDSL](https://wiki.haskell.org/Embedded_domain_specific_language) to write WebAssembly based smart contracts using the Rust programming language targeting Substrate blockchains.
For more information please visit [the ink! tutorial](https://substrate.dev/substrate-contracts-workshop/#/0/building-your-contract).
## Developer Documentation
| `ink_abi` | `ink_core` | `ink_model` |
......@@ -39,120 +41,92 @@ ink! is an [eDSL](https://wiki.haskell.org/Embedded_domain_specific_language) to
Use the scripts provided under `scripts` directory in order to run checks on either the workspace or all examples. Please do this before pushing work in a PR.
### Examples
## Examples
For building the example smart contracts found under `examples` you will need to have `cargo-contract` installed.
```
cargo install cargo-contract
cargo install --git https://github.com/paritytech/ink cargo-contract
```
Execute the following command in shell while in an example's directory:
```
cargo contract build
```
Add `--force` option to update to the most recent `cargo-contract` version.
### Testing
Please visit [the documentation](https://substrate.dev/substrate-contracts-workshop/#/0/building-your-contract)
about building contracts and generating metadata.
Off-chain testing is done by `cargo test`.
If you want to test all workspace crates, do `cargo test --all`.
### Hello, World! - The Flipper
## Example
The `Flipper` contract is a simple contract containing only a single `bool` value
that it can flip from `true` to `false` and vice versa and return the current state.
Below is an example using ink! demonstrating a simple Flipper smart contract
that has a boolean state that can be flipped or returned.
Below you can see the code using the `ink_lang2` frontend to ink!.
```rust
contract! {
/// Specify concrete implementation of contract environment types
#![env = ink_core::env::DefaultSrmlTypes]
use ink_core::storage;
use ink_lang2 as ink;
/// Flips its state between `true` and `false`.
#[ink::contract(version = "0.1.0")]
mod flipper {
/// The storage of the flipper contract.
#[ink(storage)]
struct Flipper {
/// The current state of our flag.
/// The single `bool` value.
value: storage::Value<bool>,
}
impl Deploy for Flipper {
/// Initializes our state to `false` upon deploying our smart contract.
fn deploy(&mut self) {
self.value.set(false)
impl Flipper {
/// Instantiates a new Flipper contract and initializes `value` to `init_value`.
#[ink(constructor)]
fn new(&mut self, init_value: bool) {
self.value.set(init_value);
}
}
impl Flipper {
/// Flips the current state of our smart contract.
pub(external) fn flip(&mut self) {
*self.value = !*self.value;
/// Instantiates a new Flipper contract and initializes `value` to `false` by default.
#[ink(constructor)]
fn default(&mut self) {
self.new(false)
}
/// Returns the current state.
pub(external) fn get(&self) -> bool {
/// Flips `value` from `true` to `false` or vice versa.
#[ink(message)]
fn flip(&mut self) {
*self.value = !self.get();
}
/// Returns the current state of `value`.
#[ink(message)]
fn get(&self) -> bool {
*self.value
}
}
}
/// Run off-chain tests with `cargo test`.
#[cfg(tests)]
mod tests {
use super::*;
#[test]
fn it_works() {
let mut flipper = Flipper::deploy_mock();
assert_eq!(flipper.get(), false);
flipper.flip();
assert_eq!(flipper.get(), true);
/// As in normal Rust code we are able to define tests like below.
///
/// Simply execute `cargo test` in order to test your contract.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_works() {
// Note that `#[ink(constructor)]` functions that above have been
// defined as `&mut self` can be used as normal Rust constructors
// in test mode.
let flipper = Flipper::default();
assert_eq!(flipper.get(), false);
}
#[test]
fn it_works() {
let mut flipper = Flipper::new(false);
assert_eq!(flipper.get(), false);
flipper.flip();
assert_eq!(flipper.get(), true);
}
}
}
```
## Documentation
- User
- Wiki: [link](https://github.com/paritytech/ink/wiki)
- Developer
- [`core`][F2]: Developer documentation for the core abstractions
- Storage allocators, SRML environment definitions
- Offchain test environment
- Utilities for smart contracts like collections
- [`model`][G2]: Developer documentation for the model abstractions
- Virtual model of a smart contract
- Contains smart contract ABI dispatch
- Used to build an actual smart contract eDSL on
## Goals
### Core Goals
| | |
|:-:|:-|
| **Ecosystem** | Easy integration with the Rust ecosystem. |
| **Tooling** | Rust tooling works out-of-the-box for smart contract code. This includes auto-completion, syntax highlighting, code coverage for tests, go-to definitions and other IDE goodies. |
| **Testing** | Easy to build, test, deploy and run. |
| **Development** | Development can be done entirely off-chain to speed up the process. |
### Key Attributes
| | |
|:-:|:-|
| **Efficient** | Compile smart contract code to machine code that is _at least_ as efficient as if you used the low-level function calls directly. |
| **Robust** | Make it as simple as possible to write code that just does what is expected and as difficult as possible to write incorrect or exploitable code. |
| **Simple** | Smart contract code should be as easy-to-read as possible. |
| **Accessible** | Make it accessible to users by providing excellent documentation and teaching materials. |
## Structure
| Module | Description |
|:-------|:------------|
| `cli` | A minimalist tool to setup a smart contract project easily. |
| `core` | The core utilities used to write smart contracts. |
| `model` | Medium-level abstractions to write smart contracts heavily inspired by [Fleetwood](https://github.com/paritytech/fleetwood). |
| `lang` | The actual eDSL based on `ink_core` and `ink_model` to provide a user friendly interface to writing smart contract code. |
| `examples` | Features some smart contracts written for clarity with focus on teaching users how to use pDSL to write their own contracts. |
## Contribution
Visit our [contribution guidelines](CONTRIBUTING.md) for more information.
......
......@@ -653,6 +653,8 @@ pub struct EventParamSpec<F: Form = MetaForm> {
/// The type of the parameter.
#[serde(rename = "type")]
ty: TypeSpec<F>,
/// The documentation associated with the arguments.
docs: Vec<&'static str>,
}
impl IntoCompact for EventParamSpec {
......@@ -663,6 +665,7 @@ impl IntoCompact for EventParamSpec {
name: registry.register_string(self.name),
indexed: self.indexed,
ty: self.ty.into_compact(registry),
docs: self.docs,
}
}
}
......@@ -677,6 +680,8 @@ impl EventParamSpec {
indexed: false,
// We initialize every parameter type as `()`.
ty: TypeSpec::new::<()>(),
// We start with empty docs.
docs: vec![],
},
}
}
......@@ -703,6 +708,21 @@ impl EventParamSpecBuilder {
this
}
/// Sets the documentation of the event parameter.
pub fn docs<D>(self, docs: D) -> Self
where
D: IntoIterator<Item = &'static str>,
{
debug_assert!(self.spec.docs.is_empty());
Self {
spec: EventParamSpec {
docs: docs.into_iter().collect::<Vec<_>>(),
..self.spec
},
..self
}
}
/// Finishes constructing the event parameter spec.
pub fn done(self) -> EventParamSpec {
self.spec
......
[package]
name = "cargo-contract"
version = "0.1.1"
version = "0.1.2"
authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs"
edition = "2018"
......
......@@ -10,6 +10,7 @@ name = "abi-gen"
path = "main.rs"
[dependencies]
contract = { path = "../..", package = "{{name}}", features = ["ink-generate-abi", "test-env"] }
serde = { version = "1.0", features = ["derive"] }
contract = { path = "../..", package = "{{name}}", default-features = false, features = ["ink-generate-abi"] }
ink_lang2 = { path = "../../../../../lang2", default-features = false, features = ["ink-generate-abi"] }
serde = "1.0"
serde_json = "1.0"
fn main() -> Result<(), std::io::Error> {
let abi = contract::ink_generate_abi();
let abi = <contract::{{camel_name}} as ink_lang2::GenerateAbi>::generate_abi();
let contents = serde_json::to_string_pretty(&abi)?;
std::fs::create_dir("target").ok();
std::fs::write("target/abi.json", contents)?;
......
[package]
name = "{{name}}"
name = {{name}}
version = "0.1.0"
authors = ["[your_name] <[your_email]>"]
edition = "2018"
[dependencies]
ink_abi = { git = "https://github.com/paritytech/ink", package = "ink_abi", default-features = false, optional = true }
ink_core = { git = "https://github.com/paritytech/ink", package = "ink_core", default-features = false }
ink_model = { git = "https://github.com/paritytech/ink", package = "ink_model", default-features = false }
ink_lang = { git = "https://github.com/paritytech/ink", package = "ink_lang", default-features = false }
ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true }
ink_core = { path = "../../../core", default-features = false }
ink_lang2 = { path = "../../../lang2", default-features = false }
scale = { package = "parity-scale-codec", version = "1.0", default-features = false, features = ["derive"] }
type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true }
[lib]
name = "{{name}}"
name = {{name}}
crate-type = [
# Used for normal contract Wasm blobs.
"cdylib",
# Used for ABI generation.
"rlib",
# Used for normal contract Wasm blobs.
"cdylib",
# Used for ABI generation.
"rlib",
]
[features]
......@@ -27,23 +26,20 @@ default = ["test-env"]
std = [
"ink_abi/std",
"ink_core/std",
"ink_model/std",
"ink_lang/std",
"scale/std",
"type-metadata/std",
]
test-env = [
"std",
"ink_core/test-env",
"ink_model/test-env",
"ink_lang/test-env",
"ink_lang2/test-env",
]
ink-generate-abi = [
"std",
"ink_abi",
"type-metadata",
"ink_core/ink-generate-abi",
"ink_lang/ink-generate-abi",
"ink_lang2/ink-generate-abi",
]
ink-as-dependency = []
......
#![feature(proc_macro_hygiene)]
#![cfg_attr(not(feature = "std"), no_std)]
use ink_core::storage;
use ink_lang2 as ink;
#[ink::contract(version = "0.1.0")]
mod {{name}} {
/// Defines the storage of your contract.
/// Add new fields to the below struct in order
/// to add new static storage fields to your contract.
#[ink(storage)]
struct {{camel_name}} {
/// Stores a single `bool` value on the storage.
value: storage::Value<bool>,
}
impl {{camel_name}} {
/// Constructor that initializes the `bool` value to the given `init_value`.
#[ink(constructor)]
fn new(&mut self, init_value: bool) {
self.value.set(init_value);
}
/// Constructor that initializes the `bool` value to `false`.
///
/// Constructors can delegate to other constructors.
#[ink(constructor)]
fn default(&mut self) {
self.new(false)
}
/// A message that can be called on instantiated contracts.
/// This one flips the value of the stored `bool` from `true`
/// to `false` and vice versa.
#[ink(message)]
fn flip(&mut self) {
*self.value = !self.get();
}
/// Simply returns the current value of our `bool`.
#[ink(message)]
fn get(&self) -> bool {
*self.value
}
}
/// Unit tests in Rust are normally defined within such a `#[cfg(test)]`
/// module and test functions are marked with a `#[test]` attribute.
/// The below code is technically just normal Rust code.
#[cfg(test)]
mod tests {
/// Imports all the definitions from the outer scope so we can use them here.
use super::*;
/// We test if the default constructor does its job.
#[test]
fn default_works() {
/// Note that even though we defined our `#[ink(constructor)]`
/// above as `&mut self` functions that return nothing we can call
/// them in test code as if they were normal Rust constructors
/// that take no `self` argument but return `Self`.
let {{name}} = {{camel_name}}::default();
assert_eq!({{name}}.get(), false);
}
/// We test a simple use case of our contract.
#[test]
fn it_works() {
let mut {{name}} = {{camel_name}}::new(false);
assert_eq!({{name}}.get(), false);
{{name}}.flip();
assert_eq!({{name}}.get(), true);
}
}
}
#![cfg_attr(not(feature = "std"), no_std)]
use ink_core::{
memory::format,
storage,
};
use ink_lang::contract;
contract! {
#![env = ink_core::env::DefaultSrmlTypes]
/// This simple dummy contract has a `bool` value that can
/// alter between `true` and `false` using the `flip` message.
/// Users can retrieve its current state using the `get` message.
struct {{camel_name}} {
/// The current state of our flag.
value: storage::Value<bool>,
}
impl Deploy for {{camel_name}} {
/// Initializes our state to `false` upon deploying our smart contract.
fn deploy(&mut self) {
self.value.set(false)
}
}
impl {{camel_name}} {
/// Flips the current state of our smart contract.
pub(external) fn flip(&mut self) {
*self.value = !*self.value;
}
/// Returns the current state.
pub(external) fn get(&self) -> bool {
env.println(&format!("Storage Value: {:?}", *self.value));
*self.value
}
}
}
#[cfg(all(test, feature = "test-env"))]
mod tests {
use super::*;
#[test]
fn it_works() {
let mut contract = {{camel_name}}::deploy_mock();
assert_eq!(contract.get(), false);
contract.flip();
assert_eq!(contract.get(), true);
}
}
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of ink!.
//
// ink! is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ink! is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ink!. If not, see <http://www.gnu.org/licenses/>.
use crate::{
env2::{
call::{
state,
CallData,
Selector,
},
errors::CallError,
Env,
EnvAccessMut,
EnvTypes,
},
memory::vec::Vec,
};
use core::marker::PhantomData;
/// Represents a return type.
///
/// Used as a marker type to differentiate at compile-time between invoke and evaluate.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ReturnType<T>(PhantomData<fn() -> T>);
/// The final parameters to the cross-contract call.
pub struct CallParams<E, R>
where
E: EnvTypes,
{
/// The account ID of the to-be-called smart contract.
callee: E::AccountId,
/// The maximum gas costs allowed for the call.
gas_limit: u64,
/// The transferred value for the call.
value: E::Balance,
/// The expected return type.
return_type: PhantomData<ReturnType<R>>,
/// The already encoded call data respecting the ABI.
call_data: CallData,
}
/// Builds up a call.
pub struct CallBuilder<E, R, Seal>
where
E: EnvTypes,
{
/// The current parameters that have been built up so far.
params: CallParams<E, R>,
/// Seal state.
seal: PhantomData<Seal>,
}
impl<E, R> CallParams<E, R>
where
E: EnvTypes,
{
/// The code hash of the contract.
pub fn callee(&self) -> &E::AccountId {
&self.callee
}
/// The gas limit for the contract instantiation.
pub fn gas_limit(&self) -> u64 {
self.gas_limit
}
/// The endowment for the instantiated contract.
pub fn endowment(&self) -> &E::Balance {
&self.value
}
/// The raw encoded input data.
pub fn input_data(&self) -> &CallData {
&self.call_data
}
}
impl<E, R> CallParams<E, R>
where
E: EnvTypes,
E::Balance: Default,
{
/// Creates the default set of parameters for the cross-contract call.
fn new(callee: E::AccountId, selector: Selector) -> Self {
Self {
callee,
gas_limit: 0,
value: E::Balance::default(),
return_type: PhantomData,
call_data: CallData::new(selector),
}
}
/// Returns a builder for a cross-contract call that might return data.
pub fn eval(
callee: E::AccountId,
selector: Selector,
) -> CallBuilder<E, ReturnType<R>, state::Unsealed> {
CallBuilder {
params: CallParams::new(callee, selector),
seal: Default::default(),
}
}
/// Returns a builder for a cross-contract call that cannot return data.
///
/// Prefer this over [`eval`] if possible since it is the more efficient operation.
pub fn invoke(
callee: E::AccountId,
selector: Selector,
) -> CallBuilder<E, (), state::Unsealed> {
CallBuilder {
params: CallParams::new(callee, selector),
seal: Default::default(),
}
}
}
impl<E, R, Seal> CallBuilder<E, R, Seal>
where
E: EnvTypes,
{
/// Sets the maximumly allowed gas costs for the call.
pub fn gas_limit(mut self, gas_limit: u64) -> Self {
self.params.gas_limit = gas_limit;
self
}
/// Sets the value transferred upon the execution of the call.
pub fn value(mut self, value: E::Balance) -> Self {
self.params.value = value;
self
}
}
impl<E, R> CallBuilder<E, R, state::Unsealed>
where
E: EnvTypes,
{
/// Pushes an argument to the inputs of the call.
pub fn push_arg<A>(mut self, arg: &A) -> Self
where
A: scale::Encode,
{
self.params.call_data.push_arg(arg);
self
}
/// Seals the call builder to prevent further arguments.
pub fn seal(self) -> CallBuilder<E, R, state::Sealed> {
CallBuilder {
params: self.params,
seal: Default::default(),
}
}
}
impl<E, R, Seal> CallBuilder<E, ReturnType<R>, Seal>
where
E: Env,
R: scale::Decode,
{
/// Fires the call to the remote smart contract.
/// Returns the returned data back to the caller.
///
/// # Note
///
/// Prefer using the [`fire_using`] method whenever possible
/// since it is more efficient.
pub fn fire(self) -> Result<R, CallError>
where
R: scale::Decode,
{
E::eval_contract(&mut Vec::new(), &self.params).map_err(|_| CallError)
}
/// Fires the call to the smart contract and returns
/// the return value of the call back to the caller.
///
/// # Note
///
/// Uses the provided environmental access in order to
/// dispatch the call which is more efficient than the
/// [`fire`] method.
pub fn fire_using(self, env: &mut EnvAccessMut<E>) -> Result<R, CallError>
where
R: scale::Decode,
{
env.eval_contract(&self.params).map_err(|_| CallError)
}
}
impl<E, Seal> CallBuilder<E, (), Seal>
where
E: Env,
{
/// Fires the cross-call to the smart contract.
///
/// # Note
///
/// Prefer using the [`fire_using`] method whenever possible
/// since it is more efficient.
pub fn fire(self) -> Result<(), CallError> {
E::invoke_contract(&mut