Użycie Bibliotek Ethereum aby Uruchomić Smart Kontrakt na Moonbeam

Lucas | Eaglenode
18 min readJun 12, 2021

--

Wstęp

Ten przewodnik omawia korzystanie z kompilatora Solidity i trzech różnych bibliotek Ethereum do ręcznego podpisywania i wysyłania transakcji na Moonbeam. Trzy biblioteki omówione w tym samouczku to:

Poza tym do kompilacji inteligentnego kontraktu zostaną użyte dwie inne biblioteki:

  • Solc-js do kompilowania inteligentnych kontraktów Solidity za pomocą JavaScript
  • Py-solc-x do kompilowania inteligentnych kontraktów Solidity za pomocą Pythona

W przykładach w tym przewodniku założono, że masz środowisko oparte na systemie MacOS lub Ubuntu 18.04 i należy je odpowiednio dostosować w przypadku systemu Windows.

Wymagania Wstępne

Przykłady wykorzystujące zarówno web3.js, jak i ethers.js wymagają wcześniejszego zainstalowania Node.js i NPM. Dla web3.py potrzebujesz Pythona i PIP. W chwili pisania tego przewodnika używane były następujące wersje:

  • Node.js v15.10.0
  • NPM v7.5.3
  • Python v3.6.9 (web3 requires Python >= 3.5.3 and < 4)
  • PIP3 v9.0.1

Następnie utwórz katalog, w którym będą przechowywane wszystkie odpowiednie pliki:

mkdir incrementer && cd incrementer/

W przypadku bibliotek JavaScript możesz najpierw utworzyć prosty plik package.json (niewymagany):

npm init --yes

W katalogu zainstaluj odpowiednią bibliotekę i kompilator Solidity (web3.py i py-solc-x są zainstalowane w domyślnym katalogu PIP3):

Web3.js

npm i web3 npm i solc@0.8.0

Ethers.js

npm i ethers npm i solc@0.8.0

Web3.py

pip3 install web3 pip3 install py-solc-x

Wersje używane podczas publikacji tego przewodnika:

  • Web3.js v1.33 (npm ls web3)
  • Ethers.js v5.0.31 (npm ls ethers)
  • Solc (JS) v0.8.0 (npm ls solc)
  • Web3.py v5.17.0 (pip3 show web3)
  • Py-solc-x v1.1.0 (pip3 show py-solc-x)

Konfiguracja tego przykładu będzie stosunkowo prosta i będzie zawierać następujące pliki:

  • Incrementer.sol —plik z naszym kodem Solidity
  • compile.* — kompiluje umowę z kompilatorem Solidity
  • deploy.*: obsłuży wdrożenie do naszego lokalnego węzła Moonbeam
  • get.* — wykona połączenie do węzła, aby uzyskać aktualną wartość numeru
  • increment.* —dokona transakcji, aby zwiększyć liczbę przechowywaną w węźle Moonbeam
  • reset.* —funkcja do wywołania, która zresetuje zapisany numer do zera

Plik Kontraktu

Użyty kontrakt to prosty inkrementator, arbitralnie nazwany Incrementer.sol, który można znaleźć tutaj. Kod Solidity jest następujący:

pragma solidity ^0.8.0;  contract Incrementer {     
uint256 public number;
constructor(uint256 _initialNumber) {
number = _initialNumber;
}
function increment(uint256 _value) public {
number = number + _value;
}
function reset() public {
number = 0;
}
}

Funkcja constructor, która działa po wdrożeniu kontraktu, ustawia początkową wartość zmiennej liczby przechowywanej w łańcuchu (domyślnie 0). Funkcja increment dodaje podaną wartość _value do bieżącego numeru, ale należy wysłać transakcję, która modyfikuje przechowywane dane. Na koniec funkcja reset resetuje zapamiętaną wartość do zera.

Ten kontrakt jest prostym przykładem wyłącznie do celów ilustracyjnych i nie obsługuje funkcji wrappingu.

Kompilowanie Kontraktu

Jedynym celem pliku kompilacji jest użycie kompilatora Solidity do wyprowadzenia kodu bajtowego i interfejsu (ABI) naszej umowy. Możesz znaleźć fragment kodu dla każdej biblioteki tutaj (są arbitralnie nazwane compile.*):

Plik kompilacji dla obu bibliotek JavaScript jest taki sam, ponieważ współdzielą one powiązania JavaScript z kompilatorem Solidity (ten sam pakiet)

Web3.js

const fs = require('fs');
const solc = require('solc');
// Get Path and Load Contract
const source = fs.readFileSync('Incrementer.sol', 'utf8');
// Compile Contract
const input = {
language: 'Solidity',
sources: {
'Incrementer.sol': {
content: source,
},
},
settings: {
outputSelection: {
'*': {
'*': ['*'],
},
},
},
};
const tempFile = JSON.parse(solc.compile(JSON.stringify(input))); const contractFile = tempFile.contracts['Incrementer.sol']['Incrementer'];
// Export Contract Data
module.exports = contractFile;

Ethers.js

const fs = require('fs');
const solc = require('solc');
// Get Path and Load Contract
const source = fs.readFileSync('Incrementer.sol', 'utf8');
// Compile Contract
const input = {
language: 'Solidity',
sources: {
'Incrementer.sol': {
content: source,
},
},
settings: {
outputSelection: {
'*': {
'*': ['*'],
},
},
},
};
const tempFile = JSON.parse(solc.compile(JSON.stringify(input))); const contractFile = tempFile.contracts['Incrementer.sol']['Incrementer'];
// Export Contract Data
module.exports = contractFile;

Web3.py

import solcx  # If you haven't already installed the Solidity compiler, 
# uncomment the following line
# solcx.install_solc()
# Compile contract
temp_file = solcx.compile_files('Incrementer.sol')
# Export contract data
abi = temp_file['Incrementer.sol:Incrementer']['abi']
bytecode = temp_file['Incrementer.sol:Incrementer']['bin']

Web3.js and Ethers.js

W pierwszej części skryptu pobierana jest ścieżka kontraktu i odczytywana jest jego treść.

Następnie budowany jest obiekt wejściowy kompilatora Solidity, który jest przekazywany jako dane wejściowe do funkcji solc.compile.

Na koniec wyodrębnij dane kontraktu Incrementer z pliku Incrementer.sol i wyeksportuj je, aby skrypt wdrażania mógł ich użyć.

Web3.py

W pierwszej części skryptu plik kontraktu jest kompilowany za pomocą funkcji solcx.compile_files. Zauważ, że plik kontraktu znajduje się w tym samym katalogu co skrypt kompilacji.

Podczas uruchamiania compile.py może pojawić się błąd informujący o konieczności zainstalowania Solc. Jeśli tak, odkomentuj wiersz w pliku, który wykonuje solcx.install_solc() i ponownie uruchom plik kompilacji za pomocą python3 compile.py. Więcej informacji można znaleźć pod tym linkiem.

Następnie, po zakończeniu skryptu, eksportowane są dane kontraktu. W tym przykładzie zdefiniowano tylko interfejs (ABI) i kod bajtowy.

Wdrażanie Kontraktu

Niezależnie od biblioteki strategia wdrażania skompilowanego inteligentnego kontraktu jest nieco podobna. Wystąpienie kontraktu jest tworzone przy użyciu jego interfejsu (ABI) i kodu bajtowego. Z tego wystąpienia funkcja wdrażania jest używana do wysyłania podpisanej transakcji, która wdraża kontrakt. Fragment kodu dla każdej biblioteki można znaleźć tutaj (są arbitralnie nazwane deploy.*):

Dla uproszczenia plik wdrażania składa się z dwóch sekcji. W pierwszej sekcji (“Define Provider & Variables”) importowana jest biblioteka do użycia oraz ABI i kod bajtowy kontraktu. Zdefiniowany jest również dostawca i konto z (z kluczem prywatnym). Należy zauważyć, że providerRPC ma zarówno standardowy węzeł programistyczny RPC, jak i punkt końcowy dla Moonbase Alpha.

Druga sekcja (“Deploy Contract”) zawiera opis rzeczywistej części wdrożenia umowy. Zwróć uwagę, że w tym przykładzie początkowa wartość zmiennej number została ustawiona na 5. Niektóre z kluczowych wniosków zostaną omówione w dalszej części.

Web3.js

const Web3 = require('web3');
const contractFile = require('./compile');
/* -- Define Provider & Variables -- */
// Provider
const providerRPC = {
development: 'http://localhost:9933',
moonbase: 'https://rpc.testnet.moonbeam.network',
};
//Change to correct network
const web3 = new Web3(providerRPC.development);
// Variables
const account_from = {
privateKey: 'YOUR-PRIVATE-KEY-HERE',
address: 'PUBLIC-ADDRESS-OF-PK-HERE',
};
const bytecode = contractFile.evm.bytecode.object;
const abi = contractFile.abi;
/* -- Deploy Contract -- */
const deploy = async () => {
console.log(`Attempting to deploy from account ${account_from.address}`);
// Create Contract Instance
const incrementer = new web3.eth.Contract(abi);
// Create Constructor Tx
const incrementerTx = incrementer.deploy({
data: bytecode,
arguments: [5],
});
// Sign Transacation and Send
const createTransaction = await web3.eth.accounts.signTransaction(
{
data: incrementerTx.encodeABI(),
gas: await incrementerTx.estimateGas(),
},
account_from.privateKey
);
// Send Tx and Wait for Receipt
const createReceipt = await web3.eth.sendSignedTransaction( createTransaction.rawTransaction
);
console.log(
`Contract deployed at address: ${createReceipt.contractAddress}`
);
};
deploy();

Ethers.js

const ethers = require('ethers');
const contractFile = require('./compile');
/*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: {
name: 'moonbeam-development',
rpc: 'http://localhost:9933',
chainId: 1281,
},
moonbase: {
name: 'moonbase-alpha',
rpc: 'https://rpc.testnet.moonbeam.network',
chainId: 1287,
},
};
const provider = new ethers.providers.StaticJsonRpcProvider(
providerRPC.development.rpc,
{
chainId: providerRPC.development.chainId,
name: providerRPC.development.name,
}
); //Change to correct network
// Variables
const account_from = {
privateKey: 'YOUR-PRIVATE-KEY-HERE',
};
const bytecode = contractFile.evm.bytecode.object;
const abi = contractFile.abi;
// Create Wallet
let wallet = new ethers.Wallet(account_from.privateKey, provider);
/*
-- Deploy Contract --
*/
// Create Contract Instance with Signer
const incrementer = new ethers.ContractFactory(abi, bytecode, wallet);
const deploy = async () => {
console.log(`Attempting to deploy from account: ${wallet.address}`);
// Send Tx (Initial Value set to 5) and Wait for Receipt
const contract = await incrementer.deploy([5]);
await contract.deployed();
console.log(`Contract deployed at address: ${contract.address}`);
};
deploy();

Web3.py

from compile import abi, bytecode
from web3 import Web3
#
# -- Define Provider & Variables --
#
# Provider
provider_rpc = {
'development': 'http://localhost:9933',
'alphanet': 'https://rpc.testnet.moonbeam.network',
}
web3 = Web3(Web3.HTTPProvider(provider_rpc['development'])) # Change to correct network
# Variables
account_from = {
'private_key': 'YOUR-PRIVATE-KEY-HERE',
'address': 'PUBLIC-ADDRESS-OF-PK-HERE',
}
#
# -- Deploy Contract --
#
print(f'Attempting to deploy from account: { account_from["address"] }')
# Create Contract Instance
Incrementer = web3.eth.contract(abi=abi, bytecode=bytecode)
# Build Constructor Tx
construct_txn = Incrementer.constructor(5).buildTransaction(
{
'from': account_from['address'],
'nonce': web3.eth.getTransactionCount(account_from['address']),
}
)
# Sign Tx with PK
tx_create = web3.eth.account.signTransaction(construct_txn, account_from['private_key'])
# Send Tx and Wait for Receipt
tx_hash = web3.eth.sendRawTransaction(tx_create.rawTransaction)
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)
print(f'Contract deployed at address: { tx_receipt.contractAddress }')

Skrypt deploy.* udostępnia adres kontraktu jako dane wyjściowe. Jest to przydatne, ponieważ jest używane w przypadku plików interakcji z umową.

Web3.js

W pierwszej części skryptu instancja web3 (lub dostawca) jest tworzona przy użyciu konstruktora Web3 z dostawcą RPC. Zmieniając dostawcę RPC podanego konstruktorowi, możesz wybrać, do której sieci chcesz wysłać transakcję.

Klucz prywatny i powiązany z nim adres publiczny są zdefiniowane do podpisywania transakcji i logowania. Wymagany jest tylko klucz prywatny. Ponadto kod bajtowy i interfejs kontraktu (ABI) są pobierane z eksportu kompilacji.

W drugiej sekcji instancja kontraktu jest tworzona przez udostępnienie ABI. Następnie używana jest funkcja wdrażania, która wymaga kodu bajtowego i argumentów funkcji konstruktora. Spowoduje to wygenerowanie obiektu transakcji konstruktora.

Następnie transakcję konstruktora można podpisać za pomocą metody web3.eth.accounts.signTransaction(). Pole danych odpowiada kodowi bajtowemu, a argumenty wejściowe konstruktora są zakodowane razem. Zwróć uwagę, że wartość gazu jest uzyskiwana za pomocą opcji estimateGas() wewnątrz transakcji konstruktora.

Na koniec wysyłana jest podpisana transakcja, a na terminalu wyświetla się adres umowy.

Ethers.js

W pierwszej części skryptu można określić różne sieci za pomocą nazwy, adresu URL RPC (wymagane) i identyfikatora łańcucha. Dostawca (podobny do instancji web3) jest tworzony za pomocą metody ethers.providers.StaticJsonRpcProvider. Alternatywą jest użycie metody ethers.providers.JsonRpcProvide(providerRPC), która wymaga tylko adresu punktu końcowego RPC dostawcy. Mogło to jednak powodować problemy ze zgodnością ze specyfikacjami poszczególnych projektów.

Klucz prywatny jest zdefiniowany w celu utworzenia instancji portfela, co również wymaga dostawcy z poprzedniego kroku. Instancja portfela służy do podpisywania transakcji. Ponadto kod bajtowy i interfejs kontraktu (ABI) są pobierane z eksportu kompilacji.

W drugiej sekcji instancja kontraktu jest tworzona za pomocą metody ethers.ContractFactory(), która zapewnia ABI, kod bajtowy i portfel. W ten sposób instancja kontraktu ma już osobę podpisującą. Następnie używana jest funkcja deploy, która wymaga argumentów wejściowych konstruktora. Spowoduje to wysłanie transakcji do wdrożenia umowy. Aby poczekać na potwierdzenie transakcji, możesz użyć metody deployed() transakcji wdrażania kontraktu.

Na koniec w terminalu wyświetlany jest adres umowy.

Web3.py

W pierwszej części skryptu instancja web3 (lub dostawca) jest tworzona przy użyciu metody Web3(Web3.HTTPProvider(provider_rpc)) z dostawcą RPC. Zmieniając dostawcę RPC, możesz wybrać sieć, do której chcesz wysłać transakcję.

Klucz prywatny i powiązany z nim adres publiczny są definiowane do podpisywania transakcji i ustalania adresu nadawcy.

W drugiej sekcji instancja kontraktu jest tworzona za pomocą metody web3.eth.contract(), dostarczając ABI i kod bajtowy zaimportowany z pliku kompilacji. Następnie można zbudować transakcję konstruktora za pomocą metody constructor().buildTransaction() wystąpienia kontraktu. Zauważ, że wewnątrz constructor() musisz określić argumenty wejściowe konstruktora. Należy również określić konto from. Upewnij się, że używasz tego powiązanego z kluczem prywatnym. Licznik transakcji można również uzyskać za pomocą metody web3.eth.getTransactionCount(adress).

Transakcję konstruktora można podpisać za pomocą metody web3.eth.account.signTransaction(), przekazując transakcję konstruktora i klucz prywatny.

Na koniec wysyłana jest podpisana transakcja, a na terminalu wyświetla się adres umowy.

Odczytywanie z Kontraktu(metody wywołania)

Metody wywołania to rodzaj interakcji, który nie modyfikuje przechowywania umowy (zmiana zmiennych), co oznacza, że ​​nie trzeba wysyłać żadnej transakcji.

Przyjrzyjmy się plikowi get.* (najprostszemu ze wszystkich), który pobiera bieżącą wartość przechowywaną w kontrakcie. Fragment kodu dla każdej biblioteki można znaleźć tutaj (są arbitralnie nazwane get.*):

Dla uproszczenia plik get składa się z dwóch sekcji. W pierwszej sekcji (“Define Provider & Variables”) importowana jest biblioteka do użycia i ABI umowy. Zdefiniowany jest również dostawca i adres umowy. Należy zauważyć, że providerRPC ma zarówno standardowy węzeł programistyczny RPC, jak i punkt końcowy dla Moonbase Alpha.

Druga sekcja (“Call Function”) przedstawia faktyczne wywołanie umowy. Niezależnie od biblioteki tworzona jest instancja kontraktu (połączona z adresem kontraktu), z której odpytywana jest metoda wywołania. Niektóre z kluczowych wniosków zostaną omówione poniżej.

Web3.js

const Web3 = require('web3');
const { abi } = require('./compile');
/*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: 'http://localhost:9933',
moonbase: 'https://rpc.testnet.moonbeam.network',
};
const web3 = new Web3(providerRPC.development); //Change to correct network
// Variables
const contractAddress = 'CONTRACT-ADDRESS-HERE';
/*
-- Call Function --
*/
// Create Contract Instance
const incrementer = new web3.eth.Contract(abi, contractAddress);
const get = async () => {
console.log(`Making a call to contract at address: ${contractAddress}`);
// Call Contract
const data = await incrementer.methods.number().call();
console.log(`The current number stored is: ${data}`);
};
get();

Ethers.js

const ethers = require('ethers');
const { abi } = require('./compile');
/*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: {
name: 'moonbeam-development',
rpc: 'http://localhost:9933',
chainId: 1281,
},
moonbase: {
name: 'moonbase-alpha',
rpc: 'https://rpc.testnet.moonbeam.network',
chainId: 1287,
},
};
const provider = new ethers.providers.StaticJsonRpcProvider(
providerRPC.development.rpc,
{
chainId: providerRPC.development.chainId,
name: providerRPC.development.name,
}
); //Change to correct network
// Variables
const contractAddress = 'CONTRACT-ADDRESS-HERE';
/*
-- Call Function --
*/
// Create Contract Instance
const incrementer = new ethers.Contract(contractAddress, abi, provider);
const get = async () => {
console.log(`Making a call to contract at address: ${contractAddress}`);
// Call Contract
const data = await incrementer.number();
console.log(`The current number stored is: ${data}`);
};
get();

Web3.py

from compile import abi, bytecode
from web3 import Web3
#
# -- Define Provider & Variables --
#
# Provider
provider_rpc = {
'development': 'http://localhost:9933',
'alphanet': 'https://rpc.testnet.moonbeam.network',
}
web3 = Web3(Web3.HTTPProvider(provider_rpc["development"])) # Change to correct network
# Variables
contract_address = 'CONTRACT-ADDRESS-HERE'
#
# -- Call Function --
#
print(f'Making a call to contract at address: { contract_address }')
# Create Contract Instance
Incrementer = web3.eth.contract(address=contract_address, abi=abi)
# Call Contract
number = Incrementer.functions.number().call()
print(f'The current number stored is: { number } ')

Web3.js

W pierwszej części skryptu instancja web3 (lub dostawca) jest tworzona przy użyciu konstruktora Web3 z dostawcą RPC. Zmieniając dostawcę RPC podanego konstruktorowi, możesz wybrać, do której sieci chcesz wysłać transakcję.

Interfejs kontraktu (ABI) i adres są również potrzebne do interakcji z nim.

W drugiej sekcji instancja kontraktu jest tworzona za pomocą metody web3.eth.Contract() poprzez podanie ABI i adresu. Następnie metoda do wywołania może zostać odpytana za pomocą funkcji contract.methods.methodName(_input).call(), zastępując contract, methodName i _input wystąpieniem kontraktu, funkcją do wywołania i wejściem funkcji (jeśli to konieczne). Ta obietnica, gdy zostanie rozwiązana, zwróci żądaną wartość.

Na koniec wartość jest wyświetlana w terminalu.

Ethers.js

W pierwszej części skryptu można określić różne sieci za pomocą nazwy, adresu URL RPC (wymagane) i identyfikatora łańcucha. Dostawca (podobny do instancji web3) jest tworzony za pomocą metody ethers.providers.StaticJsonRpcProvider. Alternatywą jest użycie metody ethers.providers.JsonRpcProvide(providerRPC), która wymaga tylko adresu punktu końcowego RPC dostawcy. Mogło to jednak powodować problemy ze zgodnością ze specyfikacjami poszczególnych projektów.

Interfejs kontraktu (ABI) i adres są również potrzebne do interakcji z nim.

W drugiej sekcji instancja kontraktu jest tworzona za pomocą metody ethers.Contract(), podając jej adres, ABI i dostawcę. Następnie można wykonać zapytanie o metodę do wywołania za pomocą funkcji contract.methodName(_input), zastępując metodę contract methodName i _input z wystąpieniem umowy, funkcją do wywołania i wejściem funkcji (w razie potrzeby). Ta obietnica, gdy zostanie rozwiązana, zwróci żądaną wartość.

Na koniec wartość jest wyświetlana w terminalu.

Web3.py

W pierwszej części skryptu instancja web3 (lub dostawca) jest tworzona przy użyciu metody Web3(Web3.HTTPProvider(provider_rpc)) z dostawcą RPC. Zmieniając dostawcę RPC, możesz wybrać sieć, do której chcesz wysłać transakcję.

Interfejs kontraktu (ABI) i adres są również potrzebne do interakcji z nim.

W drugiej sekcji instancja kontraktu jest tworzona za pomocą metody web3.eth.contract() poprzez podanie ABI i adresu. Następnie metoda do wywołania może zostać odpytana za pomocą funkcji contract.functions.method_name(input).call(), zastępując contract, method_name i input instancją kontraktu, funkcją do wywołania i wejściem funkcji (jeśli to konieczne). Zwraca żądaną wartość.

Na koniec wartość jest wyświetlana w terminalu.

Interakcja z umową (metody wysyłania)

Metody wysyłania to rodzaj interakcji, który modyfikuje przechowywanie kontraktu (zmiana zmiennych), co oznacza, że ​​transakcja musi być podpisana i wysłana.

Najpierw przyjrzyjmy się plikowi increment.*, który inkrementuje aktualną liczbę zapisaną w kontrakcie o podaną wartość. Możesz znaleźć fragment kodu dla każdej biblioteki tutaj (zostały one arbitralnie nazwane increment.*):

Dla uproszczenia plik przyrostowy składa się z dwóch sekcji. W pierwszej sekcji (“Define Provider & Variables”) importowana jest biblioteka do użycia i ABI umowy. Określono również dostawcę, adres umowy i wartość funkcji increment. Należy zauważyć, że providerRPC ma zarówno standardowy węzeł programistyczny RPC, jak i punkt końcowy dla Moonbase Alpha.

Druga sekcja (“Send Function”) przedstawia rzeczywistą funkcję, która ma być wywołana z transakcją. Niezależnie od biblioteki tworzona jest instancja kontraktu (powiązana z adresem kontraktu), z której odpytywana jest funkcja, która ma być użyta.

Web3.js

const Web3 = require('web3');
const { abi } = require('./compile');
/*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: 'http://localhost:9933',
moonbase: 'https://rpc.testnet.moonbeam.network',
};
const web3 = new Web3(providerRPC.development); //Change to correct network
// Variables
const account_from = {
privateKey: 'YOUR-PRIVATE-KEY-HERE',
};
const contractAddress = 'CONTRACT-ADDRESS-HERE';
const _value = 3;
/*
-- Send Function --
*/
// Create Contract Instance
const incrementer = new web3.eth.Contract(abi, contractAddress);
// Build Increment Tx
const incrementTx = incrementer.methods.increment(_value);
const increment = async () => {
console.log(
`Calling the increment by ${_value} function in contract at address: ${contractAddress}`
);
// Sign Tx with PK
const createTransaction = await web3.eth.accounts.signTransaction(
{
to: contractAddress,
data: incrementTx.encodeABI(),
gas: await incrementTx.estimateGas(),
},
account_from.privateKey
);
// Send Tx and Wait for Receipt
const createReceipt = await web3.eth.sendSignedTransaction(
createTransaction.rawTransaction
);
console.log(`Tx successful with hash: ${createReceipt.transactionHash}`);
};
increment();

Ethers.js

const ethers = require('ethers');
const { abi } = require('./compile');
/*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: {
name: 'moonbeam-development',
rpc: 'http://localhost:9933',
chainId: 1281,
},
moonbase: {
name: 'moonbase-alpha',
rpc: 'https://rpc.testnet.moonbeam.network',
chainId: 1287,
},
};
const provider = new ethers.providers.StaticJsonRpcProvider(
providerRPC.development.rpc,
{
chainId: providerRPC.development.chainId,
name: providerRPC.development.name,
}
); //Change to correct network
// Variables
const account_from = {
privateKey: 'YOUR-PRIVATE-KEY-HERE',
};
const contractAddress = 'CONTRACT-ADDRESS-HERE';
const _value = 3;
// Create Wallet
let wallet = new ethers.Wallet(account_from.privateKey, provider);
/*
-- Send Function --
*/
// Create Contract Instance with Signer
const incrementer = new ethers.Contract(contractAddress, abi, wallet);
const increment = async () => {
console.log(
`Calling the increment by ${_value} function in contract at address: ${contractAddress}`
);
// Sign-Send Tx and Wait for Receipt
const createReceipt = await incrementer.increment([_value]);
await createReceipt.wait();
console.log(`Tx successful with hash: ${createReceipt.hash}`);
};
increment();

Web3.py

from compile import abi, bytecode
from web3 import Web3
#
# -- Define Provider & Variables --
#
# Provider
provider_rpc = {
'development': 'http://localhost:9933',
'alphanet': 'https://rpc.testnet.moonbeam.network',
}
web3 = Web3(Web3.HTTPProvider(provider_rpc["development"])) # Change to correct network
# Variables
account_from = {
'private_key': 'YOUR-PRIVATE-KEY-HERE',
'address': 'PUBLIC-ADDRESS-OF-PK-HERE',
}
contract_address = 'CONTRACT-ADDRESS-HERE'
value = 3
#
# -- Send Function --
#
print(
f'Calling the increment by { value } function in contract at address: { contract_address }'
)
# Create Contract Instance
Incrementer = web3.eth.contract(address=contract_address, abi=abi)
# Build Increment Tx
increment_tx = Incrementer.functions.increment(value).buildTransaction(
{
'from': account_from['address'],
'nonce': web3.eth.getTransactionCount(account_from['address']),
}
)
# Sign Tx with PK
tx_create = web3.eth.account.signTransaction(increment_tx, account_from['private_key'])
# Send Tx and Wait for Receipt
tx_hash = web3.eth.sendRawTransaction(tx_create.rawTransaction)
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)
print(f'Tx successful with hash: { tx_receipt.transactionHash.hex() }')

Drugim plikiem do interakcji z umową jest plik reset.*, który resetuje liczbę przechowywaną w umowie do zera. Fragment kodu dla każdej biblioteki można znaleźć tutaj (są arbitralnie nazwane reset.*):

Struktura każdego pliku jest bardzo podobna do jego odpowiednika increment.* dla każdej biblioteki. Główną różnicą jest wywoływana metoda.

Web3.js

const Web3 = require('web3');
const { abi } = require('./compile');
/*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: 'http://localhost:9933',
moonbase: 'https://rpc.testnet.moonbeam.network',
};
const web3 = new Web3(providerRPC.development); //Change to correct network
// Variables
const account_from = {
privateKey: 'YOUR-PRIVATE-KEY-HERE',
};
const contractAddress = 'CONTRACT-ADDRESS-HERE';
/*
-- Send Function --
*/
// Create Contract Instance
const incrementer = new web3.eth.Contract(abi, contractAddress);
// Build Reset Tx
const resetTx = incrementer.methods.reset();
const reset = async () => {
console.log(
`Calling the reset function in contract at address: ${contractAddress}`
);
// Sign Tx with PK
const createTransaction = await web3.eth.accounts.signTransaction(
{
to: contractAddress,
data: resetTx.encodeABI(),
gas: await resetTx.estimateGas(),
},
account_from.privateKey
);
// Send Tx and Wait for Receipt
const createReceipt = await web3.eth.sendSignedTransaction(
createTransaction.rawTransaction
);
console.log(`Tx successful with hash: ${createReceipt.transactionHash}`);
};
reset();

Ethers.js

const ethers = require('ethers');
const { abi } = require('./compile');
/*
-- Define Provider & Variables --
*/
// Provider
const providerRPC = {
development: {
name: 'moonbeam-development',
rpc: 'http://localhost:9933',
chainId: 1281,
},
moonbase: {
name: 'moonbase-alpha',
rpc: 'https://rpc.testnet.moonbeam.network',
chainId: 1287,
},
};
const provider = new ethers.providers.StaticJsonRpcProvider(
providerRPC.development.rpc,
{
chainId: providerRPC.development.chainId,
name: providerRPC.development.name,
}
); //Change to correct network
// Variables
const account_from = {
privateKey: 'YOUR-PRIVATE-KEY-HERE',
};
const contractAddress = 'CONTRACT-ADDRESS-HERE';
// Create Wallet
let wallet = new ethers.Wallet(account_from.privateKey, provider);
/*
-- Send Function --
*/
// Create Contract Instance with Signer
const incrementer = new ethers.Contract(contractAddress, abi, wallet);
const reset = async () => {
console.log(
`Calling the reset function in contract at address: ${contractAddress}`
);
// Sign-Send Tx and Wait for Receipt
const createReceipt = await incrementer.reset();
await createReceipt.wait();
console.log(`Tx successful with hash: ${createReceipt.hash}`);
};
reset();

Web3.py

from compile import abi, bytecode
from web3 import Web3
#
# -- Define Provider & Variables --
#
# Provider
provider_rpc = {
'development': 'http://localhost:9933',
'alphanet': 'https://rpc.testnet.moonbeam.network',
}
web3 = Web3(Web3.HTTPProvider(provider_rpc["development"])) # Change to correct network
# Variables
account_from = {
'private_key': 'YOUR-PRIVATE-KEY-HERE',
'address': 'PUBLIC-ADDRESS-OF-PK-HERE',
}
contract_address = 'CONTRACT-ADDRESS-HERE'
#
# -- Call Function --
#
print(f'Calling the reset function in contract at address: { contract_address }')
# Create Contract Instance
Incrementer = web3.eth.contract(address=contract_address, abi=abi)
# Build Reset Tx
reset_tx = Incrementer.functions.reset().buildTransaction(
{
'from': account_from['address'],
'nonce': web3.eth.getTransactionCount(account_from['address']),
}
)
# Sign Tx with PK
tx_create = web3.eth.account.signTransaction(reset_tx, account_from['private_key'])
# Send Tx and Wait for Receipt
tx_hash = web3.eth.sendRawTransaction(tx_create.rawTransaction)
tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash)
print(f'Tx successful with hash: { tx_receipt.transactionHash.hex() }')

Web3.js

W pierwszej części skryptu (increment lub reset plików) instancja web3 (lub dostawca) jest tworzona przy użyciu konstruktora Web3 z dostawcą RPC. Zmieniając dostawcę RPC podanego konstruktorowi, możesz wybrać, do której sieci chcesz wysłać transakcję.

Klucz prywatny i powiązany z nim adres publiczny są zdefiniowane do podpisywania transakcji i logowania. Wymagany jest tylko klucz prywatny. Do interakcji z nią potrzebny jest również interfejs umowy (ABI) i adres. Jeśli to konieczne, możesz zdefiniować dowolną zmienną wymaganą jako dane wejściowe do funkcji, z którą zamierzasz wchodzić w interakcję.

W drugiej sekcji instancja kontraktu jest tworzona za pomocą metody web3.eth.Contract() poprzez podanie ABI i adresu. Następnie możesz zbudować obiekt transakcji za pomocą funkcji contract.methods.methodName(_input), zastępując contract, methodName i _input instancją umowy, funkcją do wywołania i wejściem funkcji (jeśli to konieczne).

Następnie transakcję można podpisać za pomocą metody web3.eth.accounts.signTransaction(). Pole danych odpowiada obiektowi transakcji z poprzedniego kroku. Zwróć uwagę, że wartość gazu jest uzyskiwana za pomocą opcji estimateGas() wewnątrz obiektu transakcji.

Na koniec wysyłana jest podpisana transakcja, a w terminalu wyświetlany jest skrót transakcji.

Ethers.js

W pierwszej części skryptu (pliki increment lub reset) można określić różne sieci za pomocą nazwy, adresu URL RPC (wymagane) i identyfikatora łańcucha. Dostawca (podobny do instancji web3) jest tworzony za pomocą metody ethers.providers.StaticJsonRpcProvider. Alternatywą jest użycie metody ethers.providers.JsonRpcProvide(providerRPC), która wymaga tylko adresu punktu końcowego RPC dostawcy. Mogło to jednak powodować problemy ze zgodnością ze specyfikacjami poszczególnych projektów.

Klucz prywatny jest zdefiniowany w celu utworzenia instancji portfela, co również wymaga dostawcy z poprzedniego kroku. Instancja portfela służy do podpisywania transakcji. Do interakcji z nią potrzebny jest również interfejs umowy (ABI) i adres. Jeśli to konieczne, możesz zdefiniować dowolną zmienną wymaganą jako dane wejściowe do funkcji, z którą zamierzasz wchodzić w interakcję.

W drugiej sekcji instancja kontraktu jest tworzona za pomocą metody ethers.Contract(), podając jej adres, ABI i portfel. W ten sposób instancja kontraktu ma już osobę podpisującą. Następnie można wysłać transakcję odpowiadającą konkretnej funkcji za pomocą funkcji contract.methodName(_input), zastępując contract, methodName i _input instancją umowy, funkcją do wywołania i wejściem funkcji (jeśli to konieczne). Aby poczekać na potwierdzenie transakcji, możesz użyć metody wait() transakcji wdrażania kontraktu.

Na koniec w terminalu wyświetlany jest skrót transakcji.

Web3.py

W pierwszej części skryptu (przyrost lub zresetowanie plików) instancja web3 (lub dostawca) jest tworzona przy użyciu metody Web3(Web3.HTTPProvider(provider_rpc)) z dostawcą RPC. Zmieniając dostawcę RPC, możesz wybrać sieć, do której chcesz wysłać transakcję.

Klucz prywatny i powiązany z nim adres publiczny są definiowane do podpisywania transakcji i ustalania adresu nadawcy. Do interakcji z nią potrzebny jest również interfejs umowy (ABI) i adres.

W drugiej sekcji instancja kontraktu jest tworzona za pomocą metody web3.eth.contract() poprzez podanie ABI i adresu. Następnie możesz zbudować obiekt transakcji za pomocą funkcji contract.functions.methodName(_input).buildTransaction, zastępując contract, methodName i _input wystąpieniem umowy, funkcją do wywołania i wejściem funkcji (jeśli to konieczne). Wewnątrz buildTransaction() konto nadawcy musi być określone. Upewnij się, że używasz tego powiązanego z kluczem prywatnym. Licznik transakcji można również uzyskać za pomocą metody web3.eth.getTransactionCount(adres).

Transakcję można podpisać za pomocą metody web3.eth.account.signTransaction(), przekazując obiekt transakcji z poprzedniego kroku oraz klucz prywatny.

Na koniec w terminalu wyświetlany jest skrót transakcji.

Uruchamianie Skryptów

W tej sekcji pokazany wcześniej kod został dostosowany do docelowego węzła programistycznego, który można uruchomić, postępując zgodnie z tym samouczkiem. Ponadto każda transakcja została wysłana z konta z przedpłatą, które pochodzi z node:

  • klucz prywatny: 99B3C12287537E38C90A9219D4CB074A89A16E9CDB20BF85728EBD97C343E342
  • adres publiczny: 0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b

Najpierw wdróż kontrakt, uruchamiając (zwróć uwagę, że nazwa katalogu została zmieniona dla każdej biblioteki):

Web3.js

node deploy.js

Ethers.js

node deploy.js

Web3.py

python3 deploy.py

Spowoduje to wdrożenie umowy i zwrócenie adresu:

Web3.js

Ethers.js

Web3.py

Następnie uruchom plik increment. Możesz użyć pliku get, aby zweryfikować wartość numeru zapisanego w umowie przed i po inkrementacji:

Web3.js

# Get value 
node get.js
# Increment value
node increment.js
# Get value
node get.js

Ethers.js

# Get value 
node get.js
# Increment value
increment.js
# Get value
node get.js

Web3.py

# Get value 
python3 get.py
# Increment value
python3 increment.py
# Get value
python3 get.py

Spowoduje to wyświetlenie wartości przed transakcją inkrementacyjną, hash transakcji oraz wartość po:

Web3.js

Ethers.js

Web3.py

Na koniec uruchom plik resetowania. Po raz kolejny możesz użyć pliku get do weryfikacji wartości numeru zapisanego w umowie przed i po jej zresetowaniu:

Web3.js

# Get value 
node get.js
# Reset value
node reset.js
# Get value
node get.js

Ethers.js

# Get value 
node get.js
# Reset value
node reset.js
# Get value
node get.js

Web3.py

# Get value 
python3 get.py
# Reset value
python3 reset.py
# Get value
python3 get.py

Spowoduje to wyświetlenie wartości przed zresetowaniem transakcji, hash transakcji oraz wartość po:

Web3.js

Ethers.js

Web3.py

--

--

No responses yet