From 64ec788b06b2ba509852870a1ffa858873e158e1 Mon Sep 17 00:00:00 2001 From: pcorrado-np Date: Fri, 16 Jun 2023 13:18:21 -0300 Subject: [PATCH] CON-21 translation: basics/selectors.md, basics/verification.md, intro/ink-vs-solidity.md & intro/polkadot.md --- .../current/basics/selectors.md | 81 + .../current/basics/verification.md | 208 ++ .../current/intro/ink-vs-solidity.md | 1879 ++++++++--------- .../current/intro/polkadot.md | 54 +- 4 files changed, 1253 insertions(+), 969 deletions(-) create mode 100644 i18n/es/docusaurus-plugin-content-docs/current/basics/selectors.md create mode 100644 i18n/es/docusaurus-plugin-content-docs/current/basics/verification.md diff --git a/i18n/es/docusaurus-plugin-content-docs/current/basics/selectors.md b/i18n/es/docusaurus-plugin-content-docs/current/basics/selectors.md new file mode 100644 index 0000000000..1926bebab7 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/basics/selectors.md @@ -0,0 +1,81 @@ +--- +title: Selectores +hide_title: true +slug: /basics/selectors +--- + +# Selectores + +Los selectors en ink! son una forma de lenguaje agnóstico para identificar constructores y mensajes. Son strings hexadecimales de cuatro bytes que se ven asi: `0x633aa551`. + +Podemos encontrar el selector de un constructor de ink! o el mensaje en la [metadata del contrato](/basics/metadata) buscando el campo `selector` del dispatchable que nos interese. + +Aquí hay un ejemplo de como se puede elegir el nombre del mensaje y el selector desde la metadata del contrato usando [`jq`](https://stedolan.github.io/jq/). + +``` +cat target/ink/flipper.json | jq '.spec.messages[0] | "\(.label): \(.selector)"' +"flip: 0x633aa551" +``` + +## Cálculo del selector + +Si no se tiene acceso a la metadata del contrato uno puede calcularlo por si mismo. + +El algoritmo que ink! usa es bastante directo: + +1. Obtener _solo_ el nombre del constructor o el mensaje +2. Computar el hash `BLAKE2` del nombre +3. Tomar los primeros cuatro bytes del hash como selector + +Vamos a dar un breve ejemplo de cómo se ve en la práctica. Considerar el siguiente mensaje: + +```rust +#[ink(message)] +fn frobinate(&mut self, fro: bool, bi: u32, nate: AccountId) -> bool { + unimplemented!() +} +``` + +Para calcular el selector: +1. Tomar el nombre del mensaje, `frobinate` +2. Computar `BLAKE2("frobinate") = 0x8e39d7f22ef4f9f1404fe5200768179a8b4f2b67799082d7b39f6a8ca82da8f1` +3. Tomar los primeros cuatro bytes, `0x8e39d7f2` + +## Cálculo del selector: Traits de ink! + +Estas reglas cambian un poco si se define cualquier mensaje usando el `[ink::trait_definition]` [macro](/basics/trait-definitions). +Para el primer paso, en lugar de tomar el nombre del mensaje _justo_ , ahora agregamos además el _nombre del trait_ para el cálculo del selector. + +``` +cat target/ink/trait-flipper.json | jq '.spec.messages[0] | "\(.label): \(.selector)"' +"Flip::flip: 0xaa97cade" +``` + +Veamos cómo se vería ésto en la práctica. Consideremos el siguiente trait: + +```rust +#[ink::trait_definition] +pub trait Frobinate { + fn frobinate(&mut self, fro: bool, bi: u32, nate: AccountId) -> bool; +} + +-- snip -- + +impl Frobinate for Contract { + #[ink(message)] + fn frobinate(&mut self, fro: bool, bi: u32, nate: AccountId) -> bool { + unimplemented!() + } +} +``` + +Para calcular el selector: +1. Tomar el nombre del trait **y** el nombre del mensaje, `Frobinate::frobinate` +2. Computar `BLAKE2("Frobinate::frobinate") = 0x8915412ad772b2a116917cf75df4ba461b5808556a73f729bce582fb79200c5b` +3. Tomar los primeros cuatro bytes, `0x8915412a` + +:::tip + +No preocuparse si no se puede calcular el hash de un string `BLAKE2` a mano. Se puede usar la [utilidad de Substrate de Shawn](https://www.shawntabrizi.com/substrate-js-utilities/) para que lo haga por uno! + +::: \ No newline at end of file diff --git a/i18n/es/docusaurus-plugin-content-docs/current/basics/verification.md b/i18n/es/docusaurus-plugin-content-docs/current/basics/verification.md new file mode 100644 index 0000000000..7ae6acca0f --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/basics/verification.md @@ -0,0 +1,208 @@ +--- +title: Verificación de un contrato +slug: /basics/contract-verification +hide_title: true +--- + + + +# Verificación de un Contrato + +La verificación de un contrato es el proceso de hacer coincidir un contrato deployado de ink! con el código fuente y la metadata generada cuando éste fue compilado. + +El proceso de verificación para lenguajes de smart contracts basados en Wasm es más complejo que los lenguajes basados en EVM tal como Solidity debido a que el compilador de Rust no provee una compilación determinista de los contratos. + +Para verificar un smart contract de ink! o Wasm el proceso de verificación debe recompilar el contrato en un entorno de host idéntico al que fue creado originalmente. La manera más simple de hacerlo es usar un contenedor de Docker. + +Como esto no será posible con los smart contracts existentes, se ha creado un mecanismo alternativo en el cual un deployador de contrato puede proveer un archivo de metadatos firmado para asociarlo al contrato. Este enfoque también se describe abajo. + +:::note +Por el momento, la herramienta CLI `cargo-contract` no provee una imagen Docker para ink! creando compilaciones verificables. El siguiente [issue](https://github.com/paritytech/cargo-contract/issues/1065) se ha creado con detalles sobre esto. + +Como una solución provisoria, los Labs de Web3 están publicando una [imagen de contenedor](https://github.com/web3labs/ink-verifier-image) para la verificación de código fuente de los smart contracts ink! +::: + +Los Labs de Web3 han puesto a disposición una versión pública de su [servicio de verificación](https://github.com/web3labs/ink-verifier-server) para atender a los ecosistemas de ink! y DotSama. Esto puede ser usado junto al contenedor de compilación de imagen verificable para validar los smart contracts de ink!. + +Los siguientes pasos describen cómo crear una compilación verificable y de ese modo validarlo usando estos servicios. + +## Realizar una compilación verificable + +Ya deberían estar familiarizados con el uso de `cargo-contract` para +[compilar su contrato](/getting-started/building-your-contract). + +Necesitarán instalar el contenedor de imagen verificada de ink!: +``` +cargo install — git +https://github.com/web3labs/ink-verifier-image.git +``` + +Ahora se puede realizar una compilación verificada al ejecutar el siguiente comando en la carpeta de proyecto de smart contracts: +``` +build-verifiable-ink -i ghcr.io/web3labs/ink-verifier . +``` + +:::note +Las compilaciones reproducibles sólo funcionan con cargo-contract >= 2.0.2 +y los contratos generados con esa versión en adelante. Para solucionarlo se puede usar en su lugar el archivo de metadatos firmados. +::: + +Si se usara el [ejemplo flipper](/getting-started/creating-an-ink-project) se vería un resultado similar al de abajo: + +``` +... + [5/5] Generating bundle + +Original wasm size: 20.6K, Optimized: 1.4K + +The contract was built in RELEASE mode. + +Your contract artifacts are ready. You can find them in: +/build/package/src/target/ink + + - flipper.contract (code + metadata) + - flipper.wasm (the contract's code) + - flipper.json (the contract's metadata) + adding: src/ (stored 0%) + adding: src/Cargo.lock (deflated 75%) + adding: src/Cargo.toml (deflated 52%) + adding: src/lib.rs (deflated 72%) + adding: flipper.contract (deflated 64%) +Verification package in /build/target/ink/package.zip +Archive: /build/target/ink/package.zip + Length Date Time Name +--------- ---------- ----- ---- + 0 2023-03-08 21:41 src/ + 105695 2023-03-08 21:28 src/Cargo.lock + 573 2023-03-08 20:40 src/Cargo.toml + 5177 2023-03-08 20:40 src/lib.rs + 5278 2023-03-08 21:41 flipper.contract +--------- ------- + 116723 5 files +``` + +Si se tiene cualquier problema ejecutando la compilación, uno mismo puede compilarla ejecutando los siguientes comandos: +``` +cd ../ +git clone https://github.com/web3labs/ink-verifier-image.git +cd ink-verifier-image +docker build . -t ink-verifier:develop +cd ../flipper +build-verifiable-ink -t develop . +``` + +Habrá ahora un paquete de zipfile disponible que contendrá el código fuente del contrato, los metadatos y el binario de Wasm: +``` +tree -L 3 +. +├── Cargo.lock +├── Cargo.toml +├── lib.rs +└── target + └── ink + └── package.zip +``` + +Ahora que se ha creado la compilación verificada puede [deployar su contrato](getting-started/deploy-your-contract). + +Una vez deployado, se deberá tomar nota del código hash del contrato para poder verificarlo. + +## Verificando un smart contract de ink! + +### Usando la web app del servicio de verificación + +El [servicio de verificación de ink!](https://github.com/web3labs/ink-verifier-server) es un servicio web RESTful creado para verificar smart contracts deployados usando el [pallet-contracts](https://crates.io/crates/pallet-contracts). + +Web3 Labs alberga una instancia pública del servicio en +[ink-verifier.sirato.xyz](https://ink-verifier.sirato.xyz/). Una interfaz Swagger para el servicio está también disponible en +[ink-verifier.sirato.xyz/api/api-docs/](https://ink-verifier.sirato.xyz/api/api-docs/). + +ink! Verification Service Swagger endpoint + +El proceso de verificación incluye los siguientes pasos: + +1. Un solicitante sube un archivo de paquete fuente para una red y un código hash +2. El servidor controla que: + - El código fuente para la red y el código hash no han sido verificados aún o no están siendo verificados + - Hay suficientes recursos del host para comenzar una nueva verificación +3. El servidor descarga el Wasm byte code limpio correspondiente a la red y el código fuente provistos +4. El servidor transmite el archivo si es un archivo comprimido +5. El servidor mueve los archivos de staging al directorio processing +6. El servidor ejecuta un contenedor para que la imagen de verificación valide el paquete en processing. Ver el flujo de tareas de verificación del código fuente para detalles. +7. En el caso de una salida del contenedor el servidor mueve los artefactos verificados al directorio publish si la verificación fue exitosa, de otro modo mantiene un registro en el directorio de errores. + +Funciona con cualquier red que se define en el paquete [@polkadot/apps-config](https://github.com/polkadot-js/apps/tree/master/packages/apps-config/src/endpoints). + +Para verificar un contrato deployado usando el servicio necesitará usar el endpoint `/verify/{network}/{codeHash}` que está documentado [aquí](https://ink-verifier.sirato.xyz/api/api-docs/#/default/post_verify__network___codeHash_). + +Una vez que el contrato ha sido verificado se pueden usar los endpoints +`/contract/{codeHash}/metadata.json` y `/contract/{codeHash}/src` para recuperar los metadatos y el código fuente respectivamente. + + +### Usando Sirato + +Sirato Substrate es un explorador de smart contracts para los smart contracts de ink!. Se integra con el servicio de verificación de contratos permitiendo a los usuarios cargar archivos de paquete generados por la imagen de compilación verificable por medio de la UI Sirato en lugar de tener que usar el endpoint web. + +Por otra parte una vez que un contrato ha sido verificado, los detalles de cualquier actividad de contrato y eventos que tienen lugar en una parachain o Substrate chain son decodificados para el usuario en Sirato. + +Por ejemplo, para poder verificar un contrato deployado en la parachain Rococo, puede dirigirse a la instancia de Sirato en [substrate.sirato.xyz](https://substrate.sirato.xyz/). + + +Sirato Substrate + +Desde allí se puede navegar al código deployado al hacer click en la referencia de código que compatible con el código hash devuelto por la devuelto por la llamada de instanciación de cargo contract. + +Alternativamente, se puede acceder directamente entrando en la URL +`https://substrate.sirato.xyz/codes/0x`. + +Sirato contract instance view + +Ahora hacer clic en la pestaña código fuente: +Sirato package upload page + +Luego cargar el archivo verificado `package.zip` que se generó anteriormente. + +Sirato package upload complete + +Ahora se puede comenzar el proceso de verificación que inicia una compilación de los recursos provistos. + +Sirato package verification + +Una vez que el proceso ha terminado se verá el mensaje +`Contract successfully verified`. + +Sirato package verification complete + +Al hacer click en los archivos verificados se mostrarán los metadatos y archivos fuente asociados a su contrato. + +Sirato browse verified contract + +Luego si accedemos de nuevo a nuestra instancia de contrato, cualquier método o evento estará ahora decodificado. + +Sirato decoded contract transaction + +Podemos verificar esto invocando un método en el contrato. Podemos ver ahora el método decodificado que fue llamado en Sirato. + +Another Sirato decoded contract transaction + +### Carga de metadatos no verificados + +El servicio de verificación soporta la carga de metadatos del contrato firmado como una alternativa adicional para metadatos generados por compilación reproducible. Por favor tener en cuenta que los metadatos firmados no están verificados y el titular del código hash es confiable. + +Esta carácteristica responde a: + +1. El soporte para los datos `build_info` sólo está disponible desde `cargo-contract` 2.0.2. +2. Aún no hay una imagen oficial o procedimiento con respecto a compilaciones reproducibles. +3. Mientras tanto queremos expandir la utilidad del servicio. + +Aunque está lejos de ser la manera ideal para vincular los metadatos a un código hash dado evita un exploit trivial al: +- Verificar que la firma es de la cuenta del propietario del código hash. +- Verificar que el mensaje firmado coincide con el sha256 del metadata.json subido + el código hash del bytecode del contrato. + + Para proveer los metadatos firmados, será necesario usar el endpoint + `/upload/{network}/{codeHash}` que está documentado [aquí](https://ink-verifier.sirato.xyz/api/api-docs/#/default/post_upload__network___codeHash_) + +Para el cuerpo de la solicitud se necesitará firmar el mensaje usando la cuenta que subió el contrato. Se puede usar la [herramienta firmar y verificar](https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/signing) en Polkadot.js. + +También se puede usar Sirato para esto, se pueden encontrar instrucciones +[aquí](https://github.com/web3labs/ink-verifier-server/blob/main/docs/TUTORIAL.md#s2---owner-signed-metadata). diff --git a/i18n/es/docusaurus-plugin-content-docs/current/intro/ink-vs-solidity.md b/i18n/es/docusaurus-plugin-content-docs/current/intro/ink-vs-solidity.md index 6ad74a7163..d455f94ba4 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/intro/ink-vs-solidity.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/intro/ink-vs-solidity.md @@ -1,941 +1,938 @@ ---- -title: ink! vs. Solidity -slug: /ink-vs-solidity -hide_title: true ---- - - - -# ink! vs. Solidity - -Debajo se expone una brefe comparación entre ink! y Solidity: - -
- -||ink!|Solidity| -|:---|:---|:--------------| -|Máquina Virtual|Cualquier Máquina Virtual EVM M) Wasm| EVM | -|Encoding|Wasm| EVM Byte Code | -|Lenguaje|Rust| Propio | -|Protección de Overflow|Incluído por default| Si | -|Funciones de Constructor|Múltiples| Una única | -|Herramientas|Todas las soportadas por Rust| Propia | -|Sistema de versiones|Semantica| Semantica | -|Tiene Metadata?|Si| Si | -|Proyecto de múltiples archivos|Planeado| Si | -|Entradas de Guardado|Variable| 256 bits | -|Tipos Soportados|Docs| Docs | -|Tiene interfaces?|Si (Traits de Rust)| Si | - -
- -:::note -TODO -::: - -## Solidity to ink! Guide - -### Table of Contents - -- [Solidity to ink! Guide](#solidity-to-ink-guide) -- [Table of Contents](#table-of-contents) -- [Converting a Solidity Contract to ink!](#converting-a-solidity-contract-to-ink) - - [1. Generate New ink! Contract](#1-generate-new-ink-contract) - - [2. Build ink! Contract](#2-build-ink-contract) - - [3. Convert Solidity class fields to Rust struct](#3-convert-solidity-class-fields-to-rust-struct) - - [4. Convert each function](#4-convert-each-function) -- [Best Practices + Tips](#best-practices--tips) -- [Syntax Equivalencies](#syntax-equivalencies) - - [`public function`](#public-function) - - [`mapping declaration`](#mapping-declaration) - - [`mapping usage`](#mapping-usage) - - [`struct`](#struct) - - [`assertions / requires`](#assertions--requires) - - [`timestamp`](#timestamp) - - [`contract caller`](#contract-caller) - - [`contract's address`](#contracts-address) - - [`bytes`](#bytes) - - [`uint256`](#uint256) - - [`payable`](#payable) - - [`received deposit / payment`](#received-deposit--payment) - - [`contract balance`](#contract-balance) - - [`transfer tokens from contract`](#transfer-tokens-from-contract) - - [`events & indexed`](#events--indexed) - - [`errors and returning`](#errors-and-returning) - - [`throw`](#throw) - - [`assert`](#assert) - - [`require and revert`](#require-and-revert) - - [`nested mappings + custom / advanced structures`](#nested-mappings--custom--advanced-structures) - - [`cross-contract calling`](#cross-contract-calling) - - [`submit generic transaction / dynamic cross-contract calling`](#submit-generic-transaction--dynamic-cross-contract-calling) -- [Limitations of ink! v3](#limitations-of-ink-v3) -- [Troubleshooting Errors](#troubleshooting-errors) -- [unit testing (off-chain)](#unit-testing-off-chain) - -## Converting a Solidity Contract to ink! - -### 1. Generate a new ink! contract - -Run the following command to generate the skeleton for an ink! contract. -The command will set up the boilerplate code for ink!'s "Hello, World!" -(the [`flipper`](https://github.com/paritytech/ink-examples/tree/main/flipper) contract)). - -``` -cargo contract new -``` - -### 2. Build the contract - -``` -cargo contract build -``` - -### 3. Convert Solidity class fields to Rust struct - -Solidity is an object oriented language, and uses classes. ink! (Rust) does not use classes. - -An example Solidity class looks like: - - - -```c++ -contract MyContract { - bool private _theBool; - event UpdatedBool(bool indexed _theBool); - - constructor(bool theBool_) { - require(theBool_ == true, "theBool_ must start as true"); - - _theBool = theBool_; - } - - function setBool(bool newBool) public returns (bool boolChanged) { - if _theBool == newBool { - boolChanged = false; - } else { - boolChanged = true; - } - - _theBool = newBool; - // emit event - UpdatedBool(newBool); - } -} -``` - -And the equivalent contract in ink! looks like: - -```rust -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod mycontract { - #[ink(storage)] - pub struct MyContract { - the_bool: bool, // class members become struct fields - } - - #[ink(event)] - pub struct UpdatedBool { - #[ink(topic)] // -> indexed - the_bool: bool, - } - - impl MyContract { - #[ink(constructor)] - pub fn new(the_bool: bool) -> Self { - assert!(the_bool == true, "the_bool must start as true"); - Self { the_bool } - } - - #[ink(message)] // functions become struct implementations - pub fn set_bool(&mut self, new_bool: bool) -> bool { - let bool_changed = true; - - if self.the_bool == new_bool{ - bool_changed = false; - }else{ - bool_changed = true; - } - - self.the_bool = new_bool; - - self.env().emit_event(UpdatedBool { - the_bool: new_bool - }); - - // return - bool_changed - } - } -} -``` - -A few key differences are: - -- Solidity class variables / members will be placed in the contract struct in ink! -- All class methods in Solidity are `impl`emented for the contract struct in ink! -- Solidity frequently prefixes variables with an underscore (`_name`). ink! / Rust only prefixes with an underscore for _unused_ variables. -- Solidity uses camelCase. ink! uses snake_case. -- In Solidity, the variable type comes before the variable name (e.g. bool myVar). While ink! specifies var type after the var name (e.g. my_var: bool) - -### 4. Convert each function - -- Start converting each function one by one. - - A recommended approach is to, if possible, skip cross-contract calls at first and use mock data instead - - This way off-chain unit tests can be written to test the core functionality - - unit tests are off-chain and do not work with cross-contract calls - - Once fully tested, start adding in cross-contract calls and perform on-chain manual + integration tests -- Ensure that function's visibility (public, private) are matched in ink! -- In Solidity, if a function returns a `bool success`, ink! will use a `Result<()>` instead (`Result::Ok` or `Result::Err`). - - ```rust - // ink! - - // result type - pub type Result = core::result::Result; - - // ... - - // public function that returns a Result - #[ink(message)] - pub fn my_function(&self) -> Result<()> { - Ok(()) - } - ``` - -## Best Practices + Tips - -- If the Solidity contract uses a `string`, it is recommended to use a `Vec` to avoid the overhead of a `String`. See [here](https://substrate.stackexchange.com/questions/1174/why-is-it-a-bad-idea-to-use-string-in-an-ink-smart-contract) for more details on why. The smart contract should only contain the information that strictly needs to be placed on the blockchain and go through consensus. The UI should be used for displaying strings. -- Double check all `.unwrap()`s performed. Solidity does not have as strict checking as ink! does. For example, a mapping field can be accessed as simple as `myMapping[someKey]`. ink!, however, requires `self.my_mapping.get(some_key).unwrap()`. A useful way to handle `None` cases is to use `.unwrap_or(some_val)`. -- Run the contracts node with `substrate-contracts-node -lerror,runtime::contracts=debug` for debug prints, and errors to be displayed in the nodes console. -- When passing parameters to a helper, it is recommended to pass references (even for primitives) as Wasm is more efficient with references. - For example (see [erc20](https://github.com/paritytech/ink-examples/blob/main/erc20/lib.rs) example): - -```rust -/// Returns the account balance for the specified `owner`. -/// -/// Returns `0` if the account is non-existent. -#[ink(message)] -pub fn balance_of(&self, owner: AccountId) -> Balance { - self.balance_of_impl(&owner) -} - -/// Returns the account balance for the specified `owner`. -/// -/// Returns `0` if the account is non-existent. -/// -/// # Note -/// -/// Prefer to call this method over `balance_of` since this -/// works using references which are more efficient in Wasm. -#[inline] -fn balance_of_impl(&self, owner: &AccountId) -> Balance { - self.balances.get(owner).unwrap_or_default() -} -``` - -- Just as in Solidity, ink! does not have floating point numbers due to the non-deterministic nature. Instead, the frontend should add decimal points as needed. - -## Syntax Equivalencies - -### `public function` - -```c++ -// solidity -function fnName() public {} -// or -// by default, functions are public -function fnName() {} -``` - -```rust -// ink! -#[ink(message)] -pub fn fn_name(&self) {} -``` - -### `mapping declaration` - -```c++ -// solidity -mapping(address => uint128) private mapName; -``` - -```rust -//ink! -use ink_storage::{ - traits::SpreadAllocate, - Mapping, -}; - -#[ink(storage)] -#[derive(SpreadAllocate)] -pub struct ContractName { - map_name: Mapping, -} -``` - -when using a map in ink!, `ink_lang::utils::initialize_contract` must be used in the constructor. See [here](https://use.ink/datastructures/mapping) for more details. - -### `mapping usage` - -```c++ -// solidity - -// insert / update -aMap[aKey] = aValue; - -// get -aMap[aKey] -``` - -```rust -// ink! - -//insert / update -self.a_map.insert(&a_key, &a_value); - -// get -self.a_map.get(a_key).unwrap() -``` - -### `struct` - -```c++ -// solidity -struct MyPerson{ - address person; - u64 favNum; -} -``` - -```rust -// ink! -struct MyPerson { - person: AccountId, - fav_num: u64, -} -``` - -### `assertions / requires` - -```c++ -// solidity -require(someValue < 10, "someValue is not less than 10"); -``` - -```rust -// ink! -assert!(some_value < 10, "some_value is not less than 10"); -``` - -### `timestamp` - -```c++ -// solidity -block.timestamp -// or -now -``` - -```rust -// ink! -self.env().block_timestamp() -``` - -### `contract caller` - -```c++ -// solidity -address caller = msg.sender; -``` - -```rust -// ink! -let caller: AccountId = self.env().caller(); -``` - -### `contract's address` - -```c++ -// solidity -address(this) -``` - -```rust -// ink! -self.env().account_id() -``` - -### `bytes` - -Solidity has a type `bytes`. `bytes` is (essentially) equivalent to an array of uint8. So, `bytes` in Solidity => `Vec` or `[u8; ...]` in ink!. See [here](https://ethereum.stackexchange.com/questions/91119/difference-between-byte-and-uint8-datatypes-in-solidity) for more details. If desired, a `bytes` struct can be created in ink! to replicate the `bytes` type in Solidity. - -### `uint256` - -Solidity uses `uint256` and `uint` to represent a 256-bit type. - -Solidity is 256-bit / 32-byte word optimized. Meaning, using `uint256` in Solidity contracts will reduce gas usage -- but increase storage usage. The largest size ink! has built in is a `u128`. ink! compiles to Wasm. The largest primitive Wasm has is 64bit (due to most computers using 64bit). So, there is no benefit to using any larger primitive over a collection. - -When porting a `uint256` from Solidity to ink!, it is recommended to, with discretion, determine the range of the value, and choose the appropiate size (u8, u16, u32, u64, u128). If a 256-bit hash value is required, ink! has a `Hash` primitive available. In the event a value needs to be 256-bit, it is recommended to use an array (e.g. `[u64; 4]`). - -### `payable` - -```c++ -// solidity -function myFunction() payable returns (uint64) {} -``` - -```rust -#[ink(message, payable)] -pub fn my_function() -> (u64) {} -``` - -### `received deposit / payment` - -```C++ -// solidity -msg.value -``` - -```rust -// ink! -self.env().transferred_value() -``` - -### `contract balance` - -```c++ -// solidity -this.balance -``` - -```rust -// ink! -self.env().balance() -``` - -### `transfer tokens from contract` - -```c++ -// solidity -recipient.send(amount) -``` - -```rust -// ink! -if self.env().transfer(recipient, amount).is_err() { - panic!("error transferring") -} -``` - -### `events & indexed` - -```c++ -// solidity - -event MyCoolEvent( - u128 indexed indexedValue, - u128 notIndexedValue, -); - -// emit event -MyCoolEvent (someValue, someOtherValue) -``` - -```rust -// ink! - -#[ink(event)] -pub struct MyCoolEvent { - #[ink(topic)] - indexed_value: u128, - - not_indexed_value: u128, -} - -// emit event -self.env().emit_event(MyCoolEvent { - indexed_value: some_value, - not_indexed_value: some_other_value -}); -``` - -### `errors and returning` - -Solidity has several error handling mechanisms: `assert`, `require`, `revert`, and `throw`. Each of these will revert the changed state when called. See [this article](https://medium.com/blockchannel/the-use-of-revert-assert-and-require-in-solidity-and-the-new-revert-opcode-in-the-evm-1a3a7990e06e) for details on these. - -ink! uses a `Result` enum (`Ok(T)`, `Err(E)`), `assert!` and `panic!`. [This Stack Exchange](https://substrate.stackexchange.com/questions/2391/panic-in-ink-smart-contracts) answer and [GitHub discussion](https://github.com/paritytech/ink/issues/641) provide more details on these. - -#### `throw` - -Throw is deprecated in Solidity and would throw an invalid opcode error (no details) and revert the state. As an alternative to the `if...{throw;}` pattern in Solidity, a `Result::Err` should be returned for expected errors, and an `assert!` should be used for errors that should not occur. - -#### `assert` - -In Solidity, `assert` is used as internal guards against errors in the _code_. In general, properly functioning code should never hit a failing assert. `assert` in Solidity does not have error strings. In ink!, use `assert!`. `assert!` will `panic!` if it evaluates to _false_. The state will be reverted, and a `CalleeTrapped` will be returned. The (optional) error string will be printed to the debug buffer. - -```rust -// ink! -assert!(caller == owner, "caller is not owner") -``` - -#### `require and revert` - -In Solidity, `require` is used for general (normal) errors -- such as errors that occur based on user input. `require` does have the option for an error string. `revert` is very similar to `require` except that `revert` will be called in `if ... else` chains. Both `require` and `revert` will revert the chain state. In ink!, `if ... { return Err(Error::SomeError) }` should be used for `require` or `revert`. When a `Result::Err` is returned in ink!, then all state is reverted. - -In general, `Result::Err` should be used when a _calling contract_ needs to know _why_ a function failed. Otherwise, `assert!` should be used as it has less overhead than a `Result`. - -```c++ -// Solidity -function myFunction(bool returnError) public { - require(!returnError, "my error here"); - - // or - - if returnError { - revert("my error here"); - } -} -``` - -```rust -// ink! - -#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub enum Error { - /// Provide a detailed comment on the error - MyError, -} - -// result type -pub type Result = core::result::Result; - -// ... - -#[ink(message)] -pub fn my_function(&self, return_error: bool) -> Result<()> { - if return_error{ - return Err(Error::MyError) - } - Ok(()) -} -``` - -### `nested mappings + custom / advanced structures` - -In Solidity, it is easy to do nested mappings. It is not as straightforward in ink!. - -imagine the following scenario - -```c++ -// solidity -contract Dao { - struct Proposal { - mapping (address => bool) votedYes - } - - mapping (address => bool) public isWhitelisted; - Proposal[] public proposals; -} -``` - -in ink! this _seems_ like it could be represented like so: - -```rust -#[ink::contract] -mod dao { - - #[derive(SpreadAllocate)] - pub struct Proposal { - voted_yes: Mapping, - } - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct Dao { - proposals: Vec, - is_whitelisted: Mapping, - } - - impl Dao{ - #[ink(constructor)] - pub fn new(/*...*/) -> Self { - //required for mappings - ink_lang::utils::initialize_contract(|contract| {/*...*/}) - } - } -} -``` - -However, this will cause an error due to the nested mapping. [This answer](https://substrate.stackexchange.com/questions/1659/how-to-have-a-mapping-in-a-custom-structure-inside-an-ink-contract) explains in detail why nested mappings are not allowed - -So, as of now, to get around this issue an alternate data structure will need to be used. A data-structure that can be interchanged with the `Mapping` syntax and with minimal additional implementations is the `BTreeMap`. `BTreeMap` is less efficient than `Mapping`, but is an easy workaround until nested mappings are allowed. This will be used in the nested struct. Additional `derive`s will need to be added to be compatible with the #[ink(storage)] struct (see below). - -```rust -#[ink::contract] -mod dao { - - use ink_prelude::collections::BTreeMap; - - #[derive( - scale::Encode, - scale::Decode, - SpreadLayout, - PackedLayout, - SpreadAllocate, - )] - #[cfg_attr( - feature = "std", - derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) - )] - pub struct Proposal { - voted_yes: BTreeMap, - } - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct Dao { - proposals: Vec, - is_whitelisted: Mapping, - } - - impl Dao{ - #[ink(constructor)] - pub fn new(/*...*/) -> Self { - //required for mappings - ink_lang::utils::initialize_contract(|contract| {/*...*/}) - } - } -} -``` - -This almost works as expected. However, there is still one issue. `SpreadAllocate` (used with `Mapping`) requires that `Vec` implements `PackedAllocate`. To fix this, `Proposal` needs to implement `PackedAllocate`. See [here](https://docs.rs/ink_storage/3.3.1/ink_storage/traits/trait.PackedAllocate.html) for details + examples. See the following for this example: - -```rust -use ink_primitives::Key; - -pub struct Proposal { - voted_yes: BTreeMap, -} - -impl ink_storage::traits::PackedAllocate for Proposal { - fn allocate_packed(&mut self, at: &Key){ - PackedAllocate::allocate_packed(&mut *self, at) - } -} -``` - -### `cross-contract calling` - -In ink!, to do [cross-contract calling](https://use.ink/basics/cross-contract-calling), the contract will need to be added to the project. Ensure the contract is properly exporting its Structs. See the `erc20` contract example: - -```rust -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -// make the structs visible -pub use self::erc20::{ - Erc20, - // this is necessary - Erc20Ref, -}; - -#[ink::contract] -pub mod erc20 {} -``` - -In the new cross-called contract's Cargo.toml, add (or edit) the following: - -``` -[lib] -name = "erc20" -path = "lib.rs" -crate-type = [ - # Used for normal contract Wasm blobs. - "cdylib", - # Used for ABI generation. Necessary for importing as a dependency - "rlib", -] - -[features] -ink-as-dependency = [] -``` - -`ink-as-dependency` "tells the ink! code generator to always or never compile the smart contract as if it was used as a dependency of another ink! smart contract" ([source](https://use.ink/basics/cross-contract-calling)). - -Then, In the main contract's Cargo.toml, import the contract that will be cross-called. - -```rust -erc20 = { path = "erc20", default-features = false, features = ["ink-as-dependency"] } -``` - -And make sure to add it to the `std` field of the the .toml file. - -```rust -[features] -default = ["std"] -std = [ - # ... - - "erc20/std", -] -``` - -Now, import the cross-called-contract to the main contract: - -```rust -// example -use erc20::Erc20Ref; -``` - -There are two methods to setup the other contract. - -1. Instantiate the cross-called-contract in the main contract's constructor. - See [here](https://use.ink/basics/cross-contract-calling/) for a tutorial, and [here](https://github.com/paritytech/ink-examples/tree/main/delegator) for an example. -2. Or, add the `AccountId` of an already deployed contract. - Here is an example constructor to set this up: - - ```rust - use my_other_contract::MyOtherContractRef; - // ... - fn new(contract_id: AccountId) -> Self { - // for already deployed contract - let contract_ref: MyOtherContractRef = - ink_env::call::FromAccountId::from_account_id(contract_id); - Self {contract_ref} - } - ``` - -Now, to perform the cross-contract call: - -```rust -{ - self.contract_ref.some_external_function(a_param); -} -``` - -Note: as of now (ink! v3.3.1), when using cross-contract calls, emitting events will not work and compile errors will occur. See [issue #1000](https://github.com/paritytech/ink/issues/1000). Furthermore, the compiler will throw an error saying that (for example) Erc20Ref does not implement `SpreadAllocate`. This [issue #1149](https://github.com/paritytech/ink/issues/1149) explains more and has a workaround. These issues will be fixed in [issue #1134](https://github.com/paritytech/ink/issues/1134). - -### `submit generic transaction / dynamic cross-contract calling` - -```c++ -// solidity - -// invokes function found at`addr`, sends the `_amount` to the `addr`, and the `_transactionData` payload. -addr.call.value(_amount)(_transactionData) -``` - -```rust -// ink! - -// ... - -use ink_env::call::{ - build_call, - Call, - ExecutionInput, - Selector, -}; - -/// A wrapper that allows us to encode a blob of bytes. -/// -/// We use this to pass the set of untyped (bytes) parameters to the `CallBuilder`. -struct CallInput<'a>(&'a [u8]); - -impl<'a> scale::Encode for CallInput<'a> { - fn encode_to(&self, dest: &mut T) { - dest.write(self.0); - } -} - -// ... - -// see: https://github.com/paritytech/ink-examples/blob/main/multisig/lib.rs#L535 -fn invoke_transaction( - &mut self, - callee: AccountId, - transfer_amount: u128, - function_selector: [u8; 4], - transaction_data: Vec, - gas_limit: u64) -> Result<()> { - - let result = build_call::<::Env>() - .call_type( - Call::new() - .callee(callee) // contract to call - .gas_limit(*gas_limit) - .transferred_value(transfer_amount), // value to transfer with call - ) - .exec_input( - ExecutionInput::new(Selector::from(*function_selector)) - .push_arg(CallInput(transaction_data)), // SCALE-encoded parameters - ) - .returns::<()>() - .fire() - .map_err(|_| Error::TransactionFailed); - result -} - -``` - -Note: the `function_selector` bytes can be found in the generated `target/ink/.json`. - -## Limitations of ink! v3 - -- Multi-file projects are not supported with pure ink! - - implementing traits / interfaces will not work - - There are alternatives that do add this functionality such as OpenBrush -- Nested structs and data structures can be difficult to use -- Cross-contract calling prevents events from being emitted. See [here](https://github.com/paritytech/ink/issues/1000) for details. -- Cross-contract calling can not be tested off-chain with unit tests. - On-chain integration tests will need to be used. - -## Troubleshooting Errors - -- `ERROR: Validation of the Wasm failed.` - -``` -ERROR: Validation of the Wasm failed. - -ERROR: An unexpected panic function import was found in the contract Wasm. -This typically goes back to a known bug in the Rust compiler: -https://github.com/rust-lang/rust/issues/78744 - -As a workaround try to insert `overflow-checks = false` into your `Cargo.toml`. -This will disable safe math operations, but unfortunately we are currently not -aware of a better workaround until the bug in the compiler is fixed. -``` - -**Solution** -Add the following to the contract Cargo.toml: - -``` -[profile.release] -overflow-checks = false -``` - -- `"failed to load bitcode of module '...' "` - -This happens when trying to import a contract for cross-contract calling. - -**Solution** -Ensure that the following is added to Cargo.toml contract import:` - -``` -features = ["ink-as-dependency"] -``` - -so the import would look like: - -``` -mycontract = { path = "mycontract/", default-features = false, features = ["ink-as-dependency"]} -``` - -## unit testing (off-chain) - -- Unit tests are an integral part of smart-contract development and ensuring your code works off-chain before testing on-chain. -- To run ink! tests, do _not_ use `cargo +nightly contract test`. Use `cargo +nightly test`. Add the `--nocapture` flag for debug prints to show. See [here](https://substrate.stackexchange.com/questions/3197/how-to-understand-which-test-failed-in-ink) for more info why. -- From the contract module, make sure to make the the contract struct and anything else that is going to be used in the unit tests public. For example: - -```rust -// top of file -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -pub use self::mycontract::{ - MyContract -}; -``` - -- Off-chain unit tests will not work with cross-contract calls. - One workaround to ensure unit tests are still passing is to provide mock data. - -An easy approach is to use conditional compiling with `#[cfg(test)]` and `#[cfg(not(test))]`. - -Note: this solution is not ideal. ink! v4.0 will provide much better solutions. - -For example, here is a read-only ERC20 cross-contract call: - -```rust -// only compiles when *not* running tests -#[cfg(not(test))] -fn get_token_balance(&self, caller: &AccountId) -> Balance { - // calls the external ERC-20 contract - self.token.balance_of(*caller) -} - -// only compiles when running tests -#[cfg(test)] -fn get_token_balance(&self, _: &AccountId) -> Balance { - // arbitrary value - 1 -} -``` - -And if the cross-contract call _writes_ to storage, a mock field can be added to the contract struct. For example: - -```rust -#[ink(storage)] -pub struct MyContract { - #[cfg(test)] - mock_field: SomeStruct, // will serve as a fake storage -} - -... - -// on-chain, performs cross-contract call -#[cfg(not(test))] -fn do_some_write(&mut self) { - self.external_contract.write_to_field(0xDEADBEEF); -} - - -// testing environment only -#[cfg(test)] -fn do_some_write(&mut self) { - self.mock_field.my_fake_storage_item = 0xDEADBEEF; -} -``` - -- useful code to interact and modify the contract enviroment for testing - -[ink_env docs](https://paritytech.github.io/ink/ink_env/test/index.html) - -```rust -// get the default accounts (alice, bob, ...) -let accounts = ink_env::test::default_accounts::(); -accounts.alice //usage example - -// set which account calls the contract -ink_env::test::set_caller::(accounts.bob); - -// get the contract's address -let callee = ink_env::account_id::(); - -// set the contracts address. -// by default, this is alice's account -ink_env::test::set_callee::(callee); - -// transfer native currency to the contract -ink_env::test::set_value_transferred::(2); - -// increase block number (and block timestamp). -// this can be placed in a loop to advance the block many times -ink_env::test::advance_block::(); - -// generate arbitrary AccountId -AccountId::from([0x01; 32]); - -// generate arbitrary Hash -Hash::from([0x01; 32]) - -// macro for tests that are expected to panic. -#[should_panic] -``` +--- +title: ink! vs. Solidity +hide_title: true +slug: /ink-vs-solidity +--- + + + +# ink! vs. Solidity + +La siguiente tabla muestra una breve comparación de características entre ink! y Solidity: + +
+ +| | ink! | Solidity | +| :------------------------------- | :-------------------------- | :------------ | +| Máquina virtual | Cualquier VM Wasm | EVM | +| Codificación | Wasm | EVM Byte Code | +| Lenguaje | Rust | Standalone | +| Protección contra desbordamiento | Habilitado por default | Sí | +| Funciones de constructor | Multiple | Single | +| Herramientas | Cualquiera que soporte Rust | Custom | +| Versionado | Semántico | Semántico | +| Contiene Metadata? | Sí | Sí | +| Proyecto de archivos múltiples | Planeado | Sí | +| Entradas de Storage | Variable | 256 bits | +| Tipos soportados | Docs | Docs | +| Contiene Interfaces? | Sí (Traits de Rust) | Sí | + +
+ +## Guía Solidity a ink! + +### Tabla de Contenidos + + +- [ink! vs. Solidity](#ink-vs-solidity) + - [Guía Solidity a ink!](#guía-solidity-a-ink) + - [Tabla de Contenidos](#tabla-de-contenidos) + - [Convertir un contrato de Solidity a ink!](#convertir-un-contrato-de-solidity-a-ink) + - [1. Generar un nuevo contrato de ink!](#1-generar-un-nuevo-contrato-de-ink) + - [2. Compilar el contrato](#2-compilar-el-contrato) + - [3. Convertir campos de clase de Solidity a Struct de Rust](#3-convertir-campos-de-clase-de-solidity-a-struct-de-rust) + - [4. Convertir cada función](#4-convertir-cada-función) + - [Mejores prácticas + Tips](#mejores-prácticas--tips) + - [Equivalencias de sintaxis](#equivalencias-de-sintaxis) + - [`función pública`](#función-pública) + - [`declaración de mapping `](#declaración-de-mapping-) + - [`uso de mapping`](#uso-de-mapping) + - [`struct`](#struct) + - [`assertions / requires`](#assertions--requires) + - [`timestamp`](#timestamp) + - [`caller del contrato`](#caller-del-contrato) + - [`dirección de contrato`](#dirección-de-contrato) + - [`bytes`](#bytes) + - [`uint256`](#uint256) + - [`payable`](#payable) + - [`depósito recibido / pago`](#depósito-recibido--pago) + - [`balance del contrato`](#balance-del-contrato) + - [`transferir tokens desde el contrato`](#transferir-tokens-desde-el-contrato) + - [`eventos e indexado`](#eventos-e-indexado) + - [`errores y returns`](#errores-y-returns) + - [`throw`](#throw) + - [`assert`](#assert) + - [`require y revert`](#require-y-revert) + - [`mappings anidados + estructuras custom / avanzadas`](#mappings-anidados--estructuras-custom--avanzadas) + - [`llamadas entre contratos`](#llamadas-entre-contratos) + - [`enviar transacción genérica / llamadas dinámicas entre contratos`](#enviar-transacción-genérica--llamadas-dinámicas-entre-contratos) + - [Limitaciones de ink! v3](#limitaciones-de-ink-v3) + - [Solución de problemas y errores](#solución-de-problemas-y-errores) + - [unit testing (off-chain)](#unit-testing-off-chain) + +## Convertir un contrato de Solidity a ink! + +### 1. Generar un nuevo contrato de ink! + +Ejecutar el siguiente comando para crear la estructura de un contrato de ink! El comando instalará el código repetitivo para ink! "Hola, Mundo!" (el contrato [`flipper`](https://github.com/paritytech/ink-examples/tree/main/flipper)) + +``` +cargo contract new +``` + +### 2. Compilar el contrato + +``` +cargo contract build +``` + +### 3. Convertir campos de clase de Solidity a Struct de Rust + +Solidity es un lenguaje orientado a objetos y utiliza clases. ink! (Rust) no utiliza clases. + +Un ejemplo de clase de Solidity se ve así: + + + +```c++ +contract MyContract { + bool private _theBool; + event UpdatedBool(bool indexed _theBool); + + constructor(bool theBool_) { + require(theBool_ == true, "theBool_ must start as true"); + + _theBool = theBool_; + } + + function setBool(bool newBool) public returns (bool boolChanged) { + if _theBool == newBool { + boolChanged = false; + } else { + boolChanged = true; + } + + _theBool = newBool; + // emit event + UpdatedBool(newBool); + } +} +``` + +Y el equivalente en un contrato de ink! se ve así: + +```rust +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +mod mycontract { + #[ink(storage)] + pub struct MyContract { + the_bool: bool, // las properties de clase se convierten en campos del struct + } + + #[ink(event)] + pub struct UpdatedBool { + #[ink(topic)] // -> indexed + the_bool: bool, + } + + impl MyContract { + #[ink(constructor)] + pub fn new(the_bool: bool) -> Self { + assert!(the_bool == true, "the_bool must start as true"); + Self { the_bool } + } + + #[ink(message)] // las funciones se vuelven implementaciones del struct + pub fn set_bool(&mut self, new_bool: bool) -> bool { + let bool_changed = true; + + if self.the_bool == new_bool{ + bool_changed = false; + }else{ + bool_changed = true; + } + + self.the_bool = new_bool; + + self.env().emit_event(UpdatedBool { + the_bool: new_bool + }); + + // return + bool_changed + } + } +} +``` + +Algunas diferencias clave son: + +- Las variables de clase / miembros de Solidity se colocarán en el struct del contrato de ink! +- Todos lo métodos de clase en Solidity son `impl`ementados para el struct del contrato en ink! +- Solidity frecuentemente prefija variables con un guión bajo (`_name`). ink! / Rust solamente prefija con un guión bajo a las variables _no usadas_. +- Solidity usa camelCase. ink! usa snake_case. +- En Solidity, el tipo de variable aparece antes del nombre de la variable (Por ejemplo: bool myVar). Mientras que ink! especifica el tipo de var después del nombre de var (Por ejemplo: my_var: bool) + +### 4. Convertir cada función + +- Comenzar a convertir cada función una por una. + - Un enfoque recomendado es, de ser posible, evitar las llamadas cross-contract al principio y en su lugar utilizar datos simulados. + - Este modo de prueba de unidad off-chain puede ser escrito para probar la funcionalidad básica. + - las unidades de prueba son off-chain y no funcionan con llamadas cross-contract. + - Una vez que se haya probado por completo, se comienza a agregar las llamadas cross-contract y se llevan a cabo pruebas on-chain manuales y de integración. +- Asegurarse que la función de visibilidad (pública y privada) se corresponda en ink! +- Si en Solidity una función devuelve `bool success`, ink! utilizará un `Result<()>` (`Result::Ok` o `Result::Err`). + + ```rust + // ink! + + // result type + pub type Result = core::result::Result; + + // ... + + // función pública que devuelve un Result + #[ink(message)] + pub fn my_function(&self) -> Result<()> { + Ok(()) + } + ``` + +## Mejores prácticas + Tips + +- Si el contrato de Solidity utiliza un `string`, se recomienda utilizar un `Vec` para evitar la sobrecarga de un `String`. Ver [aquí](https://substrate.stackexchange.com/questions/1174/why-is-it-a-bad-idea-to-use-string-in-an-ink-smart-contract) para más detalles del porqué. El smart contract debería contener solamente la información que estrictamente necesita ubicarse en la blockchain y alcanzar un consenso. La UI (interfaz de usuario) debería usarse para visualizar strings. +- Comprobar todos los `.unwrap()`s ejecutados. Solidity no tiene un control tan estricto como ink! Por ejemplo, se puede acceder a un campo de mapping tan simple como `myMapping[someKey]`. ink!, sin embargo, requiere de `self.my_mapping.get(some_key).unwrap()`. Un modo útil para manipular casos `None` es utilizar `.unwrap_or(some_val)`. +- Ejecutar el nodo de los contratos con `substrate-contracts-node -lerror,runtime::contracts=debug` para que las impresiones depuradas y los errores se vizualicen en la consola de nodos. +- Al pasar parámetros a un helper, se recomienda pasar referencias (incluso para las primitivas) ya que Wasm es más eficiente con referencias. + Por ejemplo (ver [erc20](https://github.com/paritytech/ink-examples/blob/main/erc20/lib.rs) ejemplo): + +```rust +/// Devuelve el balance de la cuenta para el `owner` especificado. +/// +/// Devuelve `0` si es una cuenta inexistente. +#[ink(message)] +pub fn balance_of(&self, owner: AccountId) -> Balance { + self.balance_of_impl(&owner) +} + +/// Devuelve el balance de la cuenta para el `owner` especificado. +/// +/// Regresa a `0` si es una cuenta inexistente. +/// +/// # Nota +/// +/// Es preferible llamar a este método antes que `balance_of` ya que este +/// funciona usando referencias que son más eficientes en Wasm. +#[inline] +fn balance_of_impl(&self, owner: &AccountId) -> Balance { + self.balances.get(owner).unwrap_or_default() +} +``` + +- Al igual que en Solidity, ink! no tiene números de punto flotante debido al carácter no determinista. En cambio el frontend debería agregar decimales de ser necesario. + +## Equivalencias de sintaxis + +### `función pública` + +```c++ +// solidity +function fnName() public {} +// o +// por default, las funciones son públicas +function fnName() {} +``` + +```rust +// ink! +#[ink(message)] +pub fn fn_name(&self) {} +``` + +### `declaración de mapping ` + +```c++ +// solidity +mapping(address => uint128) private mapName; +``` + +```rust +//ink! +use ink_storage::{ + traits::SpreadAllocate, + Mapping, +}; + +#[ink(storage)] +#[derive(SpreadAllocate)] +pub struct ContractName { + map_name: Mapping, +} +``` + +Cuando se usa un map en ink!, `ink_lang::utils::initialize_contract` se debe usar en el constructor. Ver [aquí](https://use.ink/datastructures/mapping) para más detalles. + +### `uso de mapping` + +```c++ +// solidity + +// insertar / actualizar +aMap[aKey] = aValue; + +// obtener +aMap[aKey] +``` + +```rust +// ink! + +// insertar / actualizar +self.a_map.insert(&a_key, &a_value); + +// obtener +self.a_map.get(a_key).unwrap() +``` + +### `struct` + +```c++ +// solidity +struct MyPerson{ + address person; + u64 favNum; +} +``` + +```rust +// ink! +struct MyPerson { + person: AccountId, + fav_num: u64, +} +``` + +### `assertions / requires` + +```c++ +// solidity +require(someValue < 10, "someValue is not less than 10"); +``` + +```rust +// ink! +assert!(some_value < 10, "some_value is not less than 10"); +``` + +### `timestamp` + +```c++ +// solidity +block.timestamp +// or +now +``` + +```rust +// ink! +self.env().block_timestamp() +``` + +### `caller del contrato` + +```c++ +// solidity +address caller = msg.sender; +``` + +```rust +// ink! +let caller: AccountId = self.env().caller(); +``` + +### `dirección de contrato` + +```c++ +// solidity +address(this) +``` + +```rust +// ink! +self.env().account_id() +``` + +### `bytes` + +Solidity tiene un tipo `bytes`. `bytes` es (esencialmente) equivalente a un array de uint8. Por lo tanto `bytes` en Solidity => `Vec` o `[u8; ...]` en ink!. Ver [aquí](https://ethereum.stackexchange.com/questions/91119/difference-between-byte-and-uint8-datatypes-in-solidity) para más detalles. Si se desea, un struct de `bytes` puede ser creado en ink! para replicar el tipo `bytes` en Solidity. + +### `uint256` + +Solidity utiliza `uint256` y `uint` para representar un tipo de 256 bits. + +Solidity está optimizado para words de 256 bits / 32 bytes. Esto significa que si usamos `uint256` en los contratos Solidity se reducirá el uso de gas -- pero aumentará el uso del storage. El mayor tamaño que tiene ink! es un `u128`. ink! compila a Wasm. El mayor tamaño de primitiva de Wasm es 64bit(debido a que la mayoría de las computadoras usan 64bit). Por lo tanto no hay ningún beneficio en usar una primitiva más grande sobre una colección. + +Cuando se transfiere un `uint256` de Solidity a ink!, se recomienda determinar a discreción el rango del valor y elegir el tamaño adecuado (u8, u16, u32, u64, u128). Si se requiere un valor de hash de 256 bits, ink! tiene una primitiva de `Hash` disponible. En el caso que un valor necesite ser de 256 bits, se recomienda utilizar un array (ejemplo: `[u64; 4]`). + +### `payable` + +```c++ +// solidity +function myFunction() payable returns (uint64) {} +``` + +```rust +#[ink(message, payable)] +pub fn my_function() -> (u64) {} +``` + +### `depósito recibido / pago` + +```C++ +// solidity +msg.value +``` + +```rust +// ink! +self.env().transferred_value() +``` + +### `balance del contrato` + +```c++ +// solidity +this.balance +``` + +```rust +// ink! +self.env().balance() +``` + +### `transferir tokens desde el contrato` + +```c++ +// solidity +recipient.send(amount) +``` + +```rust +// ink! +if self.env().transfer(recipient, amount).is_err() { + panic!("error transferring") +} +``` + +### `eventos e indexado` + +```c++ +// solidity + +event MyCoolEvent( + u128 indexed indexedValue, + u128 notIndexedValue, +); + +// emitir evento +MyCoolEvent (someValue, someOtherValue) +``` + +```rust +// ink! + +#[ink(event)] +pub struct MyCoolEvent { + #[ink(topic)] + indexed_value: u128, + + not_indexed_value: u128, +} + +// emitir evento +self.env().emit_event(MyCoolEvent { + indexed_value: some_value, + not_indexed_value: some_other_value +}); +``` + +### `errores y returns` + +Solidity tiene varios mecanismos de manejo de errores: `assert`, `require`, `revert`, y `throw`. Cada uno de los cuales revertirá el estado modificado cuando sea solicitado. Ver [este artículo](https://medium.com/blockchannel/the-use-of-revert-assert-and-require-in-solidity-and-the-new-revert-opcode-in-the-evm-1a3a7990e06e) para detalles sobre esto. + +ink! usa un enum `Result` (`Ok(T)`, `Err(E)`), `assert!` y `panic!`. [Esta respuesta de Stack Exchange](https://substrate.stackexchange.com/questions/2391/panic-in-ink-smart-contracts) y [discusión de GitHub](https://github.com/paritytech/ink/issues/641) brindan más detalles sobre esto. + +#### `throw` + +Throw está deprecado en Solidity y daría un error de invalid opcode (sin detalles) y revertiría el estado. Como una alternativa para el patrón `if...{throw;}` en Solidity, un `Result::Err` debería ser devuelto para errores esperados, y un `assert!` debería ser usado para errores que no deberían ocurrir. + +#### `assert` + +En Solidity, `assert` se utiliza como un protector interno contra errores en el _código_. En general, un código que esté funcionando correctamente nunca debería tener un assert fallido. `assert` en Solidity no tiene strings de error. En ink!, usar `assert!`. `assert!` dará como resultado `panic!` si se evalúa como _falso_. El estado se revertirá, y se devolverá un `CalleeTrapped`. El string de error (opcional) se imprimirá en el búfer de depuración. + +```rust +// ink! +assert!(caller == owner, "caller is not owner") +``` + +#### `require y revert` + +En Solidity, `require` se usa para errores generales (normales) -- por ejemplo errores que ocurren basados en el input del usuario. `require` no tiene la opción para un string de error. `revert` es muy similar a `require` excepto que `revert` será llamado en cadenas `if ... else`. Ambos `require` y `revert` revertirán el estado de la chain. En ink!, `if ... { return Err(Error::SomeError) }` debería usarse para `require` o `revert`. Cuando un `Result::Err` se devuelve en ink! todo el estado se revierte. + +En general, `Result::Err` debería usarse cuando un _calling contract_ necesita saber _por qué_ una función falló. De otro modo, `assert!` debería usarse ya que tiene menos sobrecarga que un `Result`. + +```c++ +// Solidity +function myFunction(bool returnError) public { + require(!returnError, "my error here"); + + // o + + if returnError { + revert("my error here"); + } +} +``` + +```rust +// ink! + +#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub enum Error { + /// Proveer un comentario detallado sobre el error + MyError, +} + +// tipo Result +pub type Result = core::result::Result; + +// ... + +#[ink(message)] +pub fn my_function(&self, return_error: bool) -> Result<()> { + if return_error{ + return Err(Error::MyError) + } + Ok(()) +} +``` + +### `mappings anidados + estructuras custom / avanzadas` + +En Solidity, es fácil crear mappings anidados. En ink! no es tan sencillo. + +imaginemos el siguiente escenario + +```c++ +// solidity +contract Dao { + struct Proposal { + mapping (address => bool) votedYes + } + + mapping (address => bool) public isWhitelisted; + Proposal[] public proposals; +} +``` + +en ink! esto _parece_ como si se pudiera representar así: + +```rust +#[ink::contract] +mod dao { + + #[derive(SpreadAllocate)] + pub struct Proposal { + voted_yes: Mapping, + } + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct Dao { + proposals: Vec, + is_whitelisted: Mapping, + } + + impl Dao{ + #[ink(constructor)] + pub fn new(/*...*/) -> Self { + //requerido para mappings + ink_lang::utils::initialize_contract(|contract| {/*...*/}) + } + } +} +``` + +Sin embargo, esto originará un error debido al mapping anidado.[Esta respuesta](https://substrate.stackexchange.com/questions/1659/how-to-have-a-mapping-in-a-custom-structure-inside-an-ink-contract) explica en detalle por qué no se permiten los mappings anidados. + +Por lo tanto desde ahora será necesaria una estructura de datos alternativa para solucionar este problema. Una estructura de datos que puede ser intercambiable con la sintaxis de `Mapping` y con las implementaciones mínimas adicionales es el `BTreeMap`. `BTreeMap` es menos eficiente que `Mapping`, pero es una solución simple hasta que se permitan los mappings anidados. Esto se utilizará en el struct anidado. `derive`s adicionales deberán usarse para que sean compatibles con el struct #[ink(storage)] (ver abajo) + +```rust +#[ink::contract] +mod dao { + + use ink_prelude::collections::BTreeMap; + + #[derive( + scale::Encode, + scale::Decode, + SpreadLayout, + PackedLayout, + SpreadAllocate, + )] + #[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) + )] + pub struct Proposal { + voted_yes: BTreeMap, + } + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct Dao { + proposals: Vec, + is_whitelisted: Mapping, + } + + impl Dao{ + #[ink(constructor)] + pub fn new(/*...*/) -> Self { + //requerido para mappings + ink_lang::utils::initialize_contract(|contract| {/*...*/}) + } + } +} +``` + +Esto funciona casi como sería de esperar. Sin embargo aún existe un problema. `SpreadAllocate` (usado con `Mapping`) requiere que `Vec` implemente `PackedAllocate`. Para solucionarlo, `Proposal` necesita implementar `PackedAllocate`. Ver [aquí](https://docs.rs/ink_storage/3.3.1/ink_storage/traits/trait.PackedAllocate.html) para detalles + ejemplos. Ver lo siguiente para este ejemplo: + +```rust +use ink_primitives::Key; + +pub struct Proposal { + voted_yes: BTreeMap, +} + +impl ink_storage::traits::PackedAllocate for Proposal { + fn allocate_packed(&mut self, at: &Key){ + PackedAllocate::allocate_packed(&mut *self, at) + } +} +``` + +### `llamadas entre contratos` + +En ink!, para hacer [llamadas entre contratos](https://use.ink/basics/cross-contract-calling), se necesitará que el contrato sea agregado al proyecto. Asegurarse que el contrato esté exportando adecuadamente sus Structs. Ver el ejemplo de contrato `erc20`: + +```rust +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +// hacer visibles los structs +pub use self::erc20::{ + Erc20, + // esto es necesario + Erc20Ref, +}; + +#[ink::contract] +pub mod erc20 {} +``` + +En el Cargo.toml del nuevo contrato llamado, agregar (o editar) lo siguiente: + +``` +[lib] +name = "erc20" +path = "lib.rs" +crate-type = [ + # Usado para blobs Wasm de contrato normal. + "cdylib", + # Usado para generación de ABI. Necesario para importar como dependencia + "rlib", +] + +[features] +ink-as-dependency = [] +``` + +`ink-as-dependency` "le dice al generador de código ink! que siempre o nunca compile el smart contract como si fuera usado como dependencia de otro smart contract de ink!" ([fuente](https://use.ink/basics/cross-contract-calling)). + +Entonces, en el Cargo.toml del contrato principal, importar el contrato al que se llamará. + +```rust +erc20 = { path = "erc20", default-features = false, features = ["ink-as-dependency"] } +``` + +Y asegurarse de agregarlo al campo `std` del archivo .toml. + +```rust +[features] +default = ["std"] +std = [ + # ... + + "erc20/std", +] +``` + +Ahora, importar el contrato llamado al contrato principal: + +```rust +// ejemplo +use erc20::Erc20Ref; +``` + +Hay dos métodos para configurar el otro contrato. + +1. Instanciar el contrato llamado en el constructor del contrato principal. + Ver [aquí](https://use.ink/basics/cross-contract-calling/) para un tutorial, y [aquí](https://github.com/paritytech/ink-examples/tree/main/delegator) para un ejemplo. +2. O agregar el `AccountId` de un contrato ya deployado. + Aquí hay un ejemplo de constructor para configurar esto: + + ```rust + use my_other_contract::MyOtherContractRef; + // ... + fn new(contract_id: AccountId) -> Self { + // para un contrato ya deployado + let contract_ref: MyOtherContractRef = + ink_env::call::FromAccountId::from_account_id(contract_id); + Self {contract_ref} + } + ``` + +Ahora para realizar la llamada de contratos: + +```rust +{ + self.contract_ref.some_external_function(a_param); +} +``` + +Nota: a partir de ahora (ink! v3.3.1), cuando se está haciendo llamadas entre contratos, emitir eventos no funcionará y ocurrirán errores de compilación. Ver [issue #1000](https://github.com/paritytech/ink/issues/1000). Además el compilador dará error diciendo (por ejemplo) Erc20Ref no implementa `SpreadAllocate`. Este [issue #1149](https://github.com/paritytech/ink/issues/1149) explica más y tiene una solución. Estos problemas serán resueltos en [issue #1134](https://github.com/paritytech/ink/issues/1134). + +### `enviar transacción genérica / llamadas dinámicas entre contratos` + +```c++ +// solidity + +// invoca una función encontrada en `addr`, envía el `_amount` al `addr` y el payload de la `_transactionData`. +addr.call.value(_amount)(_transactionData) +``` + +```rust +// ink! + +// ... + +use ink_env::call::{ + build_call, + Call, + ExecutionInput, + Selector, +}; + +/// Un wrapper que nos permite codificar un blob de bytes. +/// +/// Usamos esto para pasar el conjunto de parámetros no tipados (bytes) al `CallBuilder`. +struct CallInput<'a>(&'a [u8]); + +impl<'a> scale::Encode for CallInput<'a> { + fn encode_to(&self, dest: &mut T) { + dest.write(self.0); + } +} + +// ... + +// ver: https://github.com/paritytech/ink-examples/blob/main/multisig/lib.rs#L535 +fn invoke_transaction( + &mut self, + callee: AccountId, + transfer_amount: u128, + function_selector: [u8; 4], + transaction_data: Vec, + gas_limit: u64) -> Result<()> { + + let result = build_call::<::Env>() + .call_type( + Call::new() + .callee(callee) // contrato a llamar + .gas_limit(*gas_limit) + .transferred_value(transfer_amount), // valor a transferir con la llamada + ) + .exec_input( + ExecutionInput::new(Selector::from(*function_selector)) + .push_arg(CallInput(transaction_data)), // parámetros SCALE-encoded + ) + .returns::<()>() + .fire() + .map_err(|_| Error::TransactionFailed); + result +} + +``` + +Nota: los bytes de `function_selector` pueden encontrarse en el `target/ink/.json` generado. + +## Limitaciones de ink! v3 + +- Los proyectos multi archivo no son soportados con ink! puro. + - implementar traits / interfaces no funcionará + - Hay alternativas que agregarán esta funcionalidad tal como OpenBrush +- Structs anidados y estructuras de datos serán difíciles de usar. +- Las llamadas entre contratos evitarán que los eventos sean emitidos. Ver [aquí](https://github.com/paritytech/ink/issues/1000) para detalles. +- Las llamadas entre contratos no pueden ser testeadas off-chain con tests unitarios. + Se necesitará utilizar tests de integración On-chain. + +## Solución de problemas y errores + +- `ERROR: Validation of the Wasm failed.` + +``` +ERROR: Validation of the Wasm failed. + +ERROR: An unexpected panic function import was found in the contract Wasm. +This typically goes back to a known bug in the Rust compiler: +https://github.com/rust-lang/rust/issues/78744 + +As a workaround try to insert `overflow-checks = false` into your `Cargo.toml`. +This will disable safe math operations, but unfortunately we are currently not +aware of a better workaround until the bug in the compiler is fixed. +``` + +**Solución** +Agregar lo siguiente al Cargo.toml del contrato: + +``` +[profile.release] +overflow-checks = false +``` + +- `"failed to load bitcode of module '...' "` + +Esto sucede cuando se está intentando importar un contrato para llamadas entre contratos. + +**Solución** +Asegurarse que lo siguiente se agregue a la importación del Cargo.toml del contrato: + +``` +features = ["ink-as-dependency"] +``` + +por lo tanto la importación se vería así: + +``` +mycontract = { path = "mycontract/", default-features = false, features = ["ink-as-dependency"]} +``` + +## unit testing (off-chain) + +- Los unit tests son una parte integral del desarrollo de un smart contract y garantizan que el código funciona off-chain antes de testearlo on-chain. +- Para ejecutar tests de ink!, _no_ usar `cargo +nightly contract test`. Usar `cargo +nightly test`. Agregar el indicador `--nocapture` para mostrar los prints de debug. Ver [aquí](https://substrate.stackexchange.com/questions/3197/how-to-understand-which-test-failed-in-ink) para más información sobre las razones. +- Desde el módulo del contrato, asegurarse que se hace público el struct del contrato y cualquier otra cosa que se use en el test de unidad. + Por ejemplo: + +```rust +// tope del archivo +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +pub use self::mycontract::{ + MyContract +}; +``` + +- Los unit tests Off-chain no funcionarán con las llamadas entre contratos. + Una solución para asegurarse que los unit tests están pasando es proveer datos simulados. + +Un enfoque sencillo es usar una compilación condicional con `#[cfg(test)]` y `#[cfg(not(test))]`. + +Nota: Esta solución no es ideal. ink! v4.0 proveerá soluciones mucho mejores. + +Por ejemplo, aquí hay una llamada entre contratos ERC20 sólo modo lectura: + +```rust +// sólo compila cuando no está ejecutando tests +#[cfg(not(test))] +fn get_token_balance(&self, caller: &AccountId) -> Balance { + // llama al contrato externo ERC-20 + self.token.balance_of(*caller) +} + +// sólo compila cuando está ejecutando tests +#[cfg(test)] +fn get_token_balance(&self, _: &AccountId) -> Balance { + // valor arbitrario + 1 +} +``` + +Y si la llamada entre contratos _escribe_ en el storage, un campo simulado puede agregase al struct del contrato. Por ejemplo: + +```rust +#[ink(storage)] +pub struct MyContract { + #[cfg(test)] + mock_field: SomeStruct, // servirá como un storage falso +} + +... + +// on-chain, ejecuta llamadas entre contratos +#[cfg(not(test))] +fn do_some_write(&mut self) { + self.external_contract.write_to_field(0xDEADBEEF); +} + + +// sólo para ambiente de testing +#[cfg(test)] +fn do_some_write(&mut self) { + self.mock_field.my_fake_storage_item = 0xDEADBEEF; +} +``` + +- código útil para interactuar y modificar el ambiente del contrato para testing + +[ink_env docs](https://paritytech.github.io/ink/ink_env/test/index.html) + +```rust +// obtener las cuentas default (alice, bob, ...) +let accounts = ink_env::test::default_accounts::(); +accounts.alice //ejemplo de uso + +// establecer cuál cuenta llama al contrato +ink_env::test::set_caller::(accounts.bob); + +// obtener la dirección del contrato +let callee = ink_env::account_id::(); + +// establecer la dirección del contrato +// por default, ésta es la cuenta de alice +ink_env::test::set_callee::(callee); + +// transferir la moneda nativa al contrato +ink_env::test::set_value_transferred::(2); + +// aumentar el número de bloque (y el timestamp del bloque). +// esto puede ser colocado en un bucle para adelantar el bloque varias veces +ink_env::test::advance_block::(); + +// generar un AccountId arbitrario +AccountId::from([0x01; 32]); + +// generar un Hash arbitrario +Hash::from([0x01; 32]) + +// macro para tests que se espera que entren en panic. +#[should_panic] +``` \ No newline at end of file diff --git a/i18n/es/docusaurus-plugin-content-docs/current/intro/polkadot.md b/i18n/es/docusaurus-plugin-content-docs/current/intro/polkadot.md index c51f55a46c..d0a78eccec 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/intro/polkadot.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/intro/polkadot.md @@ -1,28 +1,26 @@ ---- -title: Smart Contracts in Polkadot -hide_title: true -slug: /smart-contracts-polkadot ---- - - - -
-# Smart Contracts in Polkadot - -One of the first questions we typically get when somebody learns about Substrate, Polkadot, or Kusama is when to develop a parachain vs. when to develop a smart contract. - -The distinction here is that in the context of Polkadot and Kusama a parachain leases a slot for a couple of months for up to two years. The deal with a lease is that the parachain gets a fixed slot for executing its business logic (typically referred to as its _state transition function_) and can persist its modified state in a block. In Substrate terminology this state transition function is called the chain's _runtime_. - -The distinction to other ecosystems here is that, in the context of Polkadot, parachains and smart contracts exist at different layers of the stack: _smart contracts sit on top of parachains_. Parachains would usually be described as layer-1 blockchains ‒ except for that they don't have to build their own security, are upgradable, and interoperable. - -It's noteworthy that a parachain's state transition function doesn't get further validated ‒ it's up to the parachain how it utilizes its slot time. The parachain already pre-paid for its slot when it won the slot auction on Polkadot or Kusama. This means the parachain can build its own (blockchain) world! For example, it can decide on how transaction fees are charged ‒ or even if transaction fees are charged at all. These options are crucial when building new or more user-friendly business models. Other distinguishing factors between parachains that we observe in the wild are differences in how governance works or the crypto-economics. There are some constraints on how the parachain can build its world though. Like physics in the real world it has to adhere to certain ground rules. For Polkadot and Kusama that's for example the consensus algorithm for the Relay Chain to communicate with the parachain. From those ground rules the advantages of Polkadot and Kusama emerge. Advantages like the aforementioned shared security, cross-chain communication, or guaranteed execution slot time. - -For smart contracts, on the other hand, an existing parachain has to include the `pallet-contracts` for users to deploy smart contracts. The deployed smart contract is always untrusted code. Anyone (or any program) that has tokens of the chain can upload a smart contract without requiring permission. Smart contracts allow _permissionless_ deployment of _untrusted_ programs on a blockchain. The `pallet-contracts` has to assume that these programs are adversarial, it has to put a number of safety pillars in place to ensure that the contract can not e.g. stall the chain or cause state corruption of other contracts. For `pallet-contracts` those safety pillars include mechanisms like gas metering or deposits for storing data on-chain. - -_To restate this important distinction: developing a parachain runtime is different from developing a smart contract ‒ a smart contract sits on top of a parachain_. - -The trade-off is that with a parachain one has the freedom to decide on (nearly) all the rules that make up the parachain. With a smart contract one is constrained by what the chain allows and the safety pillars that necessarily have to be in place. A smart contract can never be as fast as a native pallet built in the parachain runtime ‒ there is too much logic in between. -A smart contract on the other hand has less friction for developing and deploying it. Developers don't have to take care of governance, crypto-economics, etc. One just needs a few tokens and can go on its merry way deploying a smart contract. It's as simple as that. - -![](/img/smart-contract-vs-parachain.png) -
\ No newline at end of file +--- +title: Smart Contracts en Polkadot +hide_title: true +slug: /smart-contracts-polkadot +--- + + + +# Smart Contracts en Polkadot + +Una de las primeras preguntas que normalmente recibimos cuando alguien aprende sobre Substrate, Polkadot, o Kusama es cuándo desarrollar una parachain vs. cuándo desarrollar un smart contract. + +La diferencia aquí es que en el contexto de Polkadot y Kusama una parachain alquila un espacio que abarca desde un par de meses hasta dos años. El acuerdo con un alquiler es que la parachain obtiene un slot fijo para ejecutar su lógica de negocio (normalmente conocida como su _función de transición de estado_) y puede persistir su estado modificado en un bloque. En terminología de Substrate esta función de transición de estado se denomina _runtime_. + +La diferencia con otros ecosistemas aquí es que, en el contexto de Polkadot, parachains y smart contracts existen en diferentes niveles del stack: _los smart contracts se ubican por encima de las parachains_. Las parachains serían generalmente descriptas como blockchains de nivel 1 - excepto por el hecho de que no tienen que construir su propia seguridad, son actualizables e interoperables. + +Vale la pena destacar que una función de transición de estado de una parachain no se valida más - depende de la parachain cómo utiliza su tiempo de slot. La parachain ya pagó por adelantado por su slot cuando ganó la subasta en Polkadot o Kusama. Eso significa que la parachain puede construir su propio mundo (de blockchain)! Por ejemplo, puede decidir cómo se cobran las comisiones por transacción - incluso si dichas comisiones se cobran o no. Estas opciones son cruciales cuando se construyen modelos de negocio nuevos o más accesibles para el usuario. Otros factores distintivos entre parachains que observamos en estado natural son diferencias en cómo funciona la gobernanza o la cripto economía. Sin embargo hay algunas limitaciones en cómo la parachain puede construir su mundo. Como la física en el mundo real, tiene que adherir a ciertas reglas básicas. Para Polkadot y Kusama eso es por ejemplo el algoritmo de consenso para que la Relay Chain se comunique con la parachain. Desde esas reglas básicas emergen las ventajas para Polkadot y Kusama. Ventajas como la seguridad compartida, comunicación cross-chain o ejecución de tiempo de slot garantizada. + +Para los smart contracts, por otro lado, una parachain existente tiene que incluir el `pallet-contracts` para que los usuarios deployen smart contracts. El smart contract deployado siempre es un código untrusted (de no confianza). Cualquiera (o cualquier programa) que tiene tokens de la chain puede subir un smart contract sin requerir permiso. Los smart contracts permiten deployar de manera _permissionless_ programas _untrusted_ en una blockchain. El `pallet-contracts` debe asumir que estos programas son contradictorios, debe ubicar un número de pilares de seguridad en su lugar para asegurar que el contrato no puede, por ejemplo, paralizar la chain o causar la corrupción del estado de otros contratos. Para el `pallet-contracts` esos pilares de seguridad incluyen mecanismos como la medición de gas o depósitos para almacenar datos on-chain. + + _Para reafirmar esta distinción importante: desarrollar un runtime de parachain es diferente a desarrollar un smart contract ‒ un smart contract se sitúa por encima de una parachain_. + +La contrapartida es que con una parachain uno tiene la libertad de decidir sobre (casi) todas las reglas que constituyen la parachain. Con un smart contract uno está limitado por lo que la chain permite y los pilares de seguridad que necesariamente deben estar en su lugar. Un smart contract nunca puede ser tan rápido como un pallet nativo incluido en el runtime de la parachain - hay demasiada lógica involucrada. +Un smart contract, por otro lado, tiene menos fricción para desarrollarlo y deployarlo. Los desarrolladores no tienen que ocuparse de la gobernanza, crypto economía, etc. Uno sólo necesita unos pocos tokens y puede simplemente seguir adelante deployando un smart contract. Es tan simple como eso. + +![](/img/smart-contract-vs-parachain.png) \ No newline at end of file