Ekaterina Kulikova |
18.05.2023 12:12 |
ZkSynk | Деплой смарт контракта #2
ZkSynk Cross-chain governance Smart Contract | Деплой смарт контракта ЗкСинк
Требования к серверу:я взяла СPХ31 хетцнер
Также нам понадобится:
- кошелек метамаск с ETH Goerli
- Тестовая сеть zksynk era testnet (можно подключить тут)
- Тестовые токенамы ETH в сети zksynk (кран тут)
- Приватный ключ от метамаска (не используйте кошельки с ральными деньгами!)
- RPC ETH1 goerli (можно взять на инфуре)
Подготавливаем сервер:
Код:
sudo apt-get update -y && sudo apt upgrade -y && sudo apt-get install make build-essential unzip lz4 gcc git jq chrony -y
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
source ~/.bashrc
nvm -v
nvm install v16.16.0
node -v
#вывод - v16.16.0
Код:
mkdir L1-governance L2-counter
cd L1-governance
npx hardhat
#нажимаем у, пойдет процесс команды, после будут еще вопросы:
https://img3.teletype.in/files/6e/f2...6c6abdd14.jpeg
Код:
yarn add -D typescript ts-node @nomiclabs/hardhat-waffle @openzeppelin/contracts @matterlabs/zksync-contracts @nomiclabs/hardhat-ethers @nomiclabs/hardhat-waffle ethereum-waffle [email protected]
L1 контракт
Код:
cd contracts
ll
#тут необходимо убедится, что папка пуста
#если в папке что-то есть, то удаляем (rm ИМЯ_ФАЙЛА)
nano Governance.sol
#вставляем:
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;
import "@matterlabs/zksync-contracts/l1/contracts/zksync/interfaces/IZkSync.sol";
contract Governance {
address public governor;
constructor() {
governor = msg.sender;
}
function callZkSync(
address zkSyncAddress,
address contractAddr,
bytes memory data,
uint256 gasLimit,
uint256 gasPerPubdataByteLimit
) external payable {
require(msg.sender == governor, "Only governor is allowed");
IZkSync zksync = IZkSync(zkSyncAddress);
zksync.requestL2Transaction{value: msg.value}(contractAddr, 0,
data, gasLimit, gasPerPubdataByteLimit, new bytes[](0), msg.sender);
}
}
Код:
cd ..
#вы должны быть в папке L1-governance
nano goerli.json
#вставляем:
{
"nodeUrl": "<GOERLI NODE URL>",
"deployerPrivateKey": "<YOUR PRIVATE KEY>"
}
#заменить <GOERLI NODE URL> на рпс ефир гоерли (можно взять на инфуре)
#заменить <YOUR PRIVATE KEY> на приватный ключ с метамаска
Код:
rm hardhat.config.ts
nano hardhat.config.ts
#вставляем:
import { HardhatUserConfig } from "hardhat/config";
import "@nomiclabs/hardhat-waffle";
// import file with Göerli params
const goerli = require("./goerli.json");
const config: HardhatUserConfig = {
solidity: {
version: "0.8.19",
},
networks: {
// Göerli network
goerli: {
url: goerli.nodeUrl,
accounts: [goerli.deployerPrivateKey],
},
},
};
export default config;
Код:
cd scripts
rm deploy.ts
nano deploy.ts
#вставляем:
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
import { ethers } from "hardhat";
async function main() {
// We get the contract to deploy
const Governance = await ethers.getContractFactory("Governance");
const contract = await Governance.deploy();
await contract.deployed();
console.log(`Governance contract was successfully deployed at ${contract.address}`);
}
// We recommend always using this async/await pattern to properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Код:
cd ..
#вы должны быть в папке L1-governance
yarn hardhat compile
yarn hardhat run --network goerli ./scripts/deploy.ts
#вывод должен быть такой:
Governance contract was successfully deployed at 0x613...
#запоминаем адрес контракта Governance
L2 контракт
Код:
cd $HOME/L2-counter
yarn init -y
yarn add -D typescript ts-node @matterlabs/hardhat-zksync-deploy @ethersproject/web @types/node @ethersproject/hash @matterlabs/hardhat-zksync-deploy ethers@^5.7.2 @ethersproject/[email protected] zksync-web3 hardhat @matterlabs/hardhat-zksync-solc @matterlabs/hardhat-zksync-deploy
nano hardhat.config.ts
#вставляем:
import "@matterlabs/hardhat-zksync-deploy";
import "@matterlabs/hardhat-zksync-solc";
module.exports = {
zksolc: {
version: "1.3.10",
compilerSource: "binary",
},
defaultNetwork: "zkSyncTestnet",
networks: {
hardhat: {
zksync: true,
},
zkSyncTestnet: {
url: "https://testnet.era.zksync.dev",
ethNetwork: "<GOERLI RPC URL>",
zksync: true,
},
},
solidity: {
version: "0.8.19",
},
};
Код:
#заменить <GOERLI RPC URL> на рпс ефир гоерли (можно взять на инфуре)
Код:
mkdir contracts && cd contracts
nano Counter.sol
#вставляем:
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;
contract Counter {
uint256 public value = 0;
address public governance;
constructor(address newGovernance) {
governance = newGovernance;
}
function increment() public {
require(msg.sender == governance, "Only governance is allowed");
value += 1;
}
}
Код:
cd ..
yarn hardhat compile
Код:
mkdir deploy && cd deploy
nano deploy.ts
#вставляем:
import { utils, Wallet } from "zksync-web3";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
// Insert the address of the governance contract
const GOVERNANCE_ADDRESS = "<GOVERNANCE-ADDRESS>";
// An example of a deploy script that will deploy and call a simple contract.
export default async function (hre: HardhatRuntimeEnvironment) {
console.log(`Running deploy script for the Counter contract`);
// Initialize the wallet.
const wallet = new Wallet("<WALLET-PRIVATE-KEY>");
// Create deployer object and load the artifact of the contract you want to deploy.
const deployer = new Deployer(hre, wallet);
const artifact = await deployer.loadArtifact("Counter");
// Deposit some funds to L2 to be able to perform deposits.
const deploymentFee = await deployer.estimateDeployFee(artifact, [utils.applyL1ToL2Alias(GOVERNANCE_ADDRESS)]);
const depositHandle = await deployer.zkWallet.deposit({
to: deployer.zkWallet.address,
token: utils.ETH_ADDRESS,
amount: deploymentFee.mul(2),
});
// Wait until the deposit is processed on zkSync
await depositHandle.wait();
// Deploy this contract. The returned object will be of a `Contract` type, similar to the ones in `ethers`.
// The address of the governance is an argument for contract constructor.
const counterContract = await deployer.deploy(artifact, [utils.applyL1ToL2Alias(GOVERNANCE_ADDRESS)]);
// Show the contract info.
const contractAddress = counterContract.address;
console.log(`${artifact.contractName} was deployed to ${contractAddress}`);
}
Код:
#заменить <GOVERNANCE-ADDRESS> на адрес контракта говернанс
#заменить <WALLET-PRIVATE-KEY> на приватный ключ с метамаска
Код:
cd ..
#вы должны быть в папке L2-counter
yarn hardhat deploy-zksync
#вывод Counter was deployed to 0x1b6...
#запоминаем адрес контракта Counter
mkdir scripts
cd scripts
wget https://raw.githubusercontent.com/kulikovae/zksynk/main/L2-counter/counter.json
Код:
nano display-value.ts
#вставляем:
import { Contract, Provider } from 'zksync-web3';
const COUNTER_ADDRESS = '<COUNTER-ADDRESS>';
const COUNTER_ABI = require('./counter.json');
async function main() {
// Initialize the provider
const l2Provider = new Provider('https://testnet.era.zksync.dev');
const counterContract = new Contract(
COUNTER_ADDRESS,
COUNTER_ABI,
l2Provider
);
const value = (await counterContract.value()).toString();
console.log(`The counter value is ${value}`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
#заменить <COUNTER-ADDRESS> на адрес контракта каунтер
Вызов значения каунтер
Код:
cd
mkdir call
cd L2-counter/scripts
cp counter.json display-value.ts /root/call
cd
cd call
npm init
#везде энтер нажимаем
npm i typescript ts-node ethers@^5.7.2 zksync-web3 hardhat @matterlabs/hardhat-zksync-solc @matterlabs/hardhat-zksync-deploy
npm i --save-dev @types/node
npx ts-node ./display-value.ts
#правильный вывод: The counter value is 0
Вызов L2 контракта с L1
Код:
cd
cd L1-governance/artifacts/contracts/Governance.sol/
cat Governance.json
#тут копируем значение массива аби(начинается и заканчивается квадратными скобками)
#ниже на фото начало и конец(без запятой!):
https://img2.teletype.in/files/db/b5...b4dc02696.jpeg
https://img4.teletype.in/files/bf/23...da710107e.jpeg
Все, что скопировали необходимо будет вставить в следующий файл:
Код:
cd
cd call
nano governance.json
#сюда вставили, сохранили, закрыли
Код:
nano increment-counter.ts
#вставляем:
import { BigNumber, Contract, ethers, Wallet } from "ethers";
import { Provider, utils } from "zksync-web3";
const GOVERNANCE_ABI = require('./governance.json');
const GOVERNANCE_ADDRESS = '<GOVERNANCE-ADDRESS>';
const COUNTER_ABI = require('./counter.json');
const COUNTER_ADDRESS = '<COUNTER-ADDRESS>';
async function main() {
// Enter your Ethereum L1 provider RPC URL.
const l1Provider = new ethers.providers.JsonRpcProvider("<RPC-URL>");
// Set up the Governor wallet to be the same as the one that deployed the governance contract.
const wallet = new ethers.Wallet("<YOUR-PRIVATE-KEY>", l1Provider);
// Set a constant that accesses the Layer 1 contract.
const govcontract = new Contract(GOVERNANCE_ADDRESS, GOVERNANCE_ABI, wallet);
// Initialize the L2 provider.
const l2Provider = new Provider("https://testnet.era.zksync.dev");
// Get the current address of the zkSync L1 bridge.
const zkSyncAddress = await l2Provider.getMainContractAddress();
// Get the `Contract` object of the zkSync bridge.
const zkSyncContract = new Contract(zkSyncAddress, utils.ZKSYNC_MAIN_ABI, wallet);
// Encoding the L1 transaction is done in the same way as it is done on Ethereum.
// Use an Interface which gives access to the contract functions.
const counterInterface = new ethers.utils.Interface(COUNTER_ABI);
const data = counterInterface.encodeFunctionData("increment", []);
// The price of an L1 transaction depends on the gas price used.
// You should explicitly fetch the gas price before making the call.
const gasPrice = await l1Provider.getGasPrice();
// Define a constant for gas limit which estimates the limit for the L1 to L2 transaction.
const gasLimit = await l2Provider.estimateL1ToL2Execute({
contractAddress: COUNTER_ADDRESS,
calldata: data,
caller: utils.applyL1ToL2Alias(GOVERNANCE_ADDRESS)
});
// baseCost takes the price and limit and formats the total in wei.
// For more information on `REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT` see the [fee model documentation](../developer-guides/transactions/fee-model.md).
const baseCost = await zkSyncContract.l2TransactionBaseCost(gasPrice, gasLimit, utils.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT);
// !! If you don't include the gasPrice and baseCost in the transaction, a re-estimation of fee may generate errors.
const tx = await govcontract.callZkSync(zkSyncAddress, COUNTER_ADDRESS, data, gasLimit, utils.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT, {
// Pass the necessary ETH `value` to cover the fee for the operation
value: baseCost,
gasPrice,
});
// Wait until the L1 tx is complete.
await tx.wait();
// Get the TransactionResponse object for the L2 transaction corresponding to the execution call.
const l2Response = await l2Provider.getL2TransactionFromPriorityOp(tx);
// Output the receipt of the L2 transaction corresponding to the call to the counter contract.
const l2Receipt = await l2Response.wait();
console.log(l2Receipt);
}
// We recommend always using this async/await pattern to properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
#заменить $GOVERNANCEADDRESS на адрес контракта говернанс
#заменить $COUNTERADDRESS на адрес контракта каунтер
#заменить $PRIVATEKEY на приватный ключ мета маска
#заменить $RPC на рпс ефир гоерли (можно взять на инфуре)
Код:
npx ts-node ./increment-counter.ts
Вывод примерно такой:
https://img3.teletype.in/files/23/c9...661a6105b.jpeg
Проверяем успешность транзакции, вызывая еще раз скрипт:
Код:
npx ts-node ./display-value.ts
#Вывод должен быть: The counter value is 1
Напоминаю, что это второй контракт из серии, не забывайте выполнить первый и подписывайтесь - скоро будут еще!
|