ink
ink copied to clipboard
Rework the `Environment` trait
It is possible to customize AccountId
, Timestamp
, Balance
, Hash
, Gas
, BlockNumber
types on the contract-pallet
side. So different parachain can use different types inside.
To support it and be agnostic to the parachain/blockchain ink! provides Environment
trait that is automatically implemented by the contract with DefaultEnvironment
or can be customized with its own environment via #[ink::contract(env = CustomEnvironment)]
.
The problem with that approach is:
- ink! requires propagation of the
Env
generic via the whole stack. - It makes the development of ink!, libraries for smart contracts harder.
- It makes code harder for newcomers. They should be aware of the generics and inner struct of the pallet.
- It is impossible to import a contract written for another parachain if the types are not the same. Because one contract will use
ParachainAEnvironment
and another -ParachainBEnvironment
.
We are proposing another approach:
Create a separate crate like ink_types
that will contain definitions of all customizable items(better put it in a separate repository). It can be, for example, that file.
ink_env
crate uses ink_types
to get all requires types:
/// The default balance type.
pub type Balance = ink_types::Balance;
/// The default timestamp type.
pub type Timestamp = ink_types::Timestamp;
/// The default gas type.
pub type Gas = ink_types::Gas;
/// The default block number type.
pub type BlockNumber = ink_types::BlockNumber;
And use a default configuration.
If some parachain/blockchain want to use custom types, they fork the repository and use [patch.crates-io]
to override default types.
[patch.crates-io]
ink_types = { path = "/path/to/local/ink_types"}
or
[patch.crates-io]
ink_types = { git = "https://github.com/fork/ink_types"}
So the destination contract decides that types use across all dependencies. It solves all problems described above. If someone wants to deploy the contract to parachain/blockchain with the custom environment he only needs to add 2 lines in his Cargo.toml
.
If some parachain/blockchain want to use custom types, they fork the repository and use [patch.crates-io] to override default types.
I really don't like this approach. Part of the point of using Substrate is that things are generic and they can be swapped out easily. Having people fork a crate only to swap some types around seems like unnecessary overhead
Part of the point of using Substrate is that things are generic and they can be swapped out easily
It is that I'm suggesting!=)
Only maintainers of the network should fork crate and specify new types. The developers only should add two lines in Cargo.toml
. In real life, we will have more developers than parachains=) If you have ten parachain with unique types and 10k developers, you will have only ten crates that can be easily swapped. All contracts will be compatible between that networks because any contract didn't specify ParachainXEnvironment
.
Right now, if you have contracts that use ParachainXEnvironment
, you can't deploy it on parachain Y. You can't import that crate(with contract) and reuse the logic of that contract because it works only with ParachainXEnvironment
. You need to fork that contract(or a bunch of contracts) and swap ParachainXEnvironment
-> ParachainYEnvironment
. Of course, that problem can be solved if each struct in the contract uses the E: Environment
generic(I want to highlight that generics are not allowed in contracts and traits right now).
But what approach adds more overhead? The approach where 10k developers should propagate E: Environment
everywhere, slow down development, and fix compilation errors. Or fork crate ten times, and during the deployment specify two lines(only in your final contract or in the workspace) and don't worry about any dependency and environments?=)
I like the idea of using generics in the Substrate. And I like generics in Rust=) But smart contracts are about easy development and faster deployment. You don't need to be a guru of Rust to write a smart contract otherwise, I can use Substrate=D
In other words, they are global types for the whole parachain's smart contract ecosystem. But in that way, they still are generic because you can replace them to work with specific parachain.
With substrate, one relay chain contains many parachain, and each parachain has its Config
. But in terms of one parachain, the config is the same across all contracts on that parachain otherwise it can't work.
Personally I'd prefer to just fix AccountId to AccountId32 and Balance to u128. Substrate is already built to allow customization. What we need more is an unified ecosystem that can interoperate.
Added an example to show how it could work and how it is easy to do on the developer side=)