zkSync 2.0 es un ZK Rollup full compatible con la Ethereum Virtual Machine. Esto no solo nos permitirá ahorrar gas de la manera mas segura, sino que también podremos lanzar smart contracts escritos en solidity haciéndolo un buen lugar para acoger a DeFi, NFTs y todo lo demás que hoy está ocurriendo en Layer 1. zkSync 2.0 el primer proyecto en lograr esto ya está disponible en un alpha testnet. En este video veremos cómo usar el testnet y lanzaremos un Smart Contract con su respectiva UI. Personalmente estoy muy emocionado por este proyecto que nos permitirá ahorrar gas confiando enteramente en pruebas matemáticas. Esto es algo que especulabamos que iba a ser posible hasta dentro de varios años pero ya está a la vuelta de la esquina.
https://www.youtube.com/watch?v=t8dSW47sqhg
Antes de iniciar
Asegurate de instalar Metamask, conectar tu wallet al testnet de zkSync:
- Network Name:
zkSync alpha testnet
- New RPC URL:
https://zksync2-testnet.zksync.dev
- Chain ID:
280
y también asegúrate conseguir ether de prueba en L2 desde ya sea el bridge o el faucet que puedes encontrar en el Portal de zkSync. Para usar el bridge ocuparás Ether de Goreli que puedes conseguir en este faucet.
1. Preparativos
Necesitaremos npm para este tutorial. Les muestro cómo lo hice en Ubuntu 22.04.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
nvm install 14
nvm use 14
También necesitaremos Docker, a continuación muestro cómo lo instalé.
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
wget https://desktop.docker.com/linux/main/amd64/docker-desktop-4.8.2-amd64.deb
sudo apt install ./docker-desktop-4.8.2-amd64.deb
systemctl --user start docker-desktop
sudo usermod -aG docker $USER
Una vez instalado Docker, procedemos a crear un nuevo proyecto de Hardhat.
mkdir MyL2Project
cd MyL2Project
npm install --save-dev hardhat
npx hardhat
Luego de completar el diálogo de Hardhat en la terminal, finalizamos instalando un par de librerías extra.
# si estás en ubuntu necesitarás esto: sudo apt install build-essential
npm install --save-dev typescript ts-node ethers zksync-web3 @matterlabs/hardhat-zksync-solc@0.3 @matterlabs/hardhat-zksync-deploy@0.2 dotenv
2. Lanzar un contrato
Primero borramos el contrato que viene por defecto contracts/Greeter.sol
y escribimos uno nuestro en la carpeta contracts/
.
contracts/Hello.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
contract Hello {
string public hello;
constructor()
{
hello = "Hola mundo!";
}
function setHello(string memory _hello) public {
hello = _hello;
}
}
Hardhat también es compatible con Typescript que es lo que usaremos en este video. Así que renombramos hardhat.config.js
a hardhat.config.ts
y haremos los cambios necesarios.
hardhat.config.ts
require("@nomiclabs/hardhat-waffle");
require("@matterlabs/hardhat-zksync-deploy");
require("@matterlabs/hardhat-zksync-solc");
module.exports = {
zksolc: {
version: "0.1.0",
compilerSource: "docker",
settings: {
optimizer: {
enabled: true,
},
experimental: {
dockerImage: "matterlabs/zksolc",
},
},
},
zkSyncDeploy: {
zkSyncNetwork: "https://zksync2-testnet.zksync.dev",
ethNetwork: "goerli", // Can also be the RPC URL of the network (e.g. `https://goerli.infura.io/v3/<API_KEY>`)
},
networks: {
// To compile with zksolc, this must be the default network.
hardhat: {
zksync: true,
},
},
solidity: {
version: "0.8.12",
},
};
Ahora escribimos el script de deploy que debe estar en una nueva carpeta llamada deploy/
.
deploy/deploy.ts
import { utils, Wallet } from "zksync-web3";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
import dotenv from "dotenv"
// An example of a deploy script that will deploy and call a simple contract.
export default async function (hre: HardhatRuntimeEnvironment) {
dotenv.config()
console.log(`Running deploy script for the Hello contract`);
// Initialize the wallet.
const wallet = new Wallet("" + process.env.PRIVATE_KEY);
// Create deployer object and load the artifact of the contract we want to deploy.
const deployer = new Deployer(hre, wallet);
const artifact = await deployer.loadArtifact("Hello");
// Deploy this contract. The returned object will be of a `Contract` type, similarly to ones in `ethers`.
const helloContract = await deployer.deploy(artifact, []);
// Show the contract info.
const contractAddress = helloContract.address;
console.log(`${artifact.contractName} was deployed to ${contractAddress}`);
// Call the deployed contract.
const helloFromContract = await helloContract.hello();
console.log(`Contract says hello to us with ${helloFromContract}!`);
}
Crea un archivo llamado .env
y coloca tu llave privada adentro siguiendo el siguiente formato:
.env
PRIVATE_KEY=TULLAVEAQUÍ
Ahora compilamos el contrato.
npx hardhat compile
Y lo lanzamos.
npx hardhat deploy-zksync
3. El Frontend
Hacer un frontend para un contrato en zkSync 2.0 Testnet es igual que en cualquier blockchain compatible con la EVM. Solo asegúrate de establecer el netwrok id 280
.
Comenzamos creando un archivos de HTML.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<input id="connect_button" type="button" value="Connect" onclick="connectWallet()" style="display: none"></input>
<p id="account_address" style="display: none"></p>
<p id="web3_message"></p>
<p id="contract_state"></p>
<input type="input" value="" id="_hello"></input>
<input type="button" value="Set Hello" onclick="_setHello()"></input>
<br>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.3.5/web3.min.js"></script>
<script type="text/javascript" src="blockchain_stuff.js"></script>
</body>
</html>
<script>
function _setHello()
{
_hello = document.getElementById("_hello").value
setHello(_hello)
}
</script>
Luego el javascript necesario. Asegúrate de establecer la variable MY_CONTRACT_ADDRESS
con el address de tu contrato.
blockchain_stuff.js
const NETWORK_ID = 280
const MY_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000000000"
const MY_CONTRACT_ABI_PATH = "./json_abi/MyContract.json"
var my_contract
var accounts
var web3
function metamaskReloadCallback() {
window.ethereum.on('accountsChanged', (accounts) => {
document.getElementById("web3_message").textContent="Se cambió el account, refrescando...";
window.location.reload()
})
window.ethereum.on('networkChanged', (accounts) => {
document.getElementById("web3_message").textContent="Se el network, refrescando...";
window.location.reload()
})
}
const getWeb3 = async () => {
return new Promise((resolve, reject) => {
if(document.readyState=="complete")
{
if (window.ethereum) {
const web3 = new Web3(window.ethereum)
window.location.reload()
resolve(web3)
} else {
reject("must install MetaMask")
document.getElementById("web3_message").textContent="Error: Porfavor conéctate a Metamask";
}
}else
{
window.addEventListener("load", async () => {
if (window.ethereum) {
const web3 = new Web3(window.ethereum)
resolve(web3)
} else {
reject("must install MetaMask")
document.getElementById("web3_message").textContent="Error: Please install Metamask";
}
});
}
});
};
const getContract = async (web3, address, abi_path) => {
const response = await fetch(abi_path);
const data = await response.json();
const netId = await web3.eth.net.getId();
contract = new web3.eth.Contract(
data,
address
);
return contract
}
async function loadDapp() {
metamaskReloadCallback()
document.getElementById("web3_message").textContent="Please connect to Metamask"
var awaitWeb3 = async function () {
web3 = await getWeb3()
web3.eth.net.getId((err, netId) => {
if (netId == NETWORK_ID) {
var awaitContract = async function () {
my_contract = await getContract(web3, MY_CONTRACT_ADDRESS, MY_CONTRACT_ABI_PATH)
document.getElementById("web3_message").textContent="You are connected to Metamask"
onContractInitCallback()
web3.eth.getAccounts(function(err, _accounts){
accounts = _accounts
if (err != null)
{
console.error("An error occurred: "+err)
} else if (accounts.length > 0)
{
onWalletConnectedCallback()
document.getElementById("account_address").style.display = "block"
} else
{
document.getElementById("connect_button").style.display = "block"
}
});
};
awaitContract();
} else {
document.getElementById("web3_message").textContent="Please connect to zkSync Testnet";
}
});
};
awaitWeb3();
}
async function connectWallet() {
await window.ethereum.request({ method: "eth_requestAccounts" })
accounts = await web3.eth.getAccounts()
onWalletConnectedCallback()
}
loadDapp()
const onContractInitCallback = async () => {
var hello = await my_contract.methods.hello().call()
var contract_state = "Hello: " + hello
document.getElementById("contract_state").textContent = contract_state;
}
const onWalletConnectedCallback = async () => {
}
//// Functions ////
const setHello = async (_hello) => {
const result = await my_contract.methods.setHello(_hello)
.send({ from: accounts[0], gas: 100000, value: 0 })
.on('transactionHash', function(hash){
document.getElementById("web3_message").textContent="Waiting confirmation...";
})
.on('receipt', function(receipt){
document.getElementById("web3_message").textContent="Success."; })
.catch((revertReason) => {
console.log("ERROR! Transaction reverted: " + revertReason.receipt.transactionHash)
});
}
Ahora extraemos el JSON ABI que está en el archivo TUPROYECTO/artifacts-zk/contracts/Hello.sol/Hello.json
bajo el tag "abi"
. Que debería verse algo así una vez puesto en una nueva carpeta que creamos llamada json_abi
:
json_abi/MyContract.json
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "hello",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_hello",
"type": "string"
}
],
"name": "setHello",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
Ahora estamos listos para interactuar con nuestro contrato instalando un servidor local y luego iniciándolo.
npm i --global lite-server
lite-server
Fuentes oficiales: Github de NVM, Instalar Docker en Ubuntu, Descarga de Docker Desktop, Instalar Docker Desktop en Ubuntu, zkSync Testnet Getting Started
Gracias por ver este tutorial!
Sígannos en dev.to y en Youtube para todo lo relacionado al desarrollo en Blockchain en Español.
Top comments (0)