diff --git a/public/content/translations/pl/developers/docs/storage/index.md b/public/content/translations/pl/developers/docs/storage/index.md index 2f378ffa99d..8f5de13b87e 100644 --- a/public/content/translations/pl/developers/docs/storage/index.md +++ b/public/content/translations/pl/developers/docs/storage/index.md @@ -1,35 +1,216 @@ --- -title: Zdecentralizowana pamięć -description: Przegląd tego, czym jest zdecentralizowana pamięć i dostępne narzędzia do integracji z integracji z aplikacjami dapp. +title: "Zdecentralizowana pamięć" +description: "Przegląd tego, czym jest zdecentralizowana pamięć masowa i dostępne narzędzia do integracji z dappką." lang: pl -incomplete: true -isOutdated: true --- -W przeciwieństwie do centralnie zlokalizowanego serwera obsługiwanego przez jedną firmę lub organizację, zdecentralizowane systemy pamięci masowej składają się z sieci peer-to-peer użytkowników-operatorów, którzy posiadają część ogólnych danych, tworząc odporny system przechowywania i udostępniania plików. +W przeciwieństwie do scentralizowanego serwera zarządzanego przez konkretną firmę lub organizację, systemy pamięci zdecentralizowanej składają się z sieci peer-to-peer, w której operatorami są użytkownicy, którzy przechowują część danych, tworząc odporny system współdzielenia pamięci masowej. Mogą one występować w aplikacjach opartych na technologii blockchain lub innych sieciach peer-to-peer. + +Samo Ethereum może być używane jako zdecentralizowany system pamięci masowej i jest nim w zakresie przechowywania kodu wszystkich inteligentnych kontraktów. Ethereum jednakże nie zostało stworzone z myślą o przechowywaniu dużych ilości danych. Łańcuch stale rośnie, ale w chwili pisania tego tekstu łańcuch Ethereum ma rozmiar około 500 GB – 1 TB ([w zależności od klienta](https://etherscan.io/chartsync/chaindefault)), a każdy węzeł w sieci musi być w stanie przechować wszystkie te dane. Jeśli łańcuch miałby rozrosnąć się do większych rozmiarów (powiedzmy 5 TB), mogłoby to być niewykonalne dla części z węzłów. Ponadto koszt wdrożenia tak dużej ilości danych w sieci Mainnet byłby zaporowo wysoki ze względu na opłaty za [gaz](/developers/docs/gas). + +W związku z tymi ograniczeniami potrzebne są inne łańcuchy lub metody przechowywania dużych ilości danych w sposób zdecentralizowany. + +Rozważając różne opcje zdecentralizowanej pamięci masowej (dStorage), użytkownik powinien pamiętać o kilku kwestiach. + +- Mechanizm trwałości / struktura zachęt +- Egzekwowanie odpowiedniego przechowywania danych +- Decentralizacja +- Konsensus + +## Mechanizm trwałości / struktura zachęt {#persistence-mechanism} + +### Oparte na blockchainie {#blockchain-based} + +Jeśli element danych ma przetrwać na zawsze, trzeba skorzystać z mechanizmu trwałości danych. W przykładzie Ethereum mechanizm trwałości oparty jest na założeniu, że cały łańcuch musi być zawarty na każdym węźle sieci. Nowe fragmenty danych dołączane są do końca łańcucha, więc rośnie on nieustannie, tworząc potrzebę replikacji całości zamieszczonych na nim danych przez każdy z węzłów. + +Jest to znane jako trwałość **oparta na blockchainie**. + +Problem z trwałością opartą na blockchainie polega na tym, że łańcuch mógłby stać się zbyt duży, aby można było realnie utrzymywać i przechowywać wszystkie dane (np. [wiele źródeł](https://healthit.com.au/how-big-is-the-internet-and-how-do-we-measure-it/) szacuje, że Internet wymaga ponad 40 zetabajtów pojemności magazynowej). + +Blockchain musi mieć również jakiś rodzaj struktury zachęt. Za trwałość opartą na blockchainie przewidziana jest opłata dla walidatora. Kiedy dane dołączone są do łańcucha, walidatorowi płaci się za ich dodanie. + +Platformy z trwałością opartą o blockchain: + +- Ethereum +- [Arweave](https://www.arweave.org/) + +### Oparta na kontrakcie {#contract-based} + +**Trwałość oparta na kontrakcie** opiera się na założeniu, że dane nie mogą być replikowane przez każdy węzeł i przechowywane na zawsze, a zamiast tego muszą być utrzymywane za pomocą umów kontraktowych. Są to porozumienia zawarte pomiędzy wieloma węzłami, które zobowiązały się przechowywać fragment danych przez pewien okres. Muszą być zwrócone lub odnowione, kiedy się wyczerpią, aby utrzymać trwałość danych. + +W większości przypadków, zamiast przechowywać całość danych na łańcuchu, hasz lokalizacji danych jest na nim przechowywany. Dzięki temu cały łańcuch nie musi być skalowany, aby utrzymać wszystkie dane. + +Platformy z trwałością opartą na kontrakcie: + +- [Filecoin](https://docs.filecoin.io/basics/what-is-filecoin) +- [Skynet](https://sia.tech/) +- [Storj](https://storj.io/) +- [Züs](https://zus.network/) +- [Crust Network](https://crust.network) +- [Swarm](https://www.ethswarm.org/) +- [4EVERLAND](https://www.4everland.org/) + +### Dodatkowe uwagi {#additional-consideration} + +IPFS jest rozproszonym systemem przechowywania i dostępu do plików, stron internetowych, aplikacji oraz danych. Nie ma wbudowanego schematu zachęt, ale może, zamiast tego być używany z każdym wspomnianym powyżej rozwiązaniem motywacyjnym, aby zapewnić dłuższą trwałość. Inną metodą utrwalania danych w IPFS jest współpraca z usługą przypinania, która "przypnie" Twoje dane za ciebie. Możesz nawet uruchomić własny węzeł IPFS i przyczynić się do utrwalania własnych danych oraz danych innych osób całkowicie za darmo! + +- [IPFS](https://docs.ipfs.io/concepts/what-is-ipfs/) +- [Pinata](https://www.pinata.cloud/) _(usługa przypinania IPFS)_ +- [web3.storage](https://web3.storage/) _(usługa przypinania dla IPFS/Filecoin)_ +- [Infura](https://infura.io/product/ipfs) _(usługa przypinania IPFS)_ +- [IPFS Scan](https://ipfs-scan.io) _(eksplorator przypinania IPFS)_ +- [4EVERLAND](https://www.4everland.org/) _(usługa przypinania IPFS)_ +- [Filebase](https://filebase.com) _(usługa przypinania IPFS)_ +- [Spheron Network](https://spheron.network/) _(usługa przypinania dla IPFS/Filecoin)_ + +SWARM to zdecentralizowana metoda przechowywania i dystrybucji danych z systemem zachęt za przechowywanie i wyrocznią cen wynajmu pamięci. + +## Przechowywanie danych {#data-retention} + +Aby zachować dane, system musi mieć jakiś mechanizm ich zachowania. + +### Mechanizm wyzwań {#challenge-mechanism} + +Jednym z najpopularniejszych sposób na upewnienie się, że dane są zachowane, jest zastosowanie kryptograficznego wyzwania, które jest przedstawiane węzłowi, aby upewnić się, że wciąż ma dane. Prostym przykładem jest dowód dostępu od Arweave. Przedstawiają węzłowi wyzwanie, aby sprawdzić, czy posiada dane w najnowszym bloku oraz losowo wybranym bloku z przeszłości. Jeśli węzeł nie potrafi odpowiedzieć, zostaje ukarany. + +Typy zdecentralizowanej pamięci z mechanizmami wyzwań: + +- Züs +- Skynet +- Arweave +- Filecoin +- Sieć Crust +- 4EVERLAND + +### Decentralizacja {#decentrality} + +Nie ma świetnych narzędzi, by zmierzyć poziom decentralizacji platform, ale ogólnie rzecz biorąc, warto używać narzędzi, które nie używają potwierdzania tożsamości użytkownika. W ten sposób dostarczają one dowodu na to, że nie są scentralizowane. + +Zdecentralizowane narzędzia bez KYC: + +- Skynet +- Arweave +- Filecoin +- IPFS - Inter-Planetarny System Plików jest to zdecentralizowana pamięć i system nawiązywania plików w Ethereum +- Ethereum +- Sieć Crust +- 4EVERLAND + +### Konsensus {#consensus} + +Większość z tych narzędzi ma własną wersję [mechanizmu konsensusu](/developers/docs/consensus-mechanisms/), ale generalnie opierają się one na [**dowodzie pracy (PoW)**](/developers/docs/consensus-mechanisms/pow/) lub [**dowodzie stawki (PoS)**](/developers/docs/consensus-mechanisms/pos/). + +Oparte na proof-of-work: + +- Skynet +- Arweave + +Oparte na proof-of-stake: + +- Ethereum +- Filecoin +- Züs +- Sieć Crust ## Powiązane narzędzia {#related-tools} -**IPFS -** **_InterPlanetary File System to zdecentralizowany system przechowywania i odwoływania się do plików dla Ethereum. _** +**IPFS - _InterPlanetary File System to zdecentralizowany system przechowywania i odwoływania się do plików dla Ethereum._** -- [ipfs.io](https://ipfs.io/) +- [Ipfs.io](https://ipfs.io/) - [Dokumentacja](https://docs.ipfs.io/) - [GitHub](https://github.com/ipfs/ipfs) -**Swarm —** **platforma rozproszonej pamięci masowej i usługa dystrybucji treści dla stosu web3 Ethereum.** +**Storj DCS - _Bezpieczna, prywatna i kompatybilna z S3 zdecentralizowana chmurowa pamięć masowa obiektów dla deweloperów._** + +- [Storj.io](https://storj.io/) +- [Dokumentacja](https://docs.storj.io/) +- [GitHub](https://github.com/storj/storj) + +**Sia - _Wykorzystuje kryptografię do stworzenia niewymagającego zaufania rynku chmurowej pamięci masowej, pozwalając kupującym i sprzedającym na bezpośrednie przeprowadzanie transakcji._** + +- [Skynet.net](https://sia.tech/) +- [Dokumentacja](https://docs.sia.tech/) +- [GitHub](https://github.com/SiaFoundation/) + +**Filecoin - _Filecoin został stworzony przez ten sam zespół, który stoi za IPFS To warstwa wyposażona w zachęty oparta na ideałach IPFS._** + +- [Filecoin.io](https://filecoin.io/) +- [Dokumentacja](https://docs.filecoin.io/) +- [GitHub](https://github.com/filecoin-project/) + +**Arweave - _Arweave to platforma zdecentralizowanej pamięci masowej (dStorage) do przechowywania danych._** + +- [Arweave.org](https://www.arweave.org/) +- [Dokumentacja](https://docs.arweave.org/info/) +- [Arweave](https://github.com/ArweaveTeam/arweave/) + +**Züs - _Züs to platforma dStorage typu proof-of-stake (dowód stawki) z shardingiem i blobberami._** + +- [zus.network](https://zus.network/) +- [Dokumentacja](https://docs.zus.network/zus-docs/) +- [GitHub](https://github.com/0chain/) + +**Crust Network - _Crust to platforma zdecentralizowanej pamięci masowej (dStorage) działająca na bazie IPFS._** -- [Swarm](https://ethersphere.github.io/swarm-home/) -- [GitHub](https://github.com/ethersphere/swarm) +- [Crust.network](https://crust.network) +- [Dokumentacja](https://wiki.crust.network) +- [GitHub](https://github.com/crustio) -**OrbitDB —** **zdecentralizowana baza danych peer-to-peer oparta na IPFS.** +**Swarm - _Rozproszona platforma pamięci masowej i usługa dystrybucji treści dla stosu web3 Ethereum._** -- [Dokumentacja](https://github.com/orbitdb/field-manual) -- [GitHub](https://github.com/orbitdb/orbit-db) +- [EthSwarm.org](https://www.ethswarm.org/) +- [Dokumentacja](https://docs.ethswarm.org/) +- [GitHub](https://github.com/ethersphere/) + +**OrbitDB - _Zdecentralizowana baza danych peer-to-peer działająca na IPFS._** + +- [OrbitDB.org](https://orbitdb.org/) +- [Dokumentacja](https://github.com/orbitdb/field-manual/) +- [GitHub](https://github.com/orbitdb/orbit-db/) + +**Aleph.im - _Zdecentralizowany projekt chmurowy (baza danych, przechowywanie plików, moc obliczeniowa i DID). Unikalne połączenie technologi peer-to-peer na łańcuchu i poza nim. IPFS i kompatybilność wielołańcuchowa._** + +- [Aleph.im](https://aleph.cloud/) +- [Dokumentacja](https://docs.aleph.cloud/) +- [GitHub](https://github.com/aleph-im/) + +**Ceramic - _Kontrolowana przez użytkownika baza danych IPFS do przechowywania danych dla bogatych w dane i angażujących aplikacji._** + +- [Ceramic.network](https://ceramic.network/) +- [Dokumentacja](https://developers.ceramic.network/) +- [GitHub](https://github.com/ceramicnetwork/js-ceramic/) + +**Filebase - _Kompatybilna z S3 zdecentralizowana pamięć masowa i geo-redundantna usługa przypinania IPFS. Wszystkie pliki przesłane do IPFS przez Filebase są automatycznie przypinane do infrastruktury Filebase z potrójną replikacją na całym świecie._** + +- [Filebase.com](https://filebase.com/) +- [Dokumentacja](https://docs.filebase.com/) +- [GitHub](https://github.com/filebase) + +**4EVERLAND - _Platforma chmury obliczeniowej Web 3.0, która integruje podstawowe funkcje przechowywania, obliczeń i sieci, jest kompatybilna z S3 i zapewnia synchroniczne przechowywanie danych w zdecentralizowanych sieciach pamięci masowej, takich jak IPFS i Arweave._** + +- [4everland.org](https://www.4everland.org/) +- [Dokumentacja](https://docs.4everland.org/) +- [GitHub](https://github.com/4everland) + +**Kaleido - _Platforma typu blockchain jako usługa (BaaS) z węzłami IPFS dostępnymi na jedno kliknięcie._** + +- [Kaleido](https://kaleido.io/) +- [Dokumentacja](https://docs.kaleido.io/kaleido-services/ipfs/) +- [GitHub](https://github.com/kaleido-io) + +**Spheron Network - _Spheron to platforma jako usługa (PaaS) zaprojektowana dla dappek, które chcą uruchamiać swoje aplikacje na zdecentralizowanej infrastrukturze z najlepszą wydajnością. Zapewnia od ręki moc obliczeniową, zdecentralizowaną pamięć masową, CDN i hosting stron internetowych._** + +- [spheron.network](https://spheron.network/) +- [Dokumentacja](https://docs.spheron.network/) +- [GitHub](https://github.com/spheronFdn) ## Dalsza lektura {#further-reading} -_Znasz jakieś zasoby społeczności, które Ci pomogły? Wyedytuj tę stronę i dodaj je!_ +- [Czym jest zdecentralizowana pamięć masowa?](https://coinmarketcap.com/academy/article/what-is-decentralized-storage-a-deep-dive-by-filecoin) - _CoinMarketCap_ +- [Obalamy pięć popularnych mitów na temat zdecentralizowanej pamięci masowej](https://www.storj.io/blog/busting-five-common-myths-about-decentralized-storage) - _Storj_ + +_Znasz jakieś zasoby społeczności, które Ci pomogły? Edytuj tę stronę i dodaj je!_ ## Powiązane tematy {#related-topics} -- [Frameworki programistyczne](/developers/docs/frameworks/) +- [Frameworki deweloperskie](/developers/docs/frameworks/) diff --git a/public/content/translations/pl/developers/docs/transactions/index.md b/public/content/translations/pl/developers/docs/transactions/index.md index e3df3c50bbc..1db03e7ef48 100644 --- a/public/content/translations/pl/developers/docs/transactions/index.md +++ b/public/content/translations/pl/developers/docs/transactions/index.md @@ -1,20 +1,21 @@ --- title: Transakcje -description: Przegląd transakcji Ethereum – sposób działania, struktury danych i metody ich wysyłania za pośrednictwem aplikacji. +description: "Przegląd transakcji Ethereum – sposób działania, struktury danych i metody ich wysyłania za pośrednictwem aplikacji." lang: pl --- Transakcje to podpisane kryptograficznie instrukcje od kont. Konto inicjuje transakcję, aby zaktualizować stan sieci Ethereum. Najprostszą transakcją jest przeniesienie ETH z jednego konta na drugie. -## Warunki wstępne {#prerequisites} +## Wymagania wstępne {#prerequisites} -Aby lepiej zrozumieć tę stronę, zalecamy najpierw przeczytanie rozdziału o [kontach](/developers/docs/accounts/) oraz naszym [wprowadzeniu do Ethereum](/developers/docs/intro-to-ethereum/). +Aby lepiej zrozumieć tę stronę, zalecamy najpierw przeczytanie rozdziału [Konta](/developers/docs/accounts/) oraz naszego [wprowadzenia do Ethereum](/developers/docs/intro-to-ethereum/). ## Czym jest transakcja? {#whats-a-transaction} Transakcja Ethereum odnosi się do działania zainicjowanego przez konto zewnętrzne, czyli takie, które jest zarządzane przez człowieka, a nie przez kontrakt. Na przykład, jeśli Bob wysyła Alice 1 ETH, na koncie Boba musi się pojawić obciążenie, a na koncie Alice uznanie. Ta zmiana stanu ma miejsce w ramach transakcji. -![Schemat pokazujący transakcję powodującą zmianę stanu](./tx.png) _Diagram zaadaptowany z [Ilustrowanego Ethereum EVM](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ +![Diagram pokazujący, jak transakcja powoduje zmianę stanu](./tx.png) +_Diagram zaadaptowany z [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ Transakcje, które zmieniają stan EVM, muszą być rozesłane do całej sieci. Każdy węzeł może rozesłać prośbę o wykonanie transakcji na EVM; po tym wydarzeniu walidator wykona transakcję i roześle powstałą zmianę stanu do reszty sieci. @@ -22,17 +23,17 @@ Transakcje wymagają opłaty i muszą być uwzględnione w ważnym bloku. Aby up Przesłana transakcja zawiera następujące informacje: -- `from` — adres nadawcy, który będzie podpisywać transakcję. Będzie to konto zewnętrzne, ponieważ konta kontraktowe nie mogą wysyłać transakcji -- `to` — adres odbiorcy (jeśli jest to konto zewnętrzne, to transakcja przekaże wartość. Natomiast jeśli jest to konto kontraktowe to, transakcja wykona kod kontraktu) -- `signature` — identyfikator nadawcy. Jest on generowany, kiedy klucz prywatny nadawcy podpisuje transakcję i potwierdza, że nadawca autoryzował tę transakcję -- `nonce` — sekwencyjnie zwiększający się licznik, który wskazuje na numer transakcji konta -- `value` — kwota ETH do przesłania od nadawcy do odbiorcy (wyrażona w WEI, gdzie 1 ETH jest równy 1e+18wei) -- `input data` — opcjonalne pole do umieszczania dowolnych danych -- `gasLimit` — maksymalna ilość jednostek gazu, które mogą zostać zużyte w trakcie transakcji. [EVM](/developers/docs/evm/opcodes) określa, ile jednostek gazu wymaga każdy krok obliczeniowy -- `maxPriorityFeePerGas` — maksymalna cena zużytego gazu, która zostanie uwzględniona jako napiwek dla walidatora -- `maxFeePerGas` — maksymalna opłata za jednostkę gazu, jaką użytkownik jest w stanie zapłacić za transakcję (w tym `baseFeePerGas` i `maxPriorityFeePerGas`) +- `from` – adres nadawcy, który podpisze transakcję. To jest konto z zewnętrznym właścicielem, ponieważ inteligentne kontrakty nie mają możliwości wysyłania transakcji +- `to` – adres odbiorcy (jeśli jest to konto należące do zewnętrznego właściciela, transakcja przekaże wartość. Natomiast jeśli jest to konto kontraktowe to, transakcja wykona kod kontraktu) +- `signature` – identyfikator nadawcy. Jest on generowany, kiedy klucz prywatny nadawcy podpisuje transakcję i potwierdza, że nadawca autoryzował tę transakcję +- `nonce` – sekwencyjnie zwiększający się licznik, który wskazuje na numer transakcji z konta +- `value` – kwota ETH do przesłania od nadawcy do odbiorcy (wyrażona w WEI, gdzie 1 ETH jest równy 1e+18wei) +- `input data` – opcjonalne pole do umieszczania dowolnych danych +- `gasLimit` – maksymalna ilość jednostek gazu, które mogą zostać zużyte przez transakcję. [EVM](/developers/docs/evm/opcodes) określa, ile jednostek gazu wymaga każdy krok obliczeniowy +- `maxPriorityFeePerGas` – maksymalna cena zużytego gazu, która zostanie uwzględniona jako napiwek dla walidatora +- `maxFeePerGas` – maksymalna opłata za jednostkę gazu, jaką użytkownik jest w stanie zapłacić za transakcję (w tym `baseFeePerGas` i `maxPriorityFeePerGas`) -Gaz jest odniesieniem do obliczeń wymaganych do przetworzenia transakcji przez walidatora. Użytkownicy muszą zapłacić opłatę za to obliczenie. `gasLimit` i `maxPriorityFeePerGas` określają maksymalną opłatę transakcyjną płaconą walidatorowi. [Więcej na temat gazu](/developers/docs/gas/). +Gaz jest odniesieniem do obliczeń wymaganych do przetworzenia transakcji przez walidatora. Użytkownicy muszą zapłacić opłatę za to obliczenie. `gasLimit` i `maxPriorityFeePerGas` określają maksymalną opłatę transakcyjną płaconą walidatorowi. [Więcej o gazie](/developers/docs/gas/). Obiekt transakcji będzie wyglądał mniej więcej w ten sposób: @@ -99,22 +100,26 @@ Przykładowa odpowiedź: } ``` -- `raw` jest podpisaną transakcją w zakodowanym formacie [prefiksu o rekursywnej długości (RLP)](/developers/docs/data-structures-and-encoding/rlp) -- `tx` jest podpisaną transakcją w formacie JSON +- `raw` to podpisana transakcja w formie zakodowanej za pomocą [prefiksu o rekursywnej długości (RLP)](/developers/docs/data-structures-and-encoding/rlp) +- `tx` to podpisana transakcja w formacie JSON Dzięki hashowi podpisu można udowodnić kryptograficznie, że transakcja pochodzi od nadawcy i została przesłana do sieci. ### Pole danych {#the-data-field} -Zdecydowana większość transakcji uzyskuje dostęp do kontraktu z konta zewnętrznego. Większość kontraktów jest napisana w Solidity i interpretuje swoje pole danych zgodnie z [binarnym interfejsem aplikacji (ABI)](/glossary/#abi). +Zdecydowana większość transakcji uzyskuje dostęp do kontraktu z konta zewnętrznego. +Większość kontraktów jest napisana w Solidity i interpretuje swoje pole danych zgodnie z [binarnym interfejsem aplikacji (ABI)](/glossary/#abi). -Pierwsze cztery bajty określają, które funkcje mają zostać wywołane, korzystając z hasha nazwy funkcji i jej argumentów. Możesz czasami zidentyfikować funkcję z selektora korzystając z [tej bazy danych](https://www.4byte.directory/signatures/). +Pierwsze cztery bajty określają, które funkcje mają zostać wywołane, korzystając z hasha nazwy funkcji i jej argumentów. +Czasami można zidentyfikować funkcję na podstawie selektora, korzystając z [tej bazy danych](https://www.4byte.directory/signatures/). -Reszta danych wywoławczych to argumenty, [zakodowane zgodnie ze specyfikacją ABI](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). +Reszta danych wywołania to argumenty, [zakodowane zgodnie ze specyfikacją ABI](https://docs.soliditylang.org/en/latest/abi-spec.html#formal-specification-of-the-encoding). -Spójrzmy dla przykładu na [tę transakcję](https://etherscan.io/tx/0xd0dcbe007569fcfa1902dae0ab8b4e078efe42e231786312289b1eee5590f6a1). Kliknij **Click to show more**, aby zobaczyć dane wywoławcze. +Spójrzmy na przykład na [tę transakcję](https://etherscan.io/tx/0xd0dcbe007569fcfa1902dae0ab8b4e078efe42e231786312289b1eee5590f6a1). +Użyj **Click to see More**, aby zobaczyć dane wywołania. -Selektorem funkcji jest `0xa9059cbb`. Istnieje kilka [znanych funkcji z tym podpisem](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). W tym przypadku [kod źródłowy kontraktu](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code) został opublikowany na Etherscan, więc wiemy, że funkcją jest `transfer(address,uint256)`. +Selektor funkcji to `0xa9059cbb`. Istnieje kilka [znanych funkcji z tym podpisem](https://www.4byte.directory/signatures/?bytes4_signature=0xa9059cbb). +W tym przypadku [kod źródłowy kontraktu](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code) został przesłany do Etherscan, więc wiemy, że funkcja to `transfer(address,uint256)`. Reszta danych to: @@ -123,7 +128,9 @@ Reszta danych to: 000000000000000000000000000000000000000000000000000000003b0559f4 ``` -Zgodnie ze specyfikacją ABI wartości całkowite (takie jak adresy, które są 20-bajtowymi wartościami całkowitymi) wyświetlają się w ABI jako 32-bajtowe słowa, uzupełnione zerami z przodu. Wiemy więc, że adres `to` wygląda tak [`4f6742badb049791cd9a37ea913f2bac38d01279`](https://etherscan.io/address/0x4f6742badb049791cd9a37ea913f2bac38d01279). Wartość `value` wynosi 0x3b0559f4 = 990206452. +Zgodnie ze specyfikacją ABI wartości całkowite (takie jak adresy, które są 20-bajtowymi wartościami całkowitymi) wyświetlają się w ABI jako 32-bajtowe słowa, uzupełnione zerami z przodu. +Więc wiemy, że adres `to` to [`4f6742badb049791cd9a37ea913f2bac38d01279`](https://etherscan.io/address/0x4f6742badb049791cd9a37ea913f2bac38d01279). +Wartość `value` wynosi 0x3b0559f4 = 990206452. ## Rodzaje transakcji {#types-of-transactions} @@ -133,11 +140,11 @@ W Ethereum istnieje kilka różnych rodzajów transakcji: - Transakcje wdrożenia kontraktu: transakcja bez adresu „to”, w którym pole danych jest wykorzystywane dla kodu kontraktu. - Wykonanie kontraktu: transakcja, która wchodzi w interakcję z wdrożonym inteligentnym kontraktem. W tym przypadku adres „to” jest adresem inteligentnego kontraktu. -### Na temat gazu {#on-gas} +### O gazie {#on-gas} Jak już wspomniano, wykonanie transakcji kosztuje [gaz](/developers/docs/gas/). Proste transakcje transferu wymagają 21 000 jednostek gazu. -A więc, aby Bob mógł wysłać Alice 1 ETH przy `baseFeePerGas` wynoszącym 190 gwei i `maxPriorityFeePerGas` wynoszącym 10 gwei, Bob będzie musiał zapłacić następującą opłatę: +Aby Bob mógł wysłać Alice 1 ETH przy `baseFeePerGas` wynoszącym 190 gwei i `maxPriorityFeePerGas` wynoszącym 10 gwei, Bob będzie musiał zapłacić następującą opłatę: ``` (190 + 10) * 21 000 = 4 200 000 gwei @@ -145,16 +152,16 @@ A więc, aby Bob mógł wysłać Alice 1 ETH przy `baseFeePerGas` wynoszącym 19 0,0042 ETH ``` -Konto Boba zostanie obciążone kwotą **1,0042 ETH** (1 ETH dla Alice + 0,0042 ETH w opłatach za gaz) +Konto Boba zostanie obciążone kwotą **-1,0042 ETH** (1 ETH dla Alice + 0,0042 ETH w opłatach za gaz) -Konto Alicji zostanie zasilone kwotą **1,0 ETH** +Konto Alice zostanie zasilone kwotą **+1,0 ETH** -Podstawowa opłata wynosząca **0,00399 ETH** zostanie spalona +Podstawowa opłata zostanie spalona **-0,00399 ETH** -Walidator zatrzyma napiwek wynoszący **0,000210 ETH** +Walidator zatrzyma napiwek **+0,000210 ETH** - -![Schemat pokazujący, w jaki sposób zwracany jest niewykorzystany gaz](./gas-tx.png) _Schemat zaadaptowany z [Ilustracja Ethereum EVM](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ +![Diagram pokazujący, jak zwracany jest niewykorzystany gaz](./gas-tx.png) +_Diagram zaadaptowany z [Ethereum EVM illustrated](https://takenobu-hs.github.io/downloads/ethereum_evm_illustrated.pdf)_ Gaz niewykorzystany w transakcji jest zwracany na konto użytkownika. @@ -162,60 +169,65 @@ Gaz niewykorzystany w transakcji jest zwracany na konto użytkownika. Gaz jest wymagany dla każdej transakcji wiążącej się z inteligentnym kontraktem. -Inteligentne kontrakty mogą również zawierać funkcje takie jak [`view`](https://docs.soliditylang.org/en/latest/contracts.html#view-functions) czy [`pure`](https://docs.soliditylang.org/en/latest/contracts.html#pure-functions), które nie zmieniają stanu kontraktu. W związku z tym wywoływanie tych funkcji z konta zewnętrznego nie będzie wymagało żadnego gazu. Podstawowym wywołaniem RPC dla takiego scenariusza jest [`eth_call`](/developers/docs/apis/json-rpc#eth_call) +Inteligentne kontrakty mogą również zawierać funkcje znane jako [`view`](https://docs.soliditylang.org/en/latest/contracts.html#view-functions) lub [`pure`](https://docs.soliditylang.org/en/latest/contracts.html#pure-functions), które nie zmieniają stanu kontraktu. W związku z tym wywoływanie tych funkcji z konta zewnętrznego nie będzie wymagało żadnego gazu. Podstawowe wywołanie RPC dla tego scenariusza to [`eth_call`](/developers/docs/apis/json-rpc#eth_call). -W przeciwieństwie do dostępu przy użyciu `eth_call`, funkcje `view` czy `pure` są również często wywoływane wewnętrznie (tj. z samego kontraktu lub od innego kontraktu) co już wymaga gazu. +W przeciwieństwie do dostępu przy użyciu `eth_call`, funkcje `view` lub `pure` są również często wywoływane wewnętrznie (tj. z samego kontraktu lub z innego kontraktu), co już kosztuje gaz. -## Cykl życiowy transakcji {#transaction-lifecycle} +## Cykl życia transakcji {#transaction-lifecycle} Po przesłaniu transakcji dzieją się następujące wydarzenia: -1. Hash transakcji zostaje kryptograficznie wygenerowany: `0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017` +1. Hasz transakcji jest generowany kryptograficznie: + `0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017` 2. Transakcja zostaje następnie rozgłoszona do sieci i dodawana do puli transakcji składającej się z wszystkich innych oczekujących transakcji w sieci. 3. Walidator musi wybrać Twoją transakcję i uwzględnić ją w bloku, aby ją zweryfikować transakcję i uznać ją za „udaną”. -4. W miarę upływu czasu blok zawierający Twoją transakcję zostanie zaktualizowany do kategorii „uzasadniony”, a następnie „sfinalizowany”. Aktualizacje te znacznie zwiększają pewność, że Twoja transakcja zakończyła się sukcesem i nie będzie można jej zmienić. Po „sfinalizowaniu” bloku mógłby on zostać zmieniony tylko przez atak na poziomie sieci, który kosztowałby miliardy dolarów. +4. W miarę upływu czasu blok zawierający Twoją transakcję zostanie zaktualizowany do kategorii „uzasadniony”, a następnie „sfinalizowany”. Te aktualizacje dają znacznie + większą pewność, że Twoja transakcja zakończyła się sukcesem i nigdy nie zostanie zmieniona. Gdy blok zostanie „sfinalizowany”, może zostać zmieniony + jedynie przez atak na poziomie sieci, który kosztowałby wiele miliardów dolarów. -## Demo wizualne {#a-visual-demo} +## Demonstracja wizualna {#a-visual-demo} Zobacz, jak Austin opowiada o transakcjach, gazie i kopaniu. -## Typed Transaction Envelope {#typed-transaction-envelope} +## Typowana koperta transakcji {#typed-transaction-envelope} -Ethereum pierwotnie miało jeden format transakcji. Każda transakcja zawierała nonce, cenę gazu, limit gazu, adres docelowy, wartość, dane, v, r oraz s. Pola te są [zakodowane w RLP](/developers/docs/data-structures-and-encoding/rlp/), aby wyglądały mniej więcej tak: +Ethereum pierwotnie miało jeden format transakcji. Każda transakcja zawierała nonce, cenę gazu, limit gazu, adres docelowy, wartość, dane, v, r oraz s. Pola te są [kodowane za pomocą RLP](/developers/docs/data-structures-and-encoding/rlp/), aby wyglądały mniej więcej tak: `RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` -Ethereum wyewoluowało i wspiera wiele rodzajów transakcji, zezwalając na wdrażanie nowych funkcji takich jak listy dostępu i [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), bez wpływania na starsze formaty transakcji. +Ethereum ewoluowało, aby obsługiwać wiele typów transakcji, co pozwala na wdrażanie nowych funkcji, takich jak listy dostępu i [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), bez wpływu na starsze formaty transakcji. -[EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) jest tym, co umożliwia takie zachowanie. Transakcję są interpretowane jako: +[EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) to propozycja, która pozwala na takie zachowanie. Transakcję są interpretowane jako: `TransactionType || TransactionPayload` Gdzie pola są definiowane jako: -- `TransactionType` — liczba z zakresu od 0 do 0x7f, zapewniająca w sumie 128 możliwych rodzajów transakcji. -- `TransactionPayload` — dowolna tablica bajtów zdefiniowana przez rodzaj transakcji. +- `TransactionType` – liczba z zakresu od 0 do 0x7f, co daje w sumie 128 możliwych typów transakcji. +- `TransactionPayload` – dowolna tablica bajtów zdefiniowana przez typ transakcji. -Na podstawie wartości `TransactionType`, transakcje mogą być definiowane jako: +Na podstawie wartości `TransactionType` transakcję można sklasyfikować jako: -1. **Transakcje typu 0 (starsze):** oryginalny format transakcji używany od samego początku Ethereum. Nie zawierają funkcji z [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), takich jak dynamiczne obliczenia opłat za gaz czy list dostępu do inteligentnych kontraktów. Starsze transakcje nie mają określonego prefiksu wskazującego na ich typ w ich serializowanej formie, począwszy od bajtu `0xf8` przy użyciu kodowania [prefiksu o rekursywnej długości (RLP)](/developers/docs/data-structures-and-encoding/rlp). Wartość TransactionType dla tych transakcji wynosi `0x0`. +1. **Transakcje typu 0 (starsze):** oryginalny format transakcji używany od samego początku Ethereum. Nie obejmują one funkcji z [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), takich jak dynamiczne obliczanie opłat za gaz czy listy dostępu do inteligentnych kontraktów. Starsze transakcje nie mają określonego prefiksu wskazującego na ich typ w postaci serializowanej, zaczynając od bajtu `0xf8` przy użyciu kodowania [prefiksu o rekursywnej długości (RLP)](/developers/docs/data-structures-and-encoding/rlp). Wartość TransactionType dla tych transakcji wynosi `0x0`. -2. **Transakcje typu 1:** wprowadzone w [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) jako część [uaktualnienia Berlin](/ethereum-forks/#berlin) sieci Ethereum transakcje te zawierają parametr `accessList`. Ta lista określa adresy i klucze przechowywania, do których transakcja oczekuje dostępu, potencjalnie pomagając zmniejszyć koszty [gazu](/developers/docs/gas/) złożonych transakcji wykorzystujących inteligentne kontrakty. Zmiany rynku opłat EIP-1559 nie są uwzględnione w transakcjach typu 1. Transakcje typu 1 zawierają również parametr `yParity`, który może wynosić zarówno `0x0` jak i `0x1`, wskazując na parzystość wartości y podpisu secp256k1. Są identyfikowane przez początkowy bajt `0x01`, a ich wartość TransactionType wynosi `0x1`. +2. **Transakcje typu 1:** wprowadzone w [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) jako część [aktualizacji Berlin](/ethereum-forks/#berlin) sieci Ethereum; transakcje te zawierają parametr `accessList`. Ta lista określa adresy i klucze przechowywania, do których transakcja oczekuje dostępu, potencjalnie pomagając zmniejszyć koszty [gazu](/developers/docs/gas/) złożonych transakcji wykorzystujących inteligentne kontrakty. Zmiany rynku opłat EIP-1559 nie są uwzględnione w transakcjach typu 1. Transakcje typu 1 zawierają również parametr `yParity`, który może wynosić `0x0` lub `0x1`, wskazując parzystość wartości y podpisu secp256k1. Są one identyfikowane przez początkowy bajt `0x01`, a ich wartość TransactionType wynosi `0x1`. -3. **Transakcje typu 2**, powszechnie określane jako transakcje EIP-1559, to transakcje wprowadzone w [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), w ramach [uaktualnienia London](/ethereum-forks/#london) sieci Ethereum. Stały się one standardowym rodzajem transakcji w sieci Ethereum. Transakcje te wprowadziły nowy mechanizm rynku opłat, który poprawia przewidywalność, rozdzielając opłaty transakcyjne na opłatę podstawową oraz opłatę priorytetową. Zaczynają się bajtem `0x02` i zawierają takie pola jak `maxPriorityFeePerGas` i `maxFeePerGas`. Transakcje typu 2 są teraz domyślnymi, ze względu na ich elastyczność i wydajność, są szczególnie preferowanie podczas okresów dużego przeciążenia sieci ze względu na zdolność pomagania użytkownikom w zarządzaniu opłatami transakcyjnymi w bardziej przewidywalny sposób. Wartość TransactionType dla tych transakcji wynosi `0x2`. +3. **Transakcje typu 2**, powszechnie określane jako transakcje EIP-1559, to transakcje wprowadzone w [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) w ramach [aktualizacji London](/ethereum-forks/#london) sieci Ethereum. Stały się one standardowym rodzajem transakcji w sieci Ethereum. Transakcje te wprowadziły nowy mechanizm rynku opłat, który poprawia przewidywalność, rozdzielając opłaty transakcyjne na opłatę podstawową oraz opłatę priorytetową. Zaczynają się od bajtu `0x02` i zawierają pola takie jak `maxPriorityFeePerGas` i `maxFeePerGas`. Transakcje typu 2 są teraz domyślnymi, ze względu na ich elastyczność i wydajność, są szczególnie preferowanie podczas okresów dużego przeciążenia sieci ze względu na zdolność pomagania użytkownikom w zarządzaniu opłatami transakcyjnymi w bardziej przewidywalny sposób. Wartość TransactionType dla tych transakcji wynosi `0x2`. +4. **Transakcje typu 3 (Blob)** zostały wprowadzone w [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) jako część [aktualizacji Dencun](/ethereum-forks/#dencun) sieci Ethereum. Te transakcje zostały zaprojektowane w celu wydajniejszej obsługi danych "blob" (duże obiekty binarne), co przynosi szczególną korzyść drugim warstwom poprzez zapewnienie im możliwości przesyłania danych do głównej sieci Ethereum niższym kosztem. Transakcje typu blob zawierają dodatkowe pola, takie jak `blobVersionedHashes`, `maxFeePerBlobGas` i `blobGasPrice`. Zaczynają się od bajtu `0x03`, a ich wartość TransactionType wynosi `0x3`. Transakcje blob wprowadzają znaczący rozwój do kwestii dostępności danych i możliwości skalowania Ethereum. +5. **Transakcje typu 4** zostały wprowadzone w [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) jako część [aktualizacji Pectra](/roadmap/pectra/) Ethereum. Transakcje te są zaprojektowane tak, aby były kompatybilne w przód z abstrakcją konta. Pozwalają one kontom EOA na tymczasowe zachowywanie się jak konta inteligentnych kontraktów, bez naruszania ich pierwotnej funkcjonalności. Zawierają one parametr `authorization_list`, który określa inteligentny kontrakt, któremu EOA deleguje swoje uprawnienia. Po transakcji pole kodu EOA będzie zawierało adres delegowanego inteligentnego kontraktu. -## Dodatkowo przeczytaj {#further-reading} +## Dalsza lektura {#further-reading} -- [EIP-2718: Typed Transaction Envelope](https://eips.ethereum.org/EIPS/eip-2718) +- [EIP-2718: Typowana koperta transakcji](https://eips.ethereum.org/EIPS/eip-2718) -_Znasz jakieś zasoby społeczności, które Ci pomogły? Wyedytuj tę stronę i dodaj je!_ +_Znasz jakieś zasoby społeczności, które Ci pomogły? Edytuj tę stronę i dodaj je!_ ## Powiązane tematy {#related-topics} - [Konta](/developers/docs/accounts/) -- [Maszyna wirtualna Ethereum (EVM)](/developers/docs/evm/) -- [Paliwo](/developers/docs/gas/) +- [Wirtualna Maszyna Ethereum (EVM)](/developers/docs/evm/) +- [Gaz](/developers/docs/gas/) diff --git a/public/content/translations/pl/developers/docs/web2-vs-web3/index.md b/public/content/translations/pl/developers/docs/web2-vs-web3/index.md index a355d4260a7..7264144840b 100644 --- a/public/content/translations/pl/developers/docs/web2-vs-web3/index.md +++ b/public/content/translations/pl/developers/docs/web2-vs-web3/index.md @@ -1,6 +1,6 @@ --- -title: Web2 vs Web3 -description: +title: "Web2 w porównaniu z Web3" +description: "Porównaj scentralizowane usługi Web2 ze zdecentralizowanymi aplikacjami Web3 opartymi na technologii blockchain Ethereum." lang: pl --- @@ -27,12 +27,12 @@ Wielu deweloperów Web3 zdecydowało się budować zdecentralizowane aplikacje z To nie oznacza, że wszystkie usługi należy przekształcić w zdecentralizowane aplikacje. Te przykłady ilustrują główne różnice między usługami web2 i web3. -## Ograniczenia sieci Web3 {#web3-limitations} +## Ograniczenia Web3 {#web3-limitations} Web3 ma teraz pewne ograniczenia: - Skalowalność — transakcje są wolniejsze w web3, ponieważ są zdecentralizowane. Zmiany stanu, jak płatności, muszą być przetwarzane przez węzeł i propagowane w całej sieci. -- UX — interakcja z aplikacjami web3 może wymagać dodatkowych kroków, oprogramowania i edukacji. Może to stanowić przeszkodę w akceptacji. +- UX – interakcja z aplikacjami web3 może wymagać dodatkowych kroków, oprogramowania i edukacji. Może to stanowić przeszkodę w akceptacji. - Dostępność — brak integracji z nowoczesnymi przeglądarkami internetowymi sprawia, że web3 jest mniej dostępna dla większości użytkowników. - Koszt — większość udanych zdecentralizowanych aplikacji umieszcza bardzo małe części swojego kodu w blockchainie, ponieważ jest to kosztowne. @@ -40,23 +40,23 @@ Web3 ma teraz pewne ograniczenia: W tabeli poniżej wymieniono pewne zalety i wady scentralizowanych i zdecentralizowanych sieci cyfrowych. -| Systemy scentralizowane | Systemy zdecentralizowane | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Niska średnica sieci (wszyscy uczestnicy są podłączeni do organu centralnego); informacja jest szybko rozsyłana, ponieważ rozsyłanie jest prowadzone przez organ centralny z wieloma zasobami obliczeniowymi. | Najodleglejsi uczestnicy sieci mogą być znacznie odsunięci od siebie. Informacje nadawane z jednej strony sieci mogą potrzebować dużo czasu, aby dotrzeć do drugiej strony. | -| Zazwyczaj wyższa wydajność (większa przepustowość, mniej całkowitych kosztów zasobów obliczeniowych) i łatwiejsze do wdrożenia. | Zwykle niższa wydajność (mniejsza przepustowość, więcej całkowitych zasobów obliczeniowych wydatkowanych) i bardziej skomplikowana do wdrożenia. | -| W przypadku sprzecznych danych rozwiązanie jest jasne i łatwe: ostatecznym źródłem prawdy jest organ centralny. | Protokół (często złożony) jest potrzebny do rozstrzygania sporów, jeśli współuczestnicy zgłaszają sprzeczne roszczenia co do stanu danych, które mają być zsynchronizowane. | -| Pojedynczy punkt awarii: złośliwe podmioty mogą być w stanie zniszczyć sieć poprzez atak na organ centralny. | Brak pojedynczego punktu awarii: sieć może nadal funkcjonować, nawet jeśli duża część uczestników zostanie zaatakowana/wyeliminowana. | +| Systemy scentralizowane | Systemy zdecentralizowane | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Niska średnica sieci (wszyscy uczestnicy są podłączeni do organu centralnego); informacja jest szybko rozsyłana, ponieważ rozsyłanie jest prowadzone przez organ centralny z wieloma zasobami obliczeniowymi. | Najodleglejsi uczestnicy sieci mogą być znacznie odsunięci od siebie. Informacje nadawane z jednej strony sieci mogą potrzebować dużo czasu, aby dotrzeć do drugiej strony. | +| Zazwyczaj wyższa wydajność (większa przepustowość, mniej całkowitych kosztów zasobów obliczeniowych) i łatwiejsze do wdrożenia. | Zwykle niższa wydajność (mniejsza przepustowość, więcej całkowitych zasobów obliczeniowych wydatkowanych) i bardziej skomplikowana do wdrożenia. | +| W przypadku sprzecznych danych rozwiązanie jest jasne i łatwe: ostatecznym źródłem prawdy jest organ centralny. | Protokół (często złożony) jest potrzebny do rozstrzygania sporów, jeśli współuczestnicy zgłaszają sprzeczne roszczenia co do stanu danych, które mają być zsynchronizowane. | +| Pojedynczy punkt awarii: złośliwe podmioty mogą być w stanie zniszczyć sieć poprzez atak na organ centralny. | Brak pojedynczego punktu awarii: sieć może nadal funkcjonować, nawet jeśli duża część uczestników zostanie zaatakowana/wyeliminowana. | | Koordynacja między uczestnikami sieci jest znacznie łatwiejsza i jest prowadzona przez organ centralny. Organ centralny może zmusić uczestników sieci do przyjmowania aktualizacji, aktualizacji protokołów itp., przy bardzo niewielkim sprzeciwie. | Koordynacja jest często trudna, ponieważ żaden przedstawiciel nie ma ostatecznego głosu w decyzjach dotyczących sieci, modernizacji protokołów itp. W najgorszym przypadku sieć jest podatna na rozpad, gdy dochodzi do nieporozumień dotyczących zmian w protokole. | -| Organ centralny może cenzurować dane, potencjalnie oddzielić części sieci od interakcji z resztą sieci. | Cenzura jest o wiele trudniejsza, ponieważ informacje mogą rozprzestrzeniać się w sieci na wiele sposobów. | -| Udział w sieci jest kontrolowany przez organ centralny. | Każdy może uczestniczyć w sieci; nie ma „stróżów” Najlepiej byłoby, gdyby koszty uczestnictwa były bardzo niskie. | +| Organ centralny może cenzurować dane, potencjalnie oddzielić części sieci od interakcji z resztą sieci. | Cenzura jest o wiele trudniejsza, ponieważ informacje mogą rozprzestrzeniać się w sieci na wiele sposobów. | +| Udział w sieci jest kontrolowany przez organ centralny. | Każdy może uczestniczyć w sieci; nie ma „stróżów” Najlepiej byłoby, gdyby koszty uczestnictwa były bardzo niskie. | Zauważ, że są to ogólne wzorce, które mogą nie występować w każdej sieci. Ponadto w rzeczywistości stopień, w jakim sieć jest scentralizowana/zdecentralizowana, leży w spektrum; żadna sieć nie jest całkowicie scentralizowana lub całkowicie zdecentralizowana. ## Dalsza lektura {#further-reading} -- [Czym jest Web3](/web3/) — _ethereum.org_ -- [Architektura aplikacji Web 3.0](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application) — _Preethi Kasireddy_ -- [Znaczenie decentralizacji](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274) _6 lutego 2017 r. — Vitalik Buterin_ -- [Dlaczego decentralizacja ma znaczenie](https://medium.com/s/story/why-decentralization-matters-5e3f79f7638e) _18 lutego 2018 r. — Chris Dixon_ -- [Czym jest Web 3.0 i dlaczego ma znaczenie](https://medium.com/fabric-ventures/what-is-web-3-0-why-it-matters-934eb07f3d2b) _31 grudnia 2019 r. — Max Mersch and Richard Muirhead_ -- [Dlaczego potrzebujemy Web 3.0](https://medium.com/@gavofyork/why-we-need-web-3-0-5da4f2bf95ab) _12 września 2018 r. — Gavin Wood_ +- [Czym jest Web3?](/web3/) - _ethereum.org_ +- [Architektura aplikacji Web 3.0](https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application) – _Preethi Kasireddy_ +- [Znaczenie decentralizacji](https://medium.com/@VitalikButerin/the-meaning-of-decentralization-a0c92b76a274) _6 lutego 2017 - Vitalik Buterin_ +- [Dlaczego decentralizacja ma znaczenie](https://onezero.medium.com/why-decentralization-matters-5e3f79f7638e) _18 lutego 2018 - Chris Dixon_ +- [Czym jest Web 3.0 i dlaczego ma znaczenie](https://medium.com/fabric-ventures/what-is-web-3-0-why-it-matters-934eb07f3d2b) _31 grudnia 2019 - Max Mersch i Richard Muirhead_ +- [Dlaczego potrzebujemy Web 3.0](https://gavofyork.medium.com/why-we-need-web-3-0-5da4f2bf95ab) _12 września 2018 - Gavin Wood_ diff --git a/public/content/translations/pl/developers/docs/wrapped-eth/index.md b/public/content/translations/pl/developers/docs/wrapped-eth/index.md index 3c433d81a87..ed1708af44e 100644 --- a/public/content/translations/pl/developers/docs/wrapped-eth/index.md +++ b/public/content/translations/pl/developers/docs/wrapped-eth/index.md @@ -1,6 +1,6 @@ --- title: Czym jest Wrapped Ether (WETH) -description: Wprowadzenie do Wrapped Ether (WETH) — kompatybilny z ERC-20 owijacz (wrapper) dla etheru (ETH). +description: "Wprowadzenie do Wrapped Ether (WETH) — kompatybilny z ERC-20 owijacz (wrapper) dla etheru (ETH)." lang: pl --- @@ -35,19 +35,16 @@ Możesz odwinąć WETH do ETH używając inteligentnego kontraktu WETH. Możesz Płacisz opłaty za gaz za owinięcie lub rozwinięcie ETH przy użyciu kontraktu WETH. - WETH generalnie uważa się za bezpieczne, ponieważ jest oparte na prostym i przetestowanym inteligentnym kontrakcie. Kontrakt WETH został również formalnie zweryfikowany, co jest największym standardem bezpieczeństwa dla inteligentnych kontraktów na Ethereum. - Oprócz [kanonicznej implementacji WETH](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) opisanej na tej stronie istnieją również jej inne warianty. Mogą to być własne tokeny stworzone przez twórców aplikacji lub wersje wyemitowane na innych blockchainach i mogą one się inaczej zachowywać lub mieć różne zabezpieczenia. **Zawsze sprawdzaj dokładnie informacje o tokenie, aby wiedzieć, z jaką implementacją WETH masz do czynienia.** - @@ -55,7 +52,6 @@ Oprócz [kanonicznej implementacji WETH](https://etherscan.io/token/0xc02aaa39b2 - [Sieć główna Ethereum](https://etherscan.io/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) - [Arbitrum](https://arbiscan.io/token/0x82af49447d8a07e3bd95bd0d56f35241523fbab1) - [Optimism](https://optimistic.etherscan.io/token/0x4200000000000000000000000000000000000006) - ## Further reading {#further-reading} diff --git a/public/content/translations/pl/developers/tutorials/Waffle-hello-world-with-buidler-tutorial/index.md b/public/content/translations/pl/developers/tutorials/Waffle-hello-world-with-buidler-tutorial/index.md index 46d9bcb7696..016814fb6a6 100644 --- a/public/content/translations/pl/developers/tutorials/Waffle-hello-world-with-buidler-tutorial/index.md +++ b/public/content/translations/pl/developers/tutorials/Waffle-hello-world-with-buidler-tutorial/index.md @@ -1,6 +1,6 @@ --- title: "Samouczek Waffle „powiedz Hello World” za pomocą hardhat i ethers" -description: Stwórz swój pierwszy projekt Waffle za pomocą hardhat i ethers.js +description: "Stwórz swój pierwszy projekt Waffle za pomocą hardhat i ethers.js" author: "MiZiet" tags: - "waffle" diff --git a/public/content/translations/pl/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md b/public/content/translations/pl/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md new file mode 100644 index 00000000000..34fc6d29707 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/a-developers-guide-to-ethereum-part-one/index.md @@ -0,0 +1,300 @@ +--- +title: "Wprowadzenie do Ethereum dla programistów Pythona, część 1" +description: "Wprowadzenie do tworzenia aplikacji na Ethereum, szczególnie przydatne dla osób znających język programowania Python" +author: Marc Garreau +lang: pl +tags: [ "python", "web3.py" ] +skill: beginner +published: 2020-09-08 +source: Snake charmers +sourceUrl: https://snakecharmers.ethereum.org/a-developers-guide-to-ethereum-pt-1/ +--- + +Słyszałeś więc o tym całym Ethereum i jesteś gotów, by zanurzyć się w króliczej norze? Ten post szybko omówi podstawy technologii blockchain, a następnie pozwoli Ci na interakcję z symulowanym węzłem Ethereum – odczytywanie danych bloków, sprawdzanie sald kont i wysyłanie transakcji. Po drodze podkreślimy różnice między tradycyjnymi sposobami tworzenia aplikacji a tym nowym, zdecentralizowanym paradygmatem. + +## Wymagania (nieobowiązkowe) {#soft-prerequisites} + +Ten post ma być przystępny dla szerokiego grona deweloperów. Użyte zostaną [narzędzia Pythona](/developers/docs/programming-languages/python/), ale są one tylko nośnikiem dla idei – nie ma problemu, jeśli nie jesteś deweloperem Pythona. Poczynię jednak kilka założeń na temat tego, co już wiesz, abyśmy mogli szybko przejść do fragmentów specyficznych dla Ethereum. + +Założenia: + +- Potrafisz poruszać się w terminalu, +- Napisałeś kilka linijek kodu w Pythonie, +- Masz zainstalowaną wersję Pythona 3.6 lub nowszą na swoim komputerze (zdecydowanie zalecane jest użycie [środowiska wirtualnego](https://realpython.com/effective-python-environment/#virtual-environments)), oraz +- używałeś `pip`, instalatora pakietów Pythona. + Jeszcze raz, jeśli któreś z tych założeń jest nieprawdziwe lub nie planujesz odtwarzać kodu z tego artykułu, prawdopodobnie i tak bez problemu sobie poradzisz. + +## Blockchain w skrócie {#blockchains-briefly} + +Istnieje wiele sposobów na opisanie Ethereum, ale w jego sercu leży blockchain. Blockchainy składają się z serii bloków, więc zacznijmy od tego. W najprostszych słowach, każdy blok w blockchainie Ethereum to tylko trochę metadanych i lista transakcji. W formacie JSON wygląda to mniej więcej tak: + +```json +{ + "number": 1234567, + "hash": "0xabc123...", + "parentHash": "0xdef456...", + ..., + "transactions": [...] +} +``` + +Każdy [blok](/developers/docs/blocks/) ma odniesienie do bloku, który był przed nim; `parentHash` to po prostu hasz poprzedniego bloku. + +Uwaga: Ethereum regularnie używa funkcji haszujących do tworzenia wartości o stałym rozmiarze („haszy”). Hasze odgrywają ważną rolę w Ethereum, ale na razie możesz bezpiecznie myśleć o nich jako o unikalnych identyfikatorach. + +![Diagram przedstawiający blockchain, w tym dane wewnątrz każdego bloku](./blockchain-diagram.png) + +_Blockchain to w zasadzie lista połączona; każdy blok ma odniesienie do poprzedniego bloku._ + +Ta struktura danych nie jest niczym nowym, ale zasady (tj. protokoły peer-to-peer), które rządzą siecią, już tak. Nie ma centralnego organu; sieć peerów musi współpracować, aby utrzymać sieć i konkurować, aby zdecydować, które transakcje zostaną uwzględnione w następnym bloku. Więc kiedy chcesz wysłać pieniądze do znajomego, musisz rozgłosić tę transakcję w sieci, a następnie poczekać, aż zostanie ona uwzględniona w nadchodzącym bloku. + +Jedynym sposobem, w jaki blockchain może zweryfikować, że pieniądze zostały faktycznie wysłane od jednego użytkownika do drugiego, jest użycie waluty natywnej dla tego blockchaina (tj. stworzonej i zarządzanej przez niego). W Ethereum waluta ta nazywa się ether, a blockchain Ethereum zawiera jedyny oficjalny rejestr sald kont. + +## Nowy paradygmat {#a-new-paradigm} + +Ten nowy zdecentralizowany stos technologiczny dał początek nowym narzędziom deweloperskim. Takie narzędzia istnieją w wielu językach programowania, ale my będziemy patrzeć przez pryzmat Pythona. Powtórzmy: nawet jeśli Python nie jest Twoim ulubionym językiem, nie powinno być problemu ze śledzeniem treści. + +Deweloperzy Pythona, którzy chcą wejść w interakcję z Ethereum, prawdopodobnie sięgną po [Web3.py](https://web3py.readthedocs.io/). Web3.py to biblioteka, która znacznie upraszcza sposób łączenia się z węzłem Ethereum, a następnie wysyłania i odbierania z niego danych. + +Uwaga: „Węzeł Ethereum” i „klient Ethereum” są używane zamiennie. W obu przypadkach odnosi się to do oprogramowania, które uruchamia uczestnik sieci Ethereum. To oprogramowanie może odczytywać dane bloków, otrzymywać aktualizacje, gdy nowe bloki są dodawane do łańcucha, rozgłaszać nowe transakcje i nie tylko. Technicznie rzecz biorąc, klient to oprogramowanie, a węzeł to komputer, na którym działa to oprogramowanie. + +[Klienci Ethereum](/developers/docs/nodes-and-clients/) mogą być skonfigurowani tak, aby byli osiągalni przez [IPC](https://wikipedia.org/wiki/Inter-process_communication), HTTP lub Websockets, więc Web3.py będzie musiało odzwierciedlać tę konfigurację. Web3.py odnosi się do tych opcji połączenia jako **dostawcy** (providers). Będziesz musiał wybrać jednego z trzech dostawców, aby połączyć instancję Web3.py ze swoim węzłem. + +![Diagram pokazujący, jak web3.py używa IPC do połączenia Twojej aplikacji z węzłem Ethereum](./web3py-and-nodes.png) + +_Skonfiguruj węzeł Ethereum i Web3.py do komunikacji za pomocą tego samego protokołu, np. IPC na tym diagramie._ + +Gdy Web3.py jest poprawnie skonfigurowane, możesz zacząć wchodzić w interakcję z blockchainem. Oto kilka przykładów użycia Web3.py jako zapowiedź tego, co nadchodzi: + +```python +# odczytaj dane bloku: +w3.eth.get_block('latest') + +# wyślij transakcję: +w3.eth.send_transaction({'from': ..., 'to': ..., 'value': ...}) +``` + +## Instalacja {#installation} + +W tym przewodniku będziemy pracować tylko w interpreterze Pythona. Nie będziemy tworzyć żadnych katalogów, plików, klas ani funkcji. + +Uwaga: w poniższych przykładach polecenia, które zaczynają się od `$`, są przeznaczone do uruchomienia w terminalu. (Nie wpisuj znaku `$`, on tylko oznacza początek linii.) + +Najpierw zainstaluj [IPython](https://ipython.org/), aby uzyskać przyjazne dla użytkownika środowisko do eksploracji. IPython oferuje między innymi uzupełnianie tabulatorem, co znacznie ułatwia sprawdzenie, co jest możliwe w Web3.py. + +```bash +pip install ipython +``` + +Web3.py jest publikowane pod nazwą `web3`. Zainstaluj go w ten sposób: + +```bash +pip install web3 +``` + +Jeszcze jedno – później będziemy symulować blockchain, co wymaga kilku dodatkowych zależności. Możesz je zainstalować za pomocą: + +```bash +pip install 'web3[tester]' +``` + +Wszystko gotowe! + +Uwaga: Pakiet `web3[tester]` działa z Pythonem do wersji 3.10.xx + +## Uruchomienie piaskownicy {#spin-up-a-sandbox} + +Otwórz nowe środowisko Pythona, uruchamiając `ipython` w terminalu. Jest to porównywalne z uruchomieniem `python`, ale z większą liczbą dodatków. + +```bash +ipython +``` + +Spowoduje to wydrukowanie informacji o wersjach Pythona i IPythona, których używasz, a następnie powinieneś zobaczyć monit oczekujący na dane wejściowe: + +```python +In [1]: +``` + +Patrzysz teraz na interaktywną powłokę Pythona. Zasadniczo jest to piaskownica do zabawy. Jeśli dotarłeś tak daleko, czas zaimportować Web3.py: + +```python +In [1]: from web3 import Web3 +``` + +## Wprowadzenie do modułu Web3 {#introducing-the-web3-module} + +Oprócz tego, że jest bramą do Ethereum, moduł [Web3](https://web3py.readthedocs.io/en/stable/overview.html#base-api) oferuje kilka wygodnych funkcji. Przyjrzyjmy się kilku z nich. + +W aplikacji Ethereum często będziesz musiał konwertować nominały walut. Moduł Web3 udostępnia do tego celu kilka metod pomocniczych: [from_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.from_wei) i [to_wei](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.to_wei). + + +Uwaga: Komputery są notorycznie złe w obsłudze arytmetyki dziesiętnej. Aby obejść ten problem, deweloperzy często przechowują kwoty w dolarach jako centy. Na przykład przedmiot o cenie 5,99 USD może być przechowywany w bazie danych jako 599. + +Podobny wzorzec jest używany podczas obsługi transakcji w etherze. Jednak zamiast dwóch miejsc po przecinku, ether ma ich 18! Najmniejszy nominał etheru nazywa się wei, więc jest to wartość określana podczas wysyłania transakcji. + +1 ether = 1000000000000000000 wei + +1 wei = 0,000000000000000001 ether + + + +Spróbuj przekonwertować niektóre wartości na wei i z wei. Zauważ, że [istnieją nazwy dla wielu nominałów](https://web3py.readthedocs.io/en/stable/troubleshooting.html#how-do-i-convert-currency-denominations) pomiędzy etherem a wei. Jednym z bardziej znanych jest **gwei**, ponieważ często w ten sposób przedstawiane są opłaty transakcyjne. + +```python +In [2]: Web3.to_wei(1, 'ether') +Out[2]: 1000000000000000000 + +In [3]: Web3.from_wei(500000000, 'gwei') +Out[3]: Decimal('0.5') +``` + +Inne metody narzędziowe w module Web3 obejmują konwertery formatu danych (np. [`toHex`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.toHex)), pomocników adresów (np. [`isAddress`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.isAddress)) i funkcje haszujące (np. [`keccak`](https://web3py.readthedocs.io/en/stable/web3.main.html#web3.Web3.keccak)). Wiele z nich zostanie omówionych w dalszej części serii. Aby wyświetlić wszystkie dostępne metody i właściwości, skorzystaj z autouzupełniania w IPython, wpisując `Web3`. i naciskając dwukrotnie klawisz tabulacji po kropce. + +## Porozmawiaj z łańcuchem {#talk-to-the-chain} + +Metody pomocnicze są świetne, ale przejdźmy do blockchaina. Następnym krokiem jest skonfigurowanie Web3.py do komunikacji z węzłem Ethereum. Tutaj mamy możliwość korzystania z dostawców IPC, HTTP lub Websocket. + +Nie będziemy podążać tą ścieżką, ale przykład kompletnego przepływu pracy z użyciem dostawcy HTTP może wyglądać mniej więcej tak: + +- Pobierz węzeł Ethereum, np. [Geth](https://geth.ethereum.org/). +- Uruchom Geth w jednym oknie terminala i poczekaj, aż zsynchronizuje się z siecią. Domyślny port HTTP to `8545`, ale można go skonfigurować. +- Poinstruuj Web3.py, aby połączyło się z węzłem przez HTTP na `localhost:8545`. + `w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))` +- Użyj instancji `w3`, aby wejść w interakcję z węzłem. + +Chociaż jest to jeden z „prawdziwych” sposobów, proces synchronizacji trwa godzinami i jest niepotrzebny, jeśli chcesz tylko środowiska programistycznego. Web3.py udostępnia w tym celu czwartego dostawcę, **EthereumTesterProvider**. Ten dostawca testowy łączy się z symulowanym węzłem Ethereum z luźnymi uprawnieniami i fałszywą walutą do zabawy. + +![Diagram pokazujący EthereumTesterProvider łączący Twoją aplikację web3.py z symulowanym węzłem Ethereum](./ethereumtesterprovider.png) + +_EthereumTesterProvider łączy się z symulowanym węzłem i jest przydatny do szybkich środowisk programistycznych._ + +Ten symulowany węzeł nazywa się [eth-tester](https://github.com/ethereum/eth-tester) i zainstalowaliśmy go jako część polecenia `pip install web3[tester]`. Konfiguracja Web3.py do używania tego dostawcy testowego jest tak prosta, jak: + +```python +In [4]: w3 = Web3(Web3.EthereumTesterProvider()) +``` + +Teraz jesteś gotowy, aby surfować po łańcuchu! To nie jest coś, co ludzie mówią. Właśnie to wymyśliłem. Zróbmy szybką wycieczkę. + +## Szybka wycieczka {#the-quick-tour} + +Po pierwsze, sprawdzenie poprawności: + +```python +In [5]: w3.is_connected() +Out[5]: True +``` + +Ponieważ używamy dostawcy testowego, nie jest to bardzo wartościowy test, ale jeśli się nie powiedzie, prawdopodobnie wpisałeś coś źle podczas tworzenia instancji zmiennej `w3`. Sprawdź dwukrotnie, czy uwzględniłeś wewnętrzne nawiasy, tj. `Web3.EthereumTesterProvider()`. + +## Przystanek wycieczki nr 1: [konta](/developers/docs/accounts/) {#tour-stop-1-accounts} + +Dla wygody dostawca testowy stworzył kilka kont i załadował je testowym etherem. + +Najpierw zobaczmy listę tych kont: + +```python +In [6]: w3.eth.accounts +Out[6]: ['0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + '0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69', ...] +``` + +Jeśli uruchomisz to polecenie, powinieneś zobaczyć listę dziesięciu ciągów znaków, które zaczynają się od `0x`. Każdy z nich to **adres publiczny** i pod pewnymi względami jest analogiczny do numeru konta w rachunku bieżącym. Podałbyś ten adres komuś, kto chciałby wysłać ci ether. + +Jak wspomniano, dostawca testowy wstępnie załadował każde z tych kont pewną ilością testowego etheru. Sprawdźmy, ile jest na pierwszym koncie: + +```python +In [7]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[7]: 1000000000000000000000000 +``` + +To mnóstwo zer! Zanim pójdziesz śmiejąc się do fałszywego banku, przypomnij sobie wcześniejszą lekcję o nominałach walut. Wartości etheru są reprezentowane w najmniejszym nominale, wei. Przelicz to na ether: + +```python +In [8]: w3.from_wei(1000000000000000000000000, 'ether') +Out[8]: Decimal('1000000') +``` + +Milion testowego etheru — całkiem nieźle. + +## Przystanek wycieczki nr 2: dane bloku {#tour-stop-2-block-data} + +Rzućmy okiem na stan tego symulowanego blockchaina: + +```python +In [9]: w3.eth.get_block('latest') +Out[9]: AttributeDict({ + 'number': 0, + 'hash': HexBytes('0x9469878...'), + 'parentHash': HexBytes('0x0000000...'), + ... + 'transactions': [] +}) +``` + +Zwracanych jest wiele informacji o bloku, ale warto zwrócić uwagę na kilka rzeczy: + +- Numer bloku to zero — niezależnie od tego, jak dawno skonfigurowałeś dostawcę testowego. W przeciwieństwie do prawdziwej sieci Ethereum, która dodaje nowy blok co 12 sekund, ta symulacja będzie czekać, aż dasz jej coś do zrobienia. +- `transactions` to pusta lista z tego samego powodu: jeszcze nic nie zrobiliśmy. Ten pierwszy blok to **pusty blok**, tylko po to, aby rozpocząć łańcuch. +- Zauważ, że `parentHash` to tylko garść pustych bajtów. Oznacza to, że jest to pierwszy blok w łańcuchu, znany również jako **blok genezy**. + +## Przystanek wycieczki nr 3: [transakcje](/developers/docs/transactions/) {#tour-stop-3-transactions} + +Utknęliśmy na bloku zero, dopóki nie pojawi się oczekująca transakcja, więc stwórzmy jedną. Wyślij kilka testowych etherów z jednego konta na drugie: + +```python +In [10]: tx_hash = w3.eth.send_transaction({ + 'from': w3.eth.accounts[0], + 'to': w3.eth.accounts[1], + 'value': w3.to_wei(3, 'ether'), + 'gas': 21000 +}) +``` + +Jest to zazwyczaj moment, w którym czeka się kilka sekund na dołączenie transakcji do nowego bloku. Pełny proces wygląda mniej więcej tak: + +1. Prześlij transakcję i zachowaj jej hasz. Dopóki blok zawierający transakcję nie zostanie utworzony i rozgłoszony, transakcja jest „oczekująca”. + `tx_hash = w3.eth.send_transaction({ … })` +2. Poczekaj, aż transakcja zostanie uwzględniona w bloku: + `w3.eth.wait_for_transaction_receipt(tx_hash)` +3. Kontynuuj logikę aplikacji. Aby wyświetlić pomyślną transakcję: + `w3.eth.get_transaction(tx_hash)` + +Nasze symulowane środowisko natychmiast doda transakcję w nowym bloku, dzięki czemu możemy od razu ją wyświetlić: + +```python +In [11]: w3.eth.get_transaction(tx_hash) +Out[11]: AttributeDict({ + 'hash': HexBytes('0x15e9fb95dc39...'), + 'blockNumber': 1, + 'transactionIndex': 0, + 'from': '0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf', + 'to': '0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', + 'value': 3000000000000000000, + ... +}) +``` + +Zobaczysz tutaj kilka znajomych szczegółów: pola `from`, `to` i `value` powinny pasować do danych wejściowych naszego wywołania `send_transaction`. Inną uspokajającą informacją jest to, że transakcja ta została uwzględniona jako pierwsza (`'transactionIndex': 0`) w bloku numer 1. + +Możemy również łatwo zweryfikować powodzenie tej transakcji, sprawdzając salda dwóch zaangażowanych kont. Trzy ethery powinny zostać przeniesione z jednego konta na drugie. + +```python +In [12]: w3.eth.get_balance(w3.eth.accounts[0]) +Out[12]: 999996999979000000000000 + +In [13]: w3.eth.get_balance(w3.eth.accounts[1]) +Out[13]: 1000003000000000000000000 +``` + +To drugie wygląda dobrze! Saldo wzrosło z 1 000 000 do 1 000 003 etherów. Ale co stało się z pierwszym kontem? Wygląda na to, że straciło nieco więcej niż trzy ethery. Niestety, nic w życiu nie jest za darmo, a korzystanie z publicznej sieci Ethereum wymaga wynagrodzenia innych za ich rolę wspierającą. Niewielka opłata transakcyjna została odjęta od konta, które przesłało transakcję – ta opłata to ilość spalonego gazu (21000 jednostek gazu za transfer ETH) pomnożona przez opłatę podstawową, która zmienia się w zależności od aktywności sieci, plus napiwek, który trafia do walidatora, który włącza transakcję do bloku. + +Więcej o [gazie](/developers/docs/gas/#post-london) + +Uwaga: W sieci publicznej opłaty transakcyjne są zmienne w zależności od zapotrzebowania w sieci i tego, jak szybko chcesz, aby transakcja została przetworzona. Jeśli interesuje Cię szczegółowe omówienie sposobu obliczania opłat, zobacz mój wcześniejszy post o tym, jak transakcje są włączane do bloku. + +## Chwila oddechu {#and-breathe} + +Siedzimy nad tym już chwilę, więc to wydaje się dobrym miejscem na przerwę. Królicza nora ciągnie się dalej, a my będziemy kontynuować eksplorację w drugiej części tej serii. Nadchodzące koncepcje: łączenie się z prawdziwym węzłem, inteligentne kontrakty i tokeny. Masz dodatkowe pytania? Daj mi znać! Twoja opinia wpłynie na to, w jakim kierunku pójdziemy dalej. Prośby mile widziane na [Twitterze](https://twitter.com/wolovim). diff --git a/public/content/translations/pl/developers/tutorials/all-you-can-cache/index.md b/public/content/translations/pl/developers/tutorials/all-you-can-cache/index.md new file mode 100644 index 00000000000..7c8a1f4745c --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/all-you-can-cache/index.md @@ -0,0 +1,867 @@ +--- +title: "Wszystko, co możesz zbuforować" +description: "Dowiedz się, jak tworzyć i używać kontraktu buforującego w celu uzyskania tańszych transakcji w pakietach zbiorczych" +author: Ori Pomerantz +tags: [ "warstwa 2", "buforowanie", "przechowywanie" ] +skill: intermediate +published: 2022-09-15 +lang: pl +--- + +Podczas korzystania z pakietów zbiorczych koszt jednego bajtu w transakcji jest znacznie wyższy niż koszt slotu pamięci. Dlatego sensowne jest buforowanie jak największej ilości informacji w łańcuchu. + +W tym artykule dowiesz się, jak stworzyć i używać kontraktu buforującego w taki sposób, aby każda wartość parametru, która prawdopodobnie będzie używana wielokrotnie, została zbuforowana i była dostępna do użycia (po pierwszym razie) przy użyciu znacznie mniejszej liczby bajtów, oraz jak napisać kod poza łańcuchem, który korzysta z tej pamięci podręcznej. + +Jeśli chcesz pominąć artykuł i po prostu zobaczyć kod źródłowy, [jest on tutaj](https://github.com/qbzzt/20220915-all-you-can-cache). Stos programistyczny to [Foundry](https://getfoundry.sh/introduction/installation/). + +## Ogólny projekt {#overall-design} + +Dla uproszczenia założymy, że wszystkie parametry transakcji to `uint256` o długości 32 bajtów. Gdy otrzymamy transakcję, będziemy analizować każdy parametr w następujący sposób: + +1. Jeśli pierwszy bajt to `0xFF`, weź następne 32 bajty jako wartość parametru i zapisz ją w pamięci podręcznej. + +2. Jeśli pierwszy bajt to `0xFE`, weź następne 32 bajty jako wartość parametru, ale _nie_ zapisuj jej w pamięci podręcznej. + +3. Dla każdej innej wartości weź cztery górne bity jako liczbę dodatkowych bajtów, a dolne cztery bity jako najbardziej znaczące bity klucza pamięci podręcznej. Oto kilka przykładów: + + | Bajty w calldata | Klucz pamięci podręcznej | + | :--------------- | -----------------------: | + | 0x0F | 0x0F | + | 0x10,0x10 | 0x10 | + | 0x12,0xAC | 0x02AC | + | 0x2D,0xEA, 0xD6 | 0x0DEAD6 | + +## Manipulacja pamięcią podręczną {#cache-manipulation} + +Pamięć podręczna jest zaimplementowana w [`Cache.sol`](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol). Przejdźmy przez niego linia po linii. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + + +contract Cache { + + bytes1 public constant INTO_CACHE = 0xFF; + bytes1 public constant DONT_CACHE = 0xFE; +``` + +Te stałe służą do interpretacji specjalnych przypadków, w których podajemy wszystkie informacje i chcemy je zapisać w pamięci podręcznej lub nie. Zapisywanie do pamięci podręcznej wymaga dwóch operacji [`SSTORE`](https://www.evm.codes/#55) w wcześniej nieużywanych slotach pamięci kosztem 22100 jednostek gazu każda, dlatego jest to opcjonalne. + +```solidity + + mapping(uint => uint) public val2key; +``` + +[Odwzorowanie](https://www.geeksforgeeks.org/solidity/solidity-mappings/) pomiędzy wartościami a ich kluczami. Ta informacja jest niezbędna do zakodowania wartości przed wysłaniem transakcji. + +```solidity + // Lokalizacja n ma wartość dla klucza n+1, ponieważ musimy zachować + // zero jako "nie w pamięci podręcznej". + uint[] public key2val; +``` + +Możemy użyć tablicy do mapowania kluczy na wartości, ponieważ to my przypisujemy klucze i dla uproszczenia robimy to sekwencyjnie. + +```solidity + function cacheRead(uint _key) public view returns (uint) { + require(_key <= key2val.length, "Odczyt niezainicjowanego wpisu w pamięci podręcznej"); + return key2val[_key-1]; + } // cacheRead +``` + +Odczytaj wartość z pamięci podręcznej. + +```solidity + // Zapisz wartość w pamięci podręcznej, jeśli jeszcze jej tam nie ma + // Funkcja publiczna tylko po to, aby umożliwić działanie testu + function cacheWrite(uint _value) public returns (uint) { + // Jeśli wartość jest już w pamięci podręcznej, zwróć bieżący klucz + if (val2key[_value] != 0) { + return val2key[_value]; + } +``` + +Nie ma sensu umieszczać tej samej wartości w pamięci podręcznej więcej niż raz. Jeśli wartość już istnieje, po prostu zwróć istniejący klucz. + +```solidity + // Ponieważ 0xFE jest przypadkiem specjalnym, największy klucz, jaki może pomieścić pamięć podręczna, to + // 0x0D, po którym następuje 15 wartości 0xFF. Jeśli długość pamięci podręcznej jest już tak + // duża, zakończ błędem. + // 1 2 3 4 5 6 7 8 9 A B C D E F + require(key2val.length+1 < 0x0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + "przepełnienie pamięci podręcznej"); +``` + +Nie sądzę, abyśmy kiedykolwiek uzyskali tak dużą pamięć podręczną (około 1,8\*1037 wpisów, co wymagałoby około 1027 TB do przechowywania). Jednak jestem na tyle stary, że pamiętam ["640 kB powinno zawsze wystarczyć"](https://quoteinvestigator.com/2011/09/08/640k-enough/). Ten test jest bardzo tani. + +```solidity + // Zapisz wartość, używając następnego klucza + val2key[_value] = key2val.length+1; +``` + +Dodaj wyszukiwanie wsteczne (od wartości do klucza). + +```solidity + key2val.push(_value); +``` + +Dodaj wyszukiwanie w przód (od klucza do wartości). Ponieważ przypisujemy wartości sekwencyjnie, możemy po prostu dodać ją po ostatniej wartości w tablicy. + +```solidity + return key2val.length; + } // cacheWrite +``` + +Zwróć nową długość `key2val`, która jest komórką, w której przechowywana jest nowa wartość. + +```solidity + function _calldataVal(uint startByte, uint length) + private pure returns (uint) +``` + +Ta funkcja odczytuje wartość z calldata o dowolnej długości (do 32 bajtów, czyli rozmiaru słowa). + +```solidity + { + uint _retVal; + + require(length < 0x21, + "Limit długości _calldataVal to 32 bajty"); + require(length + startByte <= msg.data.length, + "_calldataVal próbuje odczytać poza calldatasize"); +``` + +Ta funkcja jest wewnętrzna, więc jeśli reszta kodu jest napisana poprawnie, te testy nie są wymagane. Jednak nie kosztują wiele, więc równie dobrze możemy je mieć. + +```solidity + assembly { + _retVal := calldataload(startByte) + } +``` + +Ten kod jest w języku [Yul](https://docs.soliditylang.org/en/v0.8.16/yul.html). Odczytuje 32-bajtową wartość z calldata. Działa to nawet wtedy, gdy calldata kończy się przed `startByte+32`, ponieważ niezainicjowana przestrzeń w EVM jest uważana za zerową. + +```solidity + _retVal = _retVal >> (256-length*8); +``` + +Niekoniecznie chcemy wartości 32-bajtowej. To usuwa nadmiarowe bajty. + +```solidity + return _retVal; + } // _calldataVal + + + // Odczytaj pojedynczy parametr z calldata, zaczynając od _fromByte + function _readParam(uint _fromByte) internal + returns (uint _nextByte, uint _parameterValue) + { +``` + +Odczytaj pojedynczy parametr z calldata. Zauważ, że musimy zwrócić nie tylko odczytaną wartość, ale także lokalizację następnego bajtu, ponieważ parametry mogą mieć długość od 1 do 33 bajtów. + +```solidity + // Pierwszy bajt mówi nam, jak interpretować resztę + uint8 _firstByte; + + _firstByte = uint8(_calldataVal(_fromByte, 1)); +``` + +Solidity próbuje zredukować liczbę błędów, zabraniając potencjalnie niebezpiecznych [niejawnych konwersji typów](https://docs.soliditylang.org/en/v0.8.16/types.html#implicit-conversions). Degradacja, na przykład z 256 bitów do 8 bitów, musi być jawna. + +```solidity + + // Odczytaj wartość, ale nie zapisuj jej w pamięci podręcznej + if (_firstByte == uint8(DONT_CACHE)) + return(_fromByte+33, _calldataVal(_fromByte+1, 32)); + + // Odczytaj wartość i zapisz ją w pamięci podręcznej + if (_firstByte == uint8(INTO_CACHE)) { + uint _param = _calldataVal(_fromByte+1, 32); + cacheWrite(_param); + return(_fromByte+33, _param); + } + + // Jeśli dotarliśmy tutaj, oznacza to, że musimy odczytać z pamięci podręcznej + + // Liczba dodatkowych bajtów do odczytania + uint8 _extraBytes = _firstByte / 16; +``` + +Weź dolny [półbajt (nibble)](https://en.wikipedia.org/wiki/Nibble) i połącz go z pozostałymi bajtami, aby odczytać wartość z pamięci podręcznej. + +```solidity + uint _key = (uint256(_firstByte & 0x0F) << (8*_extraBytes)) + + _calldataVal(_fromByte+1, _extraBytes); + + return (_fromByte+_extraBytes+1, cacheRead(_key)); + + } // _readParam + + + // Odczytaj n parametrów (funkcje wiedzą, ilu parametrów oczekują) + function _readParams(uint _paramNum) internal returns (uint[] memory) { +``` + +Moglibyśmy pobrać liczbę parametrów z samych danych wywołania (calldata), ale funkcje, które nas wywołują, wiedzą, ilu parametrów oczekują. Łatwiej jest pozwolić, aby to one nam o tym powiedziały. + +```solidity + // Parametry, które odczytujemy + uint[] memory params = new uint[](_paramNum); + + // Parametry zaczynają się od 4 bajtu, przed nim jest sygnatura funkcji + uint _atByte = 4; + + for(uint i=0; i<_paramNum; i++) { + (_atByte, params[i]) = _readParam(_atByte); + } +``` + +Odczytuj parametry, aż uzyskasz potrzebną liczbę. Jeśli wykroczymy poza koniec calldata, `_readParams` cofnie wywołanie. + +```solidity + + return(params); + } // readParams + + // Do testowania _readParams, przetestuj odczyt czterech parametrów + function fourParam() public + returns (uint256,uint256,uint256,uint256) + { + uint[] memory params; + params = _readParams(4); + return (params[0], params[1], params[2], params[3]); + } // fourParam +``` + +Jedną z wielkich zalet Foundry jest to, że pozwala na pisanie testów w Solidity ([zobacz Testowanie pamięci podręcznej poniżej](#testing-the-cache)). To znacznie ułatwia testy jednostkowe. Jest to funkcja, która odczytuje cztery parametry i zwraca je, aby test mógł zweryfikować ich poprawność. + +```solidity + // Pobierz wartość, zwróć bajty, które ją zakodują (używając pamięci podręcznej, jeśli to możliwe) + function encodeVal(uint _val) public view returns(bytes memory) { +``` + +`encodeVal` to funkcja, którą kod offchain (poza łańcuchem) wywołuje, aby pomóc w tworzeniu calldata korzystających z pamięci podręcznej. Otrzymuje pojedynczą wartość i zwraca bajty, które ją kodują. Ta funkcja jest funkcją typu `view`, więc nie wymaga transakcji, a wywołana z zewnątrz nie zużywa gazu. + +```solidity + uint _key = val2key[_val]; + + // Wartości nie ma jeszcze w pamięci podręcznej, więc dodaj ją + if (_key == 0) + return bytes.concat(INTO_CACHE, bytes32(_val)); +``` + +W [EVM](/developers/docs/evm/) cała niezainicjowana pamięć masowa jest traktowana jako zera. Więc jeśli szukamy klucza dla wartości, której tam nie ma, otrzymujemy zero. W takim przypadku bajty, które ją kodują, to `INTO_CACHE` (więc zostanie zbuforowana następnym razem), a po nich rzeczywista wartość. + +```solidity + // Jeśli klucz jest <0x10, zwróć go jako pojedynczy bajt + if (_key < 0x10) + return bytes.concat(bytes1(uint8(_key))); +``` + +Pojedyncze bajty są najłatwiejsze. Używamy po prostu [`bytes.concat`](https://docs.soliditylang.org/en/v0.8.16/types.html#the-functions-bytes-concat-and-string-concat), aby zamienić typ `bytes` na tablicę bajtów o dowolnej długości. Pomimo nazwy działa dobrze, gdy jest zaopatrzona w tylko jeden argument. + +```solidity + // Wartość dwubajtowa, zakodowana jako 0x1vvv + if (_key < 0x1000) + return bytes.concat(bytes2(uint16(_key) | 0x1000)); +``` + +Gdy mamy klucz mniejszy niż 163, możemy go wyrazić w dwóch bajtach. Najpierw konwertujemy `_key`, która jest wartością 256-bitową, na wartość 16-bitową i używamy logicznego LUB, aby dodać liczbę dodatkowych bajtów do pierwszego bajtu. Następnie zamieniamy ją na wartość `bytes2`, którą można przekonwertować na `bytes`. + +```solidity + // Prawdopodobnie istnieje sprytny sposób na wykonanie następujących linii jako pętli, + // ale jest to funkcja widoku, więc optymalizuję pod kątem czasu programisty i + // prostoty. + + if (_key < 16*256**2) + return bytes.concat(bytes3(uint24(_key) | (0x2 * 16 * 256**2))); + if (_key < 16*256**3) + return bytes.concat(bytes4(uint32(_key) | (0x3 * 16 * 256**3))); + . + . + . + if (_key < 16*256**14) + return bytes.concat(bytes15(uint120(_key) | (0xE * 16 * 256**14))); + if (_key < 16*256**15) + return bytes.concat(bytes16(uint128(_key) | (0xF * 16 * 256**15))); +``` + +Pozostałe wartości (3 bajty, 4 bajty itp.) są obsługiwane w ten sam sposób, tylko z różnymi rozmiarami pól. + +```solidity + // Jeśli dotarliśmy tutaj, coś jest nie tak. + revert("Błąd w encodeVal, nie powinno się zdarzyć"); +``` + +Jeśli tu dotrzemy, oznacza to, że otrzymaliśmy klucz, który nie jest mniejszy niż 16\*25615. Ale `cacheWrite` ogranicza klucze, więc nie możemy nawet dojść do 14\*25616 (co miałoby pierwszy bajt 0xFE, więc wyglądałoby jak `DONT_CACHE`). Ale niewiele nas kosztuje dodanie testu na wypadek, gdyby przyszły programista wprowadził błąd. + +```solidity + } // encodeVal + +} // Cache +``` + +### Testowanie pamięci podręcznej {#testing-the-cache} + +Jedną z zalet Foundry jest to, że [pozwala pisać testy w Solidity](https://getfoundry.sh/forge/tests/overview/), co ułatwia pisanie testów jednostkowych. Testy dla klasy `Cache` są [tutaj](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/Cache.t.sol). Ponieważ kod testowy jest powtarzalny, jak to zwykle bywa z testami, w tym artykule wyjaśniono tylko interesujące części. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + + +// Należy uruchomić `forge test -vv` dla konsoli. +import "forge-std/console.sol"; +``` + +To tylko standardowy kod, który jest niezbędny do użycia pakietu testowego i `console.log`. + +```solidity +import "src/Cache.sol"; +``` + +Musimy znać kontrakt, który testujemy. + +```solidity +contract CacheTest is Test { + Cache cache; + + function setUp() public { + cache = new Cache(); + } +``` + +Funkcja `setUp` jest wywoływana przed każdym testem. W tym przypadku po prostu tworzymy nową pamięć podręczną, aby nasze testy nie wpływały na siebie nawzajem. + +```solidity + function testCaching() public { +``` + +Testy to funkcje, których nazwy zaczynają się od `test`. Ta funkcja sprawdza podstawową funkcjonalność pamięci podręcznej, zapisując wartości i ponownie je odczytując. + +```solidity + for(uint i=1; i<5000; i++) { + cache.cacheWrite(i*i); + } + + for(uint i=1; i<5000; i++) { + assertEq(cache.cacheRead(i), i*i); +``` + +W ten sposób przeprowadza się rzeczywiste testowanie za pomocą [funkcji `assert...`](https://getfoundry.sh/reference/forge-std/std-assertions/). W tym przypadku sprawdzamy, czy wartość, którą zapisaliśmy, jest tą samą, którą odczytaliśmy. Możemy zignorować wynik `cache.cacheWrite`, ponieważ wiemy, że klucze pamięci podręcznej są przypisywane liniowo. + +```solidity + } + } // testCaching + + + // Zapisz tę samą wartość wiele razy, upewnij się, że klucz pozostaje + // taki sam + function testRepeatCaching() public { + for(uint i=1; i<100; i++) { + uint _key1 = cache.cacheWrite(i); + uint _key2 = cache.cacheWrite(i); + assertEq(_key1, _key2); + } +``` + +Najpierw zapisujemy każdą wartość do pamięci podręcznej dwukrotnie i upewniamy się, że klucze są takie same (co oznacza, że drugi zapis tak naprawdę nie miał miejsca). + +```solidity + for(uint i=1; i<100; i+=3) { + uint _key = cache.cacheWrite(i); + assertEq(_key, i); + } + } // testRepeatCaching +``` + +Teoretycznie może istnieć błąd, który nie wpływa na kolejne zapisy w pamięci podręcznej. Dlatego tutaj wykonujemy kilka zapisów, które nie są następujące po sobie i widzimy, że wartości nadal nie są nadpisywane. + +```solidity + // Odczytaj uint z bufora pamięci (aby upewnić się, że otrzymamy z powrotem parametry, + // które wysłaliśmy) + function toUint256(bytes memory _bytes, uint256 _start) internal pure + returns (uint256) +``` + +Odczytaj 256-bitowe słowo z bufora `bytes memory`. Ta funkcja narzędziowa pozwala nam zweryfikować, czy otrzymujemy poprawne wyniki, gdy uruchamiamy wywołanie funkcji, która używa pamięci podręcznej. + +```solidity + { + require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); + uint256 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x20), _start)) + } +``` + +Yul nie obsługuje struktur danych poza `uint256`, więc gdy odwołujesz się do bardziej zaawansowanej struktury danych, takiej jak bufor pamięci `_bytes`, otrzymujesz adres tej struktury. Solidity przechowuje wartości `bytes memory` jako 32-bajtowe słowo, które zawiera długość, po której następują rzeczywiste bajty, więc aby uzyskać bajt o numerze `_start`, musimy obliczyć `_bytes+32+_start`. + +```solidity + + return tempUint; + } // toUint256 + + // Sygnatura funkcji dla fourParams(), dzięki uprzejmości + // https://www.4byte.directory/signatures/?bytes4_signature=0x3edc1e6d + bytes4 constant FOUR_PARAMS = 0x3edc1e6d; + + // Po prostu kilka stałych wartości, aby zobaczyć, czy otrzymujemy poprawne wartości z powrotem + uint256 constant VAL_A = 0xDEAD60A7; + uint256 constant VAL_B = 0xBEEF; + uint256 constant VAL_C = 0x600D; + uint256 constant VAL_D = 0x600D60A7; +``` + +Kilka stałych, których potrzebujemy do testowania. + +```solidity + function testReadParam() public { +``` + +Wywołaj `fourParams()`, funkcję, która używa `readParams`, aby przetestować, czy potrafimy poprawnie odczytać parametry. + +```solidity + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; +``` + +Nie możemy użyć normalnego mechanizmu ABI do wywołania funkcji przy użyciu pamięci podręcznej, więc musimy użyć niskopoziomowego mechanizmu [`
.call()`](https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses). Mechanizm ten przyjmuje na wejściu `bytes memory`, a na wyjściu zwraca to samo (oraz wartość logiczną). + +```solidity + // Pierwsze wywołanie, pamięć podręczna jest pusta + _callInput = bytes.concat( + FOUR_PARAMS, +``` + +Użyteczne jest, aby ten sam kontrakt obsługiwał zarówno funkcje buforowane (dla wywołań bezpośrednio z transakcji), jak i niebuforowane (dla wywołań z innych inteligentnych kontraktów). Aby to zrobić, musimy nadal polegać na mechanizmie Solidity do wywoływania poprawnej funkcji, zamiast umieszczać wszystko w [funkcji `fallback`](https://docs.soliditylang.org/en/v0.8.16/contracts.html#fallback-function). Dzięki temu kompozycyjność staje się o wiele łatwiejsza. Pojedynczy bajt w większości przypadków wystarczyłby do zidentyfikowania funkcji, więc marnujemy trzy bajty (16\*3=48 jednostek gazu). Jednak w chwili, gdy to piszę, te 48 jednostek gazu kosztuje 0,07 centa, co jest rozsądnym kosztem prostszego, mniej podatnego na błędy kodu. + +```solidity + // Pierwsza wartość, dodaj ją do pamięci podręcznej + cache.INTO_CACHE(), + bytes32(VAL_A), +``` + +Pierwsza wartość: flaga informująca, że jest to pełna wartość, która musi zostać zapisana w pamięci podręcznej, a następnie 32 bajty wartości. Pozostałe trzy wartości są podobne, z wyjątkiem tego, że `VAL_B` nie jest zapisywana do pamięci podręcznej, a `VAL_C` jest zarówno trzecim, jak i czwartym parametrem. + +```solidity + . + . + . + ); + (_success, _callOutput) = _cacheAddr.call(_callInput); +``` + +To tutaj faktycznie wywołujemy kontrakt `Cache`. + +```solidity + assertEq(_success, true); +``` + +Oczekujemy, że wywołanie się powiedzie. + +```solidity + assertEq(cache.cacheRead(1), VAL_A); + assertEq(cache.cacheRead(2), VAL_C); +``` + +Zaczynamy z pustą pamięcią podręczną, a następnie dodajemy `VAL_A`, a po niej `VAL_C`. Spodziewalibyśmy się, że pierwszy będzie miał klucz 1, a drugi 2. + +``` + assertEq(toUint256(_callOutput,0), VAL_A); + assertEq(toUint256(_callOutput,32), VAL_B); + assertEq(toUint256(_callOutput,64), VAL_C); + assertEq(toUint256(_callOutput,96), VAL_C); +``` + +Dane wyjściowe to cztery parametry. Tutaj weryfikujemy, czy są poprawne. + +```solidity + // Drugie wywołanie, możemy użyć pamięci podręcznej + _callInput = bytes.concat( + FOUR_PARAMS, + + // Pierwsza wartość w pamięci podręcznej + bytes1(0x01), +``` + +Klucze pamięci podręcznej poniżej 16 to tylko jeden bajt. + +```solidity + // Druga wartość, nie dodawaj jej do pamięci podręcznej + cache.DONT_CACHE(), + bytes32(VAL_B), + + // Trzecia i czwarta wartość, ta sama wartość + bytes1(0x02), + bytes1(0x02) + ); + . + . + . + } // testReadParam +``` + +Testy po wywołaniu są identyczne jak te po pierwszym wywołaniu. + +```solidity + function testEncodeVal() public { +``` + +Ta funkcja jest podobna do `testReadParam`, z wyjątkiem tego, że zamiast jawnie pisać parametry, używamy `encodeVal()`. + +```solidity + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(VAL_A), + cache.encodeVal(VAL_B), + cache.encodeVal(VAL_C), + cache.encodeVal(VAL_D) + ); + . + . + . + assertEq(_callInput.length, 4+1*4); + } // testEncodeVal +``` + +Jedynym dodatkowym testem w `testEncodeVal()` jest weryfikacja, czy długość `_callInput` jest prawidłowa. Dla pierwszego wywołania jest to 4+33\*4. Dla drugiego, gdzie każda wartość jest już w pamięci podręcznej, wynosi ona 4+1\*4. + +```solidity + // Przetestuj encodeVal, gdy klucz ma więcej niż jeden bajt + // Maksymalnie trzy bajty, ponieważ wypełnienie pamięci podręcznej do czterech bajtów trwa + // zbyt długo. + function testEncodeValBig() public { + // Umieść pewną liczbę wartości w pamięci podręcznej. + // Aby uprościć sprawę, użyj klucza n dla wartości n. + for(uint i=1; i<0x1FFF; i++) { + cache.cacheWrite(i); + } +``` + +Powyższa funkcja `testEncodeVal` zapisuje tylko cztery wartości do pamięci podręcznej, więc [część funkcji, która zajmuje się wartościami wielobajtowymi](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/Cache.sol#L144-L171), nie jest sprawdzana. Ale ten kod jest skomplikowany i podatny na błędy. + +Pierwsza część tej funkcji to pętla, która po kolei zapisuje do pamięci podręcznej wszystkie wartości od 1 do 0x1FFF, dzięki czemu będziemy w stanie zakodować te wartości i wiedzieć, dokąd trafią. + +```solidity + . + . + . + + _callInput = bytes.concat( + FOUR_PARAMS, + cache.encodeVal(0x000F), // Jeden bajt 0x0F + cache.encodeVal(0x0010), // Dwa bajty 0x1010 + cache.encodeVal(0x0100), // Dwa bajty 0x1100 + cache.encodeVal(0x1000) // Trzy bajty 0x201000 + ); +``` + +Przetestuj wartości jedno-, dwu- i trzybajtowe. Nie testujemy dalej, ponieważ zapisanie wystarczającej liczby wpisów na stosie (co najmniej 0x10000000, czyli około ćwierć miliarda) zajęłoby zbyt dużo czasu. + +```solidity + . + . + . + . + } // testEncodeValBig + + + // Przetestuj, że przy zbyt małym buforze otrzymamy revert + function testShortCalldata() public { +``` + +Przetestuj, co się stanie w nietypowym przypadku, gdy nie ma wystarczającej liczby parametrów. + +```solidity + . + . + . + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, false); + } // testShortCalldata +``` + +Ponieważ następuje wycofanie, wynikiem, który powinniśmy otrzymać, jest `fałsz`. + +``` + // Wywołanie z kluczami pamięci podręcznej, których tam nie ma + function testNoCacheKey() public { + . + . + . + _callInput = bytes.concat( + FOUR_PARAMS, + + // Pierwsza wartość, dodaj ją do pamięci podręcznej + cache.INTO_CACHE(), + bytes32(VAL_A), + + // Druga wartość + bytes1(0x0F), + bytes2(0x1234), + bytes11(0xA10102030405060708090A) + ); +``` + +Ta funkcja otrzymuje cztery całkowicie prawidłowe parametry, z wyjątkiem tego, że pamięć podręczna jest pusta, więc nie ma tam żadnych wartości do odczytania. + +```solidity + . + . + . + // Przetestuj, że przy zbyt długim buforze wszystko działa poprawnie + function testLongCalldata() public { + address _cacheAddr = address(cache); + bool _success; + bytes memory _callInput; + bytes memory _callOutput; + + // Pierwsze wywołanie, pamięć podręczna jest pusta + _callInput = bytes.concat( + FOUR_PARAMS, + + // Pierwsza wartość, dodaj ją do pamięci podręcznej + cache.INTO_CACHE(), bytes32(VAL_A), + + // Druga wartość, dodaj ją do pamięci podręcznej + cache.INTO_CACHE(), bytes32(VAL_B), + + // Trzecia wartość, dodaj ją do pamięci podręcznej + cache.INTO_CACHE(), bytes32(VAL_C), + + // Czwarta wartość, dodaj ją do pamięci podręcznej + cache.INTO_CACHE(), bytes32(VAL_D), + + // I jeszcze jedna wartość „na szczęście” + bytes4(0x31112233) + ); +``` + +Ta funkcja wysyła pięć wartości. Wiemy, że piąta wartość jest ignorowana, ponieważ nie jest prawidłowym wpisem w pamięci podręcznej, co spowodowałoby wycofanie transakcji, gdyby nie została uwzględniona. + +```solidity + (_success, _callOutput) = _cacheAddr.call(_callInput); + assertEq(_success, true); + . + . + . + } // testLongCalldata + +} // CacheTest + +``` + +## Przykładowa aplikacja {#a-sample-app} + +Pisanie testów w Solidity jest bardzo dobre, ale ostatecznie dapka musi być w stanie przetwarzać żądania spoza łańcucha, aby być użyteczną. Ten artykuł pokazuje, jak używać buforowania w dapce z `WORM`, co oznacza „Write Once, Read Many” (zapisz raz, czytaj wiele razy). Jeśli klucz nie jest jeszcze zapisany, można do niego zapisać wartość. Jeśli klucz jest już zapisany, następuje wycofanie transakcji. + +### Kontrakt {#the-contract} + +[To jest kontrakt](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/src/WORM.sol). W dużej mierze powtarza to, co zrobiliśmy już z `Cache` i `CacheTest`, więc omówimy tylko interesujące części. + +```solidity +import "./Cache.sol"; + +contract WORM is Cache { +``` + +Najłatwiejszym sposobem na użycie `Cache` jest odziedziczenie go w naszym własnym kontrakcie. + +```solidity + function writeEntryCached() external { + uint[] memory params = _readParams(2); + writeEntry(params[0], params[1]); + } // writeEntryCached +``` + +Ta funkcja jest podobna do `fourParam` w `CacheTest` powyżej. Ponieważ nie przestrzegamy specyfikacji ABI, najlepiej nie deklarować żadnych parametrów w funkcji. + +```solidity + // Ułatwienie wywoływania nas + // Sygnatura funkcji dla writeEntryCached(), dzięki uprzejmości + // https://www.4byte.directory/signatures/?bytes4_signature=0xe4e4f2d3 + bytes4 constant public WRITE_ENTRY_CACHED = 0xe4e4f2d3; +``` + +Kod zewnętrzny, który wywołuje `writeEntryCached`, będzie musiał ręcznie zbudować dane wywołania (calldata), zamiast używać `worm.writeEntryCached`, ponieważ nie przestrzegamy specyfikacji ABI. Posiadanie tej stałej wartości po prostu ułatwia pisanie. + +Należy pamiętać, że chociaż definiujemy `WRITE_ENTRY_CACHED` jako zmienną stanu, aby odczytać ją z zewnątrz, należy użyć funkcji pobierającej dla niej, `worm.WRITE_ENTRY_CACHED()`. + +```solidity + function readEntry(uint key) public view + returns (uint _value, address _writtenBy, uint _writtenAtBlock) +``` + +Funkcja odczytu jest typu `view`, więc nie wymaga transakcji i nie kosztuje gazu. W rezultacie nie ma korzyści z używania pamięci podręcznej dla parametru. W przypadku funkcji `view` najlepiej jest używać standardowego mechanizmu, który jest prostszy. + +### Kod testowy {#the-testing-code} + +[To jest kod testowy dla kontraktu](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/test/WORM.t.sol). Ponownie, spójrzmy tylko na to, co jest interesujące. + +```solidity + function testWReadWrite() public { + worm.writeEntry(0xDEAD, 0x60A7); + + vm.expectRevert(bytes("entry already written")); + worm.writeEntry(0xDEAD, 0xBEEF); +``` + +[To (`vm.expectRevert`)](https://book.getfoundry.sh/cheatcodes/expect-revert#expectrevert) to sposób, w jaki w teście Foundry określamy, że następne wywołanie powinno zakończyć się niepowodzeniem, oraz podajemy przyczynę niepowodzenia. Dotyczy to sytuacji, gdy używamy składni `.()` zamiast budować dane wywołania (calldata) i wywoływać kontrakt przy użyciu interfejsu niskiego poziomu (`.call()` itp.). + +```solidity + function testReadWriteCached() public { + uint cacheGoat = worm.cacheWrite(0x60A7); +``` + +Tutaj wykorzystujemy fakt, że `cacheWrite` zwraca klucz pamięci podręcznej. Nie jest to coś, czego spodziewalibyśmy się używać w produkcji, ponieważ `cacheWrite` zmienia stan, a zatem może być wywoływane tylko podczas transakcji. Transakcje nie mają wartości zwracanych; jeśli mają wyniki, to te wyniki powinny być emitowane jako zdarzenia. Zatem wartość zwracana przez `cacheWrite` jest dostępna tylko z kodu onchain, a kod onchain nie potrzebuje buforowania parametrów. + +```solidity + (_success,) = address(worm).call(_callInput); +``` + +W ten sposób mówimy Solidity, że chociaż `.call()` ma dwie wartości zwracane, interesuje nas tylko pierwsza. + +```solidity + (_success,) = address(worm).call(_callInput); + assertEq(_success, false); +``` + +Ponieważ używamy funkcji niskiego poziomu `
.call()`, nie możemy użyć `vm.expectRevert()` i musimy sprawdzić wartość logiczną powodzenia, którą otrzymujemy z wywołania. + +```solidity + event EntryWritten(uint indexed key, uint indexed value); + + . + . + . + + _callInput = bytes.concat( + worm.WRITE_ENTRY_CACHED(), worm.encodeVal(a), worm.encodeVal(b)); + vm.expectEmit(true, true, false, false); + emit EntryWritten(a, b); + (_success,) = address(worm).call(_callInput); +``` + +W ten sposób w Foundry weryfikujemy, czy kod [poprawnie emituje zdarzenie](https://getfoundry.sh/reference/cheatcodes/expect-emit/). + +### Klient {#the-client} + +Jedną rzeczą, której nie dostajesz w testach Solidity, jest kod JavaScript, który możesz wyciąć i wkleić do własnej aplikacji. Aby napisać ten kod, wdrożyłem WORM w [Optimism Goerli](https://community.optimism.io/docs/useful-tools/networks/#optimism-goerli), nowej sieci testowej [Optimism](https://www.optimism.io/). Znajduje się pod adresem [`0xd34335b1d818cee54e3323d3246bd31d94e6a78a`](https://goerli-optimism.etherscan.io/address/0xd34335b1d818cee54e3323d3246bd31d94e6a78a). + +[Kod JavaScript dla klienta można zobaczyć tutaj](https://github.com/qbzzt/20220915-all-you-can-cache/blob/main/javascript/index.js). Aby go użyć: + +1. Sklonuj repozytorium git: + + ```sh + git clone https://github.com/qbzzt/20220915-all-you-can-cache.git + ``` + +2. Zainstaluj niezbędne pakiety: + + ```sh + cd javascript + yarn + ``` + +3. Skopiuj plik konfiguracyjny: + + ```sh + cp .env.example .env + ``` + +4. Edytuj `.env` dla swojej konfiguracji: + + | Parametr | Wartość | + | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | + | MNEMONIC | Mnemonic dla konta, które ma wystarczająco dużo ETH, aby opłacić transakcję. [Darmowe ETH dla sieci Optimism Goerli można uzyskać tutaj](https://optimismfaucet.xyz/). | + | OPTIMISM_GOERLI_URL | URL do Optimism Goerli. Publiczny punkt końcowy, `https://goerli.optimism.io`, ma ograniczenia szybkości, ale jest wystarczający do tego, czego tutaj potrzebujemy | + +5. Uruchom `index.js`. + + ```sh + node index.js + ``` + + Ta przykładowa aplikacja najpierw zapisuje wpis do WORM, wyświetlając dane wywołania (calldata) i link do transakcji na Etherscan. Następnie odczytuje ten wpis i wyświetla używany klucz oraz wartości we wpisie (wartość, numer bloku i autor). + +Większość klienta to normalny JavaScript Dapp. Dlatego ponownie przejdziemy tylko przez interesujące części. + +```javascript +. +. +. +const main = async () => { + const func = await worm.WRITE_ENTRY_CACHED() + + // Za każdym razem potrzebny jest nowy klucz + const key = await worm.encodeVal(Number(new Date())) +``` + +W danym slocie można zapisać tylko raz, więc używamy znacznika czasu, aby upewnić się, że nie używamy ponownie slotów. + +```javascript +const val = await worm.encodeVal("0x600D") + +// Zapisz wpis +const calldata = func + key.slice(2) + val.slice(2) +``` + +Ethers oczekuje, że dane wywołania będą ciągiem szesnastkowym, `0x` po którym następuje parzysta liczba cyfr szesnastkowych. Ponieważ zarówno `key`, jak i `val` zaczynają się od `0x`, musimy usunąć te nagłówki. + +```javascript +const tx = await worm.populateTransaction.writeEntryCached() +tx.data = calldata + +sentTx = await wallet.sendTransaction(tx) +``` + +Podobnie jak w przypadku kodu testowego Solidity, nie możemy normalnie wywołać funkcji buforowanej. Zamiast tego musimy użyć mechanizmu niższego poziomu. + +```javascript + . + . + . + // Odczytaj właśnie zapisany wpis + const realKey = '0x' + key.slice(4) // usuń flagę FF + const entryRead = await worm.readEntry(realKey) + . + . + . +``` + +Do odczytywania wpisów możemy użyć normalnego mechanizmu. Nie ma potrzeby używania buforowania parametrów z funkcjami `view`. + +## Wnioski {#conclusion} + +Kod w tym artykule jest dowodem słuszności koncepcji, jego celem jest ułatwienie zrozumienia pomysłu. W przypadku systemu gotowego do produkcji można zaimplementować dodatkowe funkcje: + +- Obsługuj wartości, które nie są `uint256`. Na przykład ciągi znaków. +- Zamiast globalnej pamięci podręcznej, można mieć mapowanie między użytkownikami a pamięciami podręcznymi. Różni użytkownicy używają różnych wartości. +- Wartości używane dla adresów różnią się od tych używanych do innych celów. Może mieć sens posiadanie oddzielnej pamięci podręcznej tylko dla adresów. +- Obecnie klucze pamięci podręcznej działają na zasadzie algorytmu „kto pierwszy, ten ma najmniejszy klucz”. Pierwsze szesnaście wartości można wysłać jako pojedynczy bajt. Następne 4080 wartości można wysłać jako dwa bajty. Następny około milion wartości to trzy bajty itd. System produkcyjny powinien utrzymywać liczniki użycia wpisów w pamięci podręcznej i reorganizować je tak, aby szesnaście _najczęściej używanych_ wartości miało jeden bajt, następne 4080 najczęściej używanych wartości dwa bajty itd. + + Jest to jednak potencjalnie niebezpieczna operacja. Wyobraź sobie następującą sekwencję zdarzeń: + + 1. Noam Naiwny wywołuje `encodeVal`, aby zakodować adres, na który chce wysłać tokeny. Ten adres jest jednym z pierwszych używanych w aplikacji, więc zakodowana wartość to 0x06. Jest to funkcja `view`, a nie transakcja, więc jest to sprawa między Noamem a węzłem, którego używa, i nikt inny o tym nie wie. + + 2. Owen Właściciel uruchamia operację zmiany kolejności pamięci podręcznej. Bardzo niewiele osób faktycznie używa tego adresu, więc jest on teraz zakodowany jako 0x201122. Innej wartości, 1018, przypisano 0x06. + + 3. Noam Naiwny wysyła swoje tokeny na 0x06. Trafiają na adres `0x0000000000000000000000000de0b6b3a7640000`, a ponieważ nikt nie zna klucza prywatnego do tego adresu, po prostu tam utknęły. Noam _nie jest zadowolony_. + + Istnieją sposoby rozwiązania tego problemu i powiązanego z nim problemu transakcji znajdujących się w mempoolu podczas zmiany kolejności pamięci podręcznej, ale trzeba być tego świadomym. + +Pokazałem tutaj buforowanie na przykładzie Optimism, ponieważ jestem pracownikiem Optimism i jest to pakiet zbiorczy, który znam najlepiej. Ale powinno to działać z każdym pakietem zbiorczym, który pobiera minimalny koszt za przetwarzanie wewnętrzne, tak że w porównaniu zapisywanie danych transakcji do L1 jest głównym wydatkiem. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). + diff --git a/public/content/translations/pl/developers/tutorials/app-plasma/index.md b/public/content/translations/pl/developers/tutorials/app-plasma/index.md new file mode 100644 index 00000000000..3865dc71520 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/app-plasma/index.md @@ -0,0 +1,1255 @@ +--- +title: "Napisz specyficzną dla aplikacji plasmę, która zachowuje prywatność" +description: "W tym samouczku zbudujemy na wpół tajny bank na depozyty. Bank jest scentralizowanym komponentem; zna saldo każdego użytkownika. Jednakże informacje te nie są przechowywane w łańcuchu. Zamiast tego bank publikuje hasz stanu. Za każdym razem, gdy dochodzi do transakcji, bank publikuje nowy hasz wraz z dowodem o zerowej wiedzy, że posiada podpisaną transakcję, która zmienia stan hasza na nowy. Po przeczytaniu tego samouczka zrozumiesz nie tylko, jak używać dowodów o zerowej wiedzy, ale także dlaczego ich używać i jak robić to bezpiecznie." +author: Ori Pomerantz +tags: [ "zerowej-wiedzy", "serwer", "offchain", "prywatność" ] +skill: advanced +lang: pl +published: 2025-10-15 +--- + +## Wprowadzenie {#introduction} + +W przeciwieństwie do [rollupów](/developers/docs/scaling/zk-rollups/), [plasmy](/developers/docs/scaling/plasma) używają sieci głównej Ethereum do zapewnienia integralności, ale nie dostępności. W tym artykule napiszemy aplikację, która zachowuje się jak plasma, z Ethereum gwarantującym integralność (brak nieautoryzowanych zmian), ale nie dostępność (scentralizowany komponent może ulec awarii i wyłączyć cały system). + +Aplikacja, którą tu napiszemy, to bank chroniący prywatność. Różne adresy mają konta z saldami i mogą wysyłać pieniądze (ETH) na inne konta. Bank publikuje hasze stanu (konta i ich salda) oraz transakcje, ale faktyczne salda przechowuje poza łańcuchem, gdzie mogą pozostać prywatne. + +## Projekt {#design} + +To nie jest system gotowy do produkcji, ale narzędzie dydaktyczne. W związku z tym został napisany z kilkoma upraszczającymi założeniami. + +- Stała pula kont. Istnieje określona liczba kont, a każde konto należy do z góry określonego adresu. To sprawia, że system jest znacznie prostszy, ponieważ trudno jest obsługiwać struktury danych o zmiennym rozmiarze w dowodach o zerowej wiedzy. W przypadku systemu gotowego do produkcji możemy użyć [korzenia Merkle'a](/developers/tutorials/merkle-proofs-for-offline-data-integrity/) jako hasza stanu i dostarczyć dowody Merkle'a dla wymaganych sald. + +- Przechowywanie w pamięci. W systemie produkcyjnym musimy zapisywać wszystkie salda kont na dysku, aby zachować je w przypadku ponownego uruchomienia. Tutaj jest w porządku, jeśli informacje zostaną po prostu utracone. + +- Tylko przelewy. System produkcyjny wymagałby sposobu na wpłacanie aktywów do banku i ich wypłacanie. Ale celem jest tu tylko zilustrowanie koncepcji, więc ten bank jest ograniczony do przelewów. + +### Dowody o zerowej wiedzy {#zero-knowledge-proofs} + +Na poziomie podstawowym dowód o zerowej wiedzy pokazuje, że dowodzący zna pewne dane, _Daneprywatne_ takie, że istnieje relacja _Relacja_ między pewnymi danymi publicznymi, _Danepubliczne_, a _Danymiprywatnymi_. Weryfikator zna _Relację_ i _Danepubliczne_. + +Aby zachować prywatność, potrzebujemy, aby stany i transakcje były prywatne. Ale aby zapewnić integralność, potrzebujemy, aby [hasz kryptograficzny](https://en.wikipedia.org/wiki/Cryptographic_hash_function) stanów był publiczny. Aby udowodnić osobom przesyłającym transakcje, że te transakcje rzeczywiście miały miejsce, musimy również publikować hasze transakcji. + +W większości przypadków _Daneprywatne_ są danymi wejściowymi do programu dowodu o zerowej wiedzy, a _Danepubliczne_ są danymi wyjściowymi. + +Te pola w _Danychprywatnych_: + +- _Stann_, stary stan +- _Stann+1_, nowy stan +- _Transakcja_, transakcja, która zmienia stary stan na nowy. Ta transakcja musi zawierać następujące pola: + - _Adres docelowy_, który otrzymuje przelew + - _Kwota_ przelewu + - _Nonce_, aby zapewnić, że każda transakcja może zostać przetworzona tylko raz. + Adres źródłowy nie musi znajdować się w transakcji, ponieważ można go odzyskać z podpisu. +- _Podpis_, podpis upoważniający do wykonania transakcji. W naszym przypadku jedynym adresem upoważnionym do wykonania transakcji jest adres źródłowy. Ponieważ nasz system o zerowej wiedzy działa w taki sposób, oprócz podpisu Ethereum potrzebujemy również klucza publicznego konta. + +Oto pola w _Danychpublicznych_: + +- _Hasz(Stann)_ hasz starego stanu +- _Hasz(Stann+1)_ hasz nowego stanu +- _Hasz(Transakcja)_ hasz transakcji, która zmienia stan ze _Stanun_ na _Stann+1_. + +Relacja sprawdza kilka warunków: + +- Hasze publiczne są rzeczywiście poprawnymi haszami dla pól prywatnych. +- Transakcja, po zastosowaniu do starego stanu, skutkuje nowym stanem. +- Podpis pochodzi z adresu źródłowego transakcji. + +Ze względu na właściwości funkcji haszujących kryptograficznie, udowodnienie tych warunków wystarczy, aby zapewnić integralność. + +### Struktury danych {#data-structures} + +Podstawową strukturą danych jest stan przechowywany przez serwer. Dla każdego konta serwer śledzi saldo konta i [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce), używany do zapobiegania [atakom typu replay](https://en.wikipedia.org/wiki/Replay_attack). + +### Komponenty {#components} + +Ten system wymaga dwóch komponentów: + +- _Serwer_, który odbiera transakcje, przetwarza je i publikuje hasze w łańcuchu wraz z dowodami o zerowej wiedzy. +- _Inteligentny kontrakt_, który przechowuje hasze i weryfikuje dowody o zerowej wiedzy, aby zapewnić, że przejścia stanów są prawidłowe. + +### Przepływ danych i sterowania {#flows} + +Są to sposoby, w jakie różne komponenty komunikują się w celu dokonania przelewu z jednego konta na drugie. + +1. Przeglądarka internetowa przesyła podpisaną transakcję z prośbą o przelew z konta podpisującego na inne konto. + +2. Serwer weryfikuje, czy transakcja jest prawidłowa: + + - Podpisujący ma konto w banku z wystarczającym saldem. + - Odbiorca ma konto w banku. + +3. Serwer oblicza nowy stan, odejmując przelaną kwotę od salda podpisującego i dodając ją do salda odbiorcy. + +4. Serwer oblicza dowód o zerowej wiedzy, że zmiana stanu jest prawidłowa. + +5. Serwer przesyła do Ethereum transakcję, która zawiera: + + - Nowy hasz stanu + - Hasz transakcji (aby nadawca transakcji wiedział, że została przetworzona) + - Dowód o zerowej wiedzy, który udowadnia, że przejście do nowego stanu jest prawidłowe + +6. Inteligentny kontrakt weryfikuje dowód o zerowej wiedzy. + +7. Jeśli dowód o zerowej wiedzy jest poprawny, inteligentny kontrakt wykonuje następujące czynności: + - Zaktualizuj bieżący hasz stanu na nowy hasz stanu + - Wyemituj wpis w dzienniku z nowym haszem stanu i haszem transakcji + +### Narzędzia {#tools} + +Do kodu po stronie klienta użyjemy [Vite](https://vite.dev/), [React](https://react.dev/), [Viem](https://viem.sh/) i [Wagmi](https://wagmi.sh/). Są to standardowe narzędzia branżowe; jeśli ich nie znasz, możesz skorzystać z [tego samouczka](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). + +Większość serwera jest napisana w JavaScript przy użyciu [Node](https://nodejs.org/en). Część dotycząca zerowej wiedzy jest napisana w [Noir](https://noir-lang.org/). Potrzebujemy wersji `1.0.0-beta.10`, więc po [zainstalowaniu Noir zgodnie z instrukcją](https://noir-lang.org/docs/getting_started/quick_start), uruchom: + +``` +noirup -v 1.0.0-beta.10 +``` + +Blockchain, którego używamy, to `anvil`, lokalny blockchain testowy, który jest częścią [Foundry](https://getfoundry.sh/introduction/installation). + +## Implementacja {#implementation} + +Ponieważ jest to złożony system, wdrożymy go etapami. + +### Etap 1 - Ręczna zerowa wiedza {#stage-1} + +W pierwszym etapie podpiszemy transakcję w przeglądarce, a następnie ręcznie podamy informacje do dowodu o zerowej wiedzy. Kod zerowej wiedzy oczekuje, że otrzyma te informacje w pliku `server/noir/Prover.toml` (udokumentowane [tutaj](https://noir-lang.org/docs/getting_started/project_breakdown#provertoml-1)). + +Aby zobaczyć to w działaniu: + +1. Upewnij się, że masz zainstalowane [Node](https://nodejs.org/en/download) i [Noir](https://noir-lang.org/install). Najlepiej zainstalować je w systemie UNIX, takim jak macOS, Linux lub [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). + +2. Pobierz kod etapu 1 i uruchom serwer WWW, aby obsługiwać kod klienta. + + ```sh + git clone https://github.com/qbzzt/250911-zk-bank.git -b 01-manual-zk + cd 250911-zk-bank + cd client + npm install + npm run dev + ``` + + Powodem, dla którego potrzebujesz tutaj serwera WWW, jest to, że aby zapobiec niektórym rodzajom oszustw, wiele portfeli (takich jak MetaMask) nie akceptuje plików serwowanych bezpośrednio z dysku + +3. Otwórz przeglądarkę z portfelem. + +4. W portfelu wprowadź nową frazę dostępu. Pamiętaj, że spowoduje to usunięcie istniejącej frazy dostępu, więc _upewnij się, że masz kopię zapasową_. + + Fraza dostępu to `test test test test test test test test test test test junk`, domyślna fraza testowa dla anvil. + +5. Przejdź do [kodu po stronie klienta](http://localhost:5173/). + +6. Połącz się z portfelem i wybierz konto docelowe oraz kwotę. + +7. Kliknij **Podpisz** i podpisz transakcję. + +8. Pod nagłówkiem **Prover.toml** znajdziesz tekst. Zastąp `server/noir/Prover.toml` tym tekstem. + +9. Wykonaj dowód o zerowej wiedzy. + + ```sh + cd ../server/noir + nargo execute + ``` + + Dane wyjściowe powinny być podobne do + + ``` + ori@CryptoDocGuy:~/noir/250911-zk-bank/server/noir$ nargo execute + + [zkBank] Circuit witness successfully solved + [zkBank] Witness saved to target/zkBank.gz + [zkBank] Circuit output: (0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b, 0x0cfc0a67cb7308e4e9b254026b54204e34f6c8b041be207e64c5db77d95dd82d, 0x450cf9da6e180d6159290554ae3d8787, 0x6d8bc5a15b9037e52fb59b6b98722a85) + ``` + +10. Porównaj dwie ostatnie wartości z haszem widocznym w przeglądarce internetowej, aby sprawdzić, czy komunikat jest poprawnie haszowany. + +#### `server/noir/Prover.toml` {#server-noir-prover-toml} + +[Ten plik](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml) pokazuje format informacji oczekiwany przez Noir. + +```toml +message="send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 500 finney (milliEth) 0 " +``` + +Komunikat jest w formacie tekstowym, co ułatwia użytkownikowi jego zrozumienie (co jest konieczne przy podpisywaniu) i parsowanie przez kod Noir. Kwota jest podawana w finneyach, aby z jednej strony umożliwić przelewy ułamkowe, a z drugiej strony być łatwo czytelna. Ostatnia liczba to [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce). + +Ciąg ma 100 znaków długości. Dowody o zerowej wiedzy nie radzą sobie dobrze z danymi o zmiennym rozmiarze, więc często konieczne jest uzupełnianie danych. + +```toml +pubKeyX=["0x83",...,"0x75"] +pubKeyY=["0x35",...,"0xa5"] +signature=["0xb1",...,"0x0d"] +``` + +Te trzy parametry to tablice bajtów o stałym rozmiarze. + +```toml +[[accounts]] +address="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +balance=100_000 +nonce=0 + +[[accounts]] +address="0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +balance=100_000 +nonce=0 +``` + +W ten sposób określa się tablicę struktur. Dla każdego wpisu określamy adres, saldo (w milliETH, czyli [finney](https://cryptovalleyjournal.com/glossary/finney/)) oraz następną wartość nonce. + +#### `client/src/Transfer.tsx` {#client-src-transfer-tsx} + +[Ten plik](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/client/src/Transfer.tsx) implementuje przetwarzanie po stronie klienta i generuje plik `server/noir/Prover.toml` (ten, który zawiera parametry zerowej wiedzy). + +Oto wyjaśnienie ciekawszych części. + +```tsx +export default attrs => { +``` + +Ta funkcja tworzy komponent React `Transfer`, który inne pliki mogą importować. + +```tsx + const accounts = [ + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", + ] +``` + +Są to adresy kont, adresy utworzone przez `test ...` frazę dostępu `test junk`. Jeśli chcesz używać własnych adresów, po prostu zmodyfikuj tę definicję. + +```tsx + const account = useAccount() + const wallet = createWalletClient({ + transport: custom(window.ethereum!) + }) +``` + +Te [hooki Wagmi](https://wagmi.sh/react/api/hooks) pozwalają nam uzyskać dostęp do biblioteki [viem](https://viem.sh/) i portfela. + +```tsx + const message = `send ${toAccount} ${ethAmount*1000} finney (milliEth) ${nonce}`.padEnd(100, " ") +``` + +To jest komunikat, uzupełniony spacjami. Za każdym razem, gdy jedna ze zmiennych [`useState`](https://react.dev/reference/react/useState) się zmienia, komponent jest ponownie rysowany, a `message` jest aktualizowany. + +```tsx + const sign = async () => { +``` + +Ta funkcja jest wywoływana, gdy użytkownik kliknie przycisk **Podpisz**. Komunikat jest aktualizowany automatycznie, ale podpis wymaga zatwierdzenia przez użytkownika w portfelu i nie chcemy o to prosić, jeśli nie jest to konieczne. + +```tsx + const signature = await wallet.signMessage({ + account: fromAccount, + message, + }) +``` + +Poproś portfel o [podpisanie komunikatu](https://viem.sh/docs/accounts/local/signMessage). + +```tsx + const hash = hashMessage(message) +``` + +Pobierz hasz komunikatu. Pomocne jest dostarczenie go użytkownikowi do debugowania (kodu Noir). + +```tsx + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +[Pobierz klucz publiczny](https://viem.sh/docs/utilities/recoverPublicKey). Jest to wymagane dla funkcji [Noir `ecrecover`](https://github.com/colinnielsen/ecrecover-noir). + +```tsx + setSignature(signature) + setHash(hash) + setPubKey(pubKey) +``` + +Ustaw zmienne stanu. Zrobienie tego powoduje ponowne narysowanie komponentu (po wyjściu z funkcji `sign`) i pokazuje użytkownikowi zaktualizowane wartości. + +```tsx + let proverToml = ` +``` + +Tekst dla `Prover.toml`. + +```tsx +message="${message}" + +pubKeyX=${hexToArray(pubKey.slice(4,4+2*32))} +pubKeyY=${hexToArray(pubKey.slice(4+2*32))} +``` + +Viem dostarcza nam klucz publiczny w postaci 65-bajtowego ciągu szesnastkowego. Pierwszy bajt to `0x04`, znacznik wersji. Następnie 32 bajty dla `x` klucza publicznego, a potem 32 bajty dla `y` klucza publicznego. + +Jednak Noir oczekuje, że otrzyma te informacje jako dwie tablice bajtów, jedną dla `x` i jedną dla `y`. Łatwiej jest to parsować tutaj po stronie klienta, niż jako część dowodu o zerowej wiedzy. + +Należy zauważyć, że jest to ogólnie dobra praktyka w dowodach o zerowej wiedzy. Kod wewnątrz dowodu o zerowej wiedzy jest kosztowny, więc każde przetwarzanie, które można wykonać poza dowodem o zerowej wiedzy, _powinno_ być wykonane poza nim. + +```tsx +signature=${hexToArray(signature.slice(2,-2))} +``` + +Podpis jest również dostarczany jako 65-bajtowy ciąg szesnastkowy. Jednak ostatni bajt jest potrzebny tylko do odzyskania klucza publicznego. Ponieważ klucz publiczny zostanie już dostarczony do kodu Noir, nie potrzebujemy go do weryfikacji podpisu, a kod Noir go nie wymaga. + +```tsx +${accounts.map(accountInProverToml).reduce((a,b) => a+b, "")} +` +``` + +Podaj konta. + +```tsx + setProverToml(proverToml) + } + + return ( + <> +

Przelew

+``` + +To jest format HTML (a dokładniej [JSX](https://react.dev/learn/writing-markup-with-jsx)) komponentu. + +#### `server/noir/src/main.nr` {#server-noir-src-main-nr} + +[Ten plik](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/src/main.nr) to faktyczny kod o zerowej wiedzy. + +``` +use std::hash::pedersen_hash; +``` + +[Hasz Pedersena](https://rya-sge.github.io/access-denied/2024/05/07/pedersen-hash-function/) jest dostarczany ze [standardową biblioteką Noir](https://noir-lang.org/docs/noir/standard_library/cryptographic_primitives/hashes#pedersen_hash). Dowody o zerowej wiedzy często używają tej funkcji haszującej. Jest znacznie łatwiejszy do obliczenia wewnątrz [obwodów arytmetycznych](https://rareskills.io/post/arithmetic-circuit) w porównaniu ze standardowymi funkcjami haszującymi. + +``` +use keccak256::keccak256; +use dep::ecrecover; +``` + +Te dwie funkcje to biblioteki zewnętrzne, zdefiniowane w [`Nargo.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Nargo.toml). Są dokładnie tym, na co wskazują ich nazwy: funkcją, która oblicza [hasz keccak256](https://emn178.github.io/online-tools/keccak_256.html) oraz funkcją, która weryfikuje podpisy Ethereum i odzyskuje adres Ethereum osoby podpisującej. + +``` +global ACCOUNT_NUMBER : u32 = 5; +``` + +Noir jest inspirowany językiem [Rust](https://www.rust-lang.org/). Zmienne domyślnie są stałymi. W ten sposób definiujemy globalne stałe konfiguracyjne. W szczególności `ACCOUNT_NUMBER` to liczba kont, które przechowujemy. + +Typy danych o nazwie `u` to ta liczba bitów, bez znaku. Jedyne obsługiwane typy to `u8`, `u16`, `u32`, `u64` i `u128`. + +``` +global FLAT_ACCOUNT_FIELDS : u32 = 2; +``` + +Ta zmienna jest używana do haszowania Pedersena kont, jak wyjaśniono poniżej. + +``` +global MESSAGE_LENGTH : u32 = 100; +``` + +Jak wyjaśniono powyżej, długość komunikatu jest stała. Jest ona określona tutaj. + +``` +global ASCII_MESSAGE_LENGTH : [u8; 3] = [0x31, 0x30, 0x30]; +global HASH_BUFFER_SIZE : u32 = 26+3+MESSAGE_LENGTH; +``` + +[Podpisy EIP-191](https://eips.ethereum.org/EIPS/eip-191) wymagają bufora z 26-bajtowym prefiksem, po którym następuje długość komunikatu w ASCII, a na końcu sam komunikat. + +``` +struct Account { + balance: u128, + address: Field, + nonce: u32, +} +``` + +Informacje, które przechowujemy o koncie. [`Pole`](https://noir-lang.org/docs/noir/concepts/data_types/fields) to liczba, zazwyczaj do 253 bitów, która może być używana bezpośrednio w [obwodzie arytmetycznym](https://rareskills.io/post/arithmetic-circuit), który implementuje dowód o zerowej wiedzy. Tutaj używamy `Pola` do przechowywania 160-bitowego adresu Ethereum. + +``` +struct TransferTxn { + from: Field, + to: Field, + amount: u128, + nonce: u32 +} +``` + +Informacje, które przechowujemy dla transakcji przelewu. + +``` +fn flatten_account(account: Account) -> [Field; FLAT_ACCOUNT_FIELDS] { +``` + +Definicja funkcji. Parametrem jest informacja o `Koncie`. Wynikiem jest tablica zmiennych `Pole`, której długość to `FLAT_ACCOUNT_FIELDS` + +``` + let flat = [ + account.address, + ((account.balance << 32) + account.nonce.into()).into(), + ]; +``` + +Pierwszą wartością w tablicy jest adres konta. Druga zawiera zarówno saldo, jak i nonce. Wywołania `.into()` zmieniają liczbę na typ danych, którym musi być. `account.nonce` to wartość `u32`, ale aby dodać ją do `account.balance << 32`, wartości `u128`, musi to być `u128`. To jest pierwsze `.into()`. Drugi konwertuje wynik `u128` na `Pole`, aby zmieścił się w tablicy. + +``` + flat +} +``` + +W Noir funkcje mogą zwracać wartość tylko na końcu (nie ma wcześniejszego zwrotu). Aby określić wartość zwracaną, należy ją obliczyć tuż przed nawiasem zamykającym funkcję. + +``` +fn flatten_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] { +``` + +Ta funkcja zamienia tablicę kont na tablicę `Pól`, która może być użyta jako dane wejściowe do hasza Petersena. + +``` + let mut flat: [Field; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER] = [0; FLAT_ACCOUNT_FIELDS*ACCOUNT_NUMBER]; +``` + +W ten sposób określa się zmienną mutowalną, czyli _nie_ stałą. Zmienne w Noir muszą zawsze mieć wartość, więc inicjalizujemy tę zmienną samymi zerami. + +``` + for i in 0..ACCOUNT_NUMBER { +``` + +To jest pętla `for`. Należy zauważyć, że granice są stałymi. Pętle w Noir muszą mieć granice znane w czasie kompilacji. Powodem jest to, że obwody arytmetyczne nie obsługują kontroli przepływu. Podczas przetwarzania pętli `for` kompilator po prostu umieszcza kod wewnątrz niej wielokrotnie, raz dla każdej iteracji. + +``` + let fields = flatten_account(accounts[i]); + for j in 0..FLAT_ACCOUNT_FIELDS { + flat[i*FLAT_ACCOUNT_FIELDS + j] = fields[j]; + } + } + + flat +} + +fn hash_accounts(accounts: [Account; ACCOUNT_NUMBER]) -> Field { + pedersen_hash(flatten_accounts(accounts)) +} +``` + +Wreszcie doszliśmy do funkcji, która haszuje tablicę kont. + +``` +fn find_account(accounts: [Account; ACCOUNT_NUMBER], address: Field) -> u32 { + let mut account : u32 = ACCOUNT_NUMBER; + + for i in 0..ACCOUNT_NUMBER { + if accounts[i].address == address { + account = i; + } + } +``` + +Ta funkcja znajduje konto o określonym adresie. Ta funkcja byłaby strasznie nieefektywna w standardowym kodzie, ponieważ iteruje po wszystkich kontach, nawet po znalezieniu adresu. + +Jednak w dowodach o zerowej wiedzy nie ma kontroli przepływu. Jeśli kiedykolwiek będziemy musieli sprawdzić warunek, musimy go sprawdzać za każdym razem. + +Podobna rzecz dzieje się z instrukcjami `if`. Instrukcja `if` w powyższej pętli jest tłumaczona na te instrukcje matematyczne. + +_wynikwarunku = konta[i].adres == adres_ // jeden, jeśli są równe, zero w przeciwnym razie + +_kontonowe = wynikwarunku\*i + (1-wynikwarunku)\*kontostare_ + +```rust + assert (account < ACCOUNT_NUMBER, f"{address} nie ma konta"); + + account +} +``` + +Funkcja [`assert`](https://noir-lang.org/docs/dev/noir/concepts/assert) powoduje awarię dowodu o zerowej wiedzy, jeśli asercja jest fałszywa. W tym przypadku, jeśli nie możemy znaleźć konta z odpowiednim adresem. Aby zgłosić adres, używamy [ciągu formatującego](https://noir-lang.org/docs/noir/concepts/data_types/strings#format-strings). + +```rust +fn apply_transfer_txn(accounts: [Account; ACCOUNT_NUMBER], txn: TransferTxn) -> [Account; ACCOUNT_NUMBER] { +``` + +Ta funkcja stosuje transakcję przelewu i zwraca nową tablicę kont. + +```rust + let from = find_account(accounts, txn.from); + let to = find_account(accounts, txn.to); + + let (txnFrom, txnAmount, txnNonce, accountNonce) = + (txn.from, txn.amount, txn.nonce, accounts[from].nonce); +``` + +Nie możemy uzyskać dostępu do elementów struktury wewnątrz ciągu formatującego w Noir, więc tworzymy użyteczną kopię. + +```rust + assert (accounts[from].balance >= txn.amount, + f"{txnFrom} nie ma {txnAmount} finney"); + + assert (accounts[from].nonce == txn.nonce, + f"Transakcja ma nonce {txnNonce}, ale oczekuje się, że konto użyje {accountNonce}"); +``` + +Są to dwa warunki, które mogą unieważnić transakcję. + +```rust + let mut newAccounts = accounts; + + newAccounts[from].balance -= txn.amount; + newAccounts[from].nonce += 1; + newAccounts[to].balance += txn.amount; + + newAccounts +} +``` + +Utwórz nową tablicę kont, a następnie ją zwróć. + +```rust +fn readAddress(messageBytes: [u8; MESSAGE_LENGTH]) -> Field +``` + +Ta funkcja odczytuje adres z komunikatu. + +```rust +{ + let mut result : Field = 0; + + for i in 7..47 { +``` + +Adres ma zawsze 20 bajtów (czyli 40 cyfr szesnastkowych) długości i zaczyna się od znaku nr 7. + +```rust + result *= 0x10; + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + result += (messageBytes[i]-48).into(); + } + if messageBytes[i] >= 65 & messageBytes[i] <= 70 { // A-F + result += (messageBytes[i]-65+10).into() + } + if messageBytes[i] >= 97 & messageBytes[i] <= 102 { // a-f + result += (messageBytes[i]-97+10).into() + } + } + + result +} + +fn readAmountAndNonce(messageBytes: [u8; MESSAGE_LENGTH]) -> (u128, u32) +``` + +Odczytaj kwotę i nonce z komunikatu. + +```rust +{ + let mut amount : u128 = 0; + let mut nonce: u32 = 0; + let mut stillReadingAmount: bool = true; + let mut lookingForNonce: bool = false; + let mut stillReadingNonce: bool = false; +``` + +W komunikacie pierwsza liczba po adresie to kwota finney (czyli tysięczna ETH) do przelania. Druga liczba to nonce. Każdy tekst między nimi jest ignorowany. + +```rust + for i in 48..MESSAGE_LENGTH { + if messageBytes[i] >= 48 & messageBytes[i] <= 57 { // 0-9 + let digit = (messageBytes[i]-48); + + if stillReadingAmount { + amount = amount*10 + digit.into(); + } + + if lookingForNonce { // Właśnie to znaleźliśmy + stillReadingNonce = true; + lookingForNonce = false; + } + + if stillReadingNonce { + nonce = nonce*10 + digit.into(); + } + } else { + if stillReadingAmount { + stillReadingAmount = false; + lookingForNonce = true; + } + if stillReadingNonce { + stillReadingNonce = false; + } + } + } + + (amount, nonce) +} +``` + +Zwracanie [krotki](https://noir-lang.org/docs/noir/concepts/data_types/tuples) to sposób Noir na zwracanie wielu wartości z funkcji. + +```rust +fn readTransferTxn(message: str) -> TransferTxn +{ + let mut txn: TransferTxn = TransferTxn { from: 0, to: 0, amount:0, nonce:0 }; + let messageBytes = message.as_bytes(); + + txn.to = readAddress(messageBytes); + let (amount, nonce) = readAmountAndNonce(messageBytes); + txn.amount = amount; + txn.nonce = nonce; + + txn +} +``` + +Ta funkcja konwertuje komunikat na bajty, a następnie konwertuje kwoty na `TransferTxn`. + +```rust +// Odpowiednik hashMessage Viem +// https://viem.sh/docs/utilities/hashMessage#hashmessage +fn hashMessage(message: str) -> [u8;32] { +``` + +Mogliśmy użyć hasza Pedersena dla kont, ponieważ są one haszowane tylko wewnątrz dowodu o zerowej wiedzy. Jednak w tym kodzie musimy sprawdzić podpis komunikatu, który jest generowany przez przeglądarkę. W tym celu musimy postępować zgodnie z formatem podpisywania Ethereum w [EIP 191](https://eips.ethereum.org/EIPS/eip-191). Oznacza to, że musimy utworzyć połączony bufor ze standardowym prefiksem, długością komunikatu w ASCII i samym komunikatem, a następnie użyć standardowego dla Ethereum keccak256 do jego haszowania. + +```rust + // prefiks ASCII + let prefix_bytes = [ + 0x19, // \x19 + 0x45, // 'E' + 0x74, // 't' + 0x68, // 'h' + 0x65, // 'e' + 0x72, // 'r' + 0x65, // 'e' + 0x75, // 'u' + 0x6D, // 'm' + 0x20, // ' ' + 0x53, // 'S' + 0x69, // 'i' + 0x67, // 'g' + 0x6E, // 'n' + 0x65, // 'e' + 0x64, // 'd' + 0x20, // ' ' + 0x4D, // 'M' + 0x65, // 'e' + 0x73, // 's' + 0x73, // 's' + 0x61, // 'a' + 0x67, // 'g' + 0x65, // 'e' + 0x3A, // ':' + 0x0A // '\n' + ]; +``` + +Aby uniknąć przypadków, w których aplikacja prosi użytkownika o podpisanie komunikatu, który może być użyty jako transakcja lub w innym celu, EIP 191 określa, że wszystkie podpisane komunikaty zaczynają się od znaku 0x19 (nie jest to prawidłowy znak ASCII), po którym następuje `Ethereum Signed Message:` i nowa linia. + +```rust + let mut buffer: [u8; HASH_BUFFER_SIZE] = [0u8; HASH_BUFFER_SIZE]; + for i in 0..26 { + buffer[i] = prefix_bytes[i]; + } + + let messageBytes : [u8; MESSAGE_LENGTH] = message.as_bytes(); + + if MESSAGE_LENGTH <= 9 { + for i in 0..1 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+1] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 10 & MESSAGE_LENGTH <= 99 { + for i in 0..2 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+2] = messageBytes[i]; + } + } + + if MESSAGE_LENGTH >= 100 { + for i in 0..3 { + buffer[i+26] = ASCII_MESSAGE_LENGTH[i]; + } + + for i in 0..MESSAGE_LENGTH { + buffer[i+26+3] = messageBytes[i]; + } + } + + assert(MESSAGE_LENGTH < 1000, "Komunikaty, których długość przekracza trzy cyfry, nie są obsługiwane"); +``` + +Obsługuj komunikaty o długości do 999 i zakończ niepowodzeniem, jeśli jest większa. Dodałem ten kod, mimo że długość komunikatu jest stała, ponieważ ułatwia to jego zmianę. W systemie produkcyjnym prawdopodobnie po prostu założyłbyś, że `MESSAGE_LENGTH` nie zmienia się ze względu na lepszą wydajność. + +```rust + keccak256::keccak256(buffer, HASH_BUFFER_SIZE) +} +``` + +Użyj standardowej funkcji Ethereum `keccak256`. + +```rust +fn signatureToAddressAndHash( + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64] + ) -> (Field, Field, Field) // adres, pierwsze 16 bajtów hasza, ostatnie 16 bajtów hasza +{ +``` + +Ta funkcja weryfikuje podpis, co wymaga hasza komunikatu. Następnie dostarcza nam adres, który go podpisał, oraz hasz komunikatu. Hasz komunikatu jest dostarczany w dwóch wartościach `Pole`, ponieważ są one łatwiejsze w użyciu w pozostałej części programu niż tablica bajtów. + +Musimy użyć dwóch wartości `Pole`, ponieważ obliczenia na polach są wykonywane [modulo](https://en.wikipedia.org/wiki/Modulo) dużej liczby, ale ta liczba jest zwykle mniejsza niż 256 bitów (w przeciwnym razie trudno byłoby wykonać te obliczenia w EVM). + +```rust + let hash = hashMessage(message); + + let mut (hash1, hash2) = (0,0); + + for i in 0..16 { + hash1 = hash1*256 + hash[31-i].into(); + hash2 = hash2*256 + hash[15-i].into(); + } +``` + +Określ `hash1` i `hash2` jako zmienne mutowalne i zapisz w nich hasz bajt po bajcie. + +```rust + ( + ecrecover::ecrecover(pubKeyX, pubKeyY, signature, hash), +``` + +Jest to podobne do [`ecrecover` w Solidity](https://docs.soliditylang.org/en/v0.8.30/cheatsheet.html#mathematical-and-cryptographic-functions), z dwiema ważnymi różnicami: + +- Jeśli podpis nie jest prawidłowy, wywołanie kończy się niepowodzeniem `assert`, a program jest przerywany. +- Chociaż klucz publiczny można odzyskać z podpisu i hasza, jest to przetwarzanie, które można wykonać zewnętrznie, a zatem nie warto go robić wewnątrz dowodu o zerowej wiedzy. Jeśli ktoś spróbuje nas tu oszukać, weryfikacja podpisu zakończy się niepowodzeniem. + +```rust + hash1, + hash2 + ) +} + +fn main( + accounts: [Account; ACCOUNT_NUMBER], + message: str, + pubKeyX: [u8; 32], + pubKeyY: [u8; 32], + signature: [u8; 64], + ) -> pub ( + Field, // Hasz starej tablicy kont + Field, // Hasz nowej tablicy kont + Field, // Pierwsze 16 bajtów hasza komunikatu + Field, // Ostatnie 16 bajtów hasza komunikatu + ) +``` + +Wreszcie dochodzimy do funkcji `main`. Musimy udowodnić, że mamy transakcję, która prawidłowo zmienia hasz kont ze starej wartości na nową. Musimy również udowodnić, że ma ona ten konkretny hasz transakcji, aby osoba, która ją wysłała, wiedziała, że jej transakcja została przetworzona. + +```rust +{ + let mut txn = readTransferTxn(message); +``` + +Potrzebujemy, aby `txn` było mutowalne, ponieważ nie odczytujemy adresu nadawcy z komunikatu, ale z podpisu. + +```rust + let (fromAddress, txnHash1, txnHash2) = signatureToAddressAndHash( + message, + pubKeyX, + pubKeyY, + signature); + + txn.from = fromAddress; + + let newAccounts = apply_transfer_txn(accounts, txn); + + ( + hash_accounts(accounts), + hash_accounts(newAccounts), + txnHash1, + txnHash2 + ) +} +``` + +### Etap 2 - Dodawanie serwera {#stage-2} + +W drugim etapie dodajemy serwer, który odbiera i implementuje transakcje przelewów z przeglądarki. + +Aby zobaczyć to w działaniu: + +1. Zatrzymaj Vite, jeśli jest uruchomiony. + +2. Pobierz gałąź, która zawiera serwer i upewnij się, że masz wszystkie niezbędne moduły. + + ```sh + git checkout 02-add-server + cd client + npm install + cd ../server + npm install + ``` + + Nie ma potrzeby kompilowania kodu Noir, jest on taki sam jak kod, którego użyłeś w etapie 1. + +3. Uruchom serwer. + + ```sh + npm run start + ``` + +4. W osobnym oknie wiersza poleceń uruchom Vite, aby obsługiwać kod przeglądarki. + + ```sh + cd client + npm run dev + ``` + +5. Przejdź do kodu klienta pod adresem [http://localhost:5173](http://localhost:5173) + +6. Zanim będziesz mógł wystawić transakcję, musisz znać nonce, a także kwotę, którą możesz wysłać. Aby uzyskać te informacje, kliknij **Aktualizuj dane konta** i podpisz komunikat. + + Mamy tu dylemat. Z jednej strony nie chcemy podpisywać komunikatu, który można ponownie wykorzystać ([atak typu replay](https://en.wikipedia.org/wiki/Replay_attack)), dlatego w ogóle chcemy nonce. Jednak nie mamy jeszcze nonce. Rozwiązaniem jest wybranie nonce, które można użyć tylko raz i które już mamy po obu stronach, na przykład bieżący czas. + + Problem z tym rozwiązaniem polega na tym, że czas może nie być idealnie zsynchronizowany. Zamiast tego podpisujemy wartość, która zmienia się co minutę. Oznacza to, że nasze okno podatności na ataki typu replay wynosi co najwyżej jedną minutę. Biorąc pod uwagę, że w środowisku produkcyjnym podpisane żądanie będzie chronione przez TLS, a druga strona tunelu — serwer — może już ujawnić saldo i nonce (musi je znać, aby działać), jest to akceptowalne ryzyko. + +7. Gdy przeglądarka otrzyma z powrotem saldo i nonce, wyświetli formularz przelewu. Wybierz adres docelowy i kwotę, a następnie kliknij **Przelej**. Podpisz to żądanie. + +8. Aby zobaczyć przelew, kliknij **Aktualizuj dane konta** lub spójrz w okno, w którym uruchamiasz serwer. Serwer rejestruje stan za każdym razem, gdy się zmienia. + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Nasłuchiwanie na porcie 3000 + Transakcja send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 36000 finney (milliEth) 0 przetworzona + Nowy stan: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 ma 64000 (1) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 ma 100000 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC ma 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 ma 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 ma 100000 (0) + Transakcja send 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 7200 finney (milliEth) 1 przetworzona + Nowy stan: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 ma 56800 (2) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 ma 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC ma 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 ma 136000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 ma 100000 (0) + Transakcja send 0x90F79bf6EB2c4f870365E785982E1f101E93b906 3000 finney (milliEth) 2 przetworzona + Nowy stan: + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 ma 53800 (3) + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 ma 107200 (0) + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC ma 100000 (0) + 0x90F79bf6EB2c4f870365E785982E1f101E93b906 ma 139000 (0) + 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65 ma 100000 (0) + ``` + +#### `server/index.mjs` {#server-index-mjs-1} + +[Ten plik](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/index.mjs) zawiera proces serwera i współdziała z kodem Noir w [`main.nr`](https://github.com/qbzzt/250911-zk-bank/blob/02-add-server/server/noir/src/main.nr). Oto wyjaśnienie ciekawszych części. + +```js +import { Noir } from '@noir-lang/noir_js' +``` + +Biblioteka [noir.js](https://www.npmjs.com/package/@noir-lang/noir_js) stanowi interfejs między kodem JavaScript a kodem Noir. + +```js +const circuit = JSON.parse(await fs.readFile("./noir/target/zkBank.json")) +const noir = new Noir(circuit) +``` + +Załaduj obwód arytmetyczny — skompilowany program Noir, który stworzyliśmy w poprzednim etapie — i przygotuj go do wykonania. + +```js +// Informacje o koncie udostępniamy tylko w odpowiedzi na podpisane żądanie +const accountInformation = async signature => { + const fromAddress = await recoverAddress({ + hash: hashMessage("Get account data " + Math.floor((new Date().getTime())/60000)), + signature + }) +``` + +Aby podać informacje o koncie, potrzebujemy tylko podpisu. Powodem jest to, że wiemy już, jaki będzie komunikat, a zatem znamy jego hasz. + +```js +const processMessage = async (message, signature) => { +``` + +Przetwórz komunikat i wykonaj zakodowaną w nim transakcję. + +```js + // Pobierz klucz publiczny + const pubKey = await recoverPublicKey({ + hash, + signature + }) +``` + +Teraz, gdy uruchamiamy JavaScript na serwerze, możemy pobrać klucz publiczny tam, a nie na kliencie. + +```js + let noirResult + try { + noirResult = await noir.execute({ + message, + signature: signature.slice(2,-2).match(/.{2}/g).map(x => `0x${x}`), + pubKeyX, + pubKeyY, + accounts: Accounts + }) +``` + +`noir.execute` uruchamia program Noir. Parametry są równoważne z tymi podanymi w [`Prover.toml`](https://github.com/qbzzt/250911-zk-bank/blob/01-manual-zk/server/noir/Prover.toml). Zauważ, że długie wartości są podawane jako tablica ciągów szesnastkowych (`["0x60", "0xA7"]`), a nie jako pojedyncza wartość szesnastkowa (`0x60A7`), tak jak to robi Viem. + +```js + } catch (err) { + console.log(`Noir error: ${err}`) + throw Error("Nieprawidłowa transakcja, nie przetworzono") + } +``` + +Jeśli wystąpi błąd, przechwyć go, a następnie przekaż uproszczoną wersję do klienta. + +```js + Accounts[fromAccountNumber].nonce++ + Accounts[fromAccountNumber].balance -= amount + Accounts[toAccountNumber].balance += amount +``` + +Zastosuj transakcję. Zrobiliśmy to już w kodzie Noir, ale łatwiej jest to zrobić ponownie tutaj, niż wyodrębniać stamtąd wynik. + +```js +let Accounts = [ + { + address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + balance: 5000, + nonce: 0, + }, +``` + +Początkowa struktura `Konta`. + +### Etap 3 - Inteligentne kontrakty Ethereum {#stage-3} + +1. Zatrzymaj procesy serwera i klienta. + +2. Pobierz gałąź z inteligentnymi kontraktami i upewnij się, że masz wszystkie niezbędne moduły. + + ```sh + git checkout 03-smart-contracts + cd client + npm install + cd ../server + npm install + ``` + +3. Uruchom `anvil` w osobnym oknie wiersza poleceń. + +4. Wygeneruj klucz weryfikacyjny i weryfikator solidity, a następnie skopiuj kod weryfikatora do projektu Solidity. + + ```sh + cd noir + bb write_vk -b ./target/zkBank.json -o ./target --oracle_hash keccak + bb write_solidity_verifier -k ./target/vk -o ./target/Verifier.sol + cp target/Verifier.sol ../../smart-contracts/src + ``` + +5. Przejdź do inteligentnych kontraktów i ustaw zmienne środowiskowe, aby używać blockchaina `anvil`. + + ```sh + cd ../../smart-contracts + export ETH_RPC_URL=http://localhost:8545 + ETH_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + ``` + +6. Wdróż `Verifier.sol` i przechowaj adres w zmiennej środowiskowej. + + ```sh + VERIFIER_ADDRESS=`forge create src/Verifier.sol:HonkVerifier --private-key $ETH_PRIVATE_KEY --optimize --broadcast | awk '/Deployed to:/ {print $3}'` + echo $VERIFIER_ADDRESS + ``` + +7. Wdróż kontrakt `ZkBank`. + + ```sh + ZKBANK_ADDRESS=`forge create ZkBank --private-key $ETH_PRIVATE_KEY --broadcast --constructor-args $VERIFIER_ADDRESS 0x199aa62af8c1d562a6ec96e66347bf3240ab2afb5d022c895e6bf6a5e617167b | awk '/Deployed to:/ {print $3}'` + echo $ZKBANK_ADDRESS + ``` + + Wartość `0x199..67b` to hasz Pedersena początkowego stanu `Kont`. Jeśli zmodyfikujesz ten stan początkowy w pliku `server/index.mjs`, możesz uruchomić transakcję, aby zobaczyć początkowy hasz zgłoszony przez dowód o zerowej wiedzy. + +8. Uruchom serwer. + + ```sh + cd ../server + npm run start + ``` + +9. Uruchom klienta w innym oknie wiersza poleceń. + + ```sh + cd client + npm run dev + ``` + +10. Uruchom kilka transakcji. + +11. Aby zweryfikować, czy stan zmienił się w łańcuchu, uruchom ponownie proces serwera. Zobaczysz, że `ZkBank` nie akceptuje już transakcji, ponieważ oryginalna wartość hasza w transakcjach różni się od wartości hasza przechowywanej w łańcuchu. + + Jest to oczekiwany typ błędu. + + ``` + ori@CryptoDocGuy:~/x/250911-zk-bank/server$ npm run start + + > server@1.0.0 start + > node --experimental-json-modules index.mjs + + Nasłuchiwanie na porcie 3000 + Błąd weryfikacji: ContractFunctionExecutionError: Funkcja kontraktu "processTransaction" została cofnięta z następującego powodu: + Zły stary hasz stanu + + Wywołanie kontraktu: + adres: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + funkcja: processTransaction(bytes _proof, bytes32[] _publicInputs) + argumenty: (0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf00000000000000000000000000000000000000000000000b75c020998797da7800000000000000000000000000000000000000000000000 + ``` + +#### `server/index.mjs` {#server-index-mjs-2} + +Zmiany w tym pliku dotyczą głównie tworzenia faktycznego dowodu i przesyłania go do łańcucha. + +```js +import { exec } from 'child_process' +import util from 'util' + +const execPromise = util.promisify(exec) +``` + +Musimy użyć [pakietu Barretenberg](https://github.com/AztecProtocol/aztec-packages/tree/next/barretenberg), aby utworzyć faktyczny dowód do wysłania w łańcuchu. Możemy użyć tego pakietu, uruchamiając interfejs wiersza poleceń (`bb`) lub używając [biblioteki JavaScript, `bb.js`](https://www.npmjs.com/package/@aztec/bb.js). Biblioteka JavaScript jest znacznie wolniejsza niż natywne uruchamianie kodu, więc używamy tutaj [`exec`](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback), aby użyć wiersza poleceń. + +Zauważ, że jeśli zdecydujesz się na użycie `bb.js`, musisz użyć wersji kompatybilnej z wersją Noir, której używasz. W chwili pisania tego tekstu obecna wersja Noir (1.0.0-beta.11) używa `bb.js` w wersji 0.87. + +```js +const zkBankAddress = process.env.ZKBANK_ADDRESS || "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" +``` + +Adres tutaj jest tym, który otrzymujesz, gdy zaczynasz z czystym `anvil` i postępujesz zgodnie z powyższymi wskazówkami. + +```js +const walletClient = createWalletClient({ + chain: anvil, + transport: http(), + account: privateKeyToAccount("0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6") +}) +``` + +Ten klucz prywatny jest jednym z domyślnych, wstępnie zasilonych kont w `anvil`. + +```js +const generateProof = async (witness, fileID) => { +``` + +Wygeneruj dowód za pomocą pliku wykonywalnego `bb`. + +```js + const fname = `witness-${fileID}.gz` + await fs.writeFile(fname, witness) +``` + +Zapisz świadka do pliku. + +```js + await execPromise(`bb prove -b ./noir/target/zkBank.json -w ${fname} -o ${fileID} --oracle_hash keccak --output_format fields`) +``` + +Faktycznie utwórz dowód. Ten krok tworzy również plik ze zmiennymi publicznymi, ale nie potrzebujemy go. Otrzymaliśmy już te zmienne z `noir.execute`. + +```js + const proof = "0x" + JSON.parse(await fs.readFile(`./${fileID}/proof_fields.json`)).reduce((a,b) => a+b, "").replace(/0x/g, "") +``` + +Dowód to tablica JSON wartości `Pole`, z których każda jest reprezentowana jako wartość szesnastkowa. Musimy jednak wysłać go w transakcji jako pojedynczą wartość `bajtów`, którą Viem reprezentuje za pomocą dużego ciągu szesnastkowego. Tutaj zmieniamy format, łącząc wszystkie wartości, usuwając wszystkie `0x`, a następnie dodając jedno na końcu. + +```js + await execPromise(`rm -r ${fname} ${fileID}`) + + return proof +} +``` + +Posprzątaj i zwróć dowód. + +```js +const processMessage = async (message, signature) => { + . + . + . + + const publicFields = noirResult.returnValue.map(x=>'0x' + x.slice(2).padStart(64, "0")) +``` + +Pola publiczne muszą być tablicą 32-bajtowych wartości. Jednakże, ponieważ musieliśmy podzielić hasz transakcji na dwie wartości `Pole`, pojawia się on jako wartość 16-bajtowa. Tutaj dodajemy zera, aby Viem zrozumiał, że jest to w rzeczywistości 32 bajty. + +```js + const proof = await generateProof(noirResult.witness, `${fromAddress}-${nonce}`) +``` + +Każdy adres używa każdego nonce tylko raz, więc możemy użyć kombinacji `fromAddress` i `nonce` jako unikalnego identyfikatora dla pliku świadka i katalogu wyjściowego. + +```js + try { + await zkBank.write.processTransaction([ + proof, publicFields]) + } catch (err) { + console.log(`Verification error: ${err}`) + throw Error("Nie można zweryfikować transakcji w łańcuchu") + } + . + . + . +} +``` + +Wyślij transakcję do łańcucha. + +#### `smart-contracts/src/ZkBank.sol` {#smart-contracts-src-zkbank-sol} + +To jest kod w łańcuchu, który odbiera transakcję. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.21; + +import {HonkVerifier} from "./Verifier.sol"; + +contract ZkBank { + HonkVerifier immutable myVerifier; + bytes32 currentStateHash; + + constructor(address _verifierAddress, bytes32 _initialStateHash) { + currentStateHash = _initialStateHash; + myVerifier = HonkVerifier(_verifierAddress); + } +``` + +Kod w łańcuchu musi śledzić dwie zmienne: weryfikator (osobny kontrakt tworzony przez `nargo`) i bieżący hasz stanu. + +```solidity + event TransactionProcessed( + bytes32 indexed transactionHash, + bytes32 oldStateHash, + bytes32 newStateHash + ); +``` + +Za każdym razem, gdy stan się zmienia, emitujemy zdarzenie `TransactionProcessed`. + +```solidity + function processTransaction( + bytes calldata _proof, + bytes32[] calldata _publicFields + ) public { +``` + +Ta funkcja przetwarza transakcje. Otrzymuje dowód (jako `bajty`) i publiczne dane wejściowe (jako tablicę `bytes32`), w formacie wymaganym przez weryfikator (w celu zminimalizowania przetwarzania w łańcuchu, a tym samym kosztów gazu). + +```solidity + require(_publicInputs[0] == currentStateHash, + "Zły stary hasz stanu"); +``` + +Dowód o zerowej wiedzy musi polegać na tym, że transakcja zmienia nasz obecny hasz na nowy. + +```solidity + myVerifier.verify(_proof, _publicFields); +``` + +Wywołaj kontrakt weryfikatora, aby zweryfikować dowód o zerowej wiedzy. Ten krok cofa transakcję, jeśli dowód o zerowej wiedzy jest nieprawidłowy. + +```solidity + currentStateHash = _publicFields[1]; + + emit TransactionProcessed( + _publicFields[2]<<128 | _publicFields[3], + _publicFields[0], + _publicFields[1] + ); + } +} +``` + +Jeśli wszystko się zgadza, zaktualizuj hasz stanu na nową wartość i wyemituj zdarzenie `TransactionProcessed`. + +## Nadużycia przez scentralizowany komponent {#abuses} + +Bezpieczeństwo informacji składa się z trzech atrybutów: + +- _Poufność_, użytkownicy nie mogą czytać informacji, do których nie są upoważnieni. +- _Integralność_, informacje nie mogą być zmieniane, z wyjątkiem upoważnionych użytkowników w autoryzowany sposób. +- _Dostępność_, autoryzowani użytkownicy mogą korzystać z systemu. + +W tym systemie integralność jest zapewniana poprzez dowody o zerowej wiedzy. Dostępność jest znacznie trudniejsza do zagwarantowania, a poufność jest niemożliwa, ponieważ bank musi znać saldo każdego konta i wszystkie transakcje. Nie ma sposobu, aby uniemożliwić podmiotowi, który posiada informacje, udostępnianie tych informacji. + +Możliwe byłoby stworzenie prawdziwie poufnego banku przy użyciu [adresów stealth](https://vitalik.eth.limo/general/2023/01/20/stealth.html), ale to wykracza poza zakres tego artykułu. + +### Fałszywe informacje {#false-info} + +Jednym ze sposobów, w jaki serwer może naruszyć integralność, jest dostarczanie fałszywych informacji, gdy [żądane są dane](https://github.com/qbzzt/250911-zk-bank/blob/03-smart-contracts/server/index.mjs#L278-L291). + +Aby to rozwiązać, możemy napisać drugi program Noir, który otrzymuje konta jako prywatne dane wejściowe i adres, dla którego żądane są informacje, jako publiczne dane wejściowe. Danymi wyjściowymi są saldo i nonce tego adresu oraz hasz kont. + +Oczywiście tego dowodu nie można zweryfikować w łańcuchu, ponieważ nie chcemy publikować nonce i sald w łańcuchu. Można go jednak zweryfikować za pomocą kodu klienta działającego w przeglądarce. + +### Wymuszone transakcje {#forced-txns} + +Zwykłym mechanizmem zapewniającym dostępność i zapobiegającym cenzurze na L2 są [wymuszone transakcje](https://docs.optimism.io/stack/transactions/forced-transaction). Ale wymuszone transakcje nie łączą się z dowodami o zerowej wiedzy. Serwer jest jedynym podmiotem, który może weryfikować transakcje. + +Możemy zmodyfikować `smart-contracts/src/ZkBank.sol`, aby akceptować wymuszone transakcje i uniemożliwić serwerowi zmianę stanu do czasu ich przetworzenia. Jednak to naraża nas na prosty atak typu „odmowa usługi”. Co jeśli wymuszona transakcja jest nieprawidłowa i dlatego niemożliwa do przetworzenia? + +Rozwiązaniem jest posiadanie dowodu o zerowej wiedzy, że wymuszona transakcja jest nieprawidłowa. Daje to serwerowi trzy opcje: + +- Przetwórz wymuszoną transakcję, dostarczając dowód o zerowej wiedzy, że została przetworzona i nowy hasz stanu. +- Odrzuć wymuszoną transakcję i dostarcz do kontraktu dowód o zerowej wiedzy, że transakcja jest nieprawidłowa (nieznany adres, zły nonce lub niewystarczające saldo). +- Zignoruj wymuszoną transakcję. Nie ma sposobu, aby zmusić serwer do faktycznego przetworzenia transakcji, ale oznacza to, że cały system jest niedostępny. + +#### Obligacje dostępności {#avail-bonds} + +W rzeczywistej implementacji prawdopodobnie istniałby jakiś motyw zysku, aby serwer działał. Możemy wzmocnić tę zachętę, zmuszając serwer do opublikowania obligacji dostępności, którą każdy może spalić, jeśli wymuszona transakcja nie zostanie przetworzona w określonym czasie. + +### Zły kod Noir {#bad-noir-code} + +Zwykle, aby ludzie zaufali inteligentnemu kontraktowi, przesyłamy kod źródłowy do [eksploratora bloków](https://eth.blockscout.com/address/0x7D16d2c4e96BCFC8f815E15b771aC847EcbDB48b?tab=contract). Jednak w przypadku dowodów o zerowej wiedzy jest to niewystarczające. + +`Verifier.sol` zawiera klucz weryfikacyjny, który jest funkcją programu Noir. Jednak ten klucz nie mówi nam, jaki był program Noir. Aby faktycznie mieć zaufane rozwiązanie, należy przesłać program Noir (i wersję, która go utworzyła). W przeciwnym razie dowody o zerowej wiedzy mogą odzwierciedlać inny program, z tylnymi drzwiami. + +Dopóki eksploratory bloków nie zaczną pozwalać nam na przesyłanie i weryfikowanie programów Noir, powinieneś robić to samodzielnie (najlepiej do [IPFS](/developers/tutorials/ipfs-decentralized-ui/)). Wtedy zaawansowani użytkownicy będą mogli pobrać kod źródłowy, samodzielnie go skompilować, utworzyć `Verifier.sol` i zweryfikować, czy jest on identyczny z tym w łańcuchu. + +## Wnioski {#conclusion} + +Aplikacje typu plasma wymagają scentralizowanego komponentu do przechowywania informacji. To otwiera potencjalne luki w zabezpieczeniach, ale w zamian pozwala nam zachować prywatność w sposób niedostępny w samym blockchainie. Dzięki dowodom o zerowej wiedzy możemy zapewnić integralność i ewentualnie sprawić, że utrzymanie dostępności będzie ekonomicznie korzystne dla każdego, kto prowadzi scentralizowany komponent. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). + +## Podziękowania {#acknowledgements} + +- Josh Crites przeczytał szkic tego artykułu i pomógł mi w kłopotliwej kwestii związanej z Noir. + +Wszelkie pozostałe błędy są moją odpowiedzialnością. diff --git a/public/content/translations/pl/developers/tutorials/calling-a-smart-contract-from-javascript/index.md b/public/content/translations/pl/developers/tutorials/calling-a-smart-contract-from-javascript/index.md index 48a7726e19d..92c32702720 100644 --- a/public/content/translations/pl/developers/tutorials/calling-a-smart-contract-from-javascript/index.md +++ b/public/content/translations/pl/developers/tutorials/calling-a-smart-contract-from-javascript/index.md @@ -1,12 +1,8 @@ --- -title: Wywołanie inteligentnego kontraktu z JavaScript -description: Jak wywołać funkcję inteligentnego kontraktu z JavaScript za pomocą tokena Dai — przykład +title: "Wywołanie inteligentnego kontraktu z JavaScript" +description: "Jak wywołać funkcję inteligentnego kontraktu z JavaScript za pomocą tokena Dai — przykład" author: jdourlens -tags: - - "transakcje" - - "frontend" - - "JavaScript" - - "web3.js" +tags: [ "transakcje", "frontend", "JavaScript", "web3.js" ] skill: beginner lang: pl published: 2020-04-19 @@ -15,15 +11,15 @@ sourceUrl: https://ethereumdev.io/calling-a-smart-contract-from-javascript/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -W tym samouczku zobaczymy, jak wywołać funkcję [inteligentnego kontraktu](/developers/docs/smart-contracts/) za pomocą JavaScript. Najpierw odczytam stan inteligentnego kontraktu (np. saldo posiadacza ERC20), a następnie zmodyfikujemy stan blockchain poprzez transfer tokenów. Powinieneś być już zaznajomiony z [konfiguracją środowiska JS, aby wchodzić w interakcje z blockchainem](/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/). +W tym samouczku zobaczymy, jak wywołać funkcję [inteligentnego kontraktu](/developers/docs/smart-contracts/) za pomocą JavaScript. Najpierw odczytamy stan inteligentnego kontraktu (np. saldo posiadacza ERC20), a następnie zmodyfikujemy stan blockchaina, dokonując transferu tokena. Powinieneś już znać [konfigurację środowiska JS do interakcji z blockchainem](/developers/tutorials/set-up-web3js-to-use-ethereum-in-javascript/). -W tych przykładach pobawimy się tokenem DAI, w celach testowych rozwidlimy blockchain za pomocą ganache-cli i odblokujemy adres, który ma już dużo DAI: +W tym przykładzie pobawimy się tokenem DAI, w celach testowych sforkujemy blockchain za pomocą ganache-cli i odblokujemy adres, który ma już dużo DAI: ```bash ganache-cli -f https://mainnet.infura.io/v3/[YOUR INFURA KEY] -d -i 66 1 --unlock 0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81 ``` -Aby wchodzić w interakcje z inteligentnym kontraktem, potrzebujemy jego adresu i ABI: +Aby wejść w interakcję z inteligentnym kontraktem, będziemy potrzebować jego adresu i ABI: ```js const ERC20TransferABI = [ @@ -74,9 +70,9 @@ const ERC20TransferABI = [ const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f" ``` -W przypadku tego projektu usunęliśmy kompletny ABI ERC2, aby zachować tylko funkcje `balanceOf` i `transfer`, ale znajdziesz [pełny ABI ERC20 tutaj](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/). +Na potrzeby tego projektu okroiliśmy kompletne ABI ERC20, aby zachować tylko funkcje `balanceOf` i `transfer`, ale pełne ABI ERC20 można znaleźć [tutaj](https://ethereumdev.io/abi-for-erc20-contract-on-ethereum/). -Następnie musimy utworzyć instancję inteligentnego kontraktu: +Następnie musimy utworzyć instancję naszego inteligentnego kontraktu: ```js const web3 = new Web3("http://localhost:8545") @@ -84,45 +80,52 @@ const web3 = new Web3("http://localhost:8545") const daiToken = new web3.eth.Contract(ERC20TransferABI, DAI_ADDRESS) ``` -Ustawimy też dwa adresy: +Skonfigurujemy również dwa adresy: -- ten, który otrzyma transfer i -- ten, który już odblokowaliśmy, który go wyśle: +- ten, który otrzyma transfer, oraz +- ten, który już odblokowaliśmy i który go wyśle: ```js -const senderAddress = "0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81"const receiverAddress = "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" +const senderAddress = "0x4d10ae710Bd8D1C31bd7465c8CBC3add6F279E81" +const receiverAddress = "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" ``` -W następnej części wywołamy funkcję `balanceOf`, aby pobrać aktualną ilość tokenów z obu posiadanych adresów. +W następnej części wywołamy funkcję `balanceOf`, aby pobrać aktualną liczbę tokenów posiadanych przez oba adresy. -## Call: Odczyt wartości z inteligentnego kontraktu {#call-reading-value-from-a-smart-contract} +## Wywołanie: odczytywanie wartości z inteligentnego kontraktu {#call-reading-value-from-a-smart-contract} -Pierwszy przykład wywoła metodę „stałą” i wykona metodę inteligentnego kontraktu w EVM bez wysyłania żadnej transakcji. W tym celu odczytamy saldo adresu ERC20. [Przeczytaj nasz artykuł o tokenach ERC20](/developers/tutorials/understand-the-erc-20-token-smart-contract/). +W pierwszym przykładzie zostanie wywołana „stała” metoda i jej metoda inteligentnego kontraktu zostanie wykonana w EVM bez wysyłania jakiejkolwiek transakcji. W tym celu odczytamy saldo ERC20 adresu. [Przeczytaj nasz artykuł o tokenach ERC20](/developers/tutorials/understand-the-erc-20-token-smart-contract/). -Możesz uzyskać dostęp do metod utworzonej instancji kontraktu inteligentnego, dla którego podano ABI, w następujący sposób: `yourContract.methods.methodname`. Używając funkcji `call`, otrzymasz wynik wykonania funkcji. +Możesz uzyskać dostęp do metod utworzonej instancji inteligentnego kontraktu, dla którego podano ABI, w następujący sposób: `yourContract.methods.methodname`. Używając funkcji `call`, otrzymasz wynik wykonania funkcji. ```js -daiToken.methods.balanceOf(senderAddress).call(function (err, res) { if (err) { console.log("An error occurred", err) return } console.log("The balance is: ", res)}) +daiToken.methods.balanceOf(senderAddress).call(function (err, res) { + if (err) { + console.log("Wystąpił błąd", err) + return + } + console.log("Saldo wynosi: ", res) +}) ``` -Pamiętaj, że DAI ERC20 ma 18 miejsc po przecinku, co oznacza, że ​​musisz usunąć 18 zer, aby uzyskać prawidłową kwotę. uint256 są zwracane jako ciągi, ponieważ JavaScript nie obsługuje dużych wartości numerycznych. Jeśli nie masz pewności, [jak radzić sobie z dużymi liczbami w JS, sprawdź nasz samouczek na temat bignumber.js](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/). +Pamiętaj, że DAI ERC20 ma 18 miejsc po przecinku, co oznacza, że musisz usunąć 18 zer, aby uzyskać prawidłową kwotę. Wartości uint256 są zwracane jako ciągi znaków, ponieważ JavaScript nie obsługuje dużych wartości numerycznych. Jeśli nie masz pewności, [jak radzić sobie z dużymi liczbami w JS, sprawdź nasz samouczek o bignumber.js](https://ethereumdev.io/how-to-deal-with-big-numbers-in-javascript/). -## Send: Wysyłanie transakcji do funkcji inteligentnych kontraktów {#send-sending-a-transaction-to-a-smart-contract-function} +## Wyślij: wysyłanie transakcji do funkcji inteligentnego kontraktu {#send-sending-a-transaction-to-a-smart-contract-function} -W drugim przykładzie wywołamy funkcję transferu inteligentnego kontraktu DAI, aby wysłać 10 DAI na nasz drugi adres. Funkcja trasferu przyjmuje dwa parametry: adres odbiorcy oraz ilość tokenu do transferu: +W drugim przykładzie wywołamy funkcję transferu inteligentnego kontraktu DAI, aby wysłać 10 DAI na nasz drugi adres. Funkcja transferu przyjmuje dwa parametry: adres odbiorcy i liczbę tokenów do transferu: ```js daiToken.methods .transfer(receiverAddress, "100000000000000000000") .send({ from: senderAddress }, function (err, res) { if (err) { - console.log("An error occurred", err) + console.log("Wystąpił błąd", err) return } - console.log("Hash of the transaction: " + res) + console.log("Hasz transakcji: " + res) }) ``` -Funkcja wywołania zwraca skrót transakcji, która zostanie wykopana w blockchain. W Ethereum skróty transakcji są przewidywalne — w ten sposób możemy uzyskać skrót transakcji przed jej wykonaniem ([dowiedz się, jak obliczane są skróty](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction)). +Funkcja wywołania zwraca hasz transakcji, która zostanie wydobyta w blockchainie. W Ethereum hasze transakcji są przewidywalne – w ten sposób możemy uzyskać hasz transakcji przed jej wykonaniem ([dowiedz się, jak obliczane są hasze, tutaj](https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction)). -Ponieważ funkcja przesyła transakcję tylko do łańcucha bloków, nie możemy zobaczyć wyniku, dopóki nie wiemy, kiedy został wydobyty i włączony do łańcucha bloków. W następnym samouczku nauczymy się, [jak poczekać aż transakcja zostanie wykonana na blockchainie, wiedząc, że jest skrótem](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/). +Ponieważ funkcja jedynie przesyła transakcję do blockchaina, nie możemy zobaczyć wyniku, dopóki nie dowiemy się, kiedy zostanie ona wydobyta i dołączona do blockchaina. W następnym samouczku dowiemy się, [jak czekać na wykonanie transakcji w blockchainie, znając jej hasz](https://ethereumdev.io/waiting-for-a-transaction-to-be-mined-on-ethereum-with-js/). diff --git a/public/content/translations/pl/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md b/public/content/translations/pl/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md new file mode 100644 index 00000000000..4acdbf742ad --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/creating-a-wagmi-ui-for-your-contract/index.md @@ -0,0 +1,585 @@ +--- +title: "Tworzenie interfejsu użytkownika dla Twojego kontraktu" +description: "Korzystając z nowoczesnych komponentów, takich jak TypeScript, React, Vite i Wagmi, omówimy nowoczesny, ale minimalistyczny interfejs użytkownika i dowiemy się, jak połączyć portfel z interfejsem użytkownika, wywołać inteligentny kontrakt w celu odczytania informacji, wysłać transakcję do inteligentnego kontraktu i monitorować zdarzenia z inteligentnego kontraktu w celu identyfikacji zmian." +author: Ori Pomerantz +tags: [ "typescript", "react", "vite", "wagmi", "frontend" ] +skill: beginner +published: 2023-11-01 +lang: pl +sidebarDepth: 3 +--- + +Znalazłeś funkcję, której potrzebujemy w ekosystemie Ethereum. Napisałeś inteligentne kontrakty, aby go zaimplementować, a może nawet powiązany kod, który działa offchain. To świetnie! Niestety, bez interfejsu użytkownika nie będziesz mieć żadnych użytkowników, a ostatnim razem, gdy pisałeś stronę internetową, ludzie używali modemów dial-up, a JavaScript był nowością. + +Ten artykuł jest dla Ciebie. Zakładam, że znasz programowanie i może trochę JavaScript i HTML, ale Twoje umiejętności w zakresie interfejsu użytkownika są przestarzałe i nieaktualne. Razem przeanalizujemy prostą, nowoczesną aplikację, abyś zobaczył, jak się to robi w dzisiejszych czasach. + +## Dlaczego jest to ważne {#why-important} + +Teoretycznie możesz po prostu kazać ludziom używać [Etherscan](https://holesky.etherscan.io/address/0x432d810484add7454ddb3b5311f0ac2e95cecea8#writeContract) lub [Blockscout](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=write_contract), aby wchodzić w interakcję z Twoimi kontraktami. To będzie świetne dla doświadczonych Eterean. Ale my staramy się służyć [kolejnemu miliardowi ludzi](https://blog.ethereum.org/2021/05/07/ethereum-for-the-next-billion). Nie stanie się to bez wspaniałego doświadczenia użytkownika, a przyjazny interfejs użytkownika jest tego dużą częścią. + +## Aplikacja Greeter {#greeter-app} + +Istnieje wiele teorii na temat działania nowoczesnego interfejsu użytkownika i [wiele dobrych stron](https://react.dev/learn/thinking-in-react), [które to wyjaśniają](https://wagmi.sh/core/getting-started). Zamiast powtarzać świetną pracę wykonaną przez te strony, założę, że wolisz uczyć się przez działanie i zaczniesz od aplikacji, którą możesz się pobawić. Nadal potrzebujesz teorii, aby załatwić sprawy, i do tego dojdziemy - po prostu przejdziemy plik źródłowy po pliku źródłowym i omówimy sprawy, gdy do nich dojdziemy. + +### Instalacja {#installation} + +1. W razie potrzeby dodaj [blockchain Holesky](https://chainlist.org/?search=holesky&testnets=true) do swojego portfela i [pobierz testowe ETH](https://www.holeskyfaucet.io/). + +2. Sklonuj repozytorium na GitHubie. + + ```sh + git clone https://github.com/qbzzt/20230801-modern-ui.git + ``` + +3. Zainstaluj niezbędne pakiety. + + ```sh + cd 20230801-modern-ui + pnpm install + ``` + +4. Uruchom aplikację. + + ```sh + pnpm dev + ``` + +5. Przejdź do adresu URL wyświetlanego przez aplikację. W większości przypadków jest to [http://localhost:5173/](http://localhost:5173/). + +6. Możesz zobaczyć kod źródłowy kontraktu, nieco zmodyfikowaną wersję Greeter od Hardhat, [na eksploratorze blockchain](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contract). + +### Przegląd plików {#file-walk-through} + +#### `index.html` {#index-html} + +Ten plik jest standardowym szablonem HTML, z wyjątkiem tej linii, która importuje plik skryptu. + +```html + +``` + +#### `src/main.tsx` {#main-tsx} + +Rozszerzenie pliku mówi nam, że ten plik jest [komponentem React](https://www.w3schools.com/react/react_components.asp) napisanym w [TypeScript](https://www.typescriptlang.org/), rozszerzeniem JavaScriptu, które obsługuje [sprawdzanie typów](https://en.wikipedia.org/wiki/Type_system#Type_checking). TypeScript jest kompilowany do JavaScriptu, więc możemy go używać do wykonywania po stronie klienta. + +```tsx +import '@rainbow-me/rainbowkit/styles.css' +import { RainbowKitProvider } from '@rainbow-me/rainbowkit' +import * as React from 'react' +import * as ReactDOM from 'react-dom/client' +import { WagmiConfig } from 'wagmi' +import { chains, config } from './wagmi' +``` + +Importuj kod biblioteki, którego potrzebujemy. + +```tsx +import { App } from './App' +``` + +Importuj komponent React, który implementuje aplikację (patrz poniżej). + +```tsx +ReactDOM.createRoot(document.getElementById('root')!).render( +``` + +Utwórz główny komponent React. Parametr `render` to [JSX](https://www.w3schools.com/react/react_jsx.asp), język rozszerzeń, który używa zarówno HTML, jak i JavaScript/TypeScript. Wykrzyknik tutaj mówi komponentowi TypeScript: "nie wiesz, że `document.getElementById('root')` będzie prawidłowym parametrem dla `ReactDOM.createRoot`, ale nie martw się - jestem deweloperem i mówię ci, że będzie". + +```tsx + +``` + +Aplikacja znajduje się wewnątrz [komponentu `React.StrictMode`](https://react.dev/reference/react/StrictMode). Ten komponent informuje bibliotekę React o wstawieniu dodatkowych kontroli debugowania, co jest przydatne podczas tworzenia. + +```tsx + +``` + +Aplikacja znajduje się również wewnątrz [komponentu `WagmiConfig`](https://wagmi.sh/react/api/WagmiProvider). [Biblioteka wagmi (we are going to make it)](https://wagmi.sh/) łączy definicje interfejsu użytkownika React z [biblioteką viem](https://viem.sh/) do pisania zdecentralizowanej aplikacji Ethereum. + +```tsx + +``` + +I wreszcie [komponent `RainbowKitProvider`](https://www.rainbowkit.com/). Ten komponent obsługuje logowanie i komunikację między portfelem a aplikacją. + +```tsx + +``` + +Teraz możemy mieć komponent dla aplikacji, który faktycznie implementuje interfejs użytkownika. Znak `/>` na końcu komponentu informuje React, że ten komponent nie ma w sobie żadnych definicji, zgodnie ze standardem XML. + +```tsx + + + , +) +``` + +Oczywiście musimy zamknąć pozostałe komponenty. + +#### `src/App.tsx` {#app-tsx} + +```tsx +import { ConnectButton } from '@rainbow-me/rainbowkit' +import { useAccount } from 'wagmi' +import { Greeter } from './components/Greeter' + +export function App() { +``` + +To jest standardowy sposób tworzenia komponentu React – zdefiniowanie funkcji, która jest wywoływana za każdym razem, gdy musi zostać wyrenderowana. Ta funkcja zazwyczaj ma na górze kod TypeScript lub JavaScript, po którym następuje instrukcja `return`, która zwraca kod JSX. + +```tsx + const { isConnected } = useAccount() +``` + +Tutaj używamy [`useAccount`](https://wagmi.sh/react/api/hooks/useAccount), aby sprawdzić, czy jesteśmy połączeni z blockchainem przez portfel, czy nie. + +Zgodnie z konwencją, w React funkcje o nazwie `use...` są [hookami](https://www.w3schools.com/react/react_hooks.asp), które zwracają pewien rodzaj danych. Kiedy używasz takich hooków, nie tylko Twój komponent otrzymuje dane, ale gdy te dane się zmieniają, komponent jest ponownie renderowany z zaktualizowanymi informacjami. + +```tsx + return ( + <> +``` + +JSX komponentu React _musi_ zwrócić jeden komponent. Gdy mamy wiele komponentów i nie mamy niczego, co "naturalnie" je opakowuje, używamy pustego komponentu (`<> ...` `), aby uczynić je pojedynczym komponentem. + +```tsx +

Greeter

+ +``` + +Komponent [`ConnectButton`](https://www.rainbowkit.com/docs/connect-button) otrzymujemy z RainbowKit. Gdy nie jesteśmy połączeni, daje nam przycisk `Connect Wallet`, który otwiera okno modalne, które wyjaśnia działanie portfeli i pozwala wybrać, którego używasz. Gdy jesteśmy połączeni, wyświetla używany przez nas blockchain, adres naszego konta i saldo ETH. Możemy użyć tych wyświetlaczy, aby przełączyć sieć lub się rozłączyć. + +```tsx + {isConnected && ( +``` + +Gdy musimy wstawić rzeczywisty JavaScript (lub TypeScript, który zostanie skompilowany do JavaScriptu) do JSX, używamy nawiasów (`{}`). + +Składnia `a && b` jest skrótem od [`a ?` b : a`](https://www.w3schools.com/react/react_es6_ternary.asp). Oznacza to, że jeśli `a`jest prawdziwe, wynikiem jest`b`, a w przeciwnym razie wynikiem jest `a`(które może być`false`, `0` itp.). Jest to łatwy sposób, aby powiedzieć React, że komponent powinien być wyświetlany tylko wtedy, gdy spełniony jest określony warunek. + +W tym przypadku chcemy pokazać użytkownikowi `Greeter` tylko wtedy, gdy użytkownik jest połączony z blockchainem. + +```tsx + + )} + + ) +} +``` + +#### `src/components/Greeter.tsx` {#greeter-tsx} + +Ten plik zawiera większość funkcjonalności interfejsu użytkownika. Zawiera definicje, które normalnie znajdowałyby się w wielu plikach, ale ponieważ jest to samouczek, program jest zoptymalizowany pod kątem łatwości zrozumienia za pierwszym razem, a nie wydajności czy łatwości konserwacji. + +```tsx +import { useState, ChangeEventHandler } from 'react' +import { useNetwork, + useReadContract, + usePrepareContractWrite, + useContractWrite, + useContractEvent + } from 'wagmi' +``` + +Używamy tych funkcji bibliotecznych. Ponownie, są one wyjaśnione poniżej, w miejscu ich użycia. + +```tsx +import { AddressType } from 'abitype' +``` + +[Biblioteka `abitype`](https://abitype.dev/) dostarcza nam definicje TypeScript dla różnych typów danych Ethereum, takich jak [`AddressType`](https://abitype.dev/config#addresstype). + +```tsx +let greeterABI = [ + . + . + . +] as const // greeterABI +``` + +ABI - binarny interfejs aplikacji dla kontraktu `Greeter`. +Jeśli tworzysz kontrakty i interfejs użytkownika w tym samym czasie, normalnie umieszczasz je w tym samym repozytorium i używasz ABI wygenerowanego przez kompilator Solidity jako pliku w swojej aplikacji. Jednak nie jest to tutaj konieczne, ponieważ kontrakt jest już opracowany i nie ulegnie zmianie. + +```tsx +type AddressPerBlockchainType = { + [key: number]: AddressType +} +``` + +TypeScript jest silnie typowany. Używamy tej definicji do określenia adresu, na którym kontrakt `Greeter` jest wdrożony na różnych łańcuchach. Kluczem jest liczba (chainId), a wartością jest `AddressType` (adres). + +```tsx +const contractAddrs: AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' +} +``` + +Adres kontraktu na dwóch obsługiwanych sieciach: [Holesky](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=contact_code) i [Sepolia](https://eth-sepolia.blockscout.com/address/0x7143d5c190F048C8d19fe325b748b081903E3BF0?tab=contact_code). + +Uwaga: W rzeczywistości istnieje trzecia definicja, dla Redstone Holesky, zostanie ona wyjaśniona poniżej. + +```tsx +type ShowObjectAttrsType = { + name: string, + object: any +} +``` + +Ten typ jest używany jako parametr komponentu `ShowObject` (wyjaśnionego później). Zawiera nazwę obiektu i jego wartość, które są wyświetlane w celach debugowania. + +```tsx +type ShowGreetingAttrsType = { + greeting: string | undefined +} +``` + +W dowolnym momencie możemy wiedzieć, jakie jest powitanie (ponieważ odczytaliśmy je z blockchainu) lub nie wiedzieć (ponieważ jeszcze go nie otrzymaliśmy). Dlatego warto mieć typ, który może być albo ciągiem znaków, albo niczym. + +##### Komponent `Greeter` {#greeter-component} + +```tsx +const Greeter = () => { +``` + +Wreszcie możemy zdefiniować komponent. + +```tsx + const { chain } = useNetwork() +``` + +Informacje o łańcuchu, którego używamy, dzięki uprzejmości [wagmi](https://wagmi.sh/react/hooks/useNetwork). +Ponieważ jest to hak (`use...`), za każdym razem, gdy ta informacja się zmienia, komponent jest ponownie rysowany. + +```tsx + const greeterAddr = chain && contractAddrs[chain.id] +``` + +Adres kontraktu Greeter, który różni się w zależności od łańcucha (i który jest `undefined`, jeśli nie mamy informacji o łańcuchu lub jesteśmy na łańcuchu bez tego kontraktu). + +```tsx + const readResults = useReadContract({ + address: greeterAddr, + abi: greeterABI, + functionName: "greet" , // Brak argumentów + watch: true + }) +``` + +Hak [`useReadContract`](https://wagmi.sh/react/api/hooks/useReadContract) odczytuje informacje z kontraktu. Możesz zobaczyć dokładnie, jakie informacje zwraca, rozwijając `readResults` w interfejsie użytkownika. W tym przypadku chcemy, aby nadal szukał, abyśmy byli informowani o zmianie powitania. + +**Uwaga:** Moglibyśmy nasłuchiwać [zdarzeń `setGreeting`](https://eth-holesky.blockscout.com/address/0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8?tab=logs), aby wiedzieć, kiedy zmienia się powitanie i aktualizować je w ten sposób. Jednak, chociaż może to być bardziej wydajne, nie będzie miało zastosowania we wszystkich przypadkach. Gdy użytkownik przełącza się na inny łańcuch, powitanie również się zmienia, ale tej zmianie nie towarzyszy zdarzenie. Moglibyśmy mieć jedną część kodu nasłuchującą zdarzeń, a drugą do identyfikowania zmian łańcucha, ale byłoby to bardziej skomplikowane niż tylko ustawienie [parametru `watch`](https://wagmi.sh/react/api/hooks/useReadContract#watch-optional). + +```tsx + const [ newGreeting, setNewGreeting ] = useState("") +``` + +Hook [`useState`](https://www.w3schools.com/react/react_usestate.asp) React pozwala nam zdefiniować zmienną stanu, której wartość utrzymuje się od jednego renderowania komponentu do drugiego. Wartością początkową jest parametr, w tym przypadku pusty ciąg znaków. + +Hook `useState` zwraca listę z dwiema wartościami: + +1. Bieżąca wartość zmiennej stanu. +2. Funkcja do modyfikowania zmiennej stanu w razie potrzeby. Ponieważ jest to hak, za każdym razem, gdy jest wywoływany, komponent jest ponownie renderowany. + +W tym przypadku używamy zmiennej stanu dla nowego powitania, które użytkownik chce ustawić. + +```tsx + const greetingChange : ChangeEventHandler = (evt) => + setNewGreeting(evt.target.value) +``` + +Jest to procedura obsługi zdarzeń dla zmiany pola wprowadzania nowego powitania. Typ [`ChangeEventHandler`](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forms_and_events/) określa, że jest to procedura obsługi zmiany wartości elementu wejściowego HTML. Część `` jest używana, ponieważ jest to [typ generyczny](https://www.w3schools.com/typescript/typescript_basic_generics.php). + +```tsx + const preparedTx = usePrepareContractWrite({ + address: greeterAddr, + abi: greeterABI, + functionName: 'setGreeting', + args: [ newGreeting ] + }) + const workingTx = useContractWrite(preparedTx.config) +``` + +To jest proces przesyłania transakcji blockchain z perspektywy klienta: + +1. Wyślij transakcję do węzła w łańcuchu bloków za pomocą [`eth_estimateGas`](https://docs.alchemy.com/reference/eth-estimategas). +2. Poczekaj na odpowiedź z węzła. +3. Po otrzymaniu odpowiedzi, poproś użytkownika o podpisanie transakcji za pośrednictwem portfela. Ten krok _musi_ nastąpić po otrzymaniu odpowiedzi węzła, ponieważ użytkownikowi wyświetlany jest koszt gazu transakcji przed jej podpisaniem. +4. Poczekaj na zatwierdzenie przez użytkownika. +5. Wyślij transakcję ponownie, tym razem za pomocą [`eth_sendRawTransaction`](https://docs.alchemy.com/reference/eth-sendrawtransaction). + +Krok 2 prawdopodobnie zajmie zauważalną ilość czasu, podczas którego użytkownicy zastanawialiby się, czy ich polecenie zostało naprawdę odebrane przez interfejs użytkownika i dlaczego nie są jeszcze proszeni o podpisanie transakcji. To powoduje złe doświadczenie użytkownika (UX). + +Rozwiązaniem jest użycie [hooków przygotowawczych](https://wagmi.sh/react/prepare-hooks). Za każdym razem, gdy parametr się zmienia, natychmiast wysyłaj do węzła żądanie `eth_estimateGas`. Następnie, gdy użytkownik faktycznie chce wysłać transakcję (w tym przypadku przez naciśnięcie **Update greeting**), koszt gazu jest znany, a użytkownik może natychmiast zobaczyć stronę portfela. + +```tsx + return ( +``` + +Teraz możemy wreszcie utworzyć rzeczywisty kod HTML do zwrócenia. + +```tsx + <> +

Greeter

+ { + !readResults.isError && !readResults.isLoading && + + } +
+``` + +Utwórz komponent `ShowGreeting` (wyjaśniony poniżej), ale tylko wtedy, gdy powitanie zostało pomyślnie odczytane z blockchainu. + +```tsx + +``` + +Jest to pole tekstowe, w którym użytkownik może ustawić nowe powitanie. Za każdym razem, gdy użytkownik naciśnie klawisz, wywołujemy `greetingChange`, które wywołuje `setNewGreeting`. Ponieważ `setNewGreeting` pochodzi z hooka `useState`, powoduje to ponowne renderowanie komponentu `Greeter`. Oznacza to, że: + +- Musimy określić `value`, aby zachować wartość nowego powitania, ponieważ w przeciwnym razie powróciłoby ono do wartości domyślnej, czyli pustego ciągu znaków. +- `usePrepareContractWrite` jest wywoływane za każdym razem, gdy `newGreeting` się zmienia, co oznacza, że zawsze będzie miało najnowsze `newGreeting` w przygotowanej transakcji. + +```tsx + +``` + +Jeśli nie ma `workingTx.write`, oznacza to, że wciąż czekamy na informacje niezbędne do wysłania aktualizacji powitania, więc przycisk jest wyłączony. Jeśli istnieje wartość `workingTx.write`, to jest to funkcja do wywołania w celu wysłania transakcji. + +```tsx +
+ + + + + ) +} +``` + +Na koniec, aby pomóc Ci zobaczyć, co robimy, pokaż trzy obiekty, których używamy: + +- `readResults` +- `preparedTx` +- `workingTx` + +##### Komponent `ShowGreeting` {#showgreeting-component} + +Ten komponent pokazuje + +```tsx +const ShowGreeting = (attrs : ShowGreetingAttrsType) => { +``` + +Funkcja komponentu otrzymuje parametr ze wszystkimi atrybutami komponentu. + +```tsx + return {attrs.greeting} +} +``` + +##### Komponent `ShowObject` {#showobject-component} + +W celach informacyjnych używamy komponentu `ShowObject` do pokazania ważnych obiektów (`readResults` do odczytywania powitania oraz `preparedTx` i `workingTx` do transakcji, które tworzymy). + +```tsx +const ShowObject = (attrs: ShowObjectAttrsType ) => { + const keys = Object.keys(attrs.object) + const funs = keys.filter(k => typeof attrs.object[k] == "function") + return <> +
+``` + +Nie chcemy zaśmiecać interfejsu użytkownika wszystkimi informacjami, więc aby umożliwić ich przeglądanie lub zamykanie, używamy znacznika [`details`](https://www.w3schools.com/tags/tag_details.asp). + +```tsx + {attrs.name} +
+        {JSON.stringify(attrs.object, null, 2)}
+```
+
+Większość pól jest wyświetlana za pomocą [`JSON.stringify`](https://www.w3schools.com/js/js_json_stringify.asp).
+
+```tsx
+      
+ { funs.length > 0 && + <> + Functions: +
    +``` + +Wyjątkiem są funkcje, które nie są częścią [standardu JSON](https://www.json.org/json-en.html), więc muszą być wyświetlane osobno. + +```tsx + {funs.map((f, i) => +``` + +Wewnątrz JSX kod w nawiasach klamrowych `{` `}` jest interpretowany jako JavaScript. Następnie kod w nawiasach zwykłych `(` `)` jest ponownie interpretowany jako JSX. + +```tsx + (
  • {f}
  • ) + )} +``` + +React wymaga, aby znaczniki w [drzewie DOM](https://www.w3schools.com/js/js_htmldom.asp) miały odrębne identyfikatory. Oznacza to, że dzieci tego samego znacznika (w tym przypadku [lista nieuporządkowana](https://www.w3schools.com/tags/tag_ul.asp)) potrzebują różnych atrybutów `key`. + +```tsx +
+ + } +
+ +} +``` + +Zakończ różne znaczniki HTML. + +##### Ostateczny `export` {#the-final-export} + +```tsx +export { Greeter } +``` + +Komponent `Greeter` jest tym, który musimy wyeksportować dla aplikacji. + +#### `src/wagmi.ts` {#wagmi-ts} + +Na koniec, różne definicje związane z WAGMI znajdują się w `src/wagmi.ts`. Nie będę tutaj wszystkiego wyjaśniać, ponieważ większość z tego to szablon, którego prawdopodobnie nie będziesz musiał zmieniać. + +Kod tutaj nie jest dokładnie taki sam jak [na GitHubie](https://github.com/qbzzt/20230801-modern-ui/blob/main/src/wagmi.ts), ponieważ w dalszej części artykułu dodajemy kolejny łańcuch ([Redstone Holesky](https://redstone.xyz/docs/network-info)). + +```ts +import { getDefaultWallets } from '@rainbow-me/rainbowkit' +import { configureChains, createConfig } from 'wagmi' +import { holesky, sepolia } from 'wagmi/chains' +``` + +Importuj łańcuchy bloków, które obsługuje aplikacja. Listę obsługiwanych łańcuchów można zobaczyć [w viem github](https://github.com/wagmi-dev/viem/tree/main/src/chains/definitions). + +```ts +import { publicProvider } from 'wagmi/providers/public' + +const walletConnectProjectId = 'c96e690bb92b6311e8e9b2a6a22df575' +``` + +Aby móc korzystać z [WalletConnect](https://walletconnect.com/), potrzebujesz identyfikatora projektu dla swojej aplikacji. Możesz go uzyskać na stronie [cloud.walletconnect.com](https://cloud.walletconnect.com/sign-in). + +```ts +const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia ], + [ + publicProvider(), + ], +) + +const { connectors } = getDefaultWallets({ + appName: 'My wagmi + RainbowKit App', + chains, + projectId: walletConnectProjectId, +}) + +export const config = createConfig({ + autoConnect: true, + connectors, + publicClient, + webSocketPublicClient, +}) + +export { chains } +``` + +### Dodawanie kolejnego blockchaina {#add-blockchain} + +Obecnie istnieje wiele [rozwiązań skalujących L2](/layer-2/) i możesz chcieć obsługiwać niektóre, których viem jeszcze nie obsługuje. Aby to zrobić, zmodyfikuj `src/wagmi.ts`. Te instrukcje wyjaśniają, jak dodać [Redstone Holesky](https://redstone.xyz/docs/network-info). + +1. Zaimportuj typ `defineChain` z viem. + + ```ts + import { defineChain } from 'viem' + ``` + +2. Dodaj definicję sieci. + + ```ts + const redstoneHolesky = defineChain({ + id: 17_001, + name: 'Redstone Holesky', + network: 'redstone-holesky', + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + public: { + http: ['https://rpc.holesky.redstone.xyz'], + webSocket: ['wss://rpc.holesky.redstone.xyz/ws'], + }, + }, + blockExplorers: { + default: { name: 'Explorer', url: 'https://explorer.holesky.redstone.xyz' }, + }, + }) + ``` + +3. Dodaj nowy łańcuch do wywołania `configureChains`. + + ```ts + const { chains, publicClient, webSocketPublicClient } = configureChains( + [ holesky, sepolia, redstoneHolesky ], + [ publicProvider(), ], + ) + ``` + +4. Upewnij się, że aplikacja zna adres Twoich kontraktów w nowej sieci. W tym przypadku modyfikujemy `src/components/Greeter.tsx`: + + ```ts + const contractAddrs : AddressPerBlockchainType = { + // Holesky + 17000: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8', + + // Redstone Holesky + 17001: '0x4919517f82a1B89a32392E1BF72ec827ba9986D3', + + // Sepolia + 11155111: '0x7143d5c190F048C8d19fe325b748b081903E3BF0' + } + ``` + +## Wnioski {#conclusion} + +Oczywiście, tak naprawdę nie zależy Ci na udostępnieniu interfejsu użytkownika dla `Greeter`. Chcesz stworzyć interfejs użytkownika dla własnych kontraktów. Aby utworzyć własną aplikację, wykonaj następujące kroki: + +1. Określ, aby utworzyć aplikację wagmi. + + ```sh copy + pnpm create wagmi + ``` + +2. Nazwij aplikację. + +3. Wybierz framework **React**. + +4. Wybierz wariant **Vite**. + +5. Możesz [dodać zestaw Rainbow](https://www.rainbowkit.com/docs/installation#manual-setup). + +Teraz idź i spraw, aby Twoje kontrakty były użyteczne dla całego świata. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). + diff --git a/public/content/translations/pl/developers/tutorials/deploying-your-first-smart-contract/index.md b/public/content/translations/pl/developers/tutorials/deploying-your-first-smart-contract/index.md index b87270a560b..f0494c1235c 100644 --- a/public/content/translations/pl/developers/tutorials/deploying-your-first-smart-contract/index.md +++ b/public/content/translations/pl/developers/tutorials/deploying-your-first-smart-contract/index.md @@ -1,13 +1,8 @@ --- -title: Wdrożenie pierwszego inteligentnego kontraktu -description: Wprowadzenie do wdrożenia pierwszego inteligentnego kontraktu w sieci testowej Ethereum +title: "Wdrożenie pierwszego inteligentnego kontraktu" +description: "Wprowadzenie do wdrożenia pierwszego inteligentnego kontraktu w sieci testowej Ethereum" author: "jdourlens" -tags: - - "inteligentne kontrakty" - - "remix" - - "solidity" - - "pierwsze kroki" - - "wdrożenie" +tags: [ "smart kontrakty", "remix", "solidity", "wdrażanie" ] skill: beginner lang: pl published: 2020-04-03 @@ -16,32 +11,33 @@ sourceUrl: https://ethereumdev.io/deploying-your-first-smart-contract/ address: "0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE" --- -Chyba jesteś tak samo podekscytowany jak my [wdrażaniem](/developers/docs/smart-contracts/deploying/) i interakcją z pierwszym [inteligentnym kontraktem](/developers/docs/smart-contracts/) na blockchainie Ethereum. +Zgaduję, że jesteś tak samo podekscytowany jak my, aby [wdrożyć](/developers/docs/smart-contracts/deploying/) i wejść w interakcję ze swoim pierwszym [inteligentnym kontraktem](/developers/docs/smart-contracts/) na blockchainie Ethereum. -Nie martw się, ponieważ jest to nasz pierwszy inteligentny kontrakt, wdrożymy go w [lokalnej sieci testowej](/developers/docs/networks/), więc wdrożenie nic nie kosztuje i możesz bawić się nim tyle, ile chcesz. +Nie przejmuj się, ponieważ jest to nasz pierwszy inteligentny kontrakt, wdrożymy go w lokalnej [sieci testowej](/developers/docs/networks/), więc jego wdrożenie nic nie kosztuje i możesz się nim bawić, ile tylko chcesz. -## Pisanie kontraktu {#writing-our-contract} +## Pisanie naszego kontraktu {#writing-our-contract} Pierwszym krokiem jest [odwiedzenie Remix](https://remix.ethereum.org/) i utworzenie nowego pliku. W lewej górnej części interfejsu Remix dodaj nowy plik i wprowadź żądaną nazwę pliku. -![Dodawanie nowego pliku do interfejsu Remix](./remix.png) +![Dodawanie nowego pliku w interfejsie Remix](./remix.png) W nowym pliku wkleimy następujący kod. ```solidity -pragma solidity 0.5.17; +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.17; contract Counter { - // Public variable of type unsigned int to keep the number of counts + // Publiczna zmienna typu unsigned int do przechowywania liczby zliczeń uint256 public count = 0; - // Function that increments our counter + // Funkcja, która zwiększa nasz licznik function increment() public { count += 1; } - // Not necessary getter to get the count value + // Niepotrzebny getter do pobrania wartości licznika function getCount() public view returns (uint256) { return count; } @@ -49,20 +45,20 @@ contract Counter { } ``` -Jeśli masz doświadczenie w programowaniu, możesz łatwo odgadnąć, co robi ten program. Oto wyjaśnienie kolejnych wierszy: +Jeśli masz doświadczenie w programowaniu, możesz łatwo odgadnąć, co robi ten program. Oto wyjaśnienie linijka po linijce: -- Wiersz 3: Określamy kontrakt nazwą `Counter`. -- Wiersz 6: Nasz kontrakt przechowuje jedną niepodpisaną liczbę całkowitą o nazwie `count` zaczynając od 0. -- Wiersz 9: Pierwsza funkcja zmieni stan kontraktu i zwiększy `increment()` zmienną `count`>. -- Wiersz 14: Druga funkcja to tylko getter, który może odczytywać wartość zmiennej `count` poza inteligentnym kontraktem. Zauważ, że ponieważ zdefiniowaliśmy zmienną `count` jako publiczną, to nie jest konieczne, ale jest to przykład. +- Wiersz 4: Definiujemy kontrakt o nazwie `Counter`. +- Wiersz 7: Nasz kontrakt przechowuje jedną liczbę całkowitą bez znaku o nazwie `count`, zaczynając od 0. +- Wiersz 10: Pierwsza funkcja modyfikuje stan kontraktu i zwiększa naszą zmienną `count`. +- Wiersz 15: Druga funkcja to tylko getter, który umożliwia odczytanie wartości zmiennej `count` poza inteligentnym kontraktem. Zauważ, że ponieważ zdefiniowaliśmy naszą zmienną `count` jako publiczną, nie jest to konieczne, ale zostało pokazane jako przykład. -To wszystko dotyczy naszego pierwszego prostego inteligentnego kontraktu. Jak możesz wiedzieć, wygląda to na klasę z języków programowania obiektowego takich jak Java lub C++. Nadszedł czas, aby pobawić się naszym kontraktem. +To wszystko, jeśli chodzi o nasz pierwszy prosty inteligentny kontrakt. Jak być może wiesz, wygląda to jak klasa z języków programowania zorientowanego obiektowo (OOP), takich jak Java lub C++. Nadszedł czas, aby pobawić się naszym kontraktem. -## Wdrażanie kontraktu {#deploying-our-contract} +## Wdrażanie naszego kontraktu {#deploying-our-contract} -Ponieważ napisaliśmy pierwszy inteligentny kontrakt, teraz wdrożymy go w blockchainie, aby móc się nim bawić. +Ponieważ napisaliśmy nasz pierwszy inteligentny kontrakt, teraz wdrożymy go w blockchainie, aby móc się nim bawić. -[Wdrażanie inteligentnego kontraktu w blockchainie](/developers/docs/smart-contracts/deploying/) to w rzeczywistości tylko wysłanie transakcji zawierającej kod skompilowanego inteligentnego kontraktu bez określania odbiorców. +[Wdrażanie inteligentnego kontraktu na blockchainie](/developers/docs/smart-contracts/deploying/) to w rzeczywistości tylko wysłanie transakcji zawierającej kod skompilowanego inteligentnego kontraktu bez określania żadnych odbiorców. Najpierw [skompilujemy kontrakt](/developers/docs/smart-contracts/compiling/), klikając ikonę kompilacji po lewej stronie: @@ -70,30 +66,30 @@ Najpierw [skompilujemy kontrakt](/developers/docs/smart-contracts/compiling/), k Następnie kliknij przycisk kompilacji: -![Przycisk kompilacji w Remix Solidity](./remix-compile.png) +![Przycisk kompilacji w kompilatorze Remix Solidity](./remix-compile.png) -Możesz wybrać opcję „Automatyczna kompilacja”, aby umowa była zawsze kompilowana po zapisaniu zawartości w edytorze tekstowym. +Możesz wybrać opcję "Automatyczna kompilacja", aby kontrakt był zawsze kompilowany po zapisaniu zawartości w edytorze tekstowym. -Następnie przejdź do ekranu wdrażania i uruchamiania transakcji: +Następnie przejdź do ekranu "wdrażanie i uruchamianie transakcji": ![Ikona wdrażania na pasku narzędzi Remix](./remix-deploy.png) -Po przejściu do ekranu „wdróż i uruchom” transakcje sprawdź dokładnie, czy pojawia się nazwa Twojego kontraktu i kliknij Wdróż. Jak widać na górze strony, obecne środowisko to „Maszyna wirtualna JavaScript”, co oznacza, że ​​wdrożymy inteligentny kontrakt i będziemy nad nim pracować w lokalnym testowym blockchainie, aby móc testować szybciej i bez żadnych opłat. +Gdy znajdziesz się na ekranie "wdrażanie i uruchamianie transakcji", upewnij się, że wyświetla się nazwa Twojego kontraktu, a następnie kliknij "Wdróż". Jak widać na górze strony, obecne środowisko to "JavaScript VM", co oznacza, że będziemy wdrażać i wchodzić w interakcję z naszym inteligentnym kontraktem na lokalnym testowym blockchainie, aby móc go testować szybciej i bez żadnych opłat. -![Przycisk wdrażania w kompilatorze Remix Solidity](./remix-deploy.png) +![Przycisk wdrażania w kompilatorze Remix Solidity](./remix-deploy-button.png) -Po kliknięciu przycisku „Wdróż” na dole pojawi się Twój kontrakt. Kliknij strzałkę po lewej stronie, aby ją rozwinąć i wyświetlić zawartość kontraktu. To jest utworzona zmienna `counter`, funkcja `increment()` i getter `getCounter()`. +Po kliknięciu przycisku "Wdróż", na dole pojawi się Twój kontrakt. Kliknij strzałkę po lewej stronie, aby go rozwinąć i wyświetlić zawartość naszego kontraktu. To jest nasza zmienna `counter`, nasza funkcja `increment()` i getter `getCounter()`. -Jeśli klikniesz przycisk `count` lub `getCount`, zostanie pobrana i wyświetlona zmienna `count`. Ponieważ funkcja `increment` nie została jeszcze wywołana, wyświetli 0. +Jeśli klikniesz przycisk `count` lub `getCount`, zostanie odczytana zawartość zmiennej `count` z kontraktu i wyświetlona. Ponieważ nie wywołaliśmy jeszcze funkcji `increment`, wyświetli się 0. ![Przycisk funkcji w kompilatorze Remix Solidity](./remix-function-button.png) -Wywołajmy funkcję `increment`, klikając przycisk. Zobaczysz dzienniki transakcji, wyświetlone na dole okna. Zobaczysz, że dzienniki są inne, gdy naciśniesz przycisk pobierania danych zamiast przycisku `increment`. To dlatego, że odczyt danych w blockchainie nie wymaga żadnych transakcji (pisanie) ani opłat. Ponieważ tylko modyfikacja stanu łańcucha bloków wymaga dokonania transakcji: +Wywołajmy teraz funkcję `increment`, klikając przycisk. Zobaczysz logi wykonanych transakcji, które pojawią się na dole okna. Zobaczysz, że logi są inne, gdy naciskasz przycisk pobierania danych zamiast przycisku `increment`. Dzieje się tak, ponieważ odczytywanie danych z blockchaina nie wymaga żadnych transakcji (zapisu) ani opłat. Ponieważ tylko modyfikowanie stanu blockchaina wymaga wykonania transakcji: -![Dziennik transakcji](./transaction-log.png) +![Log transakcji](./transaction-log.png) -Po naciśnięciu przycisku inkrement, który wygeneruje transakcję, aby wywołać naszą funkcję `increment()`, jeśli ponownie klikniemy przycisk count lub getCount, odczytamy nowo zaktualizowany stan naszego inteligentnego kontraktu ze zmienną count większą niż 0. +Po naciśnięciu przycisku `increment`, który wygeneruje transakcję wywołującą naszą funkcję `increment()`, jeśli ponownie klikniemy przyciski `count` lub `getCount`, odczytamy nowo zaktualizowany stan naszego inteligentnego kontraktu ze zmienną `count` większą od 0. ![Nowo zaktualizowany stan inteligentnego kontraktu](./updated-state.png) -W następnym samouczku omówimy [jak dodawać zdarzenia do inteligentnych kontraktów](/developers/tutorials/logging-events-smart-contracts/). Rejestrowanie zdarzeń jest wygodnym sposobem debugowania inteligentnego kontraktu i zrozumienia, co się dzieje podczas wywoływania funkcji. +W następnym samouczku omówimy, [jak możesz dodawać zdarzenia do swoich inteligentnych kontraktów](/developers/tutorials/logging-events-smart-contracts/). Rejestrowanie zdarzeń jest wygodnym sposobem debugowania inteligentnego kontraktu i zrozumienia, co się dzieje podczas wywoływania funkcji. diff --git a/public/content/translations/pl/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md b/public/content/translations/pl/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md new file mode 100644 index 00000000000..15dc728b16c --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/develop-and-test-dapps-with-a-multi-client-local-eth-testnet/index.md @@ -0,0 +1,372 @@ +--- +title: "Jak rozwijać i testować dApp na lokalnej, wieloklientowej sieci testowej" +description: "Ten poradnik najpierw przeprowadzi Cię przez proces tworzenia i konfigurowania wieloklientowej lokalnej sieci testowej Ethereum, a następnie pokaże, jak użyć tej sieci testowej do wdrożenia i przetestowania dApp." +author: "Tedi Mitiku" +tags: + [ + "klienci", + "węzły", + "smart kontrakty", + "kompozycyjność", + "warstwa konsensusu", + "warstwa wykonawcza", + "testowanie" + ] +skill: intermediate +lang: pl +published: 2023-04-11 +--- + +## Wprowadzenie {#introduction} + +Ten poradnik przeprowadzi Cię przez proces tworzenia konfigurowalnej lokalnej sieci testowej Ethereum, wdrażania na niej smart kontraktu i używania sieci testowej do przeprowadzania testów Twojej dApp. Ten poradnik jest przeznaczony dla deweloperów dApp, którzy chcą rozwijać i testować swoje dapki lokalnie w różnych konfiguracjach sieci przed wdrożeniem na działającą sieć testową lub sieć główną. + +W tym poradniku: + +- Utworzyć lokalną sieć testową Ethereum za pomocą pakietu [`eth-network-package`](https://github.com/kurtosis-tech/eth-network-package) przy użyciu [Kurtosis](https://www.kurtosis.com/), +- Połączyć swoje środowisko programistyczne dApp Hardhat z lokalną siecią testową w celu kompilacji, wdrożenia i przetestowania dApp oraz +- Skonfigurować lokalną sieć testową, w tym parametry takie jak liczba węzłów i określone pary klientów EL/CL, aby umożliwić procesy programistyczne i testowe w różnych konfiguracjach sieci. + +### Czym jest Kurtosis? {#what-is-kurtosis} + +[Kurtosis](https://www.kurtosis.com/) to komponowalny system budowania przeznaczony do konfigurowania wielokontenerowych środowisk testowych. W szczególności umożliwia deweloperom tworzenie odtwarzalnych środowisk, które wymagają dynamicznej logiki konfiguracji, takich jak sieci testowe blockchain. + +W tym poradniku pakiet eth-network-package Kurtosis uruchamia lokalną sieć testową Ethereum z obsługą klienta warstwy wykonawczej (EL) [`geth`](https://geth.ethereum.org/), a także klientów warstwy konsensusu (CL) [`teku`](https://consensys.io/teku), [`lighthouse`](https://lighthouse.sigmaprime.io/) i [`lodestar`](https://lodestar.chainsafe.io/). Ten pakiet służy jako konfigurowalna i komponowalna alternatywa dla sieci w frameworkach takich jak Hardhat Network, Ganache i Anvil. Kurtosis oferuje deweloperom większą kontrolę i elastyczność nad sieciami testowymi, których używają, co jest głównym powodem, dla którego [Ethereum Foundation użyła Kurtosis do testowania Połączenia](https://www.kurtosis.com/blog/testing-the-ethereum-merge) i nadal używa go do testowania aktualizacji sieci. + +## Konfiguracja Kurtosis {#setting-up-kurtosis} + +Zanim przejdziesz dalej, upewnij się, że masz: + +- [Zainstalowany i uruchomiony silnik Docker](https://docs.kurtosis.com/install/#i-install--start-docker) na swoim komputerze lokalnym +- [Zainstalowany Kurtosis CLI](https://docs.kurtosis.com/install#ii-install-the-cli) (lub zaktualizowany do najnowszej wersji, jeśli masz już zainstalowany CLI) +- Zainstalowane [Node.js](https://nodejs.org/en), [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) i [npx](https://www.npmjs.com/package/npx) (dla Twojego środowiska dApp) + +## Tworzenie lokalnej sieci testowej Ethereum {#instantiate-testnet} + +Aby uruchomić lokalną sieć testową Ethereum, wykonaj: + +```python +kurtosis --enclave local-eth-testnet run github.com/kurtosis-tech/eth-network-package +``` + +Uwaga: to polecenie nazywa Twoją sieć: \"local-eth-testnet\" za pomocą flagi `--enclave`. + +Kurtosis wydrukuje kroki, które podejmuje w tle, podczas gdy interpretuje, waliduje, a następnie wykonuje instrukcje. Na końcu powinieneś zobaczyć dane wyjściowe podobne do poniższych: + +```python +INFO[2023-04-04T18:09:44-04:00] ====================================================== +INFO[2023-04-04T18:09:44-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-04T18:09:44-04:00] ====================================================== +Name: local-eth-testnet +UUID: 39372d756ae8 +Status: RUNNING +Creation Time: Tue, 04 Apr 2023 18:09:03 EDT + +========================================= Files Artifacts ========================================= +UUID Name +d4085a064230 cl-genesis-data +1c62cb792e4c el-genesis-data +bd60489b73a7 genesis-generation-config-cl +b2e593fe5228 genesis-generation-config-el +d552a54acf78 geth-prefunded-keys +5f7e661eb838 prysm-password +054e7338bb59 validator-keystore-0 + +========================================== User Services ========================================== +UUID Name Ports Status +e20f129ee0c5 cl-client-0-beacon http: 4000/tcp -> RUNNING + metrics: 5054/tcp -> + tcp-discovery: 9000/tcp -> 127.0.0.1:54263 + udp-discovery: 9000/udp -> 127.0.0.1:60470 +a8b6c926cdb4 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:54267 RUNNING + metrics: 5064/tcp -> +d7b802f623e8 el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:54253 RUNNING + rpc: 8545/tcp -> 127.0.0.1:54251 + tcp-discovery: 30303/tcp -> 127.0.0.1:54254 + udp-discovery: 30303/udp -> 127.0.0.1:53834 + ws: 8546/tcp -> 127.0.0.1:54252 +514a829c0a84 prelaunch-data-generator-1680646157905431468 STOPPED +62bd62d0aa7a prelaunch-data-generator-1680646157915424301 STOPPED +05e9619e0e90 prelaunch-data-generator-1680646157922872635 STOPPED + +``` + +Gratulacje! Użyłeś Kurtosis do utworzenia lokalnej sieci testowej Ethereum z klientem CL (`lighthouse`) i klientem EL (`geth`) za pośrednictwem Dockera. + +### Podsumowanie {#review-instantiate-testnet} + +W tej sekcji wykonałeś polecenie, które poleciło Kurtosis użycie pakietu [`eth-network-package` hostowanego zdalnie na GitHub](https://github.com/kurtosis-tech/eth-network-package) do uruchomienia lokalnej sieci testowej Ethereum w enklawie Kurtosis [Enclave](https://docs.kurtosis.com/advanced-concepts/enclaves/). Wewnątrz enklawy znajdziesz zarówno \"artefakty plików\", jak i \"usługi użytkownika\". + +[Artefakty plików](https://docs.kurtosis.com/advanced-concepts/files-artifacts/) w Twojej enklawie zawierają wszystkie dane wygenerowane i wykorzystane do uruchomienia klientów EL i CL. Dane zostały utworzone za pomocą usługi `prelaunch-data-generator` zbudowanej z tego [obrazu Docker](https://github.com/ethpandaops/ethereum-genesis-generator) + +Usługi użytkownika wyświetlają wszystkie skonteneryzowane usługi działające w Twojej enklawie. Zauważysz, że został utworzony pojedynczy węzeł, zawierający zarówno klienta EL, jak i klienta CL. + +## Połącz swoje środowisko programistyczne dApp z lokalną siecią testową Ethereum {#connect-your-dapp} + +### Konfiguracja środowiska programistycznego dApp {#set-up-dapp-env} + +Teraz, gdy masz działającą lokalną sieć testową, możesz połączyć swoje środowisko programistyczne dApp, aby korzystać z lokalnej sieci testowej. W tym poradniku zostanie użyty framework Hardhat do wdrożenia dApp do gry w blackjacka na Twojej lokalnej sieci testowej. + +Aby skonfigurować środowisko programistyczne dApp, sklonuj repozytorium zawierające naszą przykładową dApp i zainstaluj jego zależności, uruchamiając: + +```python +git clone https://github.com/kurtosis-tech/awesome-kurtosis.git && cd awesome-kurtosis/smart-contract-example && yarn +``` + +Użyty tutaj folder [smart-contract-example](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example) zawiera typową konfigurację dla dewelopera dApp korzystającego z frameworka [Hardhat](https://hardhat.org/): + +- [`contracts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/contracts) zawiera kilka prostych smart kontraktów dla dApp do gry w Blackjacka +- [`scripts/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/scripts) zawiera skrypt do wdrożenia kontraktu tokena na Twojej lokalnej sieci Ethereum +- [`test/`](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/smart-contract-example/test) zawiera prosty test .js dla Twojego kontraktu tokena, aby potwierdzić, że dla każdego gracza w naszej dApp do gry w Blackjacka zostało wybitych 1000 żetonów +- [`hardhat.config.ts`](https://github.com/kurtosis-tech/awesome-kurtosis/blob/main/smart-contract-example/hardhat.config.ts) konfiguruje Twoją instalację Hardhat + +### Konfiguracja Hardhat do korzystania z lokalnej sieci testowej {#configure-hardhat} + +Po skonfigurowaniu środowiska programistycznego dApp, połączysz teraz Hardhat z lokalną siecią testową Ethereum wygenerowaną za pomocą Kurtosis. Aby to osiągnąć, zastąp `<$YOUR_PORT>` w strukturze `localnet` w pliku konfiguracyjnym `hardhat.config.ts` portem z danych wyjściowych rpc uri dowolnej usługi `el-client-`. W tym przykładowym przypadku portem byłoby `64248`. Twój port będzie inny. + +Przykład w `hardhat.config.ts`: + +```js +localnet: { +url: 'http://127.0.0.1:<$YOUR_PORT>',// TODO: ZASTĄP $YOUR_PORT PORTEM URI WĘZŁA WYGENEROWANYM PRZEZ PAKIET SIECI ETH KURTOSIS + +// To są klucze prywatne powiązane z prefinansowanymi kontami testowymi utworzonymi przez eth-network-package +// +accounts: [ + "ef5177cd0b6b21c87db5a0bf35d4084a8a57a9d6a064f86d51ac85f2b873a4e2", + "48fcc39ae27a0e8bf0274021ae6ebd8fe4a0e12623d61464c498900b28feb567", + "7988b3a148716ff800414935b305436493e1f25237a2a03e5eebc343735e2f31", + "b3c409b6b0b3aa5e65ab2dc1930534608239a478106acf6f3d9178e9f9b00b35", + "df9bb6de5d3dc59595bcaa676397d837ff49441d211878c024eabda2cd067c9f", + "7da08f856b5956d40a72968f93396f6acff17193f013e8053f6fbb6c08c194d6", + ], +}, +``` + +Po zapisaniu pliku Twoje środowisko programistyczne dApp Hardhat jest połączone z lokalną siecią testową Ethereum! Możesz sprawdzić, czy Twoja sieć testowa działa, uruchamiając: + +```python +npx hardhat balances --network localnet +``` + +Dane wyjściowe powinny wyglądać mniej więcej tak: + +```python +0x878705ba3f8Bc32FCf7F4CAa1A35E72AF65CF766 has balance 10000000000000000000000000 +0x4E9A3d9D1cd2A2b2371b8b3F489aE72259886f1A has balance 10000000000000000000000000 +0xdF8466f277964Bb7a0FFD819403302C34DCD530A has balance 10000000000000000000000000 +0x5c613e39Fc0Ad91AfDA24587e6f52192d75FBA50 has balance 10000000000000000000000000 +0x375ae6107f8cC4cF34842B71C6F746a362Ad8EAc has balance 10000000000000000000000000 +0x1F6298457C5d76270325B724Da5d1953923a6B88 has balance 10000000000000000000000000 +``` + +To potwierdza, że Hardhat korzysta z Twojej lokalnej sieci testowej i wykrywa wstępnie zasilone konta utworzone przez `eth-network-package`. + +### Wdróż i przetestuj swoją dApp lokalnie {#deploy-and-test-dapp} + +Gdy środowisko programistyczne dApp jest w pełni połączone z lokalną siecią testową Ethereum, możesz teraz uruchamiać procesy programistyczne i testowe dla swojej dApp, korzystając z lokalnej sieci testowej. + +Aby skompilować i wdrożyć smart kontrakt `ChipToken.sol` do lokalnego prototypowania i rozwoju, uruchom: + +```python +npx hardhat compile +npx hardhat run scripts/deploy.ts --network localnet +``` + +Dane wyjściowe powinny wyglądać mniej więcej tak: + +```python +ChipToken wdrożono do: 0xAb2A01BC351770D09611Ac80f1DE076D56E0487d +``` + +Teraz spróbuj uruchomić test `simple.js` na swojej lokalnej dApp, aby potwierdzić, że każdemu graczowi w naszej dApp Blackjack zostało wybite 1000 żetonów: + +Dane wyjściowe powinny wyglądać mniej więcej tak: + +```python +npx hardhat test --network localnet +``` + +Dane wyjściowe powinny wyglądać mniej więcej tak: + +```python +ChipToken + wybij + ✔ powinien wybić 1000 żetonów dla GRACZA PIERWSZEGO + + 1 przeszedł (654ms) +``` + +### Podsumowanie {#review-dapp-workflows} + +W tym momencie skonfigurowałeś środowisko programistyczne dApp, połączyłeś je z lokalną siecią Ethereum utworzoną przez Kurtosis oraz skompilowałeś, wdrożyłeś i uruchomiłeś prosty test dla swojej dApp. + +Teraz zbadajmy, jak można skonfigurować podstawową sieć do testowania naszych dapek w różnych konfiguracjach sieci. + +## Konfiguracja lokalnej sieci testowej Ethereum {#configure-testnet} + +### Zmiana konfiguracji klienta i liczby węzłów {#configure-client-config-and-num-nodes} + +Twoją lokalną sieć testową Ethereum można skonfigurować do używania różnych par klientów EL i CL, a także różnej liczby węzłów, w zależności od scenariusza i konkretnej konfiguracji sieci, którą chcesz rozwijać lub testować. Oznacza to, że po skonfigurowaniu możesz uruchomić dostosowaną lokalną sieć testową i używać jej do uruchamiania tych samych procesów (wdrażanie, testy itp.) w różnych konfiguracjach sieci, aby upewnić się, że wszystko działa zgodnie z oczekiwaniami. Aby dowiedzieć się więcej o innych parametrach, które możesz modyfikować, odwiedź ten link. + +Spróbuj! Możesz przekazać różne opcje konfiguracyjne do `eth-network-package` za pośrednictwem pliku JSON. Ten plik JSON z parametrami sieci dostarcza określonych konfiguracji, których Kurtosis użyje do skonfigurowania lokalnej sieci Ethereum. + +Weź domyślny plik konfiguracyjny i edytuj go, aby uruchomić dwa węzły z różnymi parami EL/CL: + +- Węzeł 1 z `geth`/`lighthouse` +- Węzeł 2 z `geth`/`lodestar` +- Węzeł 3 z `geth`/`teku` + +Ta konfiguracja tworzy heterogeniczną sieć implementacji węzłów Ethereum do testowania Twojej dApp. Twój plik konfiguracyjny powinien teraz wyglądać następująco: + +```yaml +{ + "participants": + [ + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lighthouse", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "lodestar", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + { + "el_client_type": "geth", + "el_client_image": "", + "el_client_log_level": "", + "cl_client_type": "teku", + "cl_client_image": "", + "cl_client_log_level": "", + "beacon_extra_params": [], + "el_extra_params": [], + "validator_extra_params": [], + "builder_network_params": null, + }, + ], + "network_params": + { + "preregistered_validator_keys_mnemonic": "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete", + "num_validator_keys_per_node": 64, + "network_id": "3151908", + "deposit_contract_address": "0x4242424242424242424242424242424242424242", + "seconds_per_slot": 12, + "genesis_delay": 120, + "capella_fork_epoch": 5, + }, +} +``` + +Każda struktura `participants` odpowiada węzłowi w sieci, więc 3 struktury `participants` poinformują Kurtosis, aby uruchomić 3 węzły w Twojej sieci. Każda struktura `participants` pozwoli Ci określić parę EL i CL używaną dla tego konkretnego węzła. + +Struktura `network_params` konfiguruje ustawienia sieci, które są używane do tworzenia plików genezy dla każdego węzła, a także inne ustawienia, takie jak sekundy na slot sieci. + +Zapisz edytowany plik parametrów w dowolnym katalogu (w poniższym przykładzie jest on zapisany na pulpicie), a następnie użyj go do uruchomienia pakietu Kurtosis, uruchamiając: + +```python +kurtosis clean -a && kurtosis run --enclave local-eth-testnet github.com/kurtosis-tech/eth-network-package \"$(cat ~/eth-network-params.json)\" +``` + +Uwaga: polecenie `kurtosis clean -a` jest tutaj używane, aby poinstruować Kurtosis, aby zniszczył starą sieć testową i jej zawartość przed uruchomieniem nowej. + +Ponownie, Kurtosis będzie działać przez chwilę i wydrukuje poszczególne kroki, które mają miejsce. Ostatecznie, dane wyjściowe powinny wyglądać mniej więcej tak: + +```python +Starlark code successfully run. No output was returned. +INFO[2023-04-07T11:43:16-04:00] ========================================================== +INFO[2023-04-07T11:43:16-04:00] || Created enclave: local-eth-testnet || +INFO[2023-04-07T11:43:16-04:00] ========================================================== +Name: local-eth-testnet +UUID: bef8c192008e +Status: RUNNING +Creation Time: Fri, 07 Apr 2023 11:41:58 EDT + +========================================= Files Artifacts ========================================= +UUID Name +cc495a8e364a cl-genesis-data +7033fcdb5471 el-genesis-data +a3aef43fc738 genesis-generation-config-cl +8e968005fc9d genesis-generation-config-el +3182cca9d3cd geth-prefunded-keys +8421166e234f prysm-password +d9e6e8d44d99 validator-keystore-0 +23f5ba517394 validator-keystore-1 +4d28dea40b5c validator-keystore-2 + +========================================== User Services ========================================== +UUID Name Ports Status +485e6fde55ae cl-client-0-beacon http: 4000/tcp -> http://127.0.0.1:65010 RUNNING + metrics: 5054/tcp -> http://127.0.0.1:65011 + tcp-discovery: 9000/tcp -> 127.0.0.1:65012 + udp-discovery: 9000/udp -> 127.0.0.1:54455 +73739bd158b2 cl-client-0-validator http: 5042/tcp -> 127.0.0.1:65016 RUNNING + metrics: 5064/tcp -> http://127.0.0.1:65017 +1b0a233cd011 cl-client-1-beacon http: 4000/tcp -> 127.0.0.1:65021 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65023 + tcp-discovery: 9000/tcp -> 127.0.0.1:65024 + udp-discovery: 9000/udp -> 127.0.0.1:56031 + validator-metrics: 5064/tcp -> 127.0.0.1:65022 +949b8220cd53 cl-client-1-validator http: 4000/tcp -> 127.0.0.1:65028 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65030 + tcp-discovery: 9000/tcp -> 127.0.0.1:65031 + udp-discovery: 9000/udp -> 127.0.0.1:60784 + validator-metrics: 5064/tcp -> 127.0.0.1:65029 +c34417bea5fa cl-client-2 http: 4000/tcp -> 127.0.0.1:65037 RUNNING + metrics: 8008/tcp -> 127.0.0.1:65035 + tcp-discovery: 9000/tcp -> 127.0.0.1:65036 + udp-discovery: 9000/udp -> 127.0.0.1:63581 +e19738e6329d el-client-0 engine-rpc: 8551/tcp -> 127.0.0.1:64986 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64988 + tcp-discovery: 30303/tcp -> 127.0.0.1:64987 + udp-discovery: 30303/udp -> 127.0.0.1:55706 + ws: 8546/tcp -> 127.0.0.1:64989 +e904687449d9 el-client-1 engine-rpc: 8551/tcp -> 127.0.0.1:64993 RUNNING + rpc: 8545/tcp -> 127.0.0.1:64995 + tcp-discovery: 30303/tcp -> 127.0.0.1:64994 + udp-discovery: 30303/udp -> 127.0.0.1:58096 + ws: 8546/tcp -> 127.0.0.1:64996 +ad6f401126fa el-client-2 engine-rpc: 8551/tcp -> 127.0.0.1:65003 RUNNING + rpc: 8545/tcp -> 127.0.0.1:65001 + tcp-discovery: 30303/tcp -> 127.0.0.1:65000 + udp-discovery: 30303/udp -> 127.0.0.1:57269 + ws: 8546/tcp -> 127.0.0.1:65002 +12d04a9dbb69 prelaunch-data-generator-1680882122181135513 STOPPED +5b45f9c0504b prelaunch-data-generator-1680882122192182847 STOPPED +3d4aaa75e218 prelaunch-data-generator-1680882122201668972 STOPPED +``` + +Gratulacje! Pomyślnie skonfigurowałeś swoją lokalną sieć testową, aby miała 3 węzły zamiast 1. Aby uruchomić te same procesy, co wcześniej, w swojej dApp (wdrażanie i testowanie), wykonaj te same operacje, co poprzednio, zastępując `<$YOUR_PORT>` w strukturze `localnet` w pliku konfiguracyjnym `hardhat.config.ts` portem z danych wyjściowych rpc uri dowolnej usługi `el-client-` w nowej, 3-węzłowej lokalnej sieci testowej. + +## Wnioski {#conclusion} + +I to wszystko! Podsumowując ten krótki poradnik, Ty: + +- Utworzyłeś lokalną sieć testową Ethereum za pośrednictwem Dockera przy użyciu Kurtosis +- Połączyłeś swoje lokalne środowisko programistyczne dApp z lokalną siecią Ethereum +- Wdrożyłeś dApp i uruchomiłeś prosty test na lokalnej sieci Ethereum +- Skonfigurowałeś podstawową sieć Ethereum, aby miała 3 węzły + +Chcielibyśmy usłyszeć od Ciebie, co poszło Ci dobrze, co można by poprawić, lub odpowiedzieć na wszelkie Twoje pytania. Nie wahaj się skontaktować z nami za pośrednictwem [GitHub](https://github.com/kurtosis-tech/kurtosis/issues/new/choose) lub [napisz do nas e-mail](mailto:feedback@kurtosistech.com)! + +### Inne przykłady i poradniki {#other-examples-guides} + +Zachęcamy do zapoznania się z naszym [szybkim startem](https://docs.kurtosis.com/quickstart) (gdzie zbudujesz bazę danych Postgres i API) oraz innymi przykładami w naszym repozytorium [awesome-kurtosis](https://github.com/kurtosis-tech/awesome-kurtosis), gdzie znajdziesz świetne przykłady, w tym pakiety do: + +- Uruchamiania tej samej lokalnej sieci testowej Ethereum, ale z podłączonymi dodatkowymi usługami, takimi jak spamer transakcji (do symulowania transakcji), monitor forka oraz podłączona instancja Grafana i Prometheus +- Przeprowadzania [testu podsieci](https://github.com/kurtosis-tech/awesome-kurtosis/tree/main/ethereum-network-partition-test) na tej samej lokalnej sieci Ethereum diff --git a/public/content/translations/pl/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md b/public/content/translations/pl/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md new file mode 100644 index 00000000000..84a4beaaeb2 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/downsizing-contracts-to-fight-the-contract-size-limit/index.md @@ -0,0 +1,144 @@ +--- +title: "Zmniejszanie kontraktów w walce z limitem rozmiaru kontraktu" +description: "Co możesz zrobić, aby Twoje inteligentne kontrakty nie stały się zbyt duże?" +author: Markus Waas +lang: pl +tags: [ "solidity", "smart kontrakty", "przechowywanie" ] +skill: intermediate +published: 2020-06-26 +source: soliditydeveloper.com +sourceUrl: https://soliditydeveloper.com/max-contract-size +--- + +## Dlaczego istnieje limit? {#why-is-there-a-limit} + +[22 listopada 2016 r.](https://blog.ethereum.org/2016/11/18/hard-fork-no-4-spurious-dragon/) hard fork Spurious Dragon wprowadził [EIP-170](https://eips.ethereum.org/EIPS/eip-170), który dodał limit rozmiaru inteligentnego kontraktu wynoszący 24,576 kb. Dla Ciebie, jako dewelopera Solidity, oznacza to, że gdy dodajesz coraz więcej funkcjonalności do swojego kontraktu, w pewnym momencie osiągniesz limit i podczas wdrażania zobaczysz błąd: + +`Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.` + +Limit ten został wprowadzony, aby zapobiegać atakom typu „odmowa usługi” (Denial-of-Service, DOS). Każde wywołanie kontraktu jest stosunkowo tanie pod względem zużycia gazu. Jednak wpływ wywołania kontraktu na węzły Ethereum wzrasta nieproporcjonalnie w zależności od rozmiaru kodu wywoływanego kontraktu (odczytywanie kodu z dysku, wstępne przetwarzanie kodu, dodawanie danych do dowodu Merkle). Zawsze, gdy mamy do czynienia z sytuacją, w której atakujący potrzebuje niewielu zasobów, aby spowodować dużo pracy dla innych, pojawia się potencjalne zagrożenie atakami DOS. + +Początkowo był to mniejszy problem, ponieważ jednym z naturalnych ograniczeń rozmiaru kontraktu jest limit gazu w bloku. Oczywiście kontrakt musi zostać wdrożony w ramach transakcji, która zawiera cały kod bajtowy kontraktu. Jeśli w bloku umieścisz tylko tę jedną transakcję, możesz zużyć cały ten gaz, ale nie jest on nieskończony. Od czasu [aktualizacji London](/ethereum-forks/#london) limit gazu w bloku może wahać się między 15 a 30 milionami jednostek w zależności od zapotrzebowania w sieci. + +W dalszej części przyjrzymy się kilku metodom uporządkowanym według ich potencjalnego wpływu. Pomyśl o tym w kategoriach utraty wagi. Najlepszą strategią na osiągnięcie docelowej wagi (w naszym przypadku 24 kb) jest skupienie się w pierwszej kolejności na metodach o dużym wpływie. W większości przypadków wystarczy zmiana diety, aby osiągnąć cel, ale czasami potrzeba czegoś więcej. Wtedy możesz dodać trochę ćwiczeń (średni wpływ), a nawet suplementy (mały wpływ). + +## Duży wpływ {#big-impact} + +### Oddziel swoje kontrakty {#separate-your-contracts} + +To powinno być zawsze Twoje pierwsze podejście. Jak możesz podzielić kontrakt na kilka mniejszych? Zazwyczaj zmusza to do opracowania dobrej architektury dla swoich kontraktów. Mniejsze kontrakty są zawsze preferowane z perspektywy czytelności kodu. Aby podzielić kontrakty, zadaj sobie pytanie: + +- Które funkcje pasują do siebie? Każdy zestaw funkcji może najlepiej pasować do osobnego kontraktu. +- Które funkcje nie wymagają odczytywania stanu kontraktu lub tylko określonego podzbioru stanu? +- Czy możesz rozdzielić przechowywanie i funkcjonalność? + +### Biblioteki {#libraries} + +Jednym z prostych sposobów na przeniesienie kodu funkcjonalności z dala od miejsca przechowywania danych jest użycie [biblioteki](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#libraries). Nie deklaruj funkcji biblioteki jako wewnętrznych (internal), ponieważ zostaną one [dodane do kontraktu](https://ethereum.stackexchange.com/questions/12975/are-internal-functions-in-libraries-not-covered-by-linking) bezpośrednio podczas kompilacji. Ale jeśli używasz funkcji publicznych (public), to w rzeczywistości znajdą się one w osobnym kontrakcie bibliotecznym. Rozważ użycie [`using for`](https://solidity.readthedocs.io/en/v0.6.10/contracts.html#using-for), aby uczynić korzystanie z bibliotek wygodniejszym. + +### Proxy {#proxies} + +Bardziej zaawansowaną strategią jest system proxy. Biblioteki używają w tle `DELEGATECALL`, który po prostu wykonuje funkcję innego kontraktu w kontekście stanu kontraktu wywołującego. Zapoznaj się z [tym wpisem na blogu](https://hackernoon.com/how-to-make-smart-contracts-upgradable-2612e771d5a2), aby dowiedzieć się więcej o systemach proxy. Dają one więcej funkcjonalności, np. umożliwiają aktualizację, ale dodają też sporo złożoności. Nie dodawałbym ich tylko po to, aby zmniejszyć rozmiar kontraktu, chyba że z jakiegoś powodu jest to jedyna dostępna opcja. + +## Średni wpływ {#medium-impact} + +### Usuń funkcje {#remove-functions} + +To powinno być oczywiste. Funkcje znacznie zwiększają rozmiar kontraktu. + +- **Zewnętrzne (External)**: Często dla wygody dodajemy wiele funkcji typu view. Jest to w porządku, dopóki nie osiągniesz limitu rozmiaru. Wtedy warto poważnie zastanowić się nad usunięciem wszystkich funkcji oprócz tych absolutnie niezbędnych. +- **Wewnętrzne (Internal)**: Możesz także usunąć funkcje wewnętrzne/prywatne i po prostu wstawić kod w miejscu wywołania, o ile funkcja jest wywoływana tylko raz. + +### Unikaj dodatkowych zmiennych {#avoid-additional-variables} + +```solidity +function get(uint id) returns (address,address) { + MyStruct memory myStruct = myStructs[id]; + return (myStruct.addr1, myStruct.addr2); +} +``` + +```solidity +function get(uint id) returns (address,address) { + return (myStructs[id].addr1, myStructs[id].addr2); +} +``` + +Prosta zmiana, taka jak ta, robi różnicę **0,28 kb**. Istnieje szansa, że znajdziesz wiele podobnych sytuacji w swoich kontraktach, a te mogą zsumować się do znacznych oszczędności. + +### Skróć komunikaty o błędach {#shorten-error-message} + +Długie komunikaty `revert` i w szczególności wiele różnych komunikatów `revert` może niepotrzebnie zwiększać rozmiar kontraktu. Zamiast tego użyj krótkich kodów błędów i dekoduj je w swojej aplikacji. Długi komunikat może stać się znacznie krótszy: + +```solidity +require(msg.sender == owner, "Tylko właściciel tego kontraktu może wywołać tę funkcję"); +``` + +```solidity +require(msg.sender == owner, "OW1"); +``` + +### Używaj niestandardowych błędów zamiast komunikatów o błędach + +Niestandardowe błędy zostały wprowadzone w [Solidity 0.8.4](https://blog.soliditylang.org/2021/04/21/custom-errors/). Są świetnym sposobem na zmniejszenie rozmiaru Twoich kontraktów, ponieważ są kodowane w ABI jako selektory (tak jak funkcje). + +```solidity +error Unauthorized(); + +if (msg.sender != owner) { + revert Unauthorized(); +} +``` + +### Rozważ niską wartość „runs” w optymalizatorze {#consider-a-low-run-value-in-the-optimizer} + +Możesz również zmienić ustawienia optymalizatora. Domyślna wartość 200 oznacza, że próbuje on zoptymalizować kod bajtowy tak, jakby funkcja była wywoływana 200 razy. Jeśli zmienisz tę wartość na 1, w zasadzie mówisz optymalizatorowi, aby optymalizował pod kątem jednokrotnego uruchomienia każdej funkcji. Funkcja zoptymalizowana pod kątem jednokrotnego uruchomienia oznacza, że jest zoptymalizowana pod kątem samego wdrożenia. Pamiętaj, że **zwiększa to [koszty gazu](/developers/docs/gas/) za uruchamianie funkcji**, więc możesz nie chcieć tego robić. + +## Mały wpływ {#small-impact} + +### Unikaj przekazywania struktur do funkcji {#avoid-passing-structs-to-functions} + +Jeśli używasz [ABIEncoderV2](https://solidity.readthedocs.io/en/v0.6.10/layout-of-source-files.html#abiencoderv2), pomocne może być nieprzekazywanie struktur do funkcji. Zamiast przekazywać parametr jako strukturę, przekaż wymagane parametry bezpośrednio. W tym przykładzie zaoszczędziliśmy kolejne **0,1 kb**. + +```solidity +function get(uint id) returns (address,address) { + return _get(myStruct); +} + +function _get(MyStruct memory myStruct) private view returns(address,address) { + return (myStruct.addr1, myStruct.addr2); +} +``` + +```solidity +function get(uint id) returns(address,address) { + return _get(myStructs[id].addr1, myStructs[id].addr2); +} + +function _get(address addr1, address addr2) private view returns(address,address) { + return (addr1, addr2); +} +``` + +### Deklaruj poprawną widoczność dla funkcji i zmiennych {#declare-correct-visibility-for-functions-and-variables} + +- Funkcje lub zmienne, które są wywoływane tylko z zewnątrz? Deklaruj je jako `external` zamiast `public`. +- Funkcje lub zmienne wywoływane tylko z wnętrza kontraktu? Deklaruj je jako `private` lub `internal` zamiast `public`. + +### Usuń modyfikatory {#remove-modifiers} + +Modyfikatory, zwłaszcza gdy są intensywnie używane, mogą mieć znaczący wpływ na rozmiar kontraktu. Rozważ ich usunięcie i zamiast tego użyj funkcji. + +```solidity +modifier checkStuff() {} + +function doSomething() checkStuff {} +``` + +```solidity +function checkStuff() private {} + +function doSomething() { checkStuff(); } +``` + +Te wskazówki powinny pomóc znacznie zmniejszyć rozmiar kontraktu. Jeszcze raz podkreślam, zawsze skupiaj się na dzieleniu kontraktów, jeśli to możliwe, aby uzyskać największy efekt. diff --git a/public/content/translations/pl/developers/tutorials/eip-1271-smart-contract-signatures/index.md b/public/content/translations/pl/developers/tutorials/eip-1271-smart-contract-signatures/index.md new file mode 100644 index 00000000000..d2f6304813f --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/eip-1271-smart-contract-signatures/index.md @@ -0,0 +1,129 @@ +--- +title: "EIP-1271: Podpisywanie i weryfikowanie podpisów inteligentnych kontraktów" +description: "Omówienie generowania i weryfikacji podpisów inteligentnych kontraktów za pomocą EIP-1271. Omawiamy również implementację EIP-1271 stosowaną w Safe (dawniej Gnosis Safe), aby dać deweloperom inteligentnych kontraktów konkretny przykład, na którym mogą się wzorować." +author: Nathan H. Leung +lang: pl +tags: + [ + "eip-1271", + "smart kontrakty", + "weryfikacja", + "podpisywanie" + ] +skill: intermediate +published: 2023-01-12 +--- + +Standard [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) pozwala inteligentnym kontraktom weryfikować podpisy. + +W tym samouczku przedstawiamy przegląd podpisów cyfrowych, tło EIP-1271 oraz specyficzną implementację EIP-1271 używaną przez [Safe](https://safe.global/) (wcześniej Gnosis Safe). Wszystko to może posłużyć jako punkt wyjścia do implementacji EIP-1271 w Twoich własnych kontraktach. + +## Czym jest podpis? + +W tym kontekście podpis (a dokładniej „podpis cyfrowy”) to komunikat oraz pewnego rodzaju dowód, że komunikat pochodzi od określonej osoby/nadawcy/adresu. + +Na przykład podpis cyfrowy może wyglądać tak: + +1. Komunikat: „Chcę zalogować się do tej witryny za pomocą mojego portfela Ethereum”. +2. Podpisujący: Mój adres to `0x000…` +3. Dowód: Oto dowód na to, że ja, `0x000...`, faktycznie stworzyłem cały ten komunikat (zazwyczaj jest to coś kryptograficznego). + +Należy pamiętać, że podpis cyfrowy zawiera zarówno „komunikat”, jak i „podpis”. + +Dlaczego? Na przykład, gdybyś dał mi umowę do podpisania, a ja odciąłbym stronę z podpisem i oddał Ci tylko moje podpisy bez reszty umowy, umowa nie byłaby ważna. + +W ten sam sposób podpis cyfrowy nic nie znaczy bez powiązanego z nim komunikatu! + +## Dlaczego EIP-1271 istnieje? + +Aby utworzyć podpis cyfrowy do użytku w blockchainach opartych na Ethereum, zazwyczaj potrzebny jest tajny klucz prywatny, którego nikt inny nie zna. To właśnie sprawia, że Twój podpis jest Twój (nikt inny nie może stworzyć tego samego podpisu bez znajomości tajnego klucza). + +Twoje konto Ethereum (tj. konto zewnętrznego właściciela/EOA) ma przypisany do niego klucz prywatny i to właśnie ten klucz prywatny jest zazwyczaj używany, gdy strona internetowa lub dapka prosi o podpis (np. przy „Zaloguj się z Ethereum”). + +Aplikacja może [zweryfikować podpis](https://www.alchemy.com/docs/how-to-verify-a-message-signature-on-ethereum), który tworzysz za pomocą biblioteki zewnętrznej, takiej jak ethers.js, [bez znajomości Twojego klucza prywatnego](https://en.wikipedia.org/wiki/Public-key_cryptography) i mieć pewność, że to _Ty_ go stworzyłeś. + +> W rzeczywistości, ponieważ podpisy cyfrowe EOA wykorzystują kryptografię klucza publicznego, mogą być generowane i weryfikowane **offchain**! Tak działa bezgazowe głosowanie DAO — zamiast przesyłania głosów onchain, podpisy cyfrowe mogą być tworzone i weryfikowane offchain za pomocą bibliotek kryptograficznych. + +Podczas gdy konta EOA posiadają klucz prywatny, konta inteligentnych kontraktów nie posiadają żadnego rodzaju klucza prywatnego ani tajnego (więc "Zaloguj się za pomocą Ethereum", itp. nie może natywnie działać z kontami inteligentnych kontraktów). + +Problem, który EIP-1271 ma na celu rozwiązać: skąd możemy wiedzieć, że podpis inteligentnego kontraktu jest ważny, jeśli inteligentny kontrakt nie ma "tajemnicy", którą mógłby włączyć do podpisu? + +## Jak działa EIP-1271? + +Inteligentne kontrakty nie mają kluczy prywatnych, których można użyć do podpisywania komunikatów. Skąd więc możemy wiedzieć, czy podpis jest autentyczny? + +Cóż, jednym z pomysłów jest to, że możemy po prostu _zapytać_ inteligentny kontrakt, czy podpis jest autentyczny! + +EIP-1271 standaryzuje ideę „pytania” inteligentnego kontraktu, czy dany podpis jest ważny. + +Kontrakt, który implementuje EIP-1271, musi mieć funkcję o nazwie `isValidSignature`, która przyjmuje komunikat i podpis. Kontrakt może następnie uruchomić pewną logikę walidacji (specyfikacja nie narzuca tutaj niczego konkretnego), a następnie zwrócić wartość wskazującą, czy podpis jest ważny, czy nie. + +Jeśli `isValidSignature` zwróci prawidłowy wynik, to jest to prawie tak, jakby kontrakt mówił: "tak, zatwierdzam ten podpis + komunikat!". + +### Interfejs + +Oto dokładny interfejs w specyfikacji EIP-1271 (o parametrze `_hash` porozmawiamy poniżej, ale na razie pomyśl o nim jako o weryfikowanym komunikacie): + +```jsx +pragma solidity ^0.5.0; + +contract ERC1271 { + + // bytes4(keccak256("isValidSignature(bytes32,bytes)") + bytes4 constant internal MAGICVALUE = 0x1626ba7e; + + /** + * @dev Powinna zwrócić, czy podany podpis jest ważny dla podanego haszu + * @param _hash Hasz danych do podpisania + * @param _signature Tablica bajtów podpisu powiązana z _hasz + * + * MUSI zwrócić magiczną wartość bytes4 0x1626ba7e, gdy funkcja przejdzie pomyślnie. + * NIE MOŻE modyfikować stanu (używając STATICCALL dla solc < 0.5, modyfikator view dla solc > 0.5) + * MUSI zezwalać na wywołania zewnętrzne + */ + function isValidSignature( + bytes32 _hash, + bytes memory _signature) + public + view + returns (bytes4 magicValue); +} +``` + +## Przykładowa implementacja EIP-1271: Safe + +Kontrakty mogą implementować `isValidSignature` na wiele sposobów – specyfikacja nie mówi wiele o dokładnej implementacji. + +Jednym z godnych uwagi kontraktów, który implementuje EIP-1271, jest Safe (wcześniej Gnosis Safe). + +W kodzie Safe `isValidSignature` [jest zaimplementowane](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol) tak, że podpisy mogą być tworzone i weryfikowane na [dwa sposoby](https://ethereum.stackexchange.com/questions/122635/signing-messages-as-a-gnosis-safe-eip1271-support): + +1. Komunikaty onchain + 1. Tworzenie: właściciel Safe tworzy nową transakcję Safe, aby „podpisać” komunikat, przekazując go jako dane do transakcji. Gdy wystarczająca liczba właścicieli podpisze transakcję, aby osiągnąć próg multisig, transakcja jest rozgłaszana i uruchamiana. W transakcji jest funkcja Safe o nazwie (`signMessage(bytes calldata _data)`), która dodaje komunikat do listy "zatwierdzonych" komunikatów. + 2. Weryfikacja: wywołaj `isValidSignature` w kontrakcie Safe i przekaż komunikat do weryfikacji jako parametr komunikatu oraz [pustą wartość dla parametru podpisu](https://github.com/safe-global/safe-contracts/blob/main/contracts/handler/CompatibilityFallbackHandler.sol#L32) (tj. `0x`). Safe zobaczy, że parametr podpisu jest pusty i zamiast kryptograficznie weryfikować podpis, będzie wiedział, żeby po prostu sprawdzić, czy komunikat znajduje się na liście "zatwierdzonych" komunikatów. +2. Komunikaty offchain: + 1. Tworzenie: właściciel Safe tworzy komunikat offchain, a następnie prosi innych właścicieli Safe o indywidualne podpisanie komunikatu, dopóki nie będzie wystarczającej liczby podpisów, aby przekroczyć próg zatwierdzenia multisig. + 2. Weryfikacja: wywołaj `isValidSignature`. W parametrze komunikatu przekaż komunikat do zweryfikowania. W parametrze podpisu przekaż połączone ze sobą indywidualne podpisy każdego właściciela Safe, jeden po drugim. Safe sprawdzi, czy jest wystarczająco dużo podpisów, aby osiągnąć próg **oraz** czy każdy podpis jest ważny. Jeśli tak, zwróci wartość wskazującą pomyślną weryfikację podpisu. + +## Czym dokładnie jest parametr `_hash`? Dlaczego nie przekazać całego komunikatu? + +Być może zauważyłeś, że funkcja `isValidSignature` w [interfejsie EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) nie przyjmuje samego komunikatu, ale parametr `_hash`. Oznacza to, że zamiast przekazywać do `isValidSignature` pełny komunikat o dowolnej długości, przekazujemy 32-bajtowy hasz komunikatu (zazwyczaj keccak256). + +Każdy bajt calldata – tj. dane parametrów funkcji przekazywane do funkcji inteligentnego kontraktu – [kosztuje 16 jednostek gazu (4 jednostki gazu w przypadku bajtu zerowego)](https://eips.ethereum.org/EIPS/eip-2028), więc może to zaoszczędzić dużo gazu, jeśli komunikat jest długi. + +### Poprzednie specyfikacje EIP-1271 + +Istnieją specyfikacje EIP-1271, które mają funkcję `isValidSignature` z pierwszym parametrem typu `bytes` (o dowolnej długości, zamiast o stałej długości `bytes32`) i nazwą parametru `message`. Jest to [starsza wersja](https://github.com/safe-global/safe-contracts/issues/391#issuecomment-1075427206) standardu EIP-1271. + +## Jak EIP-1271 powinien być zaimplementowany w moich własnych kontraktach? + +Specyfikacja jest w tym zakresie bardzo otwarta. Implementacja Safe zawiera kilka dobrych pomysłów: + +- Można uznać podpisy EOA od "właściciela" kontraktu za ważne. +- Można przechowywać listę zatwierdzonych komunikatów i tylko je uważać za ważne. + +Ostatecznie zależy to od Ciebie jako dewelopera kontraktu! + +## Podsumowanie + +[EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) to wszechstronny standard, który pozwala inteligentnym kontraktom weryfikować podpisy. Otwiera to drzwi do tego, aby inteligentne kontrakty działały bardziej jak EOA – na przykład zapewniając sposób na działanie "Logowania za pomocą Ethereum" z inteligentnymi kontraktami – i może być wdrażany na wiele sposobów (Safe ma nietrywialną, interesującą implementację do rozważenia). diff --git a/public/content/translations/pl/developers/tutorials/erc-721-vyper-annotated-code/index.md b/public/content/translations/pl/developers/tutorials/erc-721-vyper-annotated-code/index.md new file mode 100644 index 00000000000..cd2a07345b7 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/erc-721-vyper-annotated-code/index.md @@ -0,0 +1,713 @@ +--- +title: "Omówienie kontraktu ERC-721 w języku Vyper" +description: "Kontrakt ERC-721 Ryuyi Nakamury i jego działanie" +author: Ori Pomerantz +lang: pl +tags: [ "vyper", "erc-721", "python" ] +skill: beginner +published: 2021-04-01 +--- + +## Wprowadzenie {#introduction} + +Standard [ERC-721](/developers/docs/standards/tokens/erc-721/) służy do przechowywania własności niewymienialnych tokenów (NFT). +Tokeny [ERC-20](/developers/docs/standards/tokens/erc-20/) zachowują się jak towar, ponieważ nie ma różnicy między poszczególnymi tokenami. +W przeciwieństwie do nich tokeny ERC-721 są przeznaczone dla aktywów, które są podobne, ale nie identyczne, takich jak różne [kreskówki z kotami](https://www.cryptokitties.co/) +lub tytuły własności do różnych nieruchomości. + +W tym artykule przeanalizujemy [kontrakt ERC-721 Ryuyi Nakamury](https://github.com/vyperlang/vyper/blob/master/examples/tokens/ERC721.vy). +Ten kontrakt jest napisany w [Vyper](https://vyper.readthedocs.io/en/latest/index.html), języku programowania kontraktów podobnym do Pythona, zaprojektowanym tak, aby trudniej było w nim napisać niebezpieczny kod niż w Solidity. + +## Kontrakt {#contract} + +```python +# @dev Implementacja standardu niewymienialnych tokenów ERC-721. +# @author Ryuya Nakamura (@nrryuya) +# Zmodyfikowano na podstawie: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy +``` + +Komentarze w Vyper, podobnie jak w Pythonie, zaczynają się od hasza (`#`) i trwają do końca linii. Komentarze zawierające +`@` są używane przez [NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html) do tworzenia czytelnej dla człowieka +dokumentacji. + +```python +from vyper.interfaces import ERC721 + +implements: ERC721 +``` + +Interfejs ERC-721 jest wbudowany w język Vyper. +[Definicję kodu można zobaczyć tutaj](https://github.com/vyperlang/vyper/blob/master/vyper/builtin_interfaces/ERC721.py). +Definicja interfejsu jest napisana w Pythonie, a nie w Vyper, ponieważ interfejsy są używane nie tylko w ramach +blockchainu, ale także podczas wysyłania transakcji do blockchainu z zewnętrznego klienta, który może być napisany w +Pythonie. + +Pierwsza linia importuje interfejs, a druga określa, że go tutaj implementujemy. + +### Interfejs ERC721Receiver {#receiver-interface} + +```python +# Interfejs dla kontraktu wywoływanego przez safeTransferFrom() +interface ERC721Receiver: + def onERC721Received( +``` + +ERC-721 obsługuje dwa rodzaje transferów: + +- `transferFrom`, który pozwala nadawcy określić dowolny adres docelowy i przenosi odpowiedzialność + za transfer na nadawcę. Oznacza to, że można dokonać transferu na nieprawidłowy adres, w takim przypadku + NFT zostanie utracony na zawsze. +- `safeTransferFrom`, który sprawdza, czy adres docelowy jest kontraktem. Jeśli tak, kontrakt ERC-721 + pyta kontrakt odbierający, czy chce otrzymać NFT. + +Aby odpowiedzieć na żądania `safeTransferFrom`, kontrakt odbierający musi zaimplementować `ERC721Receiver`. + +```python + _operator: address, + _from: address, +``` + +Adres `_from` to obecny właściciel tokena. Adres `_operator` to ten, który +zażądał transferu (te dwa adresy mogą nie być takie same ze względu na pozwolenia). + +```python + _tokenId: uint256, +``` + +Identyfikatory tokenów ERC-721 mają 256 bitów. Zazwyczaj są one tworzone poprzez haszowanie opisu tego, co +reprezentuje dany token. + +```python + _data: Bytes[1024] +``` + +Żądanie może zawierać do 1024 bajtów danych użytkownika. + +```python + ) -> bytes32: view +``` + +Aby zapobiec przypadkom, w których kontrakt przypadkowo akceptuje transfer, wartość zwracana nie jest wartością logiczną (boolean), +ale 256 bitami o określonej wartości. + +Ta funkcja jest typu `view`, co oznacza, że może odczytywać stan blockchaina, ale nie może go modyfikować. + +### Zdarzenia {#events} + +[Zdarzenia](https://media.consensys.net/technical-introduction-to-events-and-logs-in-ethereum-a074d65dd61e) +są emitowane w celu informowania użytkowników i serwerów spoza blockchaina o zdarzeniach. Należy pamiętać, że zawartość zdarzeń +nie jest dostępna dla kontraktów na blockchainie. + +```python +# @dev Emitowane, gdy własność dowolnego NFT zmienia się w wyniku dowolnego mechanizmu. To zdarzenie jest emitowane, gdy NFT są +# tworzone (`from` == 0) i niszczone (`to` == 0). Wyjątek: podczas tworzenia kontraktu dowolna +# liczba NFT może zostać utworzona i przypisana bez emitowania zdarzenia Transfer. W momencie dowolnego +# transferu zatwierdzony adres dla tego NFT (jeśli istnieje) jest resetowany do zera. +# @param _from Nadawca NFT (jeśli adres jest adresem zerowym, oznacza to utworzenie tokena). +# @param _to Odbiorca NFT (jeśli adres jest adresem zerowym, oznacza to zniszczenie tokena). +# @param _tokenId NFT, które zostało przetransferowane. +event Transfer: + sender: indexed(address) + receiver: indexed(address) + tokenId: indexed(uint256) +``` + +Jest to podobne do zdarzenia Transfer w ERC-20, z tym wyjątkiem, że zamiast kwoty podajemy `tokenId`. +Nikt nie jest właścicielem adresu zerowego, więc umownie używamy go do zgłaszania tworzenia i niszczenia tokenów. + +```python +# @dev Emitowane, gdy zatwierdzony adres dla NFT jest zmieniany lub ponownie zatwierdzany. Adres zerowy +# oznacza, że nie ma zatwierdzonego adresu. Kiedy emitowane jest zdarzenie Transfer, oznacza to również, +# że zatwierdzony adres dla tego NFT (jeśli istnieje) jest resetowany do zera. +# @param _owner Właściciel NFT. +# @param _approved Adres, który zatwierdzamy. +# @param _tokenId NFT, które zatwierdzamy. +event Approval: + owner: indexed(address) + approved: indexed(address) + tokenId: indexed(uint256) +``` + +Zatwierdzenie ERC-721 jest podobne do pozwolenia (allowance) w ERC-20. Określony adres ma pozwolenie na transfer określonego +tokena. Daje to mechanizm, dzięki któremu kontrakty mogą reagować na przyjęcie tokena. Kontrakty nie mogą +nasłuchiwać zdarzeń, więc jeśli po prostu prześlesz im token, nie będą o tym „wiedziały”. W ten sposób +właściciel najpierw przesyła zatwierdzenie, a następnie wysyła żądanie do kontraktu: „Zatwierdziłem transfer tokena +X, proszę go wykonać...”. + +Jest to wybór projektowy mający na celu upodobnienie standardu ERC-721 do standardu ERC-20. Ponieważ +tokeny ERC-721 nie są wymienialne, kontrakt może również zidentyfikować, że otrzymał określony token, sprawdzając +jego własność. + +```python +# @dev Emitowane, gdy operator jest włączany lub wyłączany dla właściciela. Operator może zarządzać +# wszystkimi NFT właściciela. +# @param _owner Właściciel NFT. +# @param _operator Adres, któremu ustawiamy uprawnienia operatora. +# @param _approved Status uprawnień operatora (true, jeśli uprawnienia operatora są nadane, a false, jeśli +# cofnięte). +event ApprovalForAll: + owner: indexed(address) + operator: indexed(address) + approved: bool +``` + +Czasami przydatne jest posiadanie _operatora_, który może zarządzać wszystkimi tokenami konta określonego typu (tymi, którymi zarządza określony kontrakt), podobnie jak pełnomocnictwo. Na przykład mogę chcieć nadać takie uprawnienie kontraktowi, który sprawdza, czy +nie kontaktowałem się z nim przez sześć miesięcy, a jeśli tak, to rozdziela moje aktywa między moich spadkobierców (jeśli jeden z nich o to poprosi; kontrakty +nie mogą nic zrobić bez wywołania przez transakcję). W ERC-20 możemy po prostu dać wysokie pozwolenie kontraktowi spadkowemu, +ale to nie działa w przypadku ERC-721, ponieważ tokeny nie są wymienialne. To jest odpowiednik. + +Wartość `approved` mówi nam, czy zdarzenie dotyczy zatwierdzenia, czy cofnięcia zatwierdzenia. + +### Zmienne stanu {#state-vars} + +Zmienne te zawierają aktualny stan tokenów: które z nich są dostępne i kto je posiada. Większość z nich +to obiekty `HashMap`, [jednokierunkowe mapowania, które istnieją między dwoma typami](https://vyper.readthedocs.io/en/latest/types.html#mappings). + +```python +# @dev Mapowanie z ID NFT na adres, który jest jego właścicielem. +idToOwner: HashMap[uint256, address] + +# @dev Mapowanie z ID NFT na zatwierdzony adres. +idToApprovals: HashMap[uint256, address] +``` + +Tożsamości użytkowników i kontraktów w Ethereum są reprezentowane przez 160-bitowe adresy. Te dwie zmienne mapują +identyfikatory tokenów do ich właścicieli i osób zatwierdzonych do ich transferu (maksymalnie jedna na każdy token). W Ethereum +niezainicjowane dane są zawsze zerowe, więc jeśli nie ma właściciela lub zatwierdzonego podmiotu transferującego, wartość dla danego tokena +jest zerowa. + +```python +# @dev Mapowanie adresu właściciela na liczbę jego tokenów. +ownerToNFTokenCount: HashMap[address, uint256] +``` + +Ta zmienna przechowuje liczbę tokenów dla każdego właściciela. Nie ma mapowania z właścicieli na tokeny, więc +jedynym sposobem na zidentyfikowanie tokenów, które posiada dany właściciel, jest prześledzenie historii zdarzeń w blockchainie +i odnalezienie odpowiednich zdarzeń `Transfer`. Możemy użyć tej zmiennej, aby wiedzieć, kiedy mamy wszystkie NFT i nie +musimy szukać dalej w czasie. + +Należy pamiętać, że ten algorytm działa tylko w przypadku interfejsów użytkownika i serwerów zewnętrznych. Kod działający na samym +blockchainie nie może odczytywać przeszłych zdarzeń. + +```python +# @dev Mapowanie adresu właściciela na mapowanie adresów operatorów. +ownerToOperators: HashMap[address, HashMap[address, bool]] +``` + +Konto może mieć więcej niż jednego operatora. Prosta `HashMap` jest niewystarczająca, aby +je śledzić, ponieważ każdy klucz prowadzi do pojedynczej wartości. Zamiast tego jako wartości można użyć +`HashMap[address, bool]`. Domyślnie wartość dla każdego adresu to `False`, co oznacza, że +nie jest on operatorem. W razie potrzeby można ustawić wartości na `True`. + +```python +# @dev Adres mintera (podmiotu wybijającego), który może wybić token +minter: address +``` + +Nowe tokeny muszą być w jakiś sposób tworzone. W tym kontrakcie jest jeden podmiot, który ma do tego prawo, czyli +`minter`. To prawdopodobnie wystarczy na przykład w przypadku gry. Do innych celów może być konieczne +stworzenie bardziej skomplikowanej logiki biznesowej. + +```python +# @dev Mapowanie ID interfejsu na wartość boolowską informującą, czy jest on obsługiwany +supportedInterfaces: HashMap[bytes32, bool] + +# @dev ID interfejsu ERC165 dla ERC165 +ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7 + +# @dev ID interfejsu ERC165 dla ERC721 +ERC721_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000080ac58cd +``` + +[ERC-165](https://eips.ethereum.org/EIPS/eip-165) określa mechanizm, dzięki któremu kontrakt może ujawnić, w jaki sposób aplikacje +mogą się z nim komunikować, a także z którymi standardami ERC jest zgodny. W tym przypadku kontrakt jest zgodny z ERC-165 i ERC-721. + +### Funkcje {#functions} + +To są funkcje, które faktycznie implementują ERC-721. + +#### Konstruktor {#constructor} + +```python +@external +def __init__(): +``` + +W Vyper, podobnie jak w Pythonie, funkcja konstruktora nazywa się `__init__`. + +```python + """ + @dev Konstruktor kontraktu. + """ +``` + +W Pythonie i w Vyper można również utworzyć komentarz, określając wieloliniowy ciąg znaków (który zaczyna się i kończy +na `"""`) i nie używając go w żaden sposób. Te komentarze mogą również zawierać +[NatSpec](https://vyper.readthedocs.io/en/latest/natspec.html). + +```python + self.supportedInterfaces[ERC165_INTERFACE_ID] = True + self.supportedInterfaces[ERC721_INTERFACE_ID] = True + self.minter = msg.sender +``` + +Aby uzyskać dostęp do zmiennych stanu, użyj `self.` (ponownie, tak samo jak w Pythonie). + +#### Funkcje widoku {#views} + +Są to funkcje, które nie modyfikują stanu blockchaina, a zatem mogą być wykonywane za +darmo, jeśli są wywoływane z zewnątrz. Jeśli funkcje widoku są wywoływane przez kontrakt, nadal muszą być wykonane na +każdym węźle i dlatego kosztują gaz. + +```python +@view +@external +``` + +Te słowa kluczowe przed definicją funkcji, które zaczynają się od znaku „małpy” (`@`), nazywane są _dekoratorami_. Określają +one okoliczności, w których funkcja może być wywołana. + +- `@view` określa, że ta funkcja jest widokiem. +- `@external` określa, że ta konkretna funkcja może być wywoływana przez transakcje i przez inne kontrakty. + +```python +def supportsInterface(_interfaceID: bytes32) -> bool: +``` + +W przeciwieństwie do Pythona Vyper jest [językiem z typowaniem statycznym](https://wikipedia.org/wiki/Type_system#Static_type_checking). +Nie można zadeklarować zmiennej ani parametru funkcji bez zidentyfikowania [typu danych](https://vyper.readthedocs.io/en/latest/types.html). W tym przypadku parametrem wejściowym jest `bytes32`, wartość 256-bitowa +(256 bitów to natywny rozmiar słowa [Wirtualnej Maszyny Ethereum](/developers/docs/evm/)). Wyjściem jest wartość logiczna +(boolean). Zgodnie z konwencją nazwy parametrów funkcji zaczynają się od podkreślenia (`_`). + +```python + """ + @dev Identyfikacja interfejsu jest określona w ERC-165. + @param _interfaceID Id interfejsu + """ + return self.supportedInterfaces[_interfaceID] +``` + +Zwraca wartość z HashMap `self.supportedInterfaces`, która jest ustawiana w konstruktorze (`__init__`). + +```python +### FUNKCJE WIDOKU ### + +``` + +Są to funkcje widoku, które udostępniają informacje o tokenach użytkownikom i innym kontraktom. + +```python +@view +@external +def balanceOf(_owner: address) -> uint256: + """ + @dev Zwraca liczbę NFT posiadanych przez `_owner`. + Zgłasza błąd, jeśli `_owner` jest adresem zerowym. NFT przypisane do adresu zerowego są uważane za nieprawidłowe. + @param _owner Adres, dla którego należy sprawdzić saldo. + """ + assert _owner != ZERO_ADDRESS +``` + +Ta linia [stwierdza](https://vyper.readthedocs.io/en/latest/statements.html#assert), że `_owner` nie jest +zerowy. Jeśli tak jest, występuje błąd, a operacja jest cofana. + +```python + return self.ownerToNFTokenCount[_owner] + +@view +@external +def ownerOf(_tokenId: uint256) -> address: + """ + @dev Zwraca adres właściciela NFT. + Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT. + @param _tokenId Identyfikator NFT. + """ + owner: address = self.idToOwner[_tokenId] + # Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT + assert owner != ZERO_ADDRESS + return owner +``` + +W Wirtualnej Maszynie Ethereum (EVM) każda pamięć, w której nie jest przechowywana żadna wartość, jest zerowa. +Jeśli nie ma tokena pod `_tokenId`, to wartość `self.idToOwner[_tokenId]` wynosi zero. W takim +przypadku funkcja jest cofana. + +```python +@view +@external +def getApproved(_tokenId: uint256) -> address: + """ + @dev Pobiera zatwierdzony adres dla pojedynczego NFT. + Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT. + @param _tokenId ID NFT, którego zatwierdzenie ma być sprawdzone. + """ + # Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT + assert self.idToOwner[_tokenId] != ZERO_ADDRESS + return self.idToApprovals[_tokenId] +``` + +Należy pamiętać, że `getApproved` _może_ zwrócić zero. Jeśli token jest prawidłowy, zwraca `self.idToApprovals[_tokenId]`. +Jeśli nie ma podmiotu zatwierdzającego, ta wartość wynosi zero. + +```python +@view +@external +def isApprovedForAll(_owner: address, _operator: address) -> bool: + """ + @dev Sprawdza, czy `_operator` jest zatwierdzonym operatorem dla `_owner`. + @param _owner Adres, który jest właścicielem NFT. + @param _operator Adres, który działa w imieniu właściciela. + """ + return (self.ownerToOperators[_owner])[_operator] +``` + +Ta funkcja sprawdza, czy `_operator` ma prawo zarządzać wszystkimi tokenami `_owner` w tym kontrakcie. +Ponieważ może istnieć wielu operatorów, jest to dwupoziomowa mapa HashMap. + +#### Funkcje pomocnicze transferu {#transfer-helpers} + +Funkcje te implementują operacje, które są częścią transferu lub zarządzania tokenami. + +```python + +### FUNKCJE POMOCNICZE TRANSFERU ### + +@view +@internal +``` + +Ten dekorator, `@internal`, oznacza, że funkcja jest dostępna tylko z innych funkcji w tym +samym kontrakcie. Zgodnie z konwencją te nazwy funkcji również zaczynają się od podkreślenia (`_`). + +```python +def _isApprovedOrOwner(_spender: address, _tokenId: uint256) -> bool: + """ + @dev Zwraca informację, czy dany podmiot wydający może przetransferować dany identyfikator tokena + @param spender adres podmiotu wydającego do zapytania + @param tokenId identyfikator uint256 tokena do przetransferowania + @return bool, czy msg.sender jest zatwierdzony dla danego identyfikatora tokena, + jest operatorem właściciela lub jest właścicielem tokena + """ + owner: address = self.idToOwner[_tokenId] + spenderIsOwner: bool = owner == _spender + spenderIsApproved: bool = _spender == self.idToApprovals[_tokenId] + spenderIsApprovedForAll: bool = (self.ownerToOperators[owner])[_spender] + return (spenderIsOwner or spenderIsApproved) or spenderIsApprovedForAll +``` + +Istnieją trzy sposoby, w jakie adres może być uprawniony do transferu tokena: + +1. Adres jest właścicielem tokena +2. Adres jest zatwierdzony do wydania tego tokena +3. Adres jest operatorem dla właściciela tokena + +Powyższa funkcja może być widokiem, ponieważ nie zmienia stanu. Aby obniżyć koszty operacyjne, każda +funkcja, która _może_ być widokiem, _powinna_ być widokiem. + +```python +@internal +def _addTokenTo(_to: address, _tokenId: uint256): + """ + @dev Dodaje NFT do danego adresu + Zgłasza błąd, jeśli `_tokenId` jest własnością kogoś. + """ + # Zgłasza błąd, jeśli `_tokenId` jest własnością kogoś + assert self.idToOwner[_tokenId] == ZERO_ADDRESS + # Zmień właściciela + self.idToOwner[_tokenId] = _to + # Zmień śledzenie licznika + self.ownerToNFTokenCount[_to] += 1 + + +@internal +def _removeTokenFrom(_from: address, _tokenId: uint256): + """ + @dev Usuwa NFT z danego adresu + Zgłasza błąd, jeśli `_from` nie jest obecnym właścicielem. + """ + # Zgłasza błąd, jeśli `_from` nie jest obecnym właścicielem + assert self.idToOwner[_tokenId] == _from + # Zmień właściciela + self.idToOwner[_tokenId] = ZERO_ADDRESS + # Zmień śledzenie licznika + self.ownerToNFTokenCount[_from] -= 1 +``` + +Gdy wystąpi problem z transferem, cofamy wywołanie. + +```python +@internal +def _clearApproval(_owner: address, _tokenId: uint256): + """ + @dev Czyści zatwierdzenie danego adresu + Zgłasza błąd, jeśli `_owner` nie jest obecnym właścicielem. + """ + # Zgłasza błąd, jeśli `_owner` nie jest obecnym właścicielem + assert self.idToOwner[_tokenId] == _owner + if self.idToApprovals[_tokenId] != ZERO_ADDRESS: + # Resetuj zatwierdzenia + self.idToApprovals[_tokenId] = ZERO_ADDRESS +``` + +Zmieniaj wartość tylko w razie potrzeby. Zmienne stanu znajdują się w pamięci (storage). Zapis do pamięci jest +jedną z najdroższych operacji, jakie wykonuje EVM (Wirtualna Maszyna Ethereum) (pod względem +[gazu](/developers/docs/gas/)). Dlatego dobrym pomysłem jest jego minimalizowanie, nawet zapisywanie +istniejącej wartości ma wysoki koszt. + +```python +@internal +def _transferFrom(_from: address, _to: address, _tokenId: uint256, _sender: address): + """ + @dev Wykonuje transfer NFT. + Zgłasza błąd, chyba że `msg.sender` jest obecnym właścicielem, autoryzowanym operatorem lub zatwierdzonym + adresem dla tego NFT. (UWAGA: `msg.sender` nie jest dozwolony w funkcji prywatnej, więc przekaż `_sender`.) + Zgłasza błąd, jeśli `_to` jest adresem zerowym. + Zgłasza błąd, jeśli `_from` nie jest obecnym właścicielem. + Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT. + """ +``` + +Mamy tę funkcję wewnętrzną, ponieważ istnieją dwa sposoby transferu tokenów (zwykły i bezpieczny), ale +chcemy, aby było tylko jedno miejsce w kodzie, w którym to robimy, aby ułatwić audyt. + +```python + # Sprawdź wymagania + assert self._isApprovedOrOwner(_sender, _tokenId) + # Zgłasza błąd, jeśli `_to` jest adresem zerowym + assert _to != ZERO_ADDRESS + # Wyczyść zatwierdzenie. Zgłasza błąd, jeśli `_from` nie jest obecnym właścicielem + self._clearApproval(_from, _tokenId) + # Usuń NFT. Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT + self._removeTokenFrom(_from, _tokenId) + # Dodaj NFT + self._addTokenTo(_to, _tokenId) + # Zarejestruj transfer + log Transfer(_from, _to, _tokenId) +``` + +Aby wyemitować zdarzenie w Vyper, użyj instrukcji `log` ([więcej szczegółów tutaj](https://vyper.readthedocs.io/en/latest/event-logging.html#event-logging)). + +#### Funkcje transferu {#transfer-funs} + +```python + +### FUNKCJE TRANSFERU ### + +@external +def transferFrom(_from: address, _to: address, _tokenId: uint256): + """ + @dev Zgłasza błąd, chyba że `msg.sender` jest obecnym właścicielem, autoryzowanym operatorem lub zatwierdzonym + adresem dla tego NFT. + Zgłasza błąd, jeśli `_from` nie jest obecnym właścicielem. + Zgłasza błąd, jeśli `_to` jest adresem zerowym. + Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT. + @notice Wywołujący jest odpowiedzialny za potwierdzenie, że `_to` jest w stanie odbierać NFT, w przeciwnym razie + mogą one zostać trwale utracone. + @param _from Obecny właściciel NFT. + @param _to Nowy właściciel. + @param _tokenId NFT do przetransferowania. + """ + self._transferFrom(_from, _to, _tokenId, msg.sender) +``` + +Ta funkcja pozwala na transfer na dowolny adres. O ile adres nie jest adresem użytkownika lub kontraktem, który +wie, jak transferować tokeny, każdy przetransferowany token utknie na tym adresie i będzie bezużyteczny. + +```python +@external +def safeTransferFrom( + _from: address, + _to: address, + _tokenId: uint256, + _data: Bytes[1024]=b"" + ): + """ + @dev Przenosi własność NFT z jednego adresu na inny. + Zgłasza błąd, chyba że `msg.sender` jest obecnym właścicielem, autoryzowanym operatorem lub + zatwierdzonym adresem dla tego NFT. + Zgłasza błąd, jeśli `_from` nie jest obecnym właścicielem. + Zgłasza błąd, jeśli `_to` jest adresem zerowym. + Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT. + Jeśli `_to` jest inteligentnym kontraktem, wywołuje `onERC721Received` na `_to` i zgłasza błąd, jeśli + zwracana wartość nie jest `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. + UWAGA: bytes4 jest reprezentowane przez bytes32 z dopełnieniem + @param _from Obecny właściciel NFT. + @param _to Nowy właściciel. + @param _tokenId NFT do przetransferowania. + @param _data Dodatkowe dane bez określonego formatu, wysyłane w wywołaniu do `_to`. + """ + self._transferFrom(_from, _to, _tokenId, msg.sender) +``` + +Można najpierw wykonać transfer, ponieważ w razie problemu i tak cofniemy całą operację, więc wszystko, +co zostało zrobione w wywołaniu, zostanie anulowane. + +```python + if _to.is_contract: # sprawdź, czy `_to` jest adresem kontraktu +``` + +Najpierw sprawdź, czy adres jest kontraktem (czy ma kod). Jeśli nie, załóż, że jest to adres +użytkownika, a użytkownik będzie mógł użyć tokena lub go przetransferować. Ale niech to nie uśpi twojej +czujności. Możesz stracić tokeny, nawet używając `safeTransferFrom`, jeśli przetransferujesz +je na adres, do którego nikt nie zna klucza prywatnego. + +```python + returnValue: bytes32 = ERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) +``` + +Wywołaj kontrakt docelowy, aby sprawdzić, czy może on odbierać tokeny ERC-721. + +```python + # Zgłasza błąd, jeśli miejscem docelowym transferu jest kontrakt, który nie implementuje „onERC721Received” + assert returnValue == method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32) +``` + +Jeśli miejscem docelowym jest kontrakt, ale taki, który nie akceptuje tokenów ERC-721 (lub który zdecydował się nie akceptować tego +konkretnego transferu), operacja zostanie cofnięta. + +```python +@external +def approve(_approved: address, _tokenId: uint256): + """ + @dev Ustawia lub ponownie zatwierdza zatwierdzony adres dla NFT. Adres zerowy wskazuje, że nie ma zatwierdzonego adresu. + Zgłasza błąd, chyba że `msg.sender` jest obecnym właścicielem NFT lub autoryzowanym operatorem obecnego właściciela. + Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT. (UWAGA: nie jest to zapisane w EIP) + Zgłasza błąd, jeśli `_approved` jest obecnym właścicielem. (UWAGA: nie jest to zapisane w EIP) + @param _approved Adres do zatwierdzenia dla danego ID NFT. + @param _tokenId ID tokena do zatwierdzenia. + """ + owner: address = self.idToOwner[_tokenId] + # Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT + assert owner != ZERO_ADDRESS + # Zgłasza błąd, jeśli `_approved` jest obecnym właścicielem + assert _approved != owner +``` + +Zgodnie z konwencją, jeśli nie chcesz mieć podmiotu zatwierdzającego, wyznaczasz adres zerowy, a nie siebie. + +```python + # Sprawdź wymagania + senderIsOwner: bool = self.idToOwner[_tokenId] == msg.sender + senderIsApprovedForAll: bool = (self.ownerToOperators[owner])[msg.sender] + assert (senderIsOwner or senderIsApprovedForAll) +``` + +Aby ustawić zatwierdzenie, możesz być właścicielem lub operatorem autoryzowanym przez właściciela. + +```python + # Ustaw zatwierdzenie + self.idToApprovals[_tokenId] = _approved + log Approval(owner, _approved, _tokenId) + + +@external +def setApprovalForAll(_operator: address, _approved: bool): + """ + @dev Włącza lub wyłącza zatwierdzenie dla strony trzeciej („operatora”) do zarządzania wszystkimi + aktywami `msg.sender`. Emituje również zdarzenie ApprovalForAll. + Zgłasza błąd, jeśli `_operator` to `msg.sender`. (UWAGA: nie jest to zapisane w EIP) + @notice Działa to nawet wtedy, gdy nadawca nie posiada w danym momencie żadnych tokenów. + @param _operator Adres do dodania do zestawu autoryzowanych operatorów. + @param _approved True, jeśli operator jest zatwierdzony, false, aby cofnąć zatwierdzenie. + """ + # Zgłasza błąd, jeśli `_operator` to `msg.sender` + assert _operator != msg.sender + self.ownerToOperators[msg.sender][_operator] = _approved + log ApprovalForAll(msg.sender, _operator, _approved) +``` + +#### Wybijanie nowych tokenów i niszczenie istniejących {#mint-burn} + +Konto, które utworzyło kontrakt, jest `minterem`, superużytkownikiem upoważnionym do wybijania +nowych NFT. Jednak nawet on nie może palić istniejących tokenów. Może to zrobić tylko właściciel lub podmiot +upoważniony przez właściciela. + +```python +### FUNKCJE WYBIJANIA I PALENIA ### + +@external +def mint(_to: address, _tokenId: uint256) -> bool: +``` + +Ta funkcja zawsze zwraca `True`, ponieważ jeśli operacja się nie powiedzie, jest cofana. + +```python + """ + @dev Funkcja do wybijania tokenów + Zgłasza błąd, jeśli `msg.sender` nie jest minterem. + Zgłasza błąd, jeśli `_to` jest adresem zerowym. + Zgłasza błąd, jeśli `_tokenId` jest własnością kogoś. + @param _to Adres, który otrzyma wybite tokeny. + @param _tokenId Identyfikator tokena do wybicia. + @return Wartość logiczna wskazująca, czy operacja zakończyła się powodzeniem. + """ + # Zgłasza błąd, jeśli `msg.sender` nie jest minterem + assert msg.sender == self.minter +``` + +Tylko minter (konto, które utworzyło kontrakt ERC-721) może wybijać nowe tokeny. Może to być +problemem w przyszłości, jeśli będziemy chcieli zmienić tożsamość mintera. W +kontrakcie produkcyjnym prawdopodobnie chciałbyś mieć funkcję, która pozwala minterowi na przekazanie +uprawnień mintera komuś innemu. + +```python + # Zgłasza błąd, jeśli `_to` jest adresem zerowym + assert _to != ZERO_ADDRESS + # Dodaj NFT. Zgłasza błąd, jeśli `_tokenId` jest własnością kogoś + self._addTokenTo(_to, _tokenId) + log Transfer(ZERO_ADDRESS, _to, _tokenId) + return True +``` + +Zgodnie z konwencją wybijanie nowych tokenów liczy się jako transfer z adresu zerowego. + +```python + +@external +def burn(_tokenId: uint256): + """ + @dev Pali określony token ERC721. + Zgłasza błąd, chyba że `msg.sender` jest obecnym właścicielem, autoryzowanym operatorem lub zatwierdzonym + adresem dla tego NFT. + Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT. + @param _tokenId uint256 id tokena ERC721 do spalenia. + """ + # Sprawdź wymagania + assert self._isApprovedOrOwner(msg.sender, _tokenId) + owner: address = self.idToOwner[_tokenId] + # Zgłasza błąd, jeśli `_tokenId` nie jest prawidłowym NFT + assert owner != ZERO_ADDRESS + self._clearApproval(owner, _tokenId) + self._removeTokenFrom(owner, _tokenId) + log Transfer(owner, ZERO_ADDRESS, _tokenId) +``` + +Każdy, kto ma prawo do transferu tokena, może go spalić. Chociaż spalenie wydaje się równoznaczne z +transferem na adres zerowy, adres zerowy w rzeczywistości nie otrzymuje tokena. Pozwala to na +zwolnienie całej pamięci, która była używana dla tokena, co może obniżyć koszt gazu transakcji. + +## Korzystanie z tego kontraktu {#using-contract} + +W przeciwieństwie do Solidity, Vyper nie ma dziedziczenia. Jest to celowy wybór projektowy, aby +kod był jaśniejszy, a tym samym łatwiejszy do zabezpieczenia. Tak więc, aby stworzyć własny kontrakt ERC-721 w Vyper, bierzesz ten +kontrakt i modyfikujesz go +w celu zaimplementowania pożądanej logiki biznesowej. + +## Wnioski {#conclusion} + +Dla przypomnienia, oto niektóre z najważniejszych pomysłów w tym kontrakcie: + +- Aby odbierać tokeny ERC-721 za pomocą bezpiecznego transferu, kontrakty muszą implementować interfejs `ERC721Receiver`. +- Nawet jeśli użyjesz bezpiecznego transferu, tokeny mogą utknąć, jeśli wyślesz je na adres, którego klucz prywatny + jest nieznany. +- Gdy wystąpi problem z operacją, dobrym pomysłem jest `cofnięcie` (revert) wywołania, a nie tylko zwrócenie + wartości błędu. +- Tokeny ERC-721 istnieją, gdy mają właściciela. +- Istnieją trzy sposoby autoryzacji do transferu NFT. Możesz być właścicielem, być zatwierdzonym dla konkretnego tokena + lub być operatorem dla wszystkich tokenów właściciela. +- Przeszłe zdarzenia są widoczne tylko poza blockchainem. Kod działający wewnątrz blockchaina nie może ich zobaczyć. + +Teraz idź i zaimplementuj bezpieczne kontrakty Vyper. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). + diff --git a/public/content/translations/pl/developers/tutorials/erc20-annotated-code/index.md b/public/content/translations/pl/developers/tutorials/erc20-annotated-code/index.md new file mode 100644 index 00000000000..c61ec7ae2a0 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/erc20-annotated-code/index.md @@ -0,0 +1,873 @@ +--- +title: "Przewodnik po kontrakcie ERC-20" +description: "Co znajduje się w kontrakcie ERC-20 OpenZeppelin i dlaczego tam jest?" +author: Ori Pomerantz +lang: pl +tags: [ "solidity", "erc-20" ] +skill: beginner +published: 2021-03-09 +--- + +## Wprowadzenie {#introduction} + +Jednym z najczęstszych zastosowań Ethereum jest tworzenie przez grupę wymienialnych tokenów, w pewnym sensie własnej waluty. Tokeny te zazwyczaj są zgodne ze standardem, +[ERC-20](/developers/docs/standards/tokens/erc-20/). Standard ten umożliwia pisanie narzędzi, takich jak pule płynności i portfele, które działają ze wszystkimi tokenami ERC-20 +. W tym artykule przeanalizujemy [implementację ERC20 w Solidity od OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol), a także [definicję interfejsu](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol). + +To jest kod źródłowy z adnotacjami. Jeśli chcesz zaimplementować ERC-20, +[przeczytaj ten samouczek](https://docs.openzeppelin.com/contracts/2.x/erc20-supply). + +## Interfejs {#the-interface} + +Celem standardu takiego jak ERC-20 jest umożliwienie wielu implementacji tokenów, które są interoperacyjne w różnych aplikacjach, takich jak portfele i zdecentralizowane giełdy. Aby to osiągnąć, tworzymy +[interfejs](https://www.geeksforgeeks.org/solidity/solidity-basics-of-interface/). Każdy kod, który musi używać kontraktu tokenu, +może używać tych samych definicji w interfejsie i być kompatybilnym ze wszystkimi kontraktami tokenów, które go używają, niezależnie od tego, czy jest to portfel, taki jak +MetaMask, dapka, taka jak etherscan.io, czy inny kontrakt, taki jak pula płynności. + +![Ilustracja interfejsu ERC-20](erc20_interface.png) + +Jeśli jesteś doświadczonym programistą, prawdopodobnie pamiętasz podobne konstrukcje w [Javie](https://www.w3schools.com/java/java_interface.asp) +lub nawet w [plikach nagłówkowych C](https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html). + +Jest to definicja [interfejsu ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol) +pochodząca z OpenZeppelin. Jest to tłumaczenie [standardu czytelnego dla człowieka](https://eips.ethereum.org/EIPS/eip-20) na kod Solidity. Oczywiście, sam +interfejs nie definiuje _jak_ cokolwiek zrobić. Jest to wyjaśnione w poniższym kodzie źródłowym kontraktu. + +  + +```solidity +// SPDX-License-Identifier: MIT +``` + +Pliki Solidity powinny zawierać identyfikator licencji. [Listę licencji można zobaczyć tutaj](https://spdx.org/licenses/). Jeśli potrzebujesz innej +licencji, po prostu wyjaśnij to w komentarzach. + +  + +```solidity +pragma solidity >=0.6.0 <0.8.0; +``` + +Język Solidity wciąż szybko się rozwija, a nowe wersje mogą nie być kompatybilne ze starym kodem +([zobacz tutaj](https://docs.soliditylang.org/en/v0.7.0/070-breaking-changes.html)). Dlatego dobrym pomysłem jest określenie nie tylko minimalnej +wersji języka, ale także maksymalnej wersji, najnowszej, z którą testowano kod. + +  + +```solidity +/** + * @dev Interfejs standardu ERC20 zdefiniowany w EIP. + */ +``` + +Zapis `@dev` w komentarzu jest częścią [formatu NatSpec](https://docs.soliditylang.org/en/develop/natspec-format.html), używanego do tworzenia +dokumentacji z kodu źródłowego. + +  + +```solidity +interface IERC20 { +``` + +Zgodnie z konwencją, nazwy interfejsów zaczynają się od `I`. + +  + +```solidity + /** + * @dev Zwraca liczbę istniejących tokenów. + */ + function totalSupply() external view returns (uint256); +``` + +Ta funkcja jest `zewnętrzna`, co oznacza, że [może być wywoływana tylko spoza kontraktu](https://docs.soliditylang.org/en/v0.7.0/cheatsheet.html#index-2). +Zwraca całkowitą podaż tokenów w kontrakcie. Wartość ta jest zwracana przy użyciu najpopularniejszego typu w Ethereum, 256-bitowej liczby całkowitej bez znaku (256 bitów to +natywny rozmiar słowa EVM). Ta funkcja jest również typu `view`, co oznacza, że nie zmienia ona stanu, więc może być wykonana na pojedynczym węźle zamiast być uruchamiana przez +każdy węzeł w blockchainie. Ten rodzaj funkcji nie generuje transakcji i nie kosztuje [gazu](/developers/docs/gas/). + +**Uwaga:** Teoretycznie mogłoby się wydawać, że twórca kontraktu mógłby oszukiwać, zwracając mniejszą całkowitą podaż niż rzeczywista wartość, sprawiając, że każdy token wydaje się +bardziej wartościowy, niż jest w rzeczywistości. Jednakże obawa ta ignoruje prawdziwą naturę blockchaina. Wszystko, co dzieje się w blockchainie, może zostać zweryfikowane przez +każdy węzeł. Aby to osiągnąć, kod maszynowy i pamięć masowa każdego kontraktu są dostępne na każdym węźle. Chociaż nie jest wymagane publikowanie kodu Solidity +kontraktu, nikt nie potraktuje Cię poważnie, chyba że opublikujesz kod źródłowy i wersję Solidity, za pomocą której został on skompilowany, aby można go było +zweryfikować z dostarczonym kodem maszynowym. +Na przykład zobacz [ten kontrakt](https://eth.blockscout.com/address/0xa530F85085C6FE2f866E7FdB716849714a89f4CD?tab=contract). + +  + +```solidity + /** + * @dev Zwraca liczbę tokenów posiadanych przez `konto`. + */ + function balanceOf(address account) external view returns (uint256); +``` + +Jak sama nazwa wskazuje, `balanceOf` zwraca saldo konta. Konta Ethereum są identyfikowane w Solidity za pomocą typu `adres`, który przechowuje 160 bitów. +Jest również `zewnętrzna` i typu `view`. + +  + +```solidity + /** + * @dev Przenosi tokeny w `ilości` z konta wywołującego do `odbiorcy`. + * + * Zwraca wartość logiczną wskazującą, czy operacja się powiodła. + * + * Emituje zdarzenie {Transfer}. + */ + function transfer(address recipient, uint256 amount) external returns (bool); +``` + +Funkcja `transfer` przenosi tokeny od wywołującego na inny adres. Wiąże się to ze zmianą stanu, więc nie jest to `widok`. +Gdy użytkownik wywołuje tę funkcję, tworzy transakcję i ponosi koszty gazu. Emituje również zdarzenie `Transfer`, aby poinformować wszystkich w +blockchainie o tym zdarzeniu. + +Funkcja ma dwa rodzaje danych wyjściowych dla dwóch różnych typów wywołujących: + +- Użytkownicy wywołujący funkcję bezpośrednio z interfejsu użytkownika. Zazwyczaj użytkownik przesyła transakcję + i nie czeka na odpowiedź, co może zająć nieokreśloną ilość czasu. Użytkownik może zobaczyć, co się stało + , szukając potwierdzenia transakcji (które jest identyfikowane przez hasz transakcji) lub szukając + zdarzenia `Transfer`. +- Inne kontrakty, które wywołują funkcję w ramach ogólnej transakcji. Kontrakty te otrzymują wynik natychmiast, + ponieważ działają w tej samej transakcji, więc mogą użyć wartości zwrotnej funkcji. + +Ten sam typ danych wyjściowych jest tworzony przez inne funkcje, które zmieniają stan kontraktu. + +  + +Zezwolenia pozwalają kontu na wydanie pewnej liczby tokenów należących do innego właściciela. +Jest to przydatne na przykład w przypadku kontraktów, które działają jako sprzedawcy. Kontrakty nie mogą +monitorować zdarzeń, więc jeśli kupujący miałby przenieść tokeny do kontraktu sprzedawcy +bezpośrednio, kontrakt ten nie wiedziałby, że został opłacony. Zamiast tego, kupujący zezwala +kontraktowi sprzedawcy na wydanie określonej kwoty, a sprzedawca transferuje tę kwotę. +Odbywa się to za pomocą funkcji wywoływanej przez kontrakt sprzedawcy, więc kontrakt sprzedawcy +może wiedzieć, czy się to udało. + +```solidity + /** + * @dev Zwraca pozostałą liczbę tokenów, które `spender` będzie mógł + * wydać w imieniu `owner` poprzez {transferFrom}. Domyślnie + * jest to zero. + * + * Wartość ta zmienia się po wywołaniu {approve} lub {transferFrom}. + */ + function allowance(address owner, address spender) external view returns (uint256); +``` + +Funkcja `allowance` pozwala każdemu sprawdzić, jakie jest zezwolenie, które jeden +adres (`właściciel`) daje innemu adresowi (`wydającemu`) do wydania. + +  + +```solidity + /** + * @dev Ustawia `amount` jako zezwolenie `spendera` na tokeny wywołującego. + * + * Zwraca wartość logiczną wskazującą, czy operacja się powiodła. + * + * WAŻNE: Należy pamiętać, że zmiana zezwolenia za pomocą tej metody niesie ze sobą ryzyko, + * że ktoś może użyć zarówno starego, jak i nowego zezwolenia przez niefortunne + * uporządkowanie transakcji. Jednym z możliwych rozwiązań w celu złagodzenia tego stanu wyścigu + * jest najpierw zmniejszenie zezwolenia wydającego do 0 i ustawienie + * pożądanej wartości później: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emituje zdarzenie {Approval}. + */ + function approve(address spender, uint256 amount) external returns (bool); +``` + +Funkcja `approve` tworzy zezwolenie. Pamiętaj, aby przeczytać wiadomość o tym, +jak można ją nadużywać. W Ethereum kontrolujesz kolejność własnych transakcji, +ale nie możesz kontrolować kolejności, w jakiej transakcje innych osób będą +wykonywane, chyba że nie prześlesz własnej transakcji, dopóki nie zobaczysz, że +transakcja drugiej strony miała miejsce. + +  + +```solidity + /** + * @dev Przenosi tokeny o `wartości` `amount` z `nadawcy` do `odbiorcy` przy użyciu + * mechanizmu zezwoleń. `Kwota` jest następnie odejmowana od zezwolenia wywołującego + * . + * + * Zwraca wartość logiczną wskazującą, czy operacja się powiodła. + * + * Emituje zdarzenie {Transfer}. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); +``` + +Na koniec `transferFrom` jest używane przez wydającego do faktycznego wydania zezwolenia. + +  + +```solidity + + /** + * @dev Emitowane, gdy tokeny `value` są przenoszone z jednego konta (`from`) na drugie (`to`). + * + * Zauważ, że `value` może być zerowe. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitowane, gdy pozwolenie `spendera` dla `owner` jest ustawiane przez wywołanie {approve}. `value` to nowe pozwolenie. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} +``` + +Te zdarzenia są emitowane, gdy zmienia się stan kontraktu ERC-20. + +## Właściwy kontrakt {#the-actual-contract} + +To jest właściwy kontrakt, który implementuje standard ERC-20, [pobrany stąd](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol). +Nie jest on przeznaczony do użycia w obecnej formie, ale można go [odziedziczyć](https://www.tutorialspoint.com/solidity/solidity_inheritance.htm), aby rozszerzyć go do czegoś użytecznego. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0 <0.8.0; +``` + +  + +### Instrukcje importu {#import-statements} + +Oprócz powyższych definicji interfejsu, definicja kontraktu importuje dwa inne pliki: + +```solidity + +import "../../GSN/Context.sol"; +import "./IERC20.sol"; +import "../../math/SafeMath.sol"; +``` + +- `GSN/Context.sol` to definicje wymagane do użycia [OpenGSN](https://www.opengsn.org/), systemu, który pozwala użytkownikom bez etheru korzystać z blockchainu. Zauważ, że jest to stara wersja. Jeśli chcesz zintegrować z OpenGSN, [użyj tego samouczka](https://docs.opengsn.org/javascript-client/tutorial.html). +- [Biblioteka SafeMath](https://ethereumdev.io/using-safe-math-library-to-prevent-from-overflows/), która zapobiega przepełnieniom/niedopełnieniom arytmetycznym dla wersji Solidity **<0.8.0**. W Solidity ≥0.8.0, operacje arytmetyczne automatycznie cofają się w przypadku przepełnienia/niedopełnienia, co sprawia, że biblioteka SafeMath jest niepotrzebna. Ten kontrakt używa SafeMath dla wstecznej kompatybilności ze starszymi wersjami kompilatora. + +  + +Ten komentarz wyjaśnia cel kontraktu. + +```solidity +/** + * @dev Implementacja interfejsu {IERC20}. + * + * Ta implementacja jest niezależna od sposobu tworzenia tokenów. Oznacza to, że mechanizm zasilania musi zostać dodany w kontrakcie pochodnym za pomocą {_mint}. + * Ogólny mechanizm można znaleźć w {ERC20PresetMinterPauser}. + * + * WSKAZÓWKA: Szczegółowy opis można znaleźć w naszym przewodniku + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[Jak + * wdrażać mechanizmy dostaw]. + * + * Postępowaliśmy zgodnie z ogólnymi wytycznymi OpenZeppelin: funkcje w przypadku niepowodzenia cofają się, zamiast zwracać „fałsz”. + * To zachowanie jest jednak konwencjonalne i nie jest sprzeczne z oczekiwaniami aplikacji ERC20. + * + * Dodatkowo zdarzenie {Approval} jest emitowane przy wywołaniach {transferFrom}. + * Umożliwia to aplikacjom odtworzenie uprawnień dla wszystkich kont poprzez nasłuchiwanie tych zdarzeń. + * Inne implementacje EIP mogą nie emitować tych zdarzeń, ponieważ nie jest to wymagane przez specyfikację. + * + * Wreszcie, niestandardowe funkcje {decreaseAllowance} i {increaseAllowance} zostały dodane w celu złagodzenia dobrze znanych problemów związanych z ustawianiem przydziałów. + * Zobacz {IERC20-approve}. + */ + +``` + +### Definicja kontraktu {#contract-definition} + +```solidity +contract ERC20 is Context, IERC20 { +``` + +Ta linia określa dziedziczenie, w tym przypadku z `IERC20` z góry i `Context`, dla OpenGSN. + +  + +```solidity + + using SafeMath for uint256; + +``` + +Ta linia dołącza bibliotekę `SafeMath` do typu `uint256`. Tę bibliotekę można znaleźć [tutaj](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol). + +### Definicje zmiennych {#variable-definitions} + +Te definicje określają zmienne stanu kontraktu. Te zmienne są zadeklarowane jako `private`, ale oznacza to tylko, że inne kontrakty na blockchainie nie mogą ich odczytać. _Na blockchainie nie ma sekretów_, oprogramowanie na każdym węźle ma stan każdego kontraktu w każdym bloku. Zgodnie z konwencją zmienne stanu nazywane są `_`. + +Pierwsze dwie zmienne to [mapowania](https://www.tutorialspoint.com/solidity/solidity_mappings.htm), co oznacza, że zachowują się mniej więcej tak samo jak [tablice asocjacyjne](https://wikipedia.org/wiki/Associative_array), z tym wyjątkiem, że klucze są wartościami numerycznymi. Pamięć jest przydzielana tylko dla wpisów, które mają wartości różne od domyślnej (zero). + +```solidity + mapping (address => uint256) private _balances; +``` + +Pierwsze mapowanie, `_balances`, to adresy i ich odpowiednie salda tego tokena. Aby uzyskać dostęp do salda, użyj tej składni: `_balances[]`. + +  + +```solidity + mapping (address => mapping (address => uint256)) private _allowances; +``` + +Ta zmienna, `_allowances`, przechowuje wyjaśnione wcześniej uprawnienia. Pierwszy indeks to właściciel tokenów, a drugi to kontrakt z przydziałem. Aby uzyskać dostęp do kwoty, jaką adres A może wydać z konta adresu B, użyj `_allowances[B][A]`. + +  + +```solidity + uint256 private _totalSupply; +``` + +Jak sama nazwa wskazuje, ta zmienna śledzi całkowitą podaż tokenów. + +  + +```solidity + string private _name; + string private _symbol; + uint8 private _decimals; +``` + +Te trzy zmienne są używane w celu poprawy czytelności. Dwie pierwsze są oczywiste, ale `_decimals` nie jest. + +Z jednej strony Ethereum nie ma zmiennych zmiennoprzecinkowych ani ułamkowych. Z drugiej strony ludzie lubią mieć możliwość dzielenia tokenów. Jednym z powodów, dla których ludzie zdecydowali się na złoto jako walutę, było to, że trudno było wydać resztę, gdy ktoś chciał kupić kaczkę za część krowy. + +Rozwiązaniem jest śledzenie liczb całkowitych, ale zamiast prawdziwego tokena liczenie tokena ułamkowego, który jest prawie bezwartościowy. W przypadku etheru token ułamkowy nazywa się wei, a 10^18 wei jest równe jednemu ETH. W momencie pisania tego tekstu 10 000 000 000 000 wei to w przybliżeniu jeden cent amerykański lub eurocent. + +Aplikacje muszą wiedzieć, jak wyświetlić saldo tokena. Jeśli użytkownik ma 3 141 000 000 000 000 000 wei, czy to 3,14 ETH? 31,41 ETH? 3141 ETH? W przypadku etheru zdefiniowano 10^18 wei na ETH, ale dla Twojego tokena możesz wybrać inną wartość. Jeśli dzielenie tokena nie ma sensu, możesz użyć wartości `_decimals` równej zero. Jeśli chcesz używać tego samego standardu co ETH, użyj wartości **18**. + +### Konstruktor {#the-constructor} + +```solidity + /** + * @dev Ustawia wartości dla {name} i {symbol}, inicjalizuje {decimals} z domyślną wartością 18. + * + * Aby wybrać inną wartość dla {decimals}, użyj {_setupDecimals}. + * + * Wszystkie trzy z tych wartości są niezmienne: można je ustawić tylko raz podczas tworzenia. + */ + constructor (string memory name_, string memory symbol_) public { + // W Solidity ≥0.7.0, „public” jest domyślne i można je pominąć. + + _name = name_; + _symbol = symbol_; + _decimals = 18; + } +``` + +Konstruktor jest wywoływany przy pierwszym utworzeniu kontraktu. Zgodnie z konwencją parametry funkcji są nazywane `_`. + +### Funkcje interfejsu użytkownika {#user-interface-functions} + +```solidity + /** + * @dev Zwraca nazwę tokena. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Zwraca symbol tokena, zazwyczaj krótszą wersję nazwy. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Zwraca liczbę miejsc po przecinku używaną do uzyskania jego reprezentacji przez użytkownika. + * Na przykład, jeśli `decimals` wynosi `2`, saldo `505` tokenów powinno być wyświetlone użytkownikowi jako `5,05` (`505 / 10 ** 2`). + * + * Tokeny zazwyczaj wybierają wartość 18, naśladując związek między etherem a wei. + * Jest to wartość, której używa {ERC20}, chyba że zostanie wywołana funkcja {_setupDecimals}. + * + * UWAGA: Informacje te są wykorzystywane wyłącznie do celów _wyświetlania_: w żaden sposób nie wpływają na arytmetykę kontraktu, + * w tym {IERC20-balanceOf} i {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } +``` + +Te funkcje, `name`, `symbol` i `decimals`, pomagają interfejsom użytkownika dowiedzieć się o Twoim kontrakcie, aby mogły go poprawnie wyświetlać. + +Typem zwracanym jest `string memory`, co oznacza zwrócenie ciągu znaków przechowywanego w pamięci. Zmienne, takie jak ciągi znaków, mogą być przechowywane w trzech lokalizacjach: + +| | Czas życia | Dostęp do kontraktu | Koszt gazu | +| -------------- | ----------------- | ------------------- | --------------------------------------------------------------------------- | +| Pamięć | Wywołanie funkcji | Odczyt/Zapis | Dziesiątki lub setki (więcej dla wyższych lokalizacji) | +| Calldata | Wywołanie funkcji | Tylko do odczytu | Nie może być używany jako typ zwracany, tylko jako typ parametru funkcji | +| Przechowywanie | Do zmiany | Odczyt/Zapis | Wysoki (800 za odczyt, 20 tys. za zapis) | + +W tym przypadku najlepszym wyborem jest `memory`. + +### Odczyt informacji o tokenie {#read-token-information} + +Są to funkcje, które dostarczają informacji o tokenie, albo o całkowitej podaży, albo o saldzie konta. + +```solidity + /** + * @dev Zobacz {IERC20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } +``` + +Funkcja `totalSupply` zwraca całkowitą podaż tokenów. + +  + +```solidity + /** + * @dev Zobacz {IERC20-balanceOf}. + */ + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } +``` + +Odczytaj saldo konta. Zauważ, że każdy może sprawdzić saldo konta każdego innego. Nie ma sensu próbować ukrywać tych informacji, ponieważ i tak są one dostępne w każdym węźle. _Na blockchainie nie ma żadnych tajemnic._ + +### Przelewanie tokenów {#transfer-tokens} + +```solidity + /** + * @dev Zobacz {IERC20-transfer}. + * + * Wymagania: + * + * - `recipient` nie może być adresem zerowym. + * - wywołujący musi mieć saldo co najmniej `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { +``` + +Funkcja `transfer` jest wywoływana w celu przesłania tokenów z konta nadawcy na inne. Zauważ, że chociaż zwraca ona wartość logiczną, to zawsze jest to **prawda**. Jeśli przelew się nie powiedzie, kontrakt cofa wywołanie. + +  + +```solidity + _transfer(_msgSender(), recipient, amount); + return true; + } +``` + +Funkcja `_transfer` wykonuje właściwą pracę. Jest to funkcja prywatna, która może być wywoływana tylko przez inne funkcje kontraktu. Zgodnie z konwencją funkcje prywatne są nazywane `_`, tak samo jak zmienne stanu. + +Zazwyczaj w Solidity używamy `msg.sender` dla nadawcy wiadomości. Jednakże psuje to [OpenGSN](http://opengsn.org/). Jeśli chcemy zezwolić na transakcje bez etheru za pomocą naszego tokena, musimy użyć `_msgSender()`. Zwraca `msg.sender` dla normalnych transakcji, ale dla transakcji bez etheru zwraca oryginalnego sygnatariusza, a nie kontrakt, który przekazał wiadomość. + +### Funkcje przydziału {#allowance-functions} + +Są to funkcje, które implementują funkcjonalność przydziału: `allowance`, `approve`, `transferFrom` i `_approve`. Dodatkowo implementacja OpenZeppelin wykracza poza podstawowy standard, zawierając pewne funkcje poprawiające bezpieczeństwo: `increaseAllowance` i `decreaseAllowance`. + +#### Funkcja przydziału {#allowance} + +```solidity + /** + * @dev Zobacz {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } +``` + +Funkcja `allowance` pozwala każdemu sprawdzić dowolny przydział. + +#### Funkcja approve {#approve} + +```solidity + /** + * @dev Zobacz {IERC20-approve}. + * + * Wymagania: + * + * - `spender` nie może być adresem zerowym. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { +``` + +Ta funkcja jest wywoływana w celu utworzenia przydziału. Jest podobna do powyższej funkcji `transfer`: + +- Funkcja po prostu wywołuje funkcję wewnętrzną (w tym przypadku `_approve`), która wykonuje prawdziwą pracę. +- Funkcja albo zwraca `true` (jeśli się powiedzie), albo cofa (jeśli nie). + +  + +```solidity + _approve(_msgSender(), spender, amount); + return true; + } +``` + +Używamy funkcji wewnętrznych, aby zminimalizować liczbę miejsc, w których zachodzą zmiany stanu. _Każda_ funkcja, która zmienia stan, jest potencjalnym zagrożeniem bezpieczeństwa, które musi być poddane audytowi bezpieczeństwa. W ten sposób mamy mniejsze szanse na popełnienie błędu. + +#### Funkcja transferFrom {#transferFrom} + +Jest to funkcja, którą wydający wywołuje, aby wydać przydział. Wymaga to dwóch operacji: przelania wydawanej kwoty i zmniejszenia o nią przydziału. + +```solidity + /** + * @dev Zobacz {IERC20-transferFrom}. + * + * Emituje zdarzenie {Approval} wskazujące zaktualizowany przydział. Nie jest to wymagane przez EIP. + * Zobacz notatkę na początku {ERC20}. + * + * Wymagania: + * + * - `sender` i `recipient` nie mogą być adresem zerowym. + * - `sender` musi mieć saldo co najmniej `amount`. + * - wywołujący musi mieć przydział na tokeny ``sender`` w wysokości co najmniej + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual + override returns (bool) { + _transfer(sender, recipient, amount); +``` + +  + +Wywołanie funkcji `a.sub(b, "message")` robi dwie rzeczy. Po pierwsze, oblicza `a-b`, co jest nowym przydziałem. +Po drugie, sprawdza, czy ten wynik nie jest ujemny. Jeśli jest ujemny, wywołanie cofa się z podaną wiadomością. Zauważ, że gdy wywołanie jest cofane, wszelkie przetwarzanie wykonane wcześniej podczas tego wywołania jest ignorowane, więc nie musimy cofać `_transfer`. + +```solidity + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, + "ERC20: kwota transferu przekracza przydział")); + return true; + } +``` + +#### Dodatki bezpieczeństwa OpenZeppelin {#openzeppelin-safety-additions} + +Niebezpieczne jest ustawianie niezerowego przydziału na inną niezerową wartość, ponieważ kontrolujesz tylko kolejność własnych transakcji, a nie cudzych. Wyobraź sobie, że masz dwóch użytkowników, naiwną Alicję i nieuczciwego Billa. Alicja chce skorzystać z usługi Billa, która jej zdaniem kosztuje pięć tokenów – więc daje Billowi przydział w wysokości pięciu tokenów. + +Następnie coś się zmienia i cena Billa wzrasta do dziesięciu tokenów. Alicja, która nadal chce skorzystać z usługi, wysyła transakcję, która ustala przydział dla Billa na dziesięć. W momencie, gdy Bill zobaczy tę nową transakcję w puli transakcji, wysyła transakcję, która wydaje pięć tokenów Alicji i ma znacznie wyższą cenę gazu, dzięki czemu zostanie szybciej wykopana. W ten sposób Bill może najpierw wydać pięć tokenów, a następnie, +gdy nowe zezwolenie Alicji zostanie wydobyte, wydać kolejne dziesięć, co daje łączną cenę piętnastu tokenów, czyli więcej niż +chciała autoryzować Alicja. Ta technika nazywa się +[front-runningiem](https://consensysdiligence.github.io/smart-contract-best-practices/attacks/#front-running) + +| Transakcja Alicji | Nonce Alicji | Transakcja Billa | Nonce Billa | Zezwolenie Billa | Całkowity dochód Billa od Alicji | +| ------------------------------------ | ------------ | ------------------------------------------------ | ----------- | ---------------- | -------------------------------- | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| approve(Bill, 10) | 11 | | | 10 | 5 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 15 | + +Aby uniknąć tego problemu, te dwie funkcje (`increaseAllowance` i `decreaseAllowance`) pozwalają +na modyfikację zezwolenia o określoną kwotę. Więc jeśli Bill wydał już pięć tokenów, będzie mógł +wydać tylko pięć więcej. W zależności od czasu, istnieją dwa sposoby, na które może to zadziałać, oba +z których kończą się tym, że Bill dostaje tylko dziesięć tokenów: + +A: + +| Transakcja Alicji | Nonce Alicji | Transakcja Billa | Nonce Billa | Zezwolenie Billa | Całkowity dochód Billa od Alicji | +| --------------------------------------------- | -----------: | ----------------------------------------------- | ----------: | ---------------: | -------------------------------- | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| | | transferFrom(Alice, Bill, 5) | 10,123 | 0 | 5 | +| increaseAllowance(Bill, 5) | 11 | | | 0+5 = 5 | 5 | +| | | transferFrom(Alice, Bill, 5) | 10,124 | 0 | 10 | + +B: + +| Transakcja Alicji | Nonce Alicji | Transakcja Billa | Nonce Billa | Zezwolenie Billa | Całkowity dochód Billa od Alicji | +| --------------------------------------------- | -----------: | ------------------------------------------------ | ----------: | ---------------: | -------------------------------: | +| approve(Bill, 5) | 10 | | | 5 | 0 | +| increaseAllowance(Bill, 5) | 11 | | | 5+5 = 10 | 0 | +| | | transferFrom(Alice, Bill, 10) | 10,124 | 0 | 10 | + +```solidity + /** + * @dev Atomowo zwiększa zezwolenie udzielone `spenderowi` przez wywołującego. + * + * Jest to alternatywa dla {approve}, która może być używana jako środek zaradczy na + * problemy opisane w {IERC20-approve}. + * + * Emituje zdarzenie {Approval} wskazujące zaktualizowane zezwolenie. + * + * Wymagania: + * + * - `spender` nie może być adresem zerowym. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } +``` + +Funkcja `a.add(b)` to bezpieczne dodawanie. W mało prawdopodobnym przypadku, gdy `a`+`b`>=`2^256`, nie zawija się ono +w sposób, w jaki robi to normalne dodawanie. + +```solidity + + /** + * @dev Atomowo zmniejsza zezwolenie udzielone `spenderowi` przez wywołującego. + * + * Jest to alternatywa dla {approve}, która może być używana jako środek zaradczy na + * problemy opisane w {IERC20-approve}. + * + * Emituje zdarzenie {Approval} wskazujące zaktualizowane zezwolenie. + * + * Wymagania: + * + * - `spender` nie może być adresem zerowym. + * - `spender` musi mieć zezwolenie dla wywołującego w wysokości co najmniej + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, + "ERC20: decreased allowance below zero")); + return true; + } +``` + +### Funkcje modyfikujące informacje o tokenie {#functions-that-modify-token-information} + +Są to cztery funkcje, które wykonują rzeczywistą pracę: `_transfer`, `_mint`, `_burn` i `_approve`. + +#### Funkcja _transfer {#_transfer} + +```solidity + /** + * @dev Przenosi tokeny o wartości `amount` z `nadawcy` do `odbiorcy`. + * + * Ta wewnętrzna funkcja jest odpowiednikiem {transfer} i może być używana do + * np. implementacji automatycznych opłat za tokeny, mechanizmów cięcia itp. + * + * Emituje zdarzenie {Transfer}. + * + * Wymagania: + * + * - `nadawca` nie może być adresem zerowym. + * - `odbiorca` nie może być adresem zerowym. + * - `nadawca` musi mieć saldo co najmniej `amount`. + */ + function _transfer(address sender, address recipient, uint256 amount) internal virtual { +``` + +Ta funkcja, `_transfer`, transferuje tokeny z jednego konta na drugie. Jest ona wywoływana zarówno przez +`transfer` (dla transferów z własnego konta nadawcy), jak i `transferFrom` (dla używania zezwoleń +do transferu z konta kogoś innego). + +  + +```solidity + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); +``` + +Nikt w rzeczywistości nie posiada adresu zerowego w Ethereum (tzn. nikt nie zna klucza prywatnego, którego pasujący klucz publiczny +jest przekształcany na adres zerowy). Gdy ludzie używają tego adresu, jest to zwykle błąd oprogramowania - więc +kończymy niepowodzeniem, jeśli adres zerowy jest używany jako nadawca lub odbiorca. + +  + +```solidity + _beforeTokenTransfer(sender, recipient, amount); + +``` + +Istnieją dwa sposoby wykorzystania tego kontraktu: + +1. Użyj go jako szablonu dla własnego kodu +2. [Dziedzicz po nim](https://www.bitdegree.org/learn/solidity-inheritance) i nadpisuj tylko te funkcje, które musisz zmodyfikować + +Druga metoda jest znacznie lepsza, ponieważ kod OpenZeppelin ERC-20 został już poddany audytowi i wykazano, że jest bezpieczny. Gdy używasz dziedziczenia, +jasne jest, jakie funkcje modyfikujesz, a aby zaufać twojemu kontraktowi, ludzie muszą tylko poddać audytowi te konkretne funkcje. + +Często przydatne jest wykonanie funkcji za każdym razem, gdy tokeny zmieniają właściciela. Jednak `_transfer` jest bardzo ważną funkcją i możliwe jest +napisanie jej w sposób niebezpieczny (patrz poniżej), więc najlepiej jej nie nadpisywać. Rozwiązaniem jest `_beforeTokenTransfer`, +[funkcja typu hook](https://wikipedia.org/wiki/Hooking). Możesz nadpisać tę funkcję, a będzie ona wywoływana przy każdym transferze. + +  + +```solidity + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); +``` + +To są linie, które faktycznie wykonują transfer. Zauważ, że **nic** nie ma między nimi, i że odejmujemy +przekazaną kwotę od nadawcy przed dodaniem jej do odbiorcy. Jest to ważne, ponieważ gdyby w środku było +wywołanie innego kontraktu, mogłoby to zostać wykorzystane do oszukania tego kontraktu. W ten sposób transfer +jest atomowy, nic nie może się zdarzyć w jego środku. + +  + +```solidity + emit Transfer(sender, recipient, amount); + } +``` + +Na koniec wyemituj zdarzenie `Transfer`. Zdarzenia nie są dostępne dla inteligentnych kontraktów, ale kod działający poza blockchainem +może nasłuchiwać zdarzeń i na nie reagować. Na przykład portfel może śledzić, kiedy właściciel dostaje więcej tokenów. + +#### Funkcje _mint i _burn {#_mint-and-_burn} + +Te dwie funkcje (`_mint` i `_burn`) modyfikują całkowitą podaż tokenów. +Są one wewnętrzne i w tym kontrakcie nie ma funkcji, która by je wywoływała, +więc są przydatne tylko wtedy, gdy dziedziczysz po kontrakcie i dodajesz własną +logikę, aby zdecydować, w jakich warunkach emitować nowe tokeny lub spalać istniejące +. + +**UWAGA:** Każdy token ERC-20 ma własną logikę biznesową, która dyktuje zarządzanie tokenami. +Na przykład, kontrakt o stałej podaży może wywoływać `_mint` tylko +w konstruktorze i nigdy nie wywoływać `_burn`. Kontrakt, który sprzedaje tokeny, +wywoła `_mint`, gdy zostanie opłacony, i prawdopodobnie wywoła `_burn` w pewnym momencie, +aby uniknąć niekontrolowanej inflacji. + +```solidity + /** @dev Tworzy `amount` tokenów i przypisuje je do `account`, zwiększając + * całkowitą podaż. + * + * Emituje zdarzenie {Transfer} z `from` ustawionym na adres zerowy. + * + * Wymagania: + * + * - `to` nie może być adresem zerowym. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + _beforeTokenTransfer(address(0), account, amount); + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } +``` + +Pamiętaj o aktualizacji `_totalSupply`, gdy zmieni się całkowita liczba tokenów. + +  + +```solidity + /** + * @dev Niszczy tokeny o `wartości` `amount` z `konta`, zmniejszając + * całkowitą podaż. + * + * Emituje zdarzenie {Transfer} z `to` ustawionym na adres zerowy. + * + * Wymagania: + * + * - `konto` nie może być adresem zerowym. + * - `konto` musi mieć co najmniej `amount` tokenów. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } +``` + +Funkcja `_burn` jest prawie identyczna z `_mint`, z tym że działa w przeciwnym kierunku. + +#### Funkcja _approve {#_approve} + +To jest funkcja, która faktycznie określa zezwolenia. Zauważ, że pozwala ona właścicielowi określić +zezwolenie, które jest wyższe niż bieżące saldo właściciela. Jest to w porządku, ponieważ saldo jest +sprawdzane w momencie transferu, kiedy może być inne niż saldo w momencie tworzenia zezwolenia +. + +```solidity + /** + * @dev Ustawia `amount` jako zezwolenie `spendera` na tokeny `właściciela`. + * + * Ta wewnętrzna funkcja jest odpowiednikiem `approve` i może być używana do + * np. ustawiania automatycznych zezwoleń dla niektórych podsystemów itp. + * + * Emituje zdarzenie {Approval}. + * + * Wymagania: + * + * - `właściciel` nie może być adresem zerowym. + * - `spender` nie może być adresem zerowym. + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; +``` + +  + +Emituj zdarzenie `Approval`. W zależności od tego, jak napisana jest aplikacja, kontrakt wydającego może być poinformowany o +zatwierdzeniu przez właściciela lub przez serwer, który nasłuchuje tych zdarzeń. + +```solidity + emit Approval(owner, spender, amount); + } + +``` + +### Modyfikacja zmiennej Decimals {#modify-the-decimals-variable} + +```solidity + + + /** + * @dev Ustawia {decimals} na wartość inną niż domyślna 18. + * + * OSTRZEŻENIE: Ta funkcja powinna być wywoływana tylko z konstruktora. Większość + * aplikacji, które wchodzą w interakcję z kontraktami tokenów, nie spodziewa się, + * że {decimals} kiedykolwiek się zmieni i może działać niepoprawnie, jeśli tak się stanie. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } +``` + +Ta funkcja modyfikuje zmienną `_decimals`, która służy do informowania interfejsów użytkownika, jak interpretować kwotę. +Powinieneś ją wywołać z konstruktora. Byłoby nieuczciwe wywoływać ją w dowolnym późniejszym momencie, a aplikacje +nie są zaprojektowane do obsługi tego. + +### Hooki {#hooks} + +```solidity + + /** + * @dev Hook, który jest wywoływany przed każdym transferem tokenów. Obejmuje to + * minting i burning. + * + * Warunki wywołania: + * + * - gdy `from` i `to` są oba niezerowe, `amount` tokenów ``from`` + * zostanie przetransferowane do `to`. + * - gdy `from` jest zerem, `amount` tokenów zostanie wyemitowane dla `to`. + * - gdy `to` jest zerem, `amount` tokenów ``from`` zostanie spalonych. + * - `from` i `to` nigdy nie są oba zerami. + * + * Aby dowiedzieć się więcej o hookach, przejdź do xref:ROOT:extending-contracts.adoc#using-hooks[Używanie hooków]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} +``` + +To jest funkcja hook, która ma być wywoływana podczas transferów. Jest tutaj pusta, ale jeśli potrzebujesz, +aby coś robiła, po prostu ją nadpisujesz. + +## Wnioski {#conclusion} + +Dla przypomnienia, oto niektóre z najważniejszych pomysłów w tym kontrakcie (moim zdaniem, twoje prawdopodobnie będą się różnić): + +- _W blockchainie nie ma tajemnic_. Wszelkie informacje, do których inteligentny kontrakt ma dostęp, + są dostępne dla całego świata. +- Możesz kontrolować kolejność własnych transakcji, ale nie to, kiedy odbywają się transakcje innych osób + . To jest powód, dla którego zmiana zezwolenia może być niebezpieczna, ponieważ pozwala + wydającemu wydać sumę obu zezwoleń. +- Wartości typu `uint256` zawijają się. Innymi słowy, _0-1=2^256-1_. Jeśli nie jest to pożądane + zachowanie, musisz to sprawdzić (lub użyć biblioteki SafeMath, która robi to za Ciebie). Zauważ, że to się zmieniło w + [Solidity 0.8.0](https://docs.soliditylang.org/en/breaking/080-breaking-changes.html). +- Wykonuj wszystkie zmiany stanu określonego typu w określonym miejscu, ponieważ ułatwia to audyt. + To jest powód, dla którego mamy, na przykład, `_approve`, które jest wywoływane przez `approve`, `transferFrom`, + `increaseAllowance` i `decreaseAllowance` +- Zmiany stanu powinny być atomowe, bez żadnych innych działań w ich środku (jak widać + w `_transfer`). Dzieje się tak, ponieważ podczas zmiany stanu masz niespójny stan. Na przykład, + między czasem, w którym odejmujesz od salda nadawcy, a czasem, w którym dodajesz do salda + odbiorcy, istnieje mniej tokenów, niż powinno być. Może to być potencjalnie nadużywane, jeśli + pomiędzy nimi są operacje, zwłaszcza wywołania innego kontraktu. + +Teraz, gdy zobaczyłeś, jak napisany jest kontrakt OpenZeppelin ERC-20, a zwłaszcza jak jest +on uczyniony bardziej bezpiecznym, idź i napisz własne bezpieczne kontrakty i aplikacje. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). diff --git a/public/content/translations/pl/developers/tutorials/erc20-with-safety-rails/index.md b/public/content/translations/pl/developers/tutorials/erc20-with-safety-rails/index.md new file mode 100644 index 00000000000..547dce7f155 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/erc20-with-safety-rails/index.md @@ -0,0 +1,217 @@ +--- +title: ERC-20 z zabezpieczeniami +description: "Jak pomóc ludziom unikać prostych błędów" +author: Ori Pomerantz +lang: pl +tags: [ "erc-20" ] +skill: beginner +published: 2022-08-15 +--- + +## Wprowadzenie {#introduction} + +Jedną z wielkich zalet Ethereum jest to, że nie ma centralnego organu, który mógłby modyfikować lub cofać Twoje transakcje. Jednym z wielkich problemów z Ethereum jest to, że nie ma centralnego organu z uprawnieniami do cofania błędów użytkowników lub nielegalnych transakcji. W tym artykule dowiesz się o niektórych typowych błędach popełnianych przez użytkowników z tokenami [ERC-20](/developers/docs/standards/tokens/erc-20/), a także o tym, jak tworzyć kontrakty ERC-20, które pomagają użytkownikom unikać tych błędów lub które dają centralnemu organowi pewne uprawnienia (na przykład do zamrażania kont). + +Pamiętaj, że chociaż będziemy używać [kontraktu tokena ERC-20 OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20), ten artykuł nie wyjaśnia go szczegółowo. Możesz znaleźć te informacje [tutaj](/developers/tutorials/erc20-annotated-code). + +Jeśli chcesz zobaczyć pełny kod źródłowy: + +1. Otwórz [Remix IDE](https://remix.ethereum.org/). +2. Kliknij ikonę klonowania z GitHuba (![ikona klonowania z GitHuba](icon-clone.png)). +3. Sklonuj repozytorium z GitHuba `https://github.com/qbzzt/20220815-erc20-safety-rails`. +4. Otwórz **contracts > erc20-safety-rails.sol**. + +## Tworzenie kontraktu ERC-20 {#creating-an-erc-20-contract} + +Zanim dodamy funkcjonalność zabezpieczeń, potrzebujemy kontraktu ERC-20. W tym artykule użyjemy [kreatora kontraktów OpenZeppelin](https://docs.openzeppelin.com/contracts/5.x/wizard). Otwórz go w innej przeglądarce i postępuj zgodnie z poniższymi instrukcjami: + +1. Wybierz **ERC20**. + +2. Wprowadź te ustawienia: + + | Parametr | Wartość | + | ---------------------- | ---------------- | + | Nazwa | SafetyRailsToken | + | Symbol | SAFE | + | Premint | 1000 | + | Funkcje | Brak | + | Kontrola dostępu | Ownable | + | Możliwość aktualizacji | Brak | + +3. Przewiń w górę i kliknij **Otwórz w Remix** (dla Remix) lub **Pobierz**, aby użyć innego środowiska. Zakładam, że używasz Remix, jeśli używasz czegoś innego, po prostu wprowadź odpowiednie zmiany. + +4. Mamy teraz w pełni funkcjonalny kontrakt ERC-20. Możesz rozwinąć `.deps` > `npm`, aby zobaczyć zaimportowany kod. + +5. Skompiluj, wdróż i pobaw się kontraktem, aby zobaczyć, że działa on jak kontrakt ERC-20. Jeśli chcesz dowiedzieć się, jak używać Remix, [skorzystaj z tego samouczka](https://remix.ethereum.org/?#activate=udapp,solidity,LearnEth). + +## Częste błędy {#common-mistakes} + +### Błędy {#the-mistakes} + +Użytkownicy czasami wysyłają tokeny na zły adres. Chociaż nie możemy czytać w ich myślach, aby wiedzieć, co zamierzali zrobić, istnieją dwa rodzaje błędów, które zdarzają się często i są łatwe do wykrycia: + +1. Wysyłanie tokenów na własny adres kontraktu. Na przykład, [token OP Optimism](https://optimism.mirror.xyz/qvd0WfuLKnePm1Gxb9dpGchPf5uDz5NSMEFdgirDS4c) zdołał zgromadzić [ponad 120 000](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000042) tokenów OP w mniej niż dwa miesiące. Reprezentuje to znaczną ilość bogactwa, które ludzie przypuszczalnie po prostu stracili. + +2. Wysyłanie tokenów na pusty adres, taki, który nie odpowiada [kontu zewnętrznemu (EOA)](/developers/docs/accounts/#externally-owned-accounts-and-key-pairs) lub [inteligentnemu kontraktowi](/developers/docs/smart-contracts). Chociaż nie mam statystyk dotyczących tego, jak często to się zdarza, [jeden incydent mógł kosztować 20 000 000 tokenów](https://gov.optimism.io/t/message-to-optimism-community-from-wintermute/2595). + +### Zapobieganie transferom {#preventing-transfers} + +Kontrakt ERC-20 OpenZeppelin zawiera [hak (hook), `_beforeTokenTransfer`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol#L364-L368), który jest wywoływany przed transferem tokena. Domyślnie ten hak nic nie robi, ale możemy podpiąć do niego naszą własną funkcjonalność, taką jak kontrole, które cofają transakcję w razie problemu. + +Aby użyć tego haka, dodaj tę funkcję po konstruktorze: + +```solidity + function _beforeTokenTransfer(address from, address to, uint256 amount) + internal virtual + override(ERC20) + { + super._beforeTokenTransfer(from, to, amount); + } +``` + +Niektóre części tej funkcji mogą być nowe, jeśli nie znasz dobrze języka Solidity: + +```solidity + internal virtual +``` + +Słowo kluczowe `virtual` oznacza, że tak jak odziedziczyliśmy funkcjonalność z `ERC20` i nadpisaliśmy tę funkcję, inne kontrakty mogą dziedziczyć po nas i nadpisywać tę funkcję. + +```solidity + override(ERC20) +``` + +Musimy jawnie określić, że [nadpisujemy](https://docs.soliditylang.org/en/v0.8.15/contracts.html#function-overriding) definicję `_beforeTokenTransfer` z tokena ERC20. Ogólnie rzecz biorąc, jawne definicje są znacznie lepsze z punktu widzenia bezpieczeństwa niż te niejawne – nie możesz zapomnieć, że coś zrobiłeś, jeśli masz to tuż przed oczami. To jest również powód, dla którego musimy określić, którą `_beforeTokenTransfer` z klasy nadrzędnej nadpisujemy. + +```solidity + super._beforeTokenTransfer(from, to, amount); +``` + +Ta linia wywołuje funkcję `_beforeTokenTransfer` kontraktu lub kontraktów, z których dziedziczyliśmy i które ją posiadają. W tym przypadku jest to tylko `ERC20`, `Ownable` nie ma tego haka. Mimo że obecnie `ERC20._beforeTokenTransfer` nic nie robi, wywołujemy ją na wypadek, gdyby w przyszłości została dodana jakaś funkcjonalność (i zdecydujemy się wtedy na ponowne wdrożenie kontraktu, ponieważ kontrakty nie zmieniają się po wdrożeniu). + +### Kodowanie wymagań {#coding-the-requirements} + +Chcemy dodać do funkcji następujące wymagania: + +- Adres `to` nie może być równy `address(this)`, czyli adresowi samego kontraktu ERC-20. +- Adres `to` nie może być pusty, musi być to: + - Konto zewnętrzne (EOA). Nie możemy bezpośrednio sprawdzić, czy adres jest EOA, ale możemy sprawdzić saldo ETH adresu. Konta EOA prawie zawsze mają jakieś saldo, nawet jeśli nie są już używane – trudno jest je wyczyścić do ostatniego wei. + - Inteligentny kontrakt. Sprawdzanie, czy adres jest inteligentnym kontraktem, jest nieco trudniejsze. Istnieje kod operacyjny, który sprawdza długość kodu zewnętrznego, o nazwie [`EXTCODESIZE`](https://www.evm.codes/#3b), ale nie jest on dostępny bezpośrednio w Solidity. Musimy do tego użyć [Yul](https://docs.soliditylang.org/en/v0.8.15/yul.html), który jest asemblerem EVM. Istnieją inne wartości, których moglibyśmy użyć z Solidity ([`
.code` i `
.codehash`](https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html#members-of-address-types)), ale kosztują więcej. + +Przeanalizujmy nowy kod linia po linii: + +```solidity + require(to != address(this), "Nie można wysyłać tokenów na adres kontraktu"); +``` + +To jest pierwsze wymaganie, sprawdzić, czy `to` i `this(address)` nie są tym samym. + +```solidity + bool isToContract; + assembly { + isToContract := gt(extcodesize(to), 0) + } +``` + +W ten sposób sprawdzamy, czy adres jest kontraktem. Nie możemy otrzymać wyniku bezpośrednio z Yul, więc zamiast tego definiujemy zmienną do przechowywania wyniku (w tym przypadku `isToContract`). Yul działa w ten sposób, że każdy kod operacyjny jest traktowany jako funkcja. Najpierw więc wywołujemy [`EXTCODESIZE`](https://www.evm.codes/#3b), aby uzyskać rozmiar kontraktu, a następnie używamy [`GT`](https://www.evm.codes/#11), aby sprawdzić, czy nie jest on zerowy (mamy do czynienia z liczbami całkowitymi bez znaku, więc oczywiście nie może być ujemny). Następnie zapisujemy wynik do `isToContract`. + +```solidity + require(to.balance != 0 || isToContract, "Nie można wysyłać tokenów na pusty adres"); +``` + +I na koniec mamy faktyczne sprawdzanie pustych adresów. + +## Dostęp administracyjny {#admin-access} + +Czasami przydatne jest posiadanie administratora, który może cofać błędy. Aby zmniejszyć potencjał nadużyć, ten administrator może być [multisigiem](https://blog.logrocket.com/security-choices-multi-signature-wallets/), dzięki czemu wiele osób musi zgodzić się na daną akcję. W tym artykule omówimy dwie funkcje administracyjne: + +1. Zamrażanie i odmrażanie kont. Może to być przydatne, na przykład, gdy konto może być zagrożone. +2. Czyszczenie aktywów. + + Czasami oszuści wysyłają fałszywe tokeny do kontraktu prawdziwego tokena, aby zyskać na wiarygodności. Na przykład, [zobacz tutaj](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe?tab=holders). Prawdziwy kontrakt ERC-20 to [0x4200....0042](https://optimism.blockscout.com/token/0x4200000000000000000000000000000000000042). Oszustwo, które go udaje, to [0x234....bbe](https://optimism.blockscout.com/token/0x2348B1a1228DDCd2dB668c3d30207c3E1852fBbe). + + Możliwe jest również, że ludzie przez pomyłkę wyślą prawdziwe tokeny ERC-20 do naszego kontraktu, co jest kolejnym powodem, dla którego warto mieć sposób na ich wyciągnięcie. + +OpenZeppelin dostarcza dwa mechanizmy umożliwiające dostęp administracyjny: + +- Kontrakty [`Ownable`](https://docs.openzeppelin.com/contracts/5.x/access-control#ownership-and-ownable) mają jednego właściciela. Funkcje, które mają [modyfikator](https://www.tutorialspoint.com/solidity/solidity_function_modifiers.htm) `onlyOwner`, mogą być wywoływane tylko przez tego właściciela. Właściciele mogą przenieść własność na kogoś innego lub całkowicie się jej zrzec. Prawa wszystkich pozostałych kont są zazwyczaj identyczne. +- Kontrakty [`AccessControl`](https://docs.openzeppelin.com/contracts/5.x/access-control#role-based-access-control) mają [kontrolę dostępu opartą na rolach (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control). + +Dla uproszczenia, w tym artykule używamy `Ownable`. + +### Zamrażanie i rozmrażanie kontraktów {#freezing-and-thawing-contracts} + +Zamrażanie i rozmrażanie kontraktów wymaga kilku zmian: + +- [Mapowanie](https://www.tutorialspoint.com/solidity/solidity_mappings.htm) adresów na [wartości logiczne](https://en.wikipedia.org/wiki/Boolean_data_type), aby śledzić, które adresy są zamrożone. Wszystkie wartości są początkowo zerowe, co dla wartości logicznych jest interpretowane jako fałsz (false). Jest to pożądane, ponieważ domyślnie konta nie są zamrożone. + + ```solidity + mapping(address => bool) public frozenAccounts; + ``` + +- [Zdarzenia](https://www.tutorialspoint.com/solidity/solidity_events.htm), aby informować wszystkich zainteresowanych, gdy konto zostanie zamrożone lub rozmrożone. Technicznie rzecz biorąc, zdarzenia nie są wymagane do tych działań, ale pomaga to kodowi offchain nasłuchiwać tych zdarzeń i wiedzieć, co się dzieje. Uważa się za dobry zwyczaj, aby inteligentny kontrakt je emitował, gdy dzieje się coś, co może być istotne dla kogoś innego. + + Zdarzenia są indeksowane, więc będzie można wyszukać wszystkie przypadki zamrożenia lub rozmrożenia konta. + + ```solidity + // Gdy konta są zamrażane lub odmrażane + event AccountFrozen(address indexed _addr); + event AccountThawed(address indexed _addr); + ``` + +- Funkcje do zamrażania i rozmrażania kont. Te dwie funkcje są prawie identyczne, więc omówimy tylko funkcję zamrażania. + + ```solidity + function freezeAccount(address addr) + public + onlyOwner + ``` + + Funkcje oznaczone jako [`public`](https://www.tutorialspoint.com/solidity/solidity_contracts.htm) mogą być wywoływane z innych inteligentnych kontraktów lub bezpośrednio przez transakcję. + + ```solidity + { + require(!frozenAccounts[addr], "Konto jest już zamrożone"); + frozenAccounts[addr] = true; + emit AccountFrozen(addr); + } // freezeAccount + ``` + + Jeśli konto jest już zamrożone, cofnij transakcję. W przeciwnym razie zamroź je i `emituj` zdarzenie. + +- Zmień `_beforeTokenTransfer`, aby uniemożliwić przenoszenie pieniędzy z zamrożonego konta. Pamiętaj, że pieniądze nadal mogą być przelewane na zamrożone konto. + + ```solidity + require(!frozenAccounts[from], "Konto jest zamrożone"); + ``` + +### Czyszczenie aktywów {#asset-cleanup} + +Aby uwolnić tokeny ERC-20 przechowywane przez ten kontrakt, musimy wywołać funkcję na kontrakcie tokena, do którego należą, albo [`transfer`](https://eips.ethereum.org/EIPS/eip-20#transfer), albo [`approve`](https://eips.ethereum.org/EIPS/eip-20#approve). W tym przypadku nie ma sensu marnować gazu na przydziały (allowances), możemy równie dobrze przenieść je bezpośrednio. + +```solidity + function cleanupERC20( + address erc20, + address dest + ) + public + onlyOwner + { + IERC20 token = IERC20(erc20); +``` + +To jest składnia do tworzenia obiektu dla kontraktu, gdy otrzymamy jego adres. Możemy to zrobić, ponieważ mamy definicję tokenów ERC20 jako część kodu źródłowego (patrz linia 4), a ten plik zawiera [definicję dla IERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol), interfejs dla kontraktu ERC-20 OpenZeppelin. + +```solidity + uint balance = token.balanceOf(address(this)); + token.transfer(dest, balance); + } +``` + +To jest funkcja czyszcząca, więc przypuszczalnie nie chcemy zostawiać żadnych tokenów. Zamiast ręcznego pobierania salda od użytkownika, możemy równie dobrze zautomatyzować ten proces. + +## Wnioski {#conclusion} + +To nie jest idealne rozwiązanie – nie ma idealnego rozwiązania problemu „użytkownik popełnił błąd”. Jednak używanie tego rodzaju kontroli może przynajmniej zapobiec niektórym błędom. Możliwość zamrażania kont, chociaż niebezpieczna, może być użyta do ograniczenia szkód niektórych hacków poprzez uniemożliwienie hakerowi dostępu do skradzionych środków. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). diff --git a/public/content/translations/pl/developers/tutorials/ethereum-for-web2-auth/index.md b/public/content/translations/pl/developers/tutorials/ethereum-for-web2-auth/index.md new file mode 100644 index 00000000000..c8983d34669 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/ethereum-for-web2-auth/index.md @@ -0,0 +1,886 @@ +--- +title: "Używanie Ethereum do uwierzytelniania web2" +description: "Po przeczytaniu tego samouczka deweloper będzie w stanie zintegrować logowanie Ethereum (web3) z logowaniem SAML, standardem używanym w web2 do zapewniania jednokrotnego logowania i innych powiązanych usług. Pozwala to na uwierzytelnianie dostępu do zasobów web2 poprzez podpisy Ethereum, z atrybutami użytkownika pochodzącymi z poświadczeń." +author: Ori Pomerantz +tags: [ "web2", "uwierzytelnianie", "eas" ] +skill: beginner +lang: pl +published: 2025-04-30 +--- + +## Wprowadzenie + +[SAML](https://www.onelogin.com/learn/saml) to standard używany w web2, który pozwala [dostawcy tożsamości (IdP)](https://en.wikipedia.org/wiki/Identity_provider#SAML_identity_provider) na dostarczanie informacji o użytkowniku [dostawcom usług (SP)](https://en.wikipedia.org/wiki/Service_provider_\(SAML\)). + +W tym samouczku dowiesz się, jak zintegrować podpisy Ethereum z SAML, aby umożliwić użytkownikom używanie ich portfeli Ethereum do uwierzytelniania się w usługach web2, które jeszcze nie obsługują natywnie Ethereum. + +Pamiętaj, że ten samouczek został napisany dla dwóch różnych grup odbiorców: + +- Ludzie z kręgu Ethereum, którzy rozumieją Ethereum i potrzebują nauczyć się SAML +- Osoby z Web2, które rozumieją SAML i uwierzytelnianie web2 i muszą nauczyć się Ethereum + +W rezultacie będzie on zawierał wiele materiałów wprowadzających, które już znasz. Możesz je pominąć. + +### SAML dla osób z kręgu Ethereum + +SAML to scentralizowany protokół. Dostawca usług (SP) akceptuje asercje (takie jak "to jest mój użytkownik Jan, powinien mieć uprawnienia do wykonywania A, B i C") od dostawcy tożsamości (IdP) tylko wtedy, gdy ma z nim wcześniej ustaloną relację zaufania lub z [urzędem certyfikacji](https://www.ssl.com/article/what-is-a-certificate-authority-ca/), który podpisał certyfikat tegoż IdP. + +Na przykład SP może być biurem podróży świadczącym usługi turystyczne dla firm, a IdP może być wewnętrzną stroną internetową firmy. Gdy pracownicy muszą zarezerwować podróż służbową, biuro podróży wysyła ich do uwierzytelnienia przez firmę, zanim pozwoli im faktycznie zarezerwować podróż. + +![Proces SAML krok po kroku](./fig-01-saml.png) + +W ten sposób trzy podmioty, przeglądarka, SP i IdP, negocjują dostęp. SP nie musi z góry wiedzieć nic o użytkowniku korzystającym z przeglądarki, wystarczy, że ufa IdP. + +### Ethereum dla osób z kręgu SAML + +Ethereum to system zdecentralizowany. + +![Logowanie Ethereum](./fig-02-eth-logon.png) + +Użytkownicy posiadają klucz prywatny (zazwyczaj przechowywany w rozszerzeniu przeglądarki). Z klucza prywatnego można wyprowadzić klucz publiczny, a z niego 20-bajtowy adres. Gdy użytkownicy muszą zalogować się do systemu, są proszeni o podpisanie wiadomości z nonce (wartością jednorazowego użytku). Serwer może zweryfikować, czy podpis został utworzony przez ten adres. + +![Pobieranie dodatkowych danych z poświadczeń](./fig-03-eas-data.png) + +Podpis weryfikuje tylko adres Ethereum. Aby uzyskać inne atrybuty użytkownika, zazwyczaj używa się [poświadczeń](https://attest.org/). Poświadczenie zazwyczaj zawiera następujące pola: + +- **Poświadczający**, adres, który dokonał poświadczenia +- **Odbiorca**, adres, którego dotyczy poświadczenie +- **Dane**, czyli dane, które są poświadczane, takie jak imię, uprawnienia itp. +- **Schemat**, identyfikator schematu używanego do interpretacji danych. + +Ze względu na zdecentralizowany charakter Ethereum każdy użytkownik może tworzyć poświadczenia. Tożsamość poświadczającego jest ważna, aby zidentyfikować, które poświadczenia uważamy za wiarygodne. + +## Konfiguracja + +Pierwszym krokiem jest zapewnienie komunikacji pomiędzy SAML SP i SAML IdP. + +1. Pobierz oprogramowanie. Przykładowe oprogramowanie do tego artykułu znajduje się [na githubie](https://github.com/qbzzt/250420-saml-ethereum). Różne etapy są przechowywane w różnych gałęziach, na tym etapie potrzebujesz gałęzi `saml-only` + + ```sh + git clone https://github.com/qbzzt/250420-saml-ethereum -b saml-only + cd 250420-saml-ethereum + pnpm install + ``` + +2. Utwórz klucze z certyfikatami z podpisem własnym. Oznacza to, że klucz jest swoim własnym urzędem certyfikacji i musi zostać ręcznie zaimportowany do dostawcy usług. Więcej informacji można znaleźć w [dokumentacji OpenSSL](https://docs.openssl.org/master/man1/openssl-req/). + + ```sh + mkdir keys + cd keys + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-sp.crt -keyout saml-sp.pem -subj /CN=sp/ + openssl req -new -x509 -days 365 -nodes -sha256 -out saml-idp.crt -keyout saml-idp.pem -subj /CN=idp/ + cd .. + ``` + +3. Uruchom serwery (zarówno SP, jak i IdP) + + ```sh + pnpm start + ``` + +4. Przejdź do SP pod adresem URL [http://localhost:3000/](http://localhost:3000/) i kliknij przycisk, aby zostać przekierowanym do IdP (port 3001). + +5. Podaj IdP swój adres e-mail i kliknij **Zaloguj się do dostawcy usług**. Zobaczysz, że zostaniesz przekierowany z powrotem do dostawcy usług (port 3000) i że zna Cię on po Twoim adresie e-mail. + +### Szczegółowe wyjaśnienie + +Oto co się dzieje, krok po kroku: + +![Normalne logowanie SAML bez Ethereum](./fig-04-saml-no-eth.png) + +#### src/config.mts + +Ten plik zawiera konfigurację zarówno dla dostawcy tożsamości, jak i dostawcy usług. Zwykle byłyby to dwa różne podmioty, ale tutaj dla uproszczenia możemy współdzielić kod. + +```typescript +const fs = await import("fs") + +const protocol="http" +``` + +Na razie tylko testujemy, więc możemy używać HTTP. + +```typescript +export const spCert = fs.readFileSync("keys/saml-sp.crt").toString() +export const idpCert = fs.readFileSync("keys/saml-idp.crt").toString() +``` + +Odczytaj klucze publiczne, które są normalnie dostępne dla obu komponentów (i są albo bezpośrednio zaufane, albo podpisane przez zaufany urząd certyfikacji). + +```typescript +export const spPort = 3000 +export const spHostname = "localhost" +export const spDir = "sp" + +export const idpPort = 3001 +export const idpHostname = "localhost" +export const idpDir = "idp" + +export const spUrl = `${protocol}://${spHostname}:${spPort}/${spDir}` +export const idpUrl = `${protocol}://${idpHostname}:${idpPort}/${idpDir}` +``` + +Adresy URL dla obu komponentów. + +```typescript +export const spPublicData = { +``` + +Dane publiczne dla dostawcy usług. + +```typescript + entityID: `${spUrl}/metadata`, +``` + +Zgodnie z konwencją, w SAML `entityID` to adres URL, pod którym dostępne są metadane podmiotu. Te metadane odpowiadają danym publicznym tutaj, z tą różnicą, że są w formie XML. + +```typescript + wantAssertionsSigned: true, + authnRequestsSigned: false, + signingCert: spCert, + allowCreate: true, + assertionConsumerService: [{ + Binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + Location: `${spUrl}/assertion`, + }] + } +``` + +Najważniejszą definicją dla naszych celów jest `assertionConsumerServer`. Oznacza to, że aby potwierdzić coś (na przykład, "użytkownik, który wysyła Ci te informacje, to somebody@example.com") dostawcy usług, musimy użyć metody [HTTP POST](https://www.w3schools.com/tags/ref_httpmethods.asp) na adres URL `http://localhost:3000/sp/assertion`. + +```typescript +export const idpPublicData = { + entityID: `${idpUrl}/metadata`, + signingCert: idpCert, + wantAuthnRequestsSigned: false, + singleSignOnService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/login` + }], + singleLogoutService: [{ + Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", + Location: `${idpUrl}/logout` + }], + } +``` + +Dane publiczne dostawcy tożsamości są podobne. Określa on, że aby zalogować użytkownika, należy wysłać żądanie POST na adres `http://localhost:3001/idp/login`, a aby go wylogować, na adres `http://localhost:3001/idp/logout`. + +#### src/sp.mts + +To jest kod, który implementuje dostawcę usług. + +```typescript +import * as config from "./config.mts" +const fs = await import("fs") +const saml = await import("samlify") +``` + +Do implementacji SAML używamy biblioteki [`samlify`](https://www.npmjs.com/package/samlify). + +```typescript +import * as validator from "@authenio/samlify-node-xmllint" +saml.setSchemaValidator(validator) +``` + +Biblioteka `samlify` oczekuje, że pakiet zweryfikuje, czy kod XML jest poprawny, podpisany oczekiwanym kluczem publicznym itp. Do tego celu używamy [`@authenio/samlify-node-xmllint`](https://www.npmjs.com/package/@authenio/samlify-node-xmllint). + +```typescript +const express = (await import("express")).default +const spRouter = express.Router() +const app = express() +``` + +[`Router`](https://expressjs.com/en/5x/api.html#router) z [`express`](https://expressjs.com/) to "mini strona internetowa", którą można zamontować wewnątrz strony internetowej. W tym przypadku używamy go do zgrupowania wszystkich definicji dostawcy usług. + +```typescript +const spPrivateKey = fs.readFileSync("keys/saml-sp.pem").toString() + +const sp = saml.ServiceProvider({ + privateKey: spPrivateKey, + ...config.spPublicData +}) +``` + +Własna reprezentacja dostawcy usług to wszystkie dane publiczne oraz klucz prywatny, którego używa do podpisywania informacji. + +```typescript +const idp = saml.IdentityProvider(config.idpPublicData); +``` + +Dane publiczne zawierają wszystko, co dostawca usług musi wiedzieć o dostawcy tożsamości. + +```typescript +spRouter.get(`/metadata`, + (req, res) => res.header("Content-Type", "text/xml").send(sp.getMetadata()) +) +``` + +Aby umożliwić interoperacyjność z innymi komponentami SAML, dostawcy usług i tożsamości powinni udostępniać swoje dane publiczne (zwane metadanymi) w formacie XML pod adresem `/metadata`. + +```typescript +spRouter.post(`/assertion`, +``` + +Jest to strona, do której przeglądarka uzyskuje dostęp w celu identyfikacji. Asercja zawiera identyfikator użytkownika (tutaj używamy adresu e-mail) i może zawierać dodatkowe atrybuty. To jest procedura obsługi kroku 7 na powyższym diagramie sekwencji. + +```typescript + async (req, res) => { + // console.log(`Odpowiedź SAML:\n${Buffer.from(req.body.SAMLResponse, 'base64').toString('utf-8')}`) +``` + +Możesz użyć polecenia w komentarzu, aby zobaczyć dane XML podane w asercji. Są one [zakodowane w base64](https://en.wikipedia.org/wiki/Base64). + +```typescript + try { + const loginResponse = await sp.parseLoginResponse(idp, 'post', req); +``` + +Analizuj żądanie logowania z serwera tożsamości. + +```typescript + res.send(` + + +

Witaj ${loginResponse.extract.nameID}

+ + + `) + res.send(); +``` + +Wyślij odpowiedź HTML, aby pokazać użytkownikowi, że otrzymaliśmy dane logowania. + +```typescript + } catch (err) { + console.error('Błąd przetwarzania odpowiedzi SAML:', err); + res.status(400).send('Uwierzytelnianie SAML nie powiodło się'); + } + } +) +``` + +Poinformuj użytkownika w przypadku niepowodzenia. + +```typescript +spRouter.get('/login', +``` + +Utwórz żądanie logowania, gdy przeglądarka spróbuje uzyskać dostęp do tej strony. To jest procedura obsługi kroku 1 na powyższym diagramie sekwencji. + +```typescript + async (req, res) => { + const loginRequest = await sp.createLoginRequest(idp, "post") +``` + +Pobierz informacje, aby wysłać żądanie logowania. + +```typescript + res.send(` + + + +``` + +Ta strona automatycznie przesyła formularz (patrz poniżej). W ten sposób użytkownik nie musi nic robić, aby zostać przekierowanym. To jest krok 2 na powyższym diagramie sekwencji. + +```typescript +
+``` + +Wyślij żądanie POST do `loginRequest.entityEndpoint` (adres URL punktu końcowego dostawcy tożsamości). + +```typescript + +``` + +Nazwa danych wejściowych to `loginRequest.type` (`SAMLRequest`). Zawartością tego pola jest `loginRequest.context`, który jest ponownie kodem XML zakodowanym w base64. + +```typescript +
+ + + `) + } +) + +app.use(express.urlencoded({extended: true})) +``` + +[Ten middleware](https://expressjs.com/en/5x/api.html#express.urlencoded) odczytuje treść [żądania HTTP](https://www.tutorialspoint.com/http/http_requests.htm). Domyślnie express je ignoruje, ponieważ większość żądań go nie wymaga. Potrzebujemy go, ponieważ POST używa treści. + +```typescript +app.use(`/${config.spDir}`, spRouter) +``` + +Zamontuj router w katalogu dostawcy usług (`/sp`). + +```typescript +app.get("/", (req, res) => { + res.send(` + + + + + + `) +}) +``` + +Jeśli przeglądarka spróbuje uzyskać dostęp do katalogu głównego, udostępnij jej link do strony logowania. + +```typescript +app.listen(config.spPort, () => { + console.log(`dostawca usług działa na http://${config.spHostname}:${config.spPort}`) +}) +``` + +Nasłuchuj na `spPort` za pomocą tej aplikacji express. + +#### src/idp.mts + +To jest dostawca tożsamości. Jest on bardzo podobny do dostawcy usług, poniższe wyjaśnienia dotyczą części, które się różnią. + +```typescript +const xmlParser = new (await import("fast-xml-parser")).XMLParser( + { + ignoreAttributes: false, // Zachowaj atrybuty + attributeNamePrefix: "@_", // Prefiks dla atrybutów + } +) +``` + +Musimy odczytać i zrozumieć żądanie XML, które otrzymujemy od dostawcy usług. + +```typescript +const getLoginPage = requestId => ` +``` + +Ta funkcja tworzy stronę z automatycznie przesyłanym formularzem, która jest zwracana w kroku 4 powyższego diagramu sekwencji. + +```typescript + + + Strona logowania + + +

Strona logowania

+
+ + Adres e-mail: +
+ +``` + +Są dwa pola, które wysyłamy do dostawcy usług: + +1. `requestId`, na który odpowiadamy. +2. Identyfikator użytkownika (na razie używamy adresu e-mail podanego przez użytkownika). + +```typescript +
+ + + +const idpRouter = express.Router() + +idpRouter.post("/loginSubmitted", async (req, res) => { + const loginResponse = await idp.createLoginResponse( +``` + +To jest procedura obsługi kroku 5 na powyższym diagramie sekwencji. [`idp.createLoginResponse`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L73-L125) tworzy odpowiedź na żądanie logowania. + +```typescript + sp, + { + authnContextClassRef: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport', + audience: sp.entityID, +``` + +Odbiorcą jest dostawca usług. + +```typescript + extract: { + request: { + id: req.body.requestId + } + }, +``` + +Informacje wyodrębnione z żądania. Jedynym parametrem w żądaniu, który nas interesuje, jest requestId, który pozwala dostawcy usług dopasować żądania i ich odpowiedzi. + +```typescript + signingKey: { privateKey: idpPrivateKey, publicKey: config.idpCert } // Zapewnij podpisywanie +``` + +Potrzebujemy `signingKey`, aby mieć dane do podpisania odpowiedzi. Dostawca usług nie ufa niepodpisanym żądaniom. + +```typescript + }, + "post", + { + email: req.body.email +``` + +To jest pole z informacjami o użytkowniku, które odsyłamy do dostawcy usług. + +```typescript + } + ); + + res.send(` + + + + +
+ +
+ + + `) +}) +``` + +Ponownie, użyj automatycznie przesyłanego formularza. To jest krok 6 na powyższym diagramie sekwencji. + +```typescript + +// Punkt końcowy IdP dla żądań logowania +idpRouter.post(`/login`, +``` + +To jest punkt końcowy, który odbiera żądanie logowania od dostawcy usług. To jest procedura obsługi kroku 3 na powyższym diagramie sekwencji. + +```typescript + async (req, res) => { + try { + // Obejście, ponieważ nie mogłem sprawić, by parseLoginRequest zadziałało. + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getLoginPage(samlRequest["samlp:AuthnRequest"]["@_ID"])) +``` + +Powinniśmy być w stanie użyć [`idp.parseLoginRequest`](https://github.com/tngan/samlify/blob/master/src/entity-idp.ts#L127-L144), aby odczytać ID żądania uwierzytelnienia. Jednak nie udało mi się go uruchomić i nie było warto poświęcać na to dużo czasu, więc użyłem [uniwersalnego parsera XML](https://www.npmjs.com/package/fast-xml-parser). Informacją, której potrzebujemy, jest atrybut `ID` wewnątrz tagu ``, który znajduje się na najwyższym poziomie XML. + +## Używanie podpisów Ethereum + +Teraz, gdy możemy wysłać tożsamość użytkownika do dostawcy usług, następnym krokiem jest uzyskanie tożsamości użytkownika w zaufany sposób. Viem pozwala nam po prostu poprosić portfel o adres użytkownika, ale oznacza to proszenie przeglądarki o informacje. Nie kontrolujemy przeglądarki, więc nie możemy automatycznie ufać odpowiedzi, którą od niej otrzymujemy. + +Zamiast tego IdP wyśle przeglądarce ciąg do podpisania. Jeśli portfel w przeglądarce podpisze ten ciąg, oznacza to, że to naprawdę ten adres (tzn. zna on klucz prywatny, który odpowiada temu adresowi). + +Aby zobaczyć to w działaniu, zatrzymaj istniejące IdP i SP i uruchom te polecenia: + +```sh +git checkout eth-signatures +pnpm install +pnpm start +``` + +Następnie przejdź [do SP](http://localhost:3000) i postępuj zgodnie z instrukcjami. + +Zauważ, że w tym momencie nie wiemy, jak uzyskać adres e-mail z adresu Ethereum, więc zamiast tego zgłaszamy `@bad.email.address` do SP. + +### Szczegółowe wyjaśnienie + +Zmiany dotyczą kroków 4–5 na poprzednim diagramie. + +![SAML z podpisem Ethereum](./fig-05-saml-w-signature.png) + +Jedynym plikiem, który zmieniliśmy, jest `idp.mts`. Oto zmienione części. + +```typescript +import { v4 as uuidv4 } from 'uuid' +import { verifyMessage } from 'viem' +``` + +Potrzebujemy tych dwóch dodatkowych bibliotek. Używamy [`uuid`](https://www.npmjs.com/package/uuid) do tworzenia wartości [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce). Sama wartość nie ma znaczenia, tylko fakt, że jest używana tylko raz. + +Biblioteka [`viem`](https://viem.sh/) pozwala nam używać definicji Ethereum. Tutaj potrzebujemy jej do zweryfikowania, czy podpis jest rzeczywiście ważny. + +```typescript +const loginPrompt = "Aby uzyskać dostęp do dostawcy usług, podpisz ten nonce: " +``` + +Portfel prosi użytkownika o pozwolenie na podpisanie wiadomości. Wiadomość, która jest tylko nonce, może dezorientować użytkowników, dlatego dołączamy ten monit. + +```typescript +// Trzymaj tutaj requestID +let nonces = {} +``` + +Potrzebujemy informacji o żądaniu, aby móc na nie odpowiedzieć. Moglibyśmy wysłać je z żądaniem (krok 4) i otrzymać je z powrotem (krok 5). Jednak nie możemy ufać informacjom, które otrzymujemy z przeglądarki, która jest pod kontrolą potencjalnie wrogiego użytkownika. Więc lepiej przechowywać je tutaj, z nonce jako kluczem. + +Zauważ, że dla uproszczenia robimy to tutaj jako zmienną. Ma to jednak kilka wad: + +- Jesteśmy podatni na atak typu „odmowa usługi”. Złośliwy użytkownik mógłby próbować logować się wielokrotnie, zapełniając naszą pamięć. +- Jeśli proces IdP musi zostać ponownie uruchomiony, tracimy istniejące wartości. +- Nie możemy równoważyć obciążenia między wieloma procesami, ponieważ każdy z nich miałby swoją własną zmienną. + +W systemie produkcyjnym użylibyśmy bazy danych i zaimplementowali jakiś mechanizm wygasania. + +```typescript +const getSignaturePage = requestId => { + const nonce = uuidv4() + nonces[nonce] = requestId +``` + +Utwórz nonce i przechowaj `requestId` do przyszłego użytku. + +```typescript + return ` + + + + + +

Proszę podpisać

+ +
+ + + +` +} +``` + +Reszta to standardowy HTML. + +```typescript +idpRouter.get("/signature/:nonce/:account/:signature", async (req, res) => { +``` + +To jest procedura obsługi kroku 5 na diagramie sekwencji. + +```typescript + const requestId = nonces[req.params.nonce] + if (requestId === undefined) { + res.send("Zły nonce") + return ; + } + + nonces[req.params.nonce] = undefined +``` + +Pobierz identyfikator żądania i usuń nonce z `nonces`, aby upewnić się, że nie można go ponownie użyć. + +```typescript + try { +``` + +Ponieważ istnieje tak wiele sposobów, w jakie podpis może być nieważny, opakowujemy to w `try...` blok `catch`, aby przechwycić wszelkie zgłoszone błędy. + +```typescript + const validSignature = await verifyMessage({ + address: req.params.account, + message: `${loginPrompt}${req.params.nonce}`, + signature: req.params.signature + }) +``` + +Użyj [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage) do zaimplementowania kroku 5.5 na diagramie sekwencji. + +```typescript + if (!validSignature) + throw("Zły podpis") + } catch (err) { + res.send("Błąd:" + err) + return ; + } +``` + +Reszta procedury obsługi jest równoważna z tym, co zrobiliśmy wcześniej w procedurze obsługi `/loginSubmitted`, z wyjątkiem jednej małej zmiany. + +```typescript + const loginResponse = await idp.createLoginResponse( + . + . + . + { + email: req.params.account + "@bad.email.address" + } + ); +``` + +Nie mamy rzeczywistego adresu e-mail (uzyskamy go w następnej sekcji), więc na razie zwracamy adres Ethereum i wyraźnie oznaczamy go jako niebędący adresem e-mail. + +```typescript +// Punkt końcowy IdP dla żądań logowania +idpRouter.post(`/login`, + async (req, res) => { + try { + // Obejście, ponieważ nie mogłem sprawić, by parseLoginRequest zadziałało. + // const loginRequest = await idp.parseLoginRequest(sp, 'post', req) + const samlRequest = xmlParser.parse(Buffer.from(req.body.SAMLRequest, 'base64').toString('utf-8')) + res.send(getSignaturePage(samlRequest["samlp:AuthnRequest"]["@_ID"])) + } catch (err) { + console.error('Błąd przetwarzania odpowiedzi SAML:', err); + res.status(400).send('Uwierzytelnianie SAML nie powiodło się'); + } + } +) +``` + +Zamiast `getLoginPage`, użyj teraz `getSignaturePage` w procedurze obsługi kroku 3. + +## Uzyskiwanie adresu e-mail + +Następnym krokiem jest uzyskanie adresu e-mail, identyfikatora wymaganego przez dostawcę usług. Aby to zrobić, używamy [Ethereum Attestation Service (EAS)](https://attest.org/). + +Najłatwiejszym sposobem na uzyskanie poświadczeń jest użycie [API GraphQL](https://docs.attest.org/docs/developer-tools/api). Używamy tego zapytania: + +``` +query GetAttestationsByRecipient { + attestations( + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } + take: 1 + ) { + data + id + attester + } +} +``` + +Ten [`schemaId`](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977) zawiera tylko adres e-mail. To zapytanie prosi o poświadczenia tego schematu. Temat poświadczenia nazywa się `recipient`. Jest to zawsze adres Ethereum. + +Ostrzeżenie: Sposób, w jaki uzyskujemy tutaj poświadczenia, ma dwa problemy z bezpieczeństwem. + +- Korzystamy z punktu końcowego API `https://optimism.easscan.org/graphql`, który jest scentralizowanym komponentem. Możemy pobrać atrybut `id`, a następnie sprawdzić on-chain, aby zweryfikować, czy poświadczenie jest prawdziwe, ale punkt końcowy API nadal może cenzurować poświadczenia, nie informując nas o nich. + + Ten problem nie jest niemożliwy do rozwiązania, moglibyśmy uruchomić własny punkt końcowy GraphQL i pobierać poświadczenia z logów łańcucha, ale jest to nadmierne dla naszych celów. + +- Nie sprawdzamy tożsamości poświadczającego. Każdy może podać nam fałszywe informacje. W rzeczywistej implementacji mielibyśmy zestaw zaufanych poświadczających i sprawdzalibyśmy tylko ich poświadczenia. + +Aby zobaczyć to w działaniu, zatrzymaj istniejące IdP i SP i uruchom te polecenia: + +```sh +git checkout email-address +pnpm install +pnpm start +``` + +Następnie podaj swój adres e-mail. Masz na to dwa sposoby: + +- Zaimportuj portfel za pomocą klucza prywatnego i użyj testowego klucza prywatnego `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`. + +- Dodaj poświadczenie dla własnego adresu e-mail: + + 1. Przejdź do [schematu w eksploratorze poświadczeń](https://optimism.easscan.org/schema/view/0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977). + + 2. Kliknij **Poświadcz za pomocą schematu**. + + 3. Wprowadź swój adres Ethereum jako odbiorcę, swój adres e-mail jako adres e-mail i wybierz **Onchain**. Następnie kliknij **Utwórz poświadczenie**. + + 4. Zatwierdź transakcję w swoim portfelu. Będziesz potrzebować trochę ETH w [łańcuchu bloków Optimism](https://app.optimism.io/bridge/deposit), aby zapłacić za gaz. + +Tak czy inaczej, po wykonaniu tej czynności przejdź do [http://localhost:3000](http://localhost:3000) i postępuj zgodnie z instrukcjami. Jeśli zaimportowałeś testowy klucz prywatny, otrzymany adres e-mail to `test_addr_0@example.com`. Jeśli użyłeś własnego adresu, powinien to być adres, który poświadczyłeś. + +### Szczegółowe wyjaśnienie + +![Przejście od adresu Ethereum do adresu e-mail](./fig-06-saml-sig-n-email.png) + +Nowe kroki to komunikacja GraphQL, kroki 5.6 i 5.7. + +Ponownie, oto zmienione części `idp.mts`. + +```typescript +import { GraphQLClient } from 'graphql-request' +import { SchemaEncoder } from '@ethereum-attestation-service/eas-sdk' +``` + +Zaimportuj biblioteki, których potrzebujemy. + +```typescript +const graphqlEndpointUrl = "https://optimism.easscan.org/graphql" +``` + +Dla każdego łańcucha bloków istnieje [oddzielny punkt końcowy](https://docs.attest.org/docs/developer-tools/api). + +```typescript +const graphqlClient = new GraphQLClient(graphqlEndpointUrl, { fetch }) +``` + +Utwórz nowego klienta `GraphQLClient`, którego możemy użyć do odpytywania punktu końcowego. + +```typescript +const graphqlSchema = 'string emailAddress' +const graphqlEncoder = new SchemaEncoder(graphqlSchema) +``` + +GraphQL daje nam tylko nieprzezroczysty obiekt danych z bajtami. Aby go zrozumieć, potrzebujemy schematu. + +```typescript +const ethereumAddressToEmail = async ethAddr => { +``` + +Funkcja do konwersji adresu Ethereum na adres e-mail. + +```typescript + const query = ` + query GetAttestationsByRecipient { +``` + +To jest zapytanie GraphQL. + +```typescript + poświadczenia( +``` + +Szukamy poświadczeń. + +```typescript + where: { + recipient: { equals: "${getAddress(ethAddr)}" } + schemaId: { equals: "0xfa2eff59a916e3cc3246f9aec5e0ca00874ae9d09e4678e5016006f07622f977" } + } +``` + +Poświadczenia, których chcemy, to te w naszym schemacie, gdzie odbiorcą jest `getAddress(ethAddr)`. Funkcja [`getAddress`](https://viem.sh/docs/utilities/getAddress#getaddress) upewnia się, że nasz adres ma poprawną [sumę kontrolną](https://github.com/ethereum/ercs/blob/master/ERCS/erc-55.md). Jest to konieczne, ponieważ w GraphQL wielkość liter ma znaczenie. "0xBAD060A7", "0xBad060A7" i "0xbad060a7" to różne wartości. + +```typescript + take: 1 +``` + +Niezależnie od tego, ile poświadczeń znajdziemy, chcemy tylko pierwsze. + +```typescript + ) { + data + id + attester + } + }` +``` + +Pola, które chcemy otrzymać. + +- `attester`: Adres, który przesłał poświadczenie. Zwykle służy to do decydowania, czy ufać poświadczeniu, czy nie. +- `id`: ID poświadczenia. Możesz użyć tej wartości, aby [odczytać poświadczenie on-chain](https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000021?tab=read_proxy&source_address=0x4E0275Ea5a89e7a3c1B58411379D1a0eDdc5b088#0xa3112a64), aby zweryfikować, czy informacje z zapytania GraphQL są poprawne. +- `data`: Dane schematu (w tym przypadku adres e-mail). + +```typescript + const queryResult = await graphqlClient.request(query) + + if (queryResult.attestations.length == 0) + return "no_address@available.is" +``` + +Jeśli nie ma poświadczenia, zwróć wartość, która jest oczywiście nieprawidłowa, ale która wydawałaby się ważna dla dostawcy usług. + +```typescript + const attestationDataFields = graphqlEncoder.decodeData(queryResult.attestations[0].data) + return attestationDataFields[0].value.value +} +``` + +Jeśli istnieje wartość, użyj `decodeData` do zdekodowania danych. Nie potrzebujemy metadanych, które dostarcza, tylko samej wartości. + +```typescript + const loginResponse = await idp.createLoginResponse( + sp, + { + . + . + . + }, + "post", + { + email: await ethereumAddressToEmail(req.params.account) + } + ); +``` + +Użyj nowej funkcji, aby uzyskać adres e-mail. + +## A co z decentralizacją? + +W tej konfiguracji użytkownicy nie mogą udawać kogoś, kim nie są, o ile polegamy na godnych zaufania poświadczających w mapowaniu adresów Ethereum na adresy e-mail. Jednak nasz dostawca tożsamości jest nadal scentralizowanym komponentem. Każdy, kto ma klucz prywatny dostawcy tożsamości, może wysyłać fałszywe informacje do dostawcy usług. + +Może istnieć rozwiązanie wykorzystujące [obliczenia wielostronne (MPC)](https://en.wikipedia.org/wiki/Secure_multi-party_computation). Mam nadzieję napisać o tym w przyszłym samouczku. + +## Podsumowanie + +Przyjęcie standardu logowania, takiego jak podpisy Ethereum, stoi przed problemem jajka i kury. Dostawcy usług chcą dotrzeć do jak najszerszego rynku. Użytkownicy chcą mieć dostęp do usług bez martwienia się o obsługę ich standardu logowania. +Tworzenie adapterów, takich jak IdP Ethereum, może pomóc nam pokonać tę przeszkodę. + +[Zobacz więcej mojej pracy tutaj](https://cryptodocguy.pro/). diff --git a/public/content/translations/pl/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md b/public/content/translations/pl/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md index cf2a15cfcc5..d155d9ef78e 100644 --- a/public/content/translations/pl/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md +++ b/public/content/translations/pl/developers/tutorials/getting-started-with-ethereum-development-using-alchemy/index.md @@ -1,100 +1,101 @@ --- -title: Pierwsze kroki programowania w Ethereum za pomocą Alchemy -description: "To jest przewodnik dla początkujących, jak rozpocząć programowanie w Ethereum za pomocą Alchemy. Przeprowadzimy Cię od rejestracji w Alchemy, przez wysłanie żądania w wierszu poleceń, do napisania pierwszego skryptu web3! Nie jest wymagane doświadczenie w programowaniu blockchain!" +title: Pierwsze kroki w programowaniu w Ethereum +description: "To jest przewodnik dla początkujących, jak zacząć programować w Ethereum. Przeprowadzimy Cię od uruchomienia punktu końcowego API, przez wysłanie żądania z wiersza poleceń, aż po napisanie Twojego pierwszego skryptu web3! Doświadczenie w programowaniu blockchain nie jest wymagane!" author: "Elan Halpern" tags: - - "pierwsze kroki" - - "javascript" - - "ethers.js" - - "węzły" - - "zapytania" - - "alchemy" + [ + "JavaScript", + "ethers.js", + "węzły", + "zapytania", + "alchemy" + ] skill: beginner lang: pl published: 2020-10-30 -source: Średni +source: Medium sourceUrl: https://medium.com/alchemy-api/getting-started-with-ethereum-development-using-alchemy-c3d6a45c567f --- -![Logo Ethereum i Alchemy](./ethereum-alchemy.png) +![Loga Ethereum i Alchemy](./ethereum-alchemy.png) -To jest przewodnik dla początkujących po rozpoczęciu programowania w Ethereum przy użyciu [Alchemy](https://alchemyapi.io/), wiodącej platformy dla twórców blockchainów wykorzystywanej przez miliony użytkowników z 70% najlepszych aplikacji blockchain, w tym Maker, 0x, MyEtherWallet, Dharma i Kyber. +To jest przewodnik dla początkujących, jak zacząć programować w Ethereum. W tym samouczku będziemy używać [Alchemy](https://alchemyapi.io/), wiodącej platformy dla deweloperów blockchain, która obsługuje miliony użytkowników z 70% najlepszych aplikacji blockchain, w tym Maker, 0x, MyEtherWallet, Dharma i Kyber. Alchemy da nam dostęp do punktu końcowego API w łańcuchu Ethereum, dzięki czemu będziemy mogli odczytywać i zapisywać transakcje. -Zarejestruj się w Alchemy, aby napisać swój pierwszy skrypt web3! Nie jest wymagane doświadczenie w programowaniu blockchain! +Przeprowadzimy Cię od rejestracji w Alchemy do napisania pierwszego skryptu web3! Doświadczenie w programowaniu blockchain nie jest wymagane! -## 1\. Zarejestruj się na darmowym koncie Alchemy {#sign-up-for-a-free-alchemy-account} +## 1. Zarejestruj darmowe konto Alchemy {#sign-up-for-a-free-alchemy-account} -Tworzenie konta z Alchemy jest łatwe, [zarejestruj się za darmo tutaj](https://auth.alchemyapi.io/signup). +Utworzenie konta w Alchemy jest proste, [zarejestruj się za darmo tutaj](https://auth.alchemy.com/). -## 2\. Utwórz aplikację Alchemy {#create-an-alchemy-app} +## 2. Utwórz aplikację Alchemy {#create-an-alchemy-app} -Aby korzystać z produktów Alchemy, potrzebujesz klucza API do uwierzytelnienia swoich żądań. +Aby komunikować się z łańcuchem Ethereum i korzystać z produktów Alchemy, potrzebujesz klucza API do uwierzytelniania swoich żądań. -Możesz [tworzyć klucze API z panelu](http://dashboard.alchemyapi.io/). Aby utworzyć nowy klucz, przejdź do „Utwórz aplikację”, jak pokazano poniżej: +Możesz [utworzyć klucze API z pulpitu nawigacyjnego](https://dashboard.alchemy.com/). Aby utworzyć nowy klucz, przejdź do „Utwórz aplikację”, jak pokazano poniżej: -Specjalne podziękowania dla [_ShapeShift_](https://shapeshift.com/) _za umożliwienie nam pokazania ich panelu!_ +Specjalne podziękowania dla [_ShapeShift_](https://shapeshift.com/) _za umożliwienie nam pokazania ich pulpitu nawigacyjnego!_ -![Pulpit Alchemy](./alchemy-dashboard.png) +![Pulpit nawigacyjny Alchemy](./alchemy-dashboard.png) -Wypełnij szczegóły w sekcji „Utwórz aplikację”, aby uzyskać swój nowy klucz. Możesz również zobaczyć aplikacje, które wcześniej stworzyłeś i aplikacje wykonane przez swój zespół tutaj. Pociągnij istniejące klucze, klikając „Wyświetl klucz” dla dowolnej aplikacji. +Wypełnij dane w sekcji „Utwórz aplikację”, aby otrzymać nowy klucz. Możesz tu również zobaczyć aplikacje utworzone wcześniej przez Ciebie i przez Twój zespół. Pobierz istniejące klucze, klikając „Wyświetl klucz” dla dowolnej aplikacji. -![Utwórz aplikację za pomocą zrzutu ekranu Alchemy](./create-app.png) +![Zrzut ekranu tworzenia aplikacji za pomocą Alchemy](./create-app.png) -Możesz również ściągnąć istniejące klucze API, umieszczając kursor myszy nad „Aplikacje” i wybierając jeden. Możesz tutaj „Wyświetlić klucz”, a także „Edytować aplikację”, aby dodać określone domeny do białej listy, zobaczyć kilka narzędzi dla programistów i wyświetlić dane analityczne. +Możesz także pobrać istniejące klucze API, najeżdżając kursorem na „Aplikacje” i wybierając jedną z nich. Możesz tu „Wyświetlić klucz”, a także „Edytować aplikację”, aby dodać określone domeny do białej listy, zobaczyć kilka narzędzi dla deweloperów i wyświetlić analitykę. -![Gif pokazuje użytkownikowi jak ściągnąć klucze API](./pull-api-keys.gif) +![Gif pokazujący, jak użytkownik może pobrać klucze API](./pull-api-keys.gif) -## 3\. Zgłoś żądanie z wiersza poleceń {#make-a-request-from-the-command-line} +## 3. Wyślij żądanie z wiersza poleceń {#make-a-request-from-the-command-line} -Współpracuj z blockchainem Ethereum przez Alchemy, używając JSON-RPC i curl. +Wchodź w interakcje z blockchainem Ethereum za pośrednictwem Alchemy za pomocą JSON-RPC i curl. -Dla żądań ręcznych, zalecamy zastosowanie `JSON-RPC` poprzez `POST`. Wystarczy przekazać nagłówek `Content-Type: application/json` i zapytanie jako treść `POST` z następującymi polami: +W przypadku żądań ręcznych zalecamy interakcję z `JSON-RPC` za pośrednictwem żądań `POST`. Po prostu przekaż nagłówek `Content-Type: application/json` i swoje zapytanie jako treść `POST` z następującymi polami: -- `jsonrpc`: wersja JSON-RPC - obecnie obsługiwana jest tylko `2.0`. -- `method`: metoda ETH API. [Zobacz materiały dot. API.](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) -- `params`: lista parametrów do przekazania do metody. -- `id`: identyfikator Twojego żądania. Zostanie zwrócony wraz z odpowiedzią, dzięki czemu można śledzić, do którego żądania należy odpowiedź. +- `jsonrpc`: Wersja JSON-RPC — obecnie obsługiwana jest tylko wersja `2.0`. +- `method`: Metoda API ETH. [Zobacz dokumentację API.](https://docs.alchemyapi.io/documentation/alchemy-api-reference/json-rpc) +- `params`: Lista parametrów do przekazania do metody. +- `id`: ID Twojego żądania. Zostanie zwrócone w odpowiedzi, dzięki czemu możesz śledzić, do którego żądania należy dana odpowiedź. -Poniżej znajduje się przykład, który można uruchomić z wiersza poleceń, aby uzyskać aktualną cenę gazu: +Oto przykład, który możesz uruchomić z wiersza poleceń, aby pobrać aktualną cenę gazu: ```bash -curl [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/v2/demo) \ +curl https://eth-mainnet.alchemyapi.io/v2/demo \ -X POST \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":73}' ``` -**_UWAGA:_** _Zamień_ [_https://eth-mainnet.alchemyapi. o/v2/demo_](https://eth-mainnet.alchemyapi.io/jsonrpc/demo) _z własnym kluczem API_ [_https://eth-mainnet.alchemyapi.io/v2/your-api-key_](https://eth-mainnet.alchemyapi.io/jsonrpc/your-api-key)_._ +_**UWAGA:** Zastąp [https://eth-mainnet.alchemyapi.io/v2/demo](https://eth-mainnet.alchemyapi.io/jsonrpc/demo) własnym kluczem API `https://eth-mainnet.alchemyapi.io/v2/**your-api-key`._ -**Wynik:** +**Wyniki:** ```json -{ "id": 73,"jsonrpc": "2.0","result": "0x09184e72a000" // 100000000000000000 } +{ "id": 73,"jsonrpc": "2.0","result": "0x09184e72a000" // 10000000000000 } ``` -## 4\. Skonfiguruj swojego klienta Web3 {#set-up-your-web3-client} +## 4. Skonfiguruj swojego klienta Web3 {#set-up-your-web3-client} -**Jeśli masz istniejącego klienta,** zmień adres URL aktualnego dostawcy węzła na adres URL Alchemy z kluczem API: `"https://eth-mainnet.alchemyapi.io/v2/your-api-key"` +**Jeśli masz już istniejącego klienta,** zmień adres URL swojego obecnego dostawcy węzła na adres URL Alchemy z Twoim kluczem API: `“https://eth-mainnet.alchemyapi.io/v2/your-api-key\"` -**_UWAGA:_** Skrypty poniżej muszą być uruchomione w **kontekście węzłów** lub **zapisane w pliku**, nie uruchamiaj z wiersza poleceń. Jeśli nie masz jeszcze zainstalowanego węzła lub npm, sprawdź ten [przewodnik konfiguracji macs](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs). +**_UWAGA:_** Poniższe skrypty muszą być uruchamiane w **kontekście node** lub **zapisane w pliku**, a nie z wiersza poleceń. Jeśli nie masz jeszcze zainstalowanego Node lub npm, zapoznaj się z tym krótkim [przewodnikiem konfiguracji dla komputerów Mac](https://app.gitbook.com/@alchemyapi/s/alchemy/guides/alchemy-for-macs). -Istnieją tony [bibliotek Web3](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries) które możesz zintegrować z alchemy. zalecamy użycie [Alchemy Web3](https://docs.alchemy.com/reference/api-overview), upuszczonego zamiennika dla web3., zbudowany i skonfigurowany do bezproblemowej pracy z Alchemy. Zapewnia to wiele zalet, takich jak automatyczne próby i solidne wsparcie WebSocket. +Istnieje mnóstwo [bibliotek Web3](https://docs.alchemyapi.io/guides/getting-started#other-web3-libraries), które można zintegrować z Alchemy, jednak zalecamy użycie [Alchemy Web3](https://docs.alchemy.com/reference/api-overview), zamiennika dla web3.js, stworzonego i skonfigurowanego do bezproblemowej pracy z Alchemy. Zapewnia to wiele korzyści, takich jak automatyczne ponawianie prób i solidne wsparcie dla WebSocket. -Aby zainstalować AlchemyWeb3.js, **przejdź do katalogu projektu** i uruchom: +Aby zainstalować AlchemyWeb3.js, **przejdź do katalogu swojego projektu** i uruchom: -**Z Yarn:** +**Za pomocą Yarn:** ``` -yarn dodaj @alch/alchemy-web3 +yarn add @alch/alchemy-web3 ``` -**Z NPM:** +**Za pomocą NPM:** ``` npm install @alch/alchemy-web3 ``` -Aby wejść w interakcję z infrastrukturą węzła Alchemy, uruchom w NodeJS lub dodaj to do pliku JavaScript: +Aby wejść w interakcję z infrastrukturą węzłów Alchemy, uruchom w NodeJS lub dodaj to do pliku JavaScript: ```js const { createAlchemyWeb3 } = require("@alch/alchemy-web3") @@ -103,52 +104,53 @@ const web3 = createAlchemyWeb3( ) ``` -## 5\. Napisz swój pierwszy skrypt Web3! {#write-your-first-web3-script} +## 5. Napisz swój pierwszy skrypt Web3! {#write-your-first-web3-script} -Teraz, aby ubrudzić sobie ręce odrobiną programowania web3, napiszemy prosty skrypt, który wypisuje najnowszy numer bloku z sieci głównej Ethereum. +Teraz, aby trochę pobrudzić sobie ręce programowaniem w web3, napiszemy prosty skrypt, który wyświetla numer ostatniego bloku z sieci głównej Ethereum. -1. **Jeśli jeszcze tego nie zrobiłeś, w swoim terminalu utwórz nowy katalog projektów i przejdź do niego:** +**1. Jeśli jeszcze tego nie zrobiłeś, utwórz w terminalu nowy katalog projektu i przejdź do niego:** ``` -mkdir web3-examplecd web3-example +mkdir web3-example +cd web3-example ``` -**2\. Zainstaluj zależność Alchemy web3 (lub dowolny web3) w swoim projekcie, jeśli jeszcze tego nie zrobiłeś:** +**2. Zainstaluj w swoim projekcie zależność Alchemy web3 (lub dowolną web3), jeśli jeszcze tego nie zrobiłeś:** ``` npm install @alch/alchemy-web3 ``` -**3. Utwórz plik o nazwie** `index.js` **i dodaj następujące treści:** +**3. Utwórz plik o nazwie `index.js` i dodaj następującą zawartość:** -> Ostatecznie powinieneś zastąpić `demo` kluczem API Alchemy. +> Docelowo należy zastąpić `demo` swoim kluczem API HTTP Alchemy. ```js async function main() { const { createAlchemyWeb3 } = require("@alch/alchemy-web3") - const web3 = createAlchemyWeb3("https://eth- mainnet.alchemyapi.io/v2/demo") + const web3 = createAlchemyWeb3("https://eth-mainnet.alchemyapi.io/v2/demo") const blockNumber = await web3.eth.getBlockNumber() - console.log("The latest block number is " + blockNumber) + console.log("Najnowszy numer bloku to " + blockNumber) } main() ``` -Nie znasz rozwiązań asynchronicznych? Sprawdź to [Medium post](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c). +Nie znasz rozwiązań asynchronicznych? Sprawdź ten [wpis na Medium](https://medium.com/better-programming/understanding-async-await-in-javascript-1d81bb079b2c). -**4\. Uruchom go w swoim terminalu, używając węzła** +**4. Uruchom to w terminalu za pomocą node** ``` node index.js ``` -**5. Powinieneś teraz zobaczyć najnowszy numer bloku w konsoli!** +**5. Powinieneś teraz zobaczyć w konsoli wynik z najnowszym numerem bloku!** ``` -The latest block number is 11043912 +Najnowszy numer bloku to 11043912 ``` -**Woo! Gratulacje! Właśnie napisałeś swój pierwszy skrypt web3 używając Alchemy 🎉** +**Woo! Gratulacje! Właśnie napisałeś swój pierwszy skrypt web3 przy użyciu Alchemy 🎉** -Nie jesteś pewien, co zrobić dalej? Spróbuj wdrożyć pierwszy kontrakt inteligentny i popracuj, programując w Solidity [_Hello World Smart_](https://docs.alchemyapi.io/tutorials/hello-world-smart-contract) _Contract Guide, lub sprawdź, co wiesz o pulpicie nawigacyjnym za pomocą_ [_Dashboard Demo App_](https://docs.alchemyapi.io/tutorials/demo-app)_!_ +Nie wiesz, co robić dalej? Spróbuj wdrożyć swój pierwszy smart kontrakt i pobrudź sobie ręce programowaniem w Solidity w naszym [przewodniku po smart kontraktach „Hello World”](https://www.alchemy.com/docs/hello-world-smart-contract) lub sprawdź swoją wiedzę o pulpicie nawigacyjnym za pomocą [aplikacji demonstracyjnej pulpitu nawigacyjnego](https://docs.alchemyapi.io/tutorials/demo-app)! -_[Zarejestruj się przy użyciu Alchemy za darmo](https://auth.alchemyapi.io/signup), sprawdź naszą [dokumentację](https://docs.alchemyapi.io/), oraz najnowsze wiadomości, obserwuj nas na [Twitterze](https://twitter.com/AlchemyPlatform)_. +_[Zarejestruj się w Alchemy za darmo](https://auth.alchemy.com/), sprawdź naszą [dokumentację](https://www.alchemy.com/docs/), a aby być na bieżąco z najnowszymi wiadomościami, obserwuj nas na [Twitterze](https://twitter.com/AlchemyPlatform)_. diff --git a/public/content/translations/pl/developers/tutorials/guide-to-smart-contract-security-tools/index.md b/public/content/translations/pl/developers/tutorials/guide-to-smart-contract-security-tools/index.md new file mode 100644 index 00000000000..128cd64046e --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/guide-to-smart-contract-security-tools/index.md @@ -0,0 +1,102 @@ +--- +title: "Przewodnik po narzędziach bezpieczeństwa inteligentnych kontraktów" +description: "Przegląd trzech różnych technik testowania i analizy programu" +author: "Trailofbits" +lang: pl +tags: [ "solidity", "smart kontrakty", "bezpieczeństwo" ] +skill: intermediate +published: 2020-09-07 +source: Building secure contracts +sourceUrl: https://github.com/crytic/building-secure-contracts/tree/master/program-analysis +--- + +Użyjemy trzech odrębnych technik testowania i analizy programu: + +- **Analiza statyczna z użyciem [Slither](/developers/tutorials/how-to-use-slither-to-find-smart-contract-bugs/).** Wszystkie ścieżki programu są aproksymowane i analizowane w tym samym czasie, poprzez różne reprezentacje programu (np. graf przepływu sterowania) +- **Fuzzing z użyciem [Echidna](/developers/tutorials/how-to-use-echidna-to-test-smart-contracts/).** Kod jest wykonywany z pseudolosowym generowaniem transakcji. Fuzzer będzie próbował znaleźć sekwencję transakcji naruszającą daną właściwość. +- **Wykonanie symboliczne z użyciem [Manticore](/developers/tutorials/how-to-use-manticore-to-find-smart-contract-bugs/).** Formalna technika weryfikacji, która tłumaczy każdą ścieżkę wykonania na formułę matematyczną, na której można sprawdzać ograniczenia. + +Każda technika ma zalety i wady i będzie przydatna w [określonych przypadkach](#determining-security-properties): + +| Technika | Narzędzie | Zastosowanie | Szybkość | Pominięte błędy | Fałszywe alarmy | +| --------------------- | --------- | ------------------------------ | -------- | --------------- | --------------- | +| Analiza statyczna | Slither | CLI i skrypty | sekundy | umiarkowany | niski | +| Fuzzing | Echidna | Właściwości Solidity | minuty | niski | brak | +| Wykonanie symboliczne | Manticore | Właściwości Solidity i skrypty | godziny | brak\* | brak | + +\* jeśli wszystkie ścieżki zostaną zbadane bez przekroczenia limitu czasu + +**Slither** analizuje kontrakty w ciągu kilku sekund, jednak analiza statyczna może prowadzić do fałszywych alarmów i będzie mniej odpowiednia do złożonych sprawdzeń (np. sprawdzeń arytmetycznych). Uruchom Slither przez API, aby uzyskać łatwy dostęp do wbudowanych detektorów lub przez API, aby uzyskać dostęp do sprawdzeń zdefiniowanych przez użytkownika. + +**Echidna** musi działać przez kilka minut i będzie generować tylko prawdziwie pozytywne wyniki. Echidna sprawdza właściwości bezpieczeństwa dostarczone przez użytkownika, napisane w Solidity. Może pominąć błędy, ponieważ opiera się na losowej eksploracji. + +**Manticore** wykonuje analizę \ Podobnie jak Echidna, Manticore weryfikuje właściwości dostarczone przez użytkownika. Potrzebuje więcej czasu na uruchomienie, ale może udowodnić poprawność właściwości i nie zgłasza fałszywych alarmów. + +## Sugerowany przepływ pracy {#suggested-workflow} + +Zacznij od wbudowanych detektorów Slither, aby upewnić się, że żadne proste błędy nie są obecnie obecne ani nie zostaną wprowadzone później. Użyj Slither do sprawdzania właściwości związanych z dziedziczeniem, zależnościami zmiennych i problemami strukturalnymi. W miarę rozrastania się bazy kodu użyj Echidny do testowania bardziej złożonych właściwości maszyny stanu. Wróć do Slither, aby opracować niestandardowe sprawdzenia zabezpieczeń niedostępnych w Solidity, takie jak ochrona przed nadpisaniem funkcji. Na koniec użyj Manticore do przeprowadzenia ukierunkowanej weryfikacji krytycznych właściwości bezpieczeństwa, np. operacji arytmetycznych. + +- Użyj CLI Slither do wykrywania typowych problemów +- Użyj Echidny do testowania właściwości bezpieczeństwa wysokiego poziomu Twojego kontraktu +- Użyj Slither do pisania niestandardowych sprawdzeń statycznych +- Użyj Manticore, aby uzyskać dogłębną pewność co do krytycznych właściwości bezpieczeństwa + +**Uwaga na temat testów jednostkowych**. Testy jednostkowe są niezbędne do tworzenia oprogramowania wysokiej jakości. Jednakże techniki te nie są najlepiej przystosowane do znajdowania luk w zabezpieczeniach. Zazwyczaj są one używane do testowania pozytywnych zachowań kodu (tj. kod działa zgodnie z oczekiwaniami w normalnym kontekście), podczas gdy luki w zabezpieczeniach mają tendencję do występowania w przypadkach brzegowych, których deweloperzy nie wzięli pod uwagę. W naszym badaniu dziesiątek przeglądów bezpieczeństwa inteligentnych kontraktów, [pokrycie testami jednostkowymi nie miało wpływu na liczbę ani wagę luk w zabezpieczeniach](https://blog.trailofbits.com/2019/08/08/246-findings-from-our-smart-contract-audits-an-executive-summary/), które znaleźliśmy w kodzie naszych klientów. + +## Określanie właściwości bezpieczeństwa {#determining-security-properties} + +Aby skutecznie testować i weryfikować swój kod, musisz zidentyfikować obszary, które wymagają uwagi. Ponieważ Twoje zasoby przeznaczone na bezpieczeństwo są ograniczone, ważne jest określenie słabych lub wartościowych części Twojej bazy kodu, aby zoptymalizować Twój wysiłek. Modelowanie zagrożeń może pomóc. Rozważ przejrzenie: + +- [Szybkie oceny ryzyka (Rapid Risk Assessments)](https://infosec.mozilla.org/guidelines/risk/rapid_risk_assessment.html) (nasze preferowane podejście, gdy brakuje czasu) +- [Przewodnik po modelowaniu zagrożeń systemu zorientowanego na dane](https://csrc.nist.gov/pubs/sp/800/154/ipd) (znany również jako NIST 800-154) +- [Modelowanie zagrożeń wg Shostacka](https://www.amazon.com/Threat-Modeling-Designing-Adam-Shostack/dp/1118809998) +- [STRIDE](https://wikipedia.org/wiki/STRIDE_\(security\)) / [DREAD](https://wikipedia.org/wiki/DREAD_\(risk_assessment_model\)) +- [PASTA](https://wikipedia.org/wiki/Threat_model#P.A.S.T.A.) +- [Użycie asercji](https://blog.regehr.org/archives/1091) + +### Komponenty {#components} + +Wiedza o tym, co chcesz sprawdzić, pomoże Ci również w wyborze odpowiedniego narzędzia. + +Szerokie obszary, które są często istotne dla inteligentnych kontraktów, obejmują: + +- **Maszyna stanu.** Większość kontraktów można przedstawić jako maszynę stanu. Rozważ sprawdzenie, czy (1) nie można osiągnąć żadnego nieprawidłowego stanu, (2) jeśli stan jest prawidłowy, to można go osiągnąć, oraz (3) żaden stan nie blokuje kontraktu. + + - Echidna i Manticore to narzędzia, które należy preferować do testowania specyfikacji maszyn stanów. + +- **Kontrola dostępu.** Jeśli Twój system ma uprzywilejowanych użytkowników (np. właściciela, kontrolerów, ...) musisz upewnić się, że (1) każdy użytkownik może wykonywać tylko autoryzowane działania oraz (2) żaden użytkownik nie może blokować działań bardziej uprzywilejowanego użytkownika. + + - Slither, Echidna i Manticore mogą sprawdzać poprawność kontroli dostępu. Na przykład Slither może sprawdzić, czy tylko funkcje z białej listy nie mają modyfikatora onlyOwner. Echidna i Manticore są przydatne do bardziej złożonej kontroli dostępu, takiej jak uprawnienie przyznawane tylko wtedy, gdy kontrakt osiągnie określony stan. + +- **Operacje arytmetyczne.** Sprawdzanie poprawności operacji arytmetycznych ma kluczowe znaczenie. Używanie `SafeMath` wszędzie jest dobrym krokiem w celu zapobiegania przepełnieniom/niedopełnieniom, jednak nadal musisz brać pod uwagę inne wady arytmetyczne, w tym problemy z zaokrąglaniem i błędy, które blokują kontrakt. + + - Manticore jest tutaj najlepszym wyborem. Echidna może być używana, jeśli arytmetyka jest poza zakresem solwera SMT. + +- **Poprawność dziedziczenia.** Kontrakty Solidity w dużym stopniu opierają się na wielokrotnym dziedziczeniu. Łatwo można wprowadzić błędy, takie jak przesłaniająca funkcja bez wywołania `super` i błędnie zinterpretowany porządek linearyzacji c3. + + - Slither jest narzędziem zapewniającym wykrycie tych problemów. + +- **Interakcje zewnętrzne.** Kontrakty wchodzą w interakcje ze sobą, a niektórym kontraktom zewnętrznym nie należy ufać. Na przykład, jeśli Twój kontrakt opiera się na zewnętrznych oracle'ach, czy pozostanie bezpieczny, jeśli połowa dostępnych oracle'i zostanie naruszona? + + - Manticore i Echidna to najlepszy wybór do testowania zewnętrznych interakcji z Twoimi kontraktami. Manticore ma wbudowany mechanizm do zaślepiania zewnętrznych kontraktów. + +- **Zgodność ze standardami.** Standardy Ethereum (np. ERC20) mają w swojej historii błędy projektowe. Bądź świadomy ograniczeń standardu, na którym budujesz. + - Slither, Echidna i Manticore pomogą Ci wykryć odchylenia od danego standardu. + +### Ściągawka doboru narzędzi {#tool-selection-cheatsheet} + +| Komponent | Narzędzia | Przykłady | +| ------------------------ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Maszyna stanu | Echidna, Manticore | | +| Kontrola dostępu | Slither, Echidna, Manticore | [Ćwiczenie 2 Slither](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise2.md), [Ćwiczenie 2 Echidna](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-2.md) | +| Operacje arytmetyczne | Manticore, Echidna | [Ćwiczenie 1 Echidna](https://github.com/crytic/building-secure-contracts/blob/master/program-analysis/echidna/exercises/Exercise-1.md), [Ćwiczenia 1-3 Manticore](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/manticore/exercises) | +| Poprawność dziedziczenia | Slither | [Ćwiczenie 1 Slither](https://github.com/crytic/slither/blob/7f54c8b948c34fb35e1d61adaa1bd568ca733253/docs/src/tutorials/exercise1.md) | +| Interakcje zewnętrzne | Manticore, Echidna | | +| Zgodność ze standardem | Slither, Echidna, Manticore | [`slither-erc`](https://github.com/crytic/slither/wiki/ERC-Conformance) | + +Inne obszary będą wymagały sprawdzenia w zależności od Twoich celów, ale te ogólne obszary zainteresowania są dobrym punktem wyjścia dla każdego systemu inteligentnych kontraktów. + +Nasze publiczne audyty zawierają przykłady zweryfikowanych lub przetestowanych właściwości. Rozważ przeczytanie sekcji `Automated Testing and Verification` w poniższych raportach, aby przejrzeć rzeczywiste właściwości bezpieczeństwa: + +- [0x](https://github.com/trailofbits/publications/blob/master/reviews/0x-protocol.pdf) +- [Balancer](https://github.com/trailofbits/publications/blob/master/reviews/BalancerCore.pdf) diff --git a/public/content/translations/pl/developers/tutorials/hello-world-smart-contract-fullstack/index.md b/public/content/translations/pl/developers/tutorials/hello-world-smart-contract-fullstack/index.md new file mode 100644 index 00000000000..99b5e8c5c77 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/hello-world-smart-contract-fullstack/index.md @@ -0,0 +1,1543 @@ +--- +title: "Inteligentny kontrakt „Witaj świecie” dla początkujących – Fullstack" +description: "Samouczek wprowadzający na temat pisania i wdrażania prostego inteligentnego kontraktu na Ethereum." +author: "nstrike2" +tags: + [ + "solidity", + "hardhat", + "alchemy", + "smart kontrakty", + "wdrażanie", + "eksplorator bloków", + "frontend", + "transakcje" + ] +skill: beginner +lang: pl +published: 2021-10-25 +--- + +Ten przewodnik jest dla Ciebie, jeśli jesteś nowy w tworzeniu blockchaina i nie wiesz, od czego zacząć lub jak wdrażać i wchodzić w interakcję z inteligentnymi kontraktami. Przeprowadzimy Cię przez proces tworzenia i wdrażania prostego inteligentnego kontraktu w sieci testowej Goerli przy użyciu [MetaMask](https://metamask.io), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org) i [Alchemy](https://alchemy.com/eth). + +Do ukończenia tego samouczka potrzebne będzie konto Alchemy. [Zarejestruj się, aby założyć darmowe konto](https://www.alchemy.com/). + +Jeśli masz jakieś pytania, skontaktuj się z nami na [Discordzie Alchemy](https://discord.gg/gWuC7zB)! + +## Część 1 – Utwórz i wdróż swój inteligentny kontrakt za pomocą Hardhat {#part-1} + +### Połącz się z siecią Ethereum {#connect-to-the-ethereum-network} + +Istnieje wiele sposobów na wysyłanie żądań do łańcucha Ethereum. Dla uproszczenia, użyjemy darmowego konta na Alchemy, platformie deweloperskiej i interfejsie API blockchaina, które pozwalają nam komunikować się z łańcuchem Ethereum bez konieczności samodzielnego uruchamiania węzła. Alchemy posiada również narzędzia deweloperskie do monitorowania i analityki. Skorzystamy z nich w tym samouczku, aby zrozumieć, co dzieje się pod maską podczas wdrażania naszego inteligentnego kontraktu. + +### Utwórz swoją aplikację i klucz API {#create-your-app-and-api-key} + +Po utworzeniu konta Alchemy, można wygenerować klucz API, tworząc aplikację. Umożliwi to wysyłanie żądań do sieci testowej Goerli. Jeśli nie znasz sieci testowych, możesz [przeczytać przewodnik Alchemy dotyczący wyboru sieci](https://www.alchemy.com/docs/choosing-a-web3-network). + +Na pulpicie nawigacyjnym Alchemy znajdź menu rozwijane **Aplikacje** w pasku nawigacyjnym i kliknij **Utwórz aplikację**. + +![Utwórz aplikację Hello world](./hello-world-create-app.png) + +Nadaj swojej aplikacji nazwę „_Hello World_” i wpisz krótki opis. Wybierz **Staging** jako środowisko i **Goerli** jako sieć. + +![widok tworzenia aplikacji hello world](./create-app-view-hello-world.png) + +_Uwaga: upewnij się, że wybrałeś **Goerli**, w przeciwnym razie ten samouczek nie zadziała._ + +Kliknij **Utwórz aplikację**. Twoja aplikacja pojawi się w poniższej tabeli. + +### Utwórz konto Ethereum {#create-an-ethereum-account} + +Aby wysyłać i odbierać transakcje, potrzebujesz konta Ethereum. Użyjemy MetaMask, wirtualnego portfela w przeglądarce, który pozwala użytkownikom zarządzać adresem konta Ethereum. + +Możesz pobrać i utworzyć konto MetaMask za darmo [tutaj](https://metamask.io/download). Podczas tworzenia konta, lub jeśli już je posiadasz, upewnij się, że przełączyłeś się na „Sieć testową Goerli” w prawym górnym rogu (aby nie mieć do czynienia z prawdziwymi pieniędzmi). + +### Krok 4: Dodaj ether z Faucet {#step-4-add-ether-from-a-faucet} + +Aby wdrożyć swój inteligentny kontrakt w sieci testowej, będziesz potrzebować trochę fałszywych ETH. Aby uzyskać ETH w sieci Goerli, przejdź do Faucet Goerli i wprowadź adres swojego konta Goerli. Należy pamiętać, że krany Goerli mogą być ostatnio nieco zawodne - sprawdź [stronę sieci testowych](/developers/docs/networks/#goerli), aby uzyskać listę opcji do wypróbowania: + +_Uwaga: ze względu na przeciążenie sieci może to trochę potrwać._ + +### Krok 5: Sprawdź swoje saldo {#step-5-check-your-balance} + +Aby dwukrotnie sprawdzić, czy ETH znajduje się w Twoim portfelu, wykonaj żądanie [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) za pomocą [narzędzia kompozytora Alchemy](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Zwróci to ilość ETH w naszym portfelu. Aby dowiedzieć się więcej, sprawdź [krótki samouczek Alchemy na temat korzystania z narzędzia kompozytora](https://youtu.be/r6sjRxBZJuU). + +Wprowadź swój adres konta MetaMask i kliknij **Wyślij żądanie**. Zobaczysz odpowiedź, która wygląda jak poniższy fragment kodu. + +```json +{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } +``` + +> _Uwaga: Ten wynik jest w wei, nie w ETH. Wei jest używane jako najmniejsza jednostka etheru._ + +Uff! Wszystkie nasze fałszywe pieniądze są na miejscu. + +### Krok 6: Zainicjuj nasz projekt {#step-6-initialize-our-project} + +Najpierw musimy utworzyć folder dla naszego projektu. Przejdź do wiersza poleceń i wprowadź następujące informacje. + +``` +mkdir hello-world +cd hello-world +``` + +Teraz, gdy jesteśmy w folderze naszego projektu, użyjemy `npm init`, aby zainicjować projekt. + +> Jeśli nie masz jeszcze zainstalowanego npm, postępuj zgodnie z [tymi instrukcjami, aby zainstalować Node.js i npm](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm). + +Dla celów tego samouczka nie ma znaczenia, jak odpowiesz na pytania inicjujące. Oto, jak zrobiliśmy to dla odniesienia: + +``` +package name: (hello-world) +version: (1.0.0) +description: hello world smart contract +entry point: (index.js) +test command: +git repository: +keywords: +author: +license: (ISC) + +About to write to /Users/.../.../.../hello-world/package.json: + +{ + "name": "hello-world", + "version": "1.0.0", + "description": "hello world smart contract", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} +``` + +Zatwierdź plik package.json i gotowe! + +### Krok 7: Pobierz Hardhat {#step-7-download-hardhat} + +Hardhat to środowisko programistyczne do kompilacji, wdrażania, testowania i debugowania oprogramowania Ethereum. Pomaga deweloperom w tworzeniu inteligentnych kontraktów i dapek lokalnie przed wdrożeniem ich na żywym łańcuchu. + +W naszym projekcie `hello-world` uruchom: + +``` +npm install --save-dev hardhat +``` + +Sprawdź tę stronę, aby uzyskać więcej szczegółów na temat [instrukcji instalacji](https://hardhat.org/getting-started/#overview). + +### Krok 8: Utwórz projekt Hardhat {#step-8-create-hardhat-project} + +W naszym folderze projektu `hello-world` uruchom: + +``` +npx hardhat +``` + +Powinieneś wtedy zobaczyć wiadomość powitalną i opcję wyboru tego, co chcesz zrobić. Wybierz „utwórz pusty hardhat.config.js”: + +``` +888 888 888 888 888 +888 888 888 888 888 +888 888 888 888 888 +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 +888 888 "88b 888P" d88" 888 888 "88b "88b 888 +888 888 .d888888 888 888 888 888 888 .d888888 888 +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 + +👷 Witamy w Hardhat v2.0.11 👷‍ + +Co chcesz zrobić? … +Utwórz przykładowy projekt +❯ Utwórz pusty plik hardhat.config.js +Zamknij +``` + +Spowoduje to wygenerowanie pliku `hardhat.config.js` w projekcie. Użyjemy tego później w samouczku, aby określić konfigurację naszego projektu. + +### Krok 9: Dodaj foldery projektu {#step-9-add-project-folders} + +Aby utrzymać porządek w projekcie, utwórzmy dwa nowe foldery. W wierszu poleceń przejdź do katalogu głównego projektu `hello-world` i wpisz: + +``` +mkdir contracts +mkdir scripts +``` + +- w `contracts/` będziemy przechowywać plik z kodem naszego inteligentnego kontraktu hello world +- w `scripts/` będziemy przechowywać skrypty do wdrażania naszego kontraktu i interakcji z nim + +### Krok 10: Napisz nasz kontrakt {#step-10-write-our-contract} + +Możesz zadać sobie pytanie, kiedy napiszemy kod? Nadszedł czas! + +Otwórz projekt hello-world w swoim ulubionym edytorze. Inteligentne kontrakty najczęściej pisane są w Solidity, którego użyjemy do napisania naszego inteligentnego kontraktu. + +1. Przejdź do folderu `contracts` i utwórz nowy plik o nazwie `HelloWorld.sol` +2. Poniżej znajduje się przykładowy inteligentny kontrakt Witaj Świecie, którego będziemy używać w tym samouczku. Skopiuj poniższą zawartość do pliku `HelloWorld.sol`. + +_Uwaga: Pamiętaj, aby przeczytać komentarze, aby zrozumieć, co robi ten kontrakt._ + +``` +// Określa wersję Solidity, używając semantycznego wersjonowania. +// Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity >=0.7.3; + +// Definiuje kontrakt o nazwie `HelloWorld`. +// Kontrakt jest zbiorem funkcji i danych (jego stanu). Po wdrożeniu kontrakt znajduje się pod określonym adresem w blockchainie Ethereum. Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + // Emitowane, gdy wywoływana jest funkcja aktualizacji + // Zdarzenia inteligentnych kontraktów to sposób, w jaki kontrakt komunikuje, że coś wydarzyło się na blockchainie do front-endu aplikacji, który może „nasłuchiwać” określonych zdarzeń i podejmować działania, gdy one wystąpią. + event UpdatedMessages(string oldStr, string newStr); + + // Deklaruje zmienną stanu `message` typu `string`. + // Zmienne stanu to zmienne, których wartości są trwale przechowywane w pamięci kontraktu. Słowo kluczowe `public` udostępnia zmienne spoza kontraktu i tworzy funkcję, którą inne kontrakty lub klienci mogą wywołać w celu uzyskania dostępu do wartości. + string public message; + + // Podobnie jak w wielu językach obiektowych opartych na klasach, konstruktor jest specjalną funkcją, która jest wykonywana tylko podczas tworzenia kontraktu. + // Konstruktory służą do inicjalizacji danych kontraktu. Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Akceptuje argument ciągu znaków `initMessage` i ustawia wartość w zmiennej pamięci kontraktu `message`). + message = initMessage; + } + + // Funkcja publiczna, która akceptuje argument w postaci ciągu znaków i aktualizuje zmienną pamięci masowej `message`. + function update(string memory newMessage) public { + string memory oldMsg = message; + message = newMessage; + emit UpdatedMessages(oldMsg, newMessage); + } +} +``` + +Jest to podstawowy inteligentny kontrakt, który przechowuje wiadomość po utworzeniu. Można go zaktualizować, wywołując funkcję `update`. + +### Krok 11: Podłącz MetaMask i Alchemy do swojego projektu {#step-11-connect-metamask-alchemy-to-your-project} + +Stworzyliśmy portfel MetaMask, konto Alchemy i napisaliśmy nasz inteligentny kontrakt, teraz nadszedł czas, aby połączyć te trzy elementy. + +Każda transakcja wysłana z Twojego portfela wymaga podpisu za pomocą Twojego unikalnego klucza prywatnego. Aby zapewnić naszemu programowi to uprawnienie, możemy bezpiecznie przechowywać nasz klucz prywatny w pliku środowiskowym. Będziemy tutaj również przechowywać klucz API dla Alchemy. + +> Aby dowiedzieć się więcej o wysyłaniu transakcji, sprawdź [ten samouczek](https://www.alchemy.com/docs/hello-world-smart-contract#step-11-connect-metamask--alchemy-to-your-project) na temat wysyłania transakcji za pomocą web3. + +Najpierw zainstaluj pakiet dotenv w katalogu swojego projektu: + +``` +npm install dotenv --save +``` + +Następnie utwórz plik `.env` w głównym katalogu projektu. Dodaj do niego swój klucz prywatny MetaMask i adres URL HTTP Alchemy API. + +Twój plik środowiskowy musi mieć nazwę `.env`, w przeciwnym razie nie zostanie rozpoznany jako plik środowiskowy. + +Nie nazywaj go `process.env` lub `.env-custom` ani w żaden inny sposób. + +- Postępuj zgodnie z [tymi instrukcjami](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key), aby wyeksportować swój klucz prywatny +- Poniżej dowiesz się, jak uzyskać adres URL interfejsu API HTTP Alchemy + +![](./get-alchemy-api-key.gif) + +Twój plik `.env` powinien wyglądać następująco: + +``` +API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +PRIVATE_KEY = "your-metamask-private-key" +``` + +Aby faktycznie połączyć je z naszym kodem, odwołamy się do tych zmiennych w naszym pliku `hardhat.config.js` w kroku 13. + +### Krok 12: Zainstaluj Ethers.js {#step-12-install-ethersjs} + +Ethers.js to biblioteka, która ułatwia interakcję i wysyłanie żądań do Ethereum poprzez opakowanie [standardowych metod JSON-RPC](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc) w bardziej przyjazne dla użytkownika metody. + +Hardhat pozwala nam na integrację [wtyczek](https://hardhat.org/plugins/) w celu uzyskania dodatkowych narzędzi i rozszerzonej funkcjonalności. Skorzystamy z [wtyczki Ethers](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) do wdrażania kontraktów. + +W katalogu projektu wpisz: + +```bash +npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" +``` + +### Krok 13: Zaktualizuj hardhat.config.js {#step-13-update-hardhat-configjs} + +Do tej pory dodaliśmy kilka zależności i wtyczek, teraz musimy zaktualizować `hardhat.config.js`, aby nasz projekt wiedział o wszystkich. + +Zaktualizuj swój `hardhat.config.js`, aby wyglądał następująco: + +```javascript +/** + * @type import('hardhat/config').HardhatUserConfig + */ + +require("dotenv").config() +require("@nomiclabs/hardhat-ethers") + +const { API_URL, PRIVATE_KEY } = process.env + +module.exports = { + solidity: "0.7.3", + defaultNetwork: "goerli", + networks: { + hardhat: {}, + goerli: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, +} +``` + +### Krok 14: Skompiluj nasz kontrakt {#step-14-compile-our-contract} + +Aby upewnić się, że wszystko do tej pory działa, skompilujmy nasz kontrakt. Zadanie `compile` jest jednym z wbudowanych zadań hardhat. + +Z wiersza poleceń uruchom: + +```bash +npx hardhat compile +``` + +Możesz otrzymać ostrzeżenie o `SPDX license identifier not provided in source file`, ale nie musisz się tym martwić - miejmy nadzieję, że wszystko inne wygląda dobrze! Jeśli nie, zawsze możesz napisać wiadomość na [discordzie Alchemy](https://discord.gg/u72VCg3). + +### Krok 15: Napisz nasz skrypt wdrożeniowy {#step-15-write-our-deploy-script} + +Teraz, gdy nasz kontrakt jest napisany, a nasz plik konfiguracyjny jest gotowy, nadszedł czas, aby napisać nasz skrypt wdrażający kontrakt. + +Przejdź do folderu `scripts/` i utwórz nowy plik o nazwie `deploy.js`, dodając do niego następującą zawartość: + +```javascript +async function main() { + const HelloWorld = await ethers.getContractFactory("HelloWorld") + + // Rozpocznij wdrażanie, zwracając obietnicę, która rozwiązuje się do obiektu kontraktu + const hello_world = await HelloWorld.deploy("Hello World!") + console.log("Contract deployed to address:", hello_world.address) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) +``` + +Hardhat wykonuje niesamowitą robotę, wyjaśniając, co robi każda z tych linii kodu w swoim [samouczku dotyczącym kontraktów](https://hardhat.org/tutorial/testing-contracts.html#writing-tests), a my przyjęliśmy ich wyjaśnienia tutaj. + +```javascript +const HelloWorld = await ethers.getContractFactory("HelloWorld") +``` + +`ContractFactory` w ethers.js jest abstrakcją używaną do wdrażania nowych inteligentnych kontraktów, więc `HelloWorld` jest tutaj [fabryką](https://en.wikipedia.org/wiki/Factory_\(object-oriented_programming\)) dla instancji naszego kontraktu witaj świecie. Podczas korzystania z wtyczki `hardhat-ethers`, instancje `ContractFactory` i `Contract` są domyślnie połączone z pierwszym sygnatariuszem (właścicielem). + +```javascript +const hello_world = await HelloWorld.deploy() +``` + +Wywołanie `deploy()` w `ContractFactory` rozpocznie wdrażanie i zwróci `Promise`, które rozwiąże się do obiektu `Contract`. Jest to obiekt, który ma metodę dla każdej z naszych funkcji inteligentnego kontraktu. + +### Krok 16: Wdróż nasz kontrakt {#step-16-deploy-our-contract} + +Jesteśmy wreszcie gotowi do wdrożenia naszego inteligentnego kontraktu! Przejdź do wiersza poleceń i uruchom: + +```bash +npx hardhat run scripts/deploy.js --network goerli +``` + +Powinieneś wtedy zobaczyć coś takiego: + +```bash +Kontrakt wdrożony pod adresem: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +``` + +**Proszę zapisać ten adres**. Będziemy go używać w dalszej części samouczka. + +Jeśli przejdziemy do [Goerli etherscan](https://goerli.etherscan.io) i wyszukamy adres naszego kontraktu, powinniśmy zobaczyć, że został on pomyślnie wdrożony. Transakcja będzie wyglądać mniej więcej tak: + +![](./etherscan-contract.png) + +Adres `From` powinien pasować do adresu konta MetaMask, a adres `To` będzie zawierał informację **Contract Creation**. Jeśli klikniemy w transakcję, zobaczymy adres naszego kontraktu w polu `Do`. + +![](./etherscan-transaction.png) + +Gratulacje! Właśnie wdrożyłeś inteligentny kontrakt w sieci testowej Ethereum. + +Aby zrozumieć, co dzieje się pod maską, przejdź do zakładki Eksplorator w naszym [pulpicie nawigacyjnym Alchemy](https://dashboard.alchemy.com/explorer). Jeśli masz wiele aplikacji Alchemy, upewnij się, że filtrujesz według aplikacji i wybierz **Hello World**. + +![](./hello-world-explorer.png) + +Tutaj zobaczysz kilka metod JSON-RPC, które Hardhat/Ethers stworzył dla nas pod maską, gdy wywołaliśmy funkcję `.deploy()`. Dwie ważne metody to [`eth_sendRawTransaction`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_sendrawtransaction), czyli żądanie zapisu naszego kontraktu w łańcuchu Goerli, oraz [`eth_getTransactionByHash`](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_gettransactionbyhash), czyli żądanie odczytania informacji o naszej transakcji na podstawie jej hasza. Aby dowiedzieć się więcej o wysyłaniu transakcji, sprawdź [nasz samouczek na temat wysyłania transakcji za pomocą Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/). + +## Część 2: Interakcja z Twoim inteligentnym kontraktem {#part-2-interact-with-your-smart-contract} + +Teraz, gdy pomyślnie wdrożyliśmy inteligentny kontrakt w sieci Goerli, nauczmy się, jak wchodzić z nim w interakcję. + +### Utwórz plik interact.js {#create-a-interactjs-file} + +To jest plik, w którym napiszemy nasz skrypt interakcji. Będziemy używać biblioteki Ethers.js, którą zainstalowałeś wcześniej w części 1. + +W folderze `scripts/` utwórz nowy plik o nazwie `interact.js` i dodaj następujący kod: + +```javascript +// interact.js + +const API_KEY = process.env.API_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS +``` + +### Zaktualizuj swój plik .env {#update-your-env-file} + +Będziemy używać nowych zmiennych środowiskowych, więc musimy je zdefiniować w pliku `.env`, który [utworzyliśmy wcześniej](#step-11-connect-metamask-&-alchemy-to-your-project). + +Będziemy musieli dodać definicję dla naszego `API_KEY` Alchemy i `CONTRACT_ADDRESS`, gdzie został wdrożony nasz inteligentny kontrakt. + +Plik `.env` powinien wyglądać następująco: + +```bash +# .env + +API_URL = "https://eth-goerli.alchemyapi.io/v2/" +API_KEY = "" +PRIVATE_KEY = "" +CONTRACT_ADDRESS = "0x" +``` + +### Pobierz swoje ABI kontraktu {#grab-your-contract-ABI} + +Nasze [ABI (Application Binary Interface)](/glossary/#abi) kontraktu to interfejs do interakcji z naszym inteligentnym kontraktem. Hardhat automatycznie generuje ABI i zapisuje go w `HelloWorld.json`. Aby użyć ABI, będziemy musieli przeanalizować zawartość, dodając następujące wiersze kodu do naszego pliku `interact.js`: + +```javascript +// interact.js +const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") +``` + +Jeśli chcesz zobaczyć ABI, możesz je wydrukować w swojej konsoli: + +```javascript +console.log(JSON.stringify(contract.abi)) +``` + +Aby zobaczyć swoje ABI wydrukowane na konsoli, przejdź do terminala i uruchom: + +```bash +npx hardhat run scripts/interact.js +``` + +### Utwórz instancję swojego kontraktu {#create-an-instance-of-your-contract} + +Aby wejść w interakcję z naszym kontraktem, musimy stworzyć jego instancję w naszym kodzie. Aby to zrobić za pomocą Ethers.js, będziemy musieli pracować z trzema koncepcjami: + +1. Dostawca - dostawca węzła, który daje Ci dostęp do odczytu i zapisu do blockchaina +2. Sygnatariusz - reprezentuje konto Ethereum, które może podpisywać transakcje +3. Kontrakt - obiekt Ethers.js reprezentujący określony kontrakt wdrożony w łańcuchu + +Użyjemy ABI kontraktu z poprzedniego kroku, aby stworzyć instancję naszego kontraktu: + +```javascript +// interact.js + +// Dostawca +const alchemyProvider = new ethers.providers.AlchemyProvider( + (network = "goerli"), + API_KEY +) + +// Sygnatariusz +const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) + +// Kontrakt +const helloWorldContract = new ethers.Contract( + CONTRACT_ADDRESS, + contract.abi, + signer +) +``` + +Dowiedz się więcej o Dostawcach, Sygnatariuszach i Kontraktach w [dokumentacji ethers.js](https://docs.ethers.io/v5/). + +### Przeczytaj wiadomość init {#read-the-init-message} + +Pamiętasz, jak wdrożyliśmy nasz kontrakt z `initMessage = "Hello world!"`? Teraz odczytamy tę wiadomość zapisaną w naszym inteligentnym kontrakcie i wydrukujemy ją na konsoli. + +W języku JavaScript funkcje asynchroniczne są używane podczas interakcji z sieciami. Aby dowiedzieć się więcej o funkcjach asynchronicznych, [przeczytaj ten artykuł](https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff). + +Użyj poniższego kodu, aby wywołać funkcję `message` w naszym inteligentnym kontrakcie i odczytać wiadomość init: + +```javascript +// interact.js + +// ... + +asynchroniczna funkcja main() { + const message = await helloWorldContract.message() + console.log("Wiadomość to: " + message) +} +main() +``` + +Po uruchomieniu pliku za pomocą `npx hardhat run scripts/interact.js` w terminalu powinniśmy zobaczyć taką odpowiedź: + +``` +Wiadomość to: Witaj świecie! +``` + +Gratulacje! Właśnie pomyślnie odczytałeś dane inteligentnego kontraktu z blockchaina Ethereum, brawo! + +### Zaktualizuj wiadomość {#update-the-message} + +Zamiast tylko odczytywać wiadomość, możemy również zaktualizować wiadomość zapisaną w naszym inteligentnym kontrakcie za pomocą funkcji `update`! Całkiem fajne, prawda? + +Aby zaktualizować wiadomość, możemy bezpośrednio wywołać funkcję `update` na naszym obiekcie Contract: + +```javascript +// interact.js + +// ... + +asynchroniczna funkcja main() { + const message = await helloWorldContract.message() + console.log("Wiadomość to: " + message) + + console.log("Aktualizowanie wiadomości...") + const tx = await helloWorldContract.update("To jest nowa wiadomość.") + await tx.wait() +} +main() +``` + +Zauważ, że w linii 11, wywołujemy `.wait()` na zwróconym obiekcie transakcji. To zapewnia, że nasz skrypt czeka na wydobycie transakcji na blockchainie przed zakończeniem funkcji. Jeśli wywołanie `.wait()` nie zostanie uwzględnione, skrypt może nie zobaczyć zaktualizowanej wartości `message` w kontrakcie. + +### Przeczytaj nową wiadomość {#read-the-new-message} + +Powinieneś być w stanie powtórzyć [poprzedni krok](#read-the-init-message), aby odczytać zaktualizowaną wartość `message`. Poświęć chwilę i sprawdź, czy możesz dokonać niezbędnych zmian, aby wydrukować tę nową wartość! + +Jeśli potrzebujesz podpowiedzi, oto jak powinien wyglądać twój plik `interact.js` w tym momencie: + +```javascript +// interact.js + +const API_KEY = process.env.API_KEY +const PRIVATE_KEY = process.env.PRIVATE_KEY +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS + +const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json") + +// dostawca - Alchemy +const alchemyProvider = new ethers.providers.AlchemyProvider( + (network = "goerli"), + API_KEY +) + +// podpisujący - ty +const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider) + +// instancja kontraktu +const helloWorldContract = new ethers.Contract( + CONTRACT_ADDRESS, + contract.abi, + signer +) + +asynchroniczna funkcja main() { + const message = await helloWorldContract.message() + console.log("Wiadomość to: " + message) + + console.log("Aktualizowanie wiadomości...") + const tx = await helloWorldContract.update("this is the new message") + await tx.wait() + + const newMessage = await helloWorldContract.message() + console.log("Nowa wiadomość to: " + newMessage) +} + +main() +``` + +Teraz po prostu uruchom skrypt, a powinieneś zobaczyć starą wiadomość, status aktualizacji i nową wiadomość wydrukowaną w terminalu! + +`npx hardhat run scripts/interact.js --network goerli` + +``` +Wiadomość to: Witaj świecie! +Aktualizowanie wiadomości... +Nowa wiadomość to: To jest nowa wiadomość. +``` + +Podczas uruchamiania tego skryptu możesz zauważyć, że krok `Aktualizowanie wiadomości...` zajmuje chwilę, zanim załaduje się nowa wiadomość. Jest to spowodowane procesem wydobycia. Jeśli jesteś ciekaw śledzenia transakcji podczas ich wydobywania, odwiedź [mempool Alchemy](https://dashboard.alchemyapi.io/mempool), aby zobaczyć status transakcji. Jeśli transakcja zostanie odrzucona, warto również sprawdzić [Goerli Etherscan](https://goerli.etherscan.io) i wyszukać swój hasz transakcji. + +## Część 3: Opublikuj swój inteligentny kontrakt w Etherscan {#part-3-publish-your-smart-contract-to-etherscan} + +Wykonałeś całą ciężką pracę, aby ożywić swój inteligentny kontrakt, teraz nadszedł czas, aby podzielić się nim ze światem! + +Weryfikując swój inteligentny kontrakt w Etherscan, każdy może zobaczyć jego kod źródłowy i wejść z nim w interakcję. Zaczynajmy! + +### Krok 1: Wygeneruj klucz API na swoim koncie Etherscan {#step-1-generate-an-api-key-on-your-etherscan-account} + +Klucz API Etherscan jest niezbędny do zweryfikowania, czy jesteś właścicielem inteligentnego kontraktu, który próbujesz opublikować. + +Jeśli nie masz jeszcze konta Etherscan, [załóż konto](https://etherscan.io/register). + +Po zalogowaniu znajdź swoją nazwę użytkownika w pasku nawigacyjnym, najedź na nią i wybierz przycisk **Mój profil**. + +Na stronie profilu powinieneś zobaczyć boczny pasek nawigacyjny. Z bocznego paska nawigacyjnego wybierz **Klucze API**. Następnie naciśnij przycisk „Dodaj”, aby utworzyć nowy klucz API, nazwij swoją aplikację **hello-world** i naciśnij przycisk **Utwórz nowy klucz API**. + +Nowy klucz API powinien pojawić się w tabeli kluczy API. Skopiuj klucz API do schowka. + +Następnie musimy dodać klucz API Etherscan do naszego pliku `.env`. + +Po dodaniu, twój plik `.env` powinien wyglądać tak: + +```javascript +API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key" +PUBLIC_KEY = "your-public-account-address" +PRIVATE_KEY = "your-private-account-address" +CONTRACT_ADDRESS = "your-contract-address" +ETHERSCAN_API_KEY = "your-etherscan-key" +``` + +### Inteligentne kontrakty wdrożone za pomocą Hardhat {#hardhat-deployed-smart-contracts} + +#### Zainstaluj hardhat-etherscan {#install-hardhat-etherscan} + +Publikowanie kontraktu w Etherscan za pomocą Hardhat jest proste. Na początek musisz zainstalować wtyczkę `hardhat-etherscan`. `hardhat-etherscan` automatycznie zweryfikuje kod źródłowy inteligentnego kontraktu i ABI w Etherscan. Aby to dodać, w katalogu `hello-world` uruchom: + +```text +npm install --save-dev @nomiclabs/hardhat-etherscan +``` + +Po zainstalowaniu, dołącz następującą instrukcję na początku pliku `hardhat.config.js` i dodaj opcje konfiguracyjne Etherscan: + +```javascript +// hardhat.config.js + +require("dotenv").config() +require("@nomiclabs/hardhat-ethers") +require("@nomiclabs/hardhat-etherscan") + +const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env + +module.exports = { + solidity: "0.7.3", + defaultNetwork: "goerli", + networks: { + hardhat: {}, + goerli: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, + etherscan: { + // Twój klucz API dla Etherscan + // Uzyskaj go na https://etherscan.io/ + apiKey: ETHERSCAN_API_KEY, + }, +} +``` + +#### Zweryfikuj swój inteligentny kontrakt na Etherscan {#verify-your-smart-contract-on-etherscan} + +Upewnij się, że wszystkie pliki są zapisane, a wszystkie zmienne `.env` są poprawnie skonfigurowane. + +Uruchom zadanie `verify`, podając adres kontraktu i sieć, w której jest wdrożony: + +```text +npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!' +``` + +Upewnij się, że `DEPLOYED_CONTRACT_ADDRESS` to adres Twojego wdrożonego inteligentnego kontraktu w sieci testowej Goerli. Ponadto ostatni argument (`'Hello World!'`) musi być tą samą wartością ciągu znaków, która została użyta [podczas kroku wdrażania w części 1](#write-our-deploy-script). + +Jeśli wszystko pójdzie dobrze, zobaczysz następujący komunikat w terminalu: + +```text +Pomyślnie przesłano kod źródłowy dla kontraktu +contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address +do weryfikacji na Etherscan. Oczekiwanie na wynik weryfikacji... + + +Pomyślnie zweryfikowano kontrakt HelloWorld na Etherscan. +https://goerli.etherscan.io/address/#contracts +``` + +Gratulacje! Twój kod inteligentnego kontraktu jest w Etherscan! + +### Sprawdź swój inteligentny kontrakt w Etherscan! {#check-out-your-smart-contract-on-etherscan} + +Po przejściu do linku podanego w terminalu, powinieneś być w stanie zobaczyć kod inteligentnego kontraktu i ABI opublikowane na Etherscan! + +**Hura - udało Ci się, mistrzu! Teraz każdy może wywołać lub zapisać do Twojego inteligentnego kontraktu! Nie możemy się doczekać, co zbudujesz dalej!** + +## Część 4 – Integracja inteligentnego kontraktu z frontendem {#part-4-integrating-your-smart-contract-with-the-frontend} + +Po ukończeniu tego samouczka dowiesz się, jak: + +- Połącz portfel MetaMask z Twoją dappką +- Odczytaj dane ze swojego inteligentnego kontraktu za pomocą interfejsu API [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) +- Podpisz transakcje Ethereum za pomocą MetaMask + +Dla tej dapki użyjemy [React](https://react.dev/) jako naszego frameworka frontendowego. Należy jednak zauważyć, że nie będziemy spędzać dużo czasu na omawianiu jego podstaw, ponieważ skupimy się głównie na wprowadzaniu funkcjonalności Web3 do naszego projektu. + +Jako warunek wstępny, powinieneś mieć podstawową wiedzę na temat React. Jeśli nie, polecamy ukończenie oficjalnego [samouczka Wprowadzenie do React](https://react.dev/learn). + +### Klonowanie plików startowych {#clone-the-starter-files} + +Najpierw przejdź do [repozytorium GitHub hello-world-part-four](https://github.com/alchemyplatform/hello-world-part-four-tutorial), aby pobrać pliki startowe dla tego projektu i sklonować to repozytorium na swój komputer lokalny. + +Otwórz sklonowane repozytorium lokalnie. Zauważ, że zawiera on dwa foldery: `starter-files` i `completed`. + +- `starter-files`- **będziemy pracować w tym katalogu**, połączymy interfejs użytkownika z Twoim portfelem Ethereum i inteligentnym kontraktem, który opublikowaliśmy w Etherscan w [części 3](#part-3). +- `completed` zawiera cały ukończony samouczek i powinien być używany tylko jako odniesienie, jeśli utkniesz. + +Następnie otwórz swoją kopię `starter-files` w ulubionym edytorze kodu, a następnie przejdź do folderu `src`. + +Cały kod, który napiszemy, będzie znajdować się w folderze `src`. Będziemy edytować komponent `HelloWorld.js` i pliki JavaScript `util/interact.js`, aby nadać naszemu projektowi funkcjonalność Web3. + +### Sprawdź pliki startowe {#check-out-the-starter-files} + +Zanim zaczniemy kodować, przeanalizujmy, co jest dla nas dostępne w plikach startowych. + +#### Uruchomienie projektu React {#get-your-react-project-running} + +Zacznijmy od uruchomienia projektu React w naszej przeglądarce. Piękno React polega na tym, że gdy nasz projekt jest już uruchomiony w przeglądarce, wszelkie zapisane przez nas zmiany będą na bieżąco aktualizowane w przeglądarce. + +Aby uruchomić projekt, przejdź do katalogu głównego folderu `starter-files` i uruchom `npm install` w terminalu, aby zainstalować zależności projektu: + +```bash +cd starter-files +npm install +``` + +Po zakończeniu instalacji uruchom `npm start` w terminalu: + +```bash +npm start +``` + +Spowoduje to otwarcie [http://localhost:3000/](http://localhost:3000/) w przeglądarce, gdzie zobaczysz frontend naszego projektu. Powinien on składać się z jednego pola (miejsce do aktualizacji wiadomości przechowywanej w inteligentnym kontrakcie), przycisku „Połącz portfel” i przycisku „Aktualizuj”. + +Jeśli spróbujesz kliknąć którykolwiek z przycisków, zauważysz, że nie działają – to dlatego, że wciąż musimy zaprogramować ich funkcjonalność. + +#### Komponent `HelloWorld.js` {#the-helloworld-js-component} + +Wróćmy do folderu `src` w naszym edytorze i otwórzmy plik `HelloWorld.js`. Jest bardzo ważne, abyśmy zrozumieli wszystko w tym pliku, ponieważ jest to główny komponent React, nad którym będziemy pracować. + +Na górze tego pliku zauważysz, że mamy kilka instrukcji importu, które są niezbędne do uruchomienia naszego projektu, w tym bibliotekę React, hooki useEffect i useState, niektóre elementy z `./util/interact.js` (opiszemy je bardziej szczegółowo wkrótce!) i logo Alchemy. + +```javascript +// HelloWorld.js + +import React from "react" +import { useEffect, useState } from "react" +import { + helloWorldContract, + connectWallet, + updateMessage, + loadCurrentMessage, + getCurrentWalletConnected, +} from "./util/interact.js" + +import alchemylogo from "./alchemylogo.svg" +``` + +Następnie mamy nasze zmienne stanu, które będziemy aktualizować po określonych zdarzeniach. + +```javascript +// HelloWorld.js + +// Zmienne stanu +const [walletAddress, setWallet] = useState("") +const [status, setStatus] = useState("") +const [message, setMessage] = useState("Brak połączenia z siecią.") +const [newMessage, setNewMessage] = useState("") +``` + +Oto, co reprezentuje każda ze zmiennych: + +- `walletAddress` - ciąg znaków, który przechowuje adres portfela użytkownika +- `status` - ciąg znaków, który przechowuje pomocną wiadomość, która prowadzi użytkownika, jak wchodzić w interakcję z dapp +- `message` - ciąg znaków, który przechowuje bieżącą wiadomość w inteligentnym kontrakcie +- `newMessage` - ciąg znaków, który przechowuje nową wiadomość, która zostanie zapisana w inteligentnym kontrakcie + +Po zmiennych stanu zobaczysz pięć niezaimplementowanych funkcji: `useEffect`, `addSmartContractListener`, `addWalletListener`, `connectWalletPressed` i `onUpdatePressed`. Poniżej wyjaśnimy, co robią: + +```javascript +// HelloWorld.js + +// wywoływane tylko raz +useEffect(async () => { + // TODO: zaimplementuj +}, []) + +function addSmartContractListener() { + // TODO: zaimplementuj +} + +function addWalletListener() { + // TODO: zaimplementuj +} + +const connectWalletPressed = async () => { + // TODO: zaimplementuj +} + +const onUpdatePressed = async () => { + // TODO: zaimplementuj +} +``` + +- [`useEffect`](https://legacy.reactjs.org/docs/hooks-effect.html) - to hook React, który jest wywoływany po wyrenderowaniu komponentu. Ponieważ ma przekazaną pustą tablicę `[]` jako właściwość (patrz linia 4), będzie wywoływany tylko podczas _pierwszego_ renderowania komponentu. Tutaj załadujemy bieżącą wiadomość zapisaną w naszym inteligentnym kontrakcie, wywołamy nasze inteligentne kontrakty i nasłuchiwacze portfela, a także zaktualizujemy nasz interfejs użytkownika, aby odzwierciedlić, czy portfel jest już podłączony. +- `addSmartContractListener` - ta funkcja konfiguruje nasłuchiwacza, który będzie obserwował zdarzenie `UpdatedMessages` naszego kontraktu HelloWorld i aktualizował nasz interfejs użytkownika, gdy wiadomość w naszym inteligentnym kontrakcie ulegnie zmianie. +- `addWalletListener` - ta funkcja konfiguruje nasłuchiwacza, który wykrywa zmiany w stanie portfela MetaMask użytkownika, np. gdy użytkownik odłącza portfel lub zmienia adresy. +- `connectWalletPressed` - ta funkcja zostanie wywołana w celu połączenia portfela MetaMask użytkownika z naszą dapp. +- `onUpdatePressed` - ta funkcja zostanie wywołana, gdy użytkownik będzie chciał zaktualizować wiadomość zapisaną w inteligentnym kontrakcie. + +Pod koniec tego pliku mamy interfejs użytkownika naszego komponentu. + +```javascript +// HelloWorld.js + +// interfejs użytkownika naszego komponentu +return ( +
+ + + +

Bieżąca wiadomość:

+

{message}

+ +

Nowa wiadomość:

+ +
+ setNewMessage(e.target.value)} + value={newMessage} + /> +

{status}

+ + +
+ +
+) +``` + +Jeśli dokładnie przeanalizujesz ten kod, zauważysz, gdzie używamy naszych różnych zmiennych stanu w naszym interfejsie użytkownika: + +- W liniach 6-12, jeśli portfel użytkownika jest podłączony (tj. `walletAddress.length > 0`), wyświetlamy skróconą wersję `walletAddress` użytkownika w przycisku o identyfikatorze „walletButton”; w przeciwnym razie jest tam po prostu napis „Połącz portfel”. +- W linii 17 wyświetlamy bieżącą wiadomość zapisaną w inteligentnym kontrakcie, która jest przechwytywana w ciągu znaków `message`. +- W liniach 23-26 używamy [komponentu kontrolowanego](https://legacy.reactjs.org/docs/forms.html#controlled-components), aby zaktualizować naszą zmienną stanu `newMessage`, gdy zmienia się wpis w polu tekstowym. + +Oprócz naszych zmiennych stanu, zobaczysz również, że funkcje `connectWalletPressed` i `onUpdatePressed` są wywoływane, gdy odpowiednio klikane są przyciski o identyfikatorach `publishButton` i `walletButton`. + +Na koniec zajmijmy się tym, gdzie ten komponent `HelloWorld.js` jest dodawany. + +Jeśli przejdziesz do pliku `App.js`, który jest głównym komponentem w React, który działa jako kontener dla wszystkich innych komponentów, zobaczysz, że nasz komponent `HelloWorld.js` jest wstrzykiwany w linii 7. + +Na koniec sprawdźmy jeszcze jeden plik, który został dla Ciebie przygotowany, plik `interact.js`. + +#### Plik `interact.js` {#the-interact-js-file} + +Ponieważ chcemy trzymać się paradygmatu [M-V-C](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), będziemy chcieli mieć osobny plik, który zawiera wszystkie nasze funkcje do zarządzania logiką, danymi i regułami naszej dapki, a następnie będziemy mogli wyeksportować te funkcje do naszego frontendu (naszego komponentu `HelloWorld.js`). + +👆🏽 To jest dokładny cel naszego pliku `interact.js`! + +Przejdź do folderu `util` w swoim katalogu `src`, a zauważysz, że dołączyliśmy plik o nazwie `interact.js`, który będzie zawierał wszystkie nasze funkcje i zmienne interakcji z inteligentnym kontraktem i portfelem. + +```javascript +// interact.js + +//export const helloWorldContract; + +export const loadCurrentMessage = async () => {} + +export const connectWallet = async () => {} + +const getCurrentWalletConnected = async () => {} + +export const updateMessage = async (message) => {} +``` + +Na początku pliku zauważysz, że skomentowaliśmy obiekt `helloWorldContract`. Później w tym samouczku odkomentujemy ten obiekt i utworzymy instancję naszego inteligentnego kontraktu w tej zmiennej, którą następnie wyeksportujemy do naszego komponentu `HelloWorld.js`. + +Cztery niezaimplementowane funkcje po naszym obiekcie `helloWorldContract` robią następujące rzeczy: + +- `loadCurrentMessage` - ta funkcja obsługuje logikę ładowania bieżącej wiadomości zapisanej w inteligentnym kontrakcie. Wywoła ona żądanie _odczytu_ do inteligentnego kontraktu Witaj Świecie za pomocą [interfejsu API Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3). +- `connectWallet` - ta funkcja połączy MetaMask użytkownika z naszą dappką. +- `getCurrentWalletConnected` - ta funkcja sprawdzi, czy konto Ethereum jest już połączone z naszą dappką podczas ładowania strony i odpowiednio zaktualizuje nasz interfejs użytkownika. +- `updateMessage` - ta funkcja zaktualizuje wiadomość zapisaną w inteligentnym kontrakcie. Wykona ona żądanie _zapisu_ do inteligentnego kontraktu Witaj Świecie, więc portfel MetaMask użytkownika będzie musiał podpisać transakcję Ethereum, aby zaktualizować wiadomość. + +Teraz, gdy rozumiemy, z czym pracujemy, dowiedzmy się, jak czytać z naszego inteligentnego kontraktu! + +### Krok 3: Odczyt z Twojego inteligentnego kontraktu {#step-3-read-from-your-smart-contract} + +Aby odczytać dane z inteligentnego kontraktu, musisz pomyślnie skonfigurować: + +- Połączenie API z łańcuchem Ethereum +- Załadowana instancja Twojego inteligentnego kontraktu +- Funkcja do wywołania funkcji inteligentnego kontraktu +- Nasłuchiwacz do obserwowania aktualizacji, gdy zmieniają się dane, które odczytujesz z inteligentnego kontraktu + +To może brzmieć jak wiele kroków, ale nie martw się! Przeprowadzimy Cię przez każdy z nich krok po kroku! :\) + +#### Ustanów połączenie API z łańcuchem Ethereum {#establish-an-api-connection-to-the-ethereum-chain} + +Pamiętasz, jak w części 2 tego samouczka użyliśmy naszego klucza Alchemy Web3 do odczytu z naszego inteligentnego kontraktu ([https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract/interacting-with-a-smart-contract#step-1-install-web3-library))? Będziesz również potrzebować klucza Alchemy Web3 w swojej dapce, aby czytać z łańcucha. + +Jeśli go jeszcze nie masz, najpierw zainstaluj [Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3), przechodząc do katalogu głównego `starter-files` i uruchamiając następujące polecenie w terminalu: + +```text +npm install @alch/alchemy-web3 +``` + +[Alchemy Web3](https://github.com/alchemyplatform/alchemy-web3) to nakładka na [Web3.js](https://docs.web3js.org/), zapewniająca ulepszone metody API i inne kluczowe korzyści, które ułatwiają życie dewelopera web3. Został zaprojektowany tak, aby wymagał minimalnej konfiguracji, dzięki czemu możesz od razu zacząć go używać w swojej aplikacji! + +Następnie zainstaluj pakiet [dotenv](https://www.npmjs.com/package/dotenv) w katalogu projektu, abyśmy mieli bezpieczne miejsce do przechowywania naszego klucza API po jego pobraniu. + +```text +npm install dotenv --save +``` + +Dla naszej dapki **będziemy używać naszego klucza API Websockets** zamiast klucza API HTTP, ponieważ pozwoli nam to skonfigurować nasłuchiwacza, który wykrywa, kiedy zmienia się wiadomość zapisana w inteligentnym kontrakcie. + +Po uzyskaniu klucza API, utwórz plik `.env` w swoim katalogu głównym i dodaj do niego adres URL Alchemy Websockets. Następnie plik `.env` powinien wyglądać następująco: + +```javascript +REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/ +``` + +Teraz jesteśmy gotowi do skonfigurowania naszego punktu końcowego Alchemy Web3 w naszej dapce! Wróćmy do naszego pliku `interact.js`, który jest zagnieżdżony w naszym folderze `util` i dodajmy następujący kod na początku pliku: + +```javascript +// interact.js + +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +//export const helloWorldContract; +``` + +Powyżej najpierw zaimportowaliśmy klucz Alchemy z naszego pliku `.env`, a następnie przekazaliśmy nasz `alchemyKey` do `createAlchemyWeb3`, aby ustanowić nasz punkt końcowy Alchemy Web3. + +Gdy ten punkt końcowy jest gotowy, nadszedł czas, aby załadować nasz inteligentny kontrakt! + +#### Ładowanie Twojego inteligentnego kontraktu Witaj Świecie {#loading-your-hello-world-smart-contract} + +Aby załadować swój inteligentny kontrakt Witaj Świecie, będziesz potrzebować jego adresu kontraktu i ABI, które można znaleźć w Etherscan, jeśli ukończyłeś [część 3 tego samouczka](/developers/tutorials/hello-world-smart-contract-fullstack/#part-3-publish-your-smart-contract-to-etherscan-part-3-publish-your-smart-contract-to-etherscan). + +#### Jak uzyskać ABI kontraktu z Etherscan {#how-to-get-your-contract-abi-from-etherscan} + +Jeśli pominąłeś część 3 tego samouczka, możesz użyć kontraktu HelloWorld z adresem [0x6f3f635A9762B47954229Ea479b4541eAF402A6A](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). Jego ABI można znaleźć [tutaj](https://goerli.etherscan.io/address/0x6f3f635a9762b47954229ea479b4541eaf402a6a#code). + +ABI kontraktu jest niezbędne do określenia, którą funkcję kontrakt wywoła, a także do zapewnienia, że funkcja zwróci dane w oczekiwanym formacie. Po skopiowaniu naszego ABI kontraktu, zapiszmy go jako plik JSON o nazwie `contract-abi.json` w swoim katalogu `src`. + +Twój plik contract-abi.json powinien być przechowywany w folderze src. + +Mając do dyspozycji adres kontraktu, ABI i punkt końcowy Alchemy Web3, możemy użyć [metody kontraktu](https://docs.web3js.org/api/web3-eth-contract/class/Contract), aby załadować instancję naszego inteligentnego kontraktu. Zaimportuj ABI kontraktu do pliku `interact.js` i dodaj adres kontraktu. + +```javascript +// interact.js + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" +``` + +Możemy teraz wreszcie odkomentować naszą zmienną `helloWorldContract` i załadować inteligentny kontrakt za pomocą naszego punktu końcowego AlchemyWeb3: + +```javascript +// interact.js +export const helloWorldContract = new web3.eth.Contract( + contractABI, + contractAddress +) +``` + +Podsumowując, pierwsze 12 linii pliku `interact.js` powinno teraz wyglądać tak: + +```javascript +// interact.js + +require("dotenv").config() +const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY +const { createAlchemyWeb3 } = require("@alch/alchemy-web3") +const web3 = createAlchemyWeb3(alchemyKey) + +const contractABI = require("../contract-abi.json") +const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A" + +export const helloWorldContract = new web3.eth.Contract( + contractABI, + contractAddress +) +``` + +Teraz, gdy nasz kontrakt jest załadowany, możemy zaimplementować naszą funkcję `loadCurrentMessage`! + +#### Implementacja `loadCurrentMessage` w pliku `interact.js` {#implementing-loadCurrentMessage-in-your-interact-js-file} + +Ta funkcja jest super prosta. Zrobimy proste asynchroniczne wywołanie web3, aby odczytać z naszego kontraktu. Nasza funkcja zwróci wiadomość zapisaną w inteligentnym kontrakcie: + +Zaktualizuj `loadCurrentMessage` w swoim pliku `interact.js` do następującej postaci: + +```javascript +// interact.js + +export const loadCurrentMessage = async () => { + const message = await helloWorldContract.methods.message().call() + return message +} +``` + +Ponieważ chcemy wyświetlić ten inteligentny kontrakt w naszym interfejsie użytkownika, zaktualizujmy funkcję `useEffect` w naszym komponencie `HelloWorld.js` do następującej postaci: + +```javascript +// HelloWorld.js + +// wywoływane tylko raz +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) +}, []) +``` + +Zauważ, że chcemy, aby nasza funkcja `loadCurrentMessage` była wywoływana tylko raz podczas pierwszego renderowania komponentu. Wkrótce zaimplementujemy `addSmartContractListener`, aby automatycznie aktualizować interfejs użytkownika po zmianie wiadomości w inteligentnym kontrakcie. + +Zanim przejdziemy do naszego nasłuchiwacza, sprawdźmy, co mamy do tej pory! Zapisz pliki `HelloWorld.js` i `interact.js`, a następnie przejdź do [http://localhost:3000/](http://localhost:3000/) + +Zauważysz, że bieżąca wiadomość nie brzmi już „Brak połączenia z siecią”. Zamiast tego odzwierciedla ona wiadomość zapisaną w inteligentnym kontrakcie. Super! + +#### Twój interfejs użytkownika powinien teraz odzwierciedlać wiadomość zapisaną w inteligentnym kontrakcie {#your-UI-should-now-reflect-the-message-stored-in-the-smart-contract} + +A propos tego nasłuchiwacza... + +#### Zaimplementuj `addSmartContractListener` {#implement-addsmartcontractlistener} + +Jeśli przypomnisz sobie plik `HelloWorld.sol`, który napisaliśmy w [części 1 tej serii samouczków](https://docs.alchemy.com/alchemy/tutorials/hello-world-smart-contract#step-10-write-our-contract), przypomnisz sobie, że istnieje zdarzenie inteligentnego kontraktu o nazwie `UpdatedMessages`, które jest emitowane po wywołaniu funkcji `update` naszego inteligentnego kontraktu (patrz linie 9 i 27): + +```javascript +// HelloWorld.sol + +// Określa wersję Solidity, używając semantycznego wersjonowania. +// Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity ^0.7.3; + +// Definiuje kontrakt o nazwie `HelloWorld`. +// Kontrakt jest zbiorem funkcji i danych (jego stanu). Po wdrożeniu kontrakt znajduje się pod określonym adresem w blockchainie Ethereum. Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + // Emitowane, gdy wywoływana jest funkcja aktualizacji + // Zdarzenia inteligentnych kontraktów to sposób, w jaki kontrakt komunikuje, że coś wydarzyło się na blockchainie do front-endu aplikacji, który może „nasłuchiwać” określonych zdarzeń i podejmować działania, gdy one wystąpią. + event UpdatedMessages(string oldStr, string newStr); + + // Deklaruje zmienną stanu `message` typu `string`. + // Zmienne stanu to zmienne, których wartości są trwale przechowywane w pamięci kontraktu. Słowo kluczowe `public` udostępnia zmienne spoza kontraktu i tworzy funkcję, którą inne kontrakty lub klienci mogą wywołać w celu uzyskania dostępu do wartości. + string public message; + + // Podobnie jak w wielu językach obiektowych opartych na klasach, konstruktor jest specjalną funkcją, która jest wykonywana tylko podczas tworzenia kontraktu. + // Konstruktory służą do inicjalizacji danych kontraktu. Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Akceptuje argument ciągu znaków `initMessage` i ustawia wartość w zmiennej pamięci kontraktu `message`). + message = initMessage; + } + + // Funkcja publiczna, która akceptuje argument w postaci ciągu znaków i aktualizuje zmienną pamięci masowej `message`. + function update(string memory newMessage) public { + string memory oldMsg = message; + message = newMessage; + emit UpdatedMessages(oldMsg, newMessage); + } +} +``` + +Zdarzenia inteligentnych kontraktów to sposób, w jaki kontrakt komunikuje, że coś się stało (tj. miało miejsce _zdarzenie_) na blockchainie do Twojej aplikacji front-endowej, która może „nasłuchiwać” określonych zdarzeń i podejmować działania, gdy one wystąpią. + +Funkcja `addSmartContractListener` będzie konkretnie nasłuchiwać zdarzenia `UpdatedMessages` naszego inteligentnego kontraktu Witaj Świecie i aktualizować nasz interfejs użytkownika, aby wyświetlić nową wiadomość. + +Zmodyfikuj `addSmartContractListener` do następującej postaci: + +```javascript +// HelloWorld.js + +function addSmartContractListener() { + helloWorldContract.events.UpdatedMessages({}, (error, data) => { + if (error) { + setStatus("😥 " + error.message) + } else { + setMessage(data.returnValues[1]) + setNewMessage("") + setStatus("🎉 Twoja wiadomość została zaktualizowana!") + } + }) +} +``` + +Przeanalizujmy, co się dzieje, gdy nasłuchiwacz wykryje zdarzenie: + +- Jeśli wystąpi błąd podczas emitowania zdarzenia, zostanie on odzwierciedlony w interfejsie użytkownika za pośrednictwem naszej zmiennej stanu `status`. +- W przeciwnym razie użyjemy zwróconego obiektu `data`. `data.returnValues` to tablica indeksowana od zera, w której pierwszy element tablicy przechowuje poprzednią wiadomość, a drugi element przechowuje zaktualizowaną. W sumie, w przypadku pomyślnego zdarzenia, ustawimy nasz ciąg znaków `message` na zaktualizowaną wiadomość, wyczyścimy ciąg znaków `newMessage` i zaktualizujemy naszą zmienną stanu `status`, aby odzwierciedlić, że nowa wiadomość została opublikowana w naszym inteligentnym kontrakcie. + +Na koniec wywołajmy naszego nasłuchiwacza w naszej funkcji `useEffect`, aby został zainicjowany podczas pierwszego renderowania komponentu `HelloWorld.js`. W sumie Twoja funkcja `useEffect` powinna wyglądać tak: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() +}, []) +``` + +Teraz, gdy jesteśmy w stanie czytać z naszego inteligentnego kontraktu, byłoby wspaniale dowiedzieć się, jak do niego pisać! Jednak, aby pisać do naszej dapki, musimy najpierw mieć do niej podłączony portfel Ethereum. + +Więc następnie zajmiemy się konfiguracją naszego portfela Ethereum (MetaMask), a następnie podłączeniem go do naszej dapki! + +### Krok 4: Skonfiguruj swój portfel Ethereum {#step-4-set-up-your-ethereum-wallet} + +Aby cokolwiek zapisać w łańcuchu Ethereum, użytkownicy muszą podpisywać transakcje za pomocą kluczy prywatnych swojego wirtualnego portfela. W tym samouczku użyjemy [MetaMask](https://metamask.io/), wirtualnego portfela w przeglądarce używanego do zarządzania adresem konta Ethereum, ponieważ znacznie ułatwia to podpisywanie transakcji dla użytkownika końcowego. + +Jeśli chcesz dowiedzieć się więcej o tym, jak działają transakcje w Ethereum, sprawdź [tę stronę](/developers/docs/transactions/) od Ethereum Foundation. + +#### Pobierz MetaMask {#download-metamask} + +Możesz pobrać i utworzyć konto MetaMask za darmo [tutaj](https://metamask.io/download). Podczas tworzenia konta, lub jeśli już je posiadasz, upewnij się, że przełączyłeś się na „Sieć testową Goerli” w prawym górnym rogu (aby nie mieć do czynienia z prawdziwymi pieniędzmi). + +#### Dodaj ether z Faucet {#add-ether-from-a-faucet} + +Aby podpisać transakcję na blockchainie Ethereum, będziemy potrzebować trochę fałszywego Eth. Aby uzyskać Eth, możesz przejść do [FaucETH](https://fauceth.komputing.org) i wprowadzić swój adres konta Goerli, kliknąć „Poproś o fundusze”, następnie wybrać „Ethereum Testnet Goerli” w menu rozwijanym i na koniec ponownie kliknąć przycisk „Poproś o fundusze”. Wkrótce powinieneś zobaczyć Eth na swoim koncie MetaMask! + +#### Sprawdź swoje saldo {#check-your-balance} + +Aby sprawdzić, czy nasze saldo jest na miejscu, wykonajmy żądanie [eth_getBalance](https://docs.alchemyapi.io/alchemy/documentation/alchemy-api-reference/json-rpc#eth_getbalance) za pomocą [narzędzia kompozytora Alchemy](https://composer.alchemyapi.io/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D). Zwróci to ilość Eth w naszym portfelu. Po wprowadzeniu adresu konta MetaMask i kliknięciu „Wyślij żądanie” powinieneś zobaczyć następującą odpowiedź: + +```text +{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"} +``` + +**UWAGA:** ten wynik jest w wei, a nie w eth. Wei jest używany jako najmniejsza jednostka etheru. Konwersja z wei na eth to: 1 eth = 10¹⁸ wei. Więc jeśli przekonwertujemy 0xde0b6b3a7640000 na system dziesiętny, otrzymamy 1\*10¹⁸, co równa się 1 eth. + +Uff! Nasze fałszywe pieniądze są na miejscu! 🤑 + +### Krok 5: Podłącz MetaMask do swojego interfejsu użytkownika {#step-5-connect-metamask-to-your-UI} + +Teraz, gdy nasz portfel MetaMask jest skonfigurowany, połączmy z nim naszą dapką! + +#### Funkcja `connectWallet` {#the-connectWallet-function} + +W naszym pliku `interact.js` zaimplementujmy funkcję `connectWallet`, którą następnie możemy wywołać w naszym komponencie `HelloWorld.js`. + +Zmodyfikujmy `connectWallet` do następującej postaci: + +```javascript +// interact.js + +export const connectWallet = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_requestAccounts", + }) + const obj = { + status: "👆🏽 Wpisz wiadomość w polu tekstowym powyżej.", + address: addressArray[0], + } + return obj + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + Musisz zainstalować MetaMask, wirtualny portfel Ethereum, w swojej + przeglądarce. + +

+
+ ), + } + } +} +``` + +Więc co dokładnie robi ten gigantyczny blok kodu? + +Po pierwsze, sprawdza, czy `window.ethereum` jest włączone w przeglądarce. + +`window.ethereum` to globalny interfejs API wstrzykiwany przez MetaMask i innych dostawców portfeli, który pozwala stronom internetowym na żądanie dostępu do kont Ethereum użytkowników. Jeśli zostanie zatwierdzony, może odczytywać dane z blockchainów, z którymi użytkownik jest połączony, i sugerować, aby użytkownik podpisywał wiadomości i transakcje. Sprawdź [dokumentację MetaMask](https://docs.metamask.io/guide/ethereum-provider.html#table-of-contents), aby uzyskać więcej informacji! + +Jeśli `window.ethereum` _nie jest_ obecne, oznacza to, że MetaMask nie jest zainstalowany. Powoduje to zwrócenie obiektu JSON, w którym zwrócony `adres` jest pustym ciągiem, a obiekt `status` JSX informuje, że użytkownik musi zainstalować MetaMask. + +Teraz, jeśli `window.ethereum` _jest_ obecne, to wtedy robi się ciekawie. + +Używając pętli try/catch, spróbujemy połączyć się z MetaMask, wywołując [`window.ethereum.request({ method: "eth_requestAccounts" });`](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts). Wywołanie tej funkcji otworzy MetaMask w przeglądarce, gdzie użytkownik zostanie poproszony o podłączenie swojego portfela do Twojej dapki. + +- Jeśli użytkownik zdecyduje się połączyć, `method: "eth_requestAccounts"` zwróci tablicę zawierającą wszystkie adresy kont użytkownika, które połączyły się z dappką. W sumie nasza funkcja `connectWallet` zwróci obiekt JSON, który zawiera _pierwszy_ `adres` w tej tablicy (patrz linia 9) oraz wiadomość `status`, która prosi użytkownika o napisanie wiadomości do inteligentnego kontraktu. +- Jeśli użytkownik odrzuci połączenie, obiekt JSON będzie zawierał pusty ciąg dla zwróconego `adresu` oraz komunikat `status`, który odzwierciedla, że użytkownik odrzucił połączenie. + +Teraz, gdy napisaliśmy tę funkcję `connectWallet`, następnym krokiem jest wywołanie jej w naszym komponencie `HelloWorld.js`. + +#### Dodaj funkcję `connectWallet` do swojego komponentu interfejsu użytkownika HelloWorld.js {#add-the-connectWallet-function-to-your-HelloWorld-js-ui-component} + +Przejdź do funkcji `connectWalletPressed` w `HelloWorld.js` i zaktualizuj ją do następującej postaci: + +```javascript +// HelloWorld.js + +const connectWalletPressed = async () => { + const walletResponse = await connectWallet() + setStatus(walletResponse.status) + setWallet(walletResponse.address) +} +``` + +Zauważ, jak większość naszej funkcjonalności jest abstrahowana od naszego komponentu `HelloWorld.js` z pliku `interact.js`? Robimy tak, aby zachować zgodność z paradygmatem M-V-C! + +W `connectWalletPressed` po prostu wykonujemy wywołanie await do naszej zaimportowanej funkcji `connectWallet`, a za pomocą jej odpowiedzi aktualizujemy nasze zmienne `status` i `walletAddress` za pomocą ich hooków stanu. + +Teraz zapiszmy oba pliki (`HelloWorld.js` i `interact.js`) i przetestujmy nasz dotychczasowy interfejs użytkownika. + +Otwórz przeglądarkę na stronie [http://localhost:3000/](http://localhost:3000/) i naciśnij przycisk „Połącz portfel” w prawym górnym rogu strony. + +Jeśli masz zainstalowany MetaMask, powinieneś zostać poproszony o podłączenie swojego portfela do Twojej dapki. Zaakceptuj zaproszenie do połączenia. + +Powinieneś zobaczyć, że przycisk portfela odzwierciedla teraz, że Twój adres jest połączony! Jeeee 🔥 + +Następnie spróbuj odświeżyć stronę... to jest dziwne. Nasz przycisk portfela prosi nas o podłączenie MetaMask, mimo że jest już podłączony... + +Jednak nie ma się czego bać! Możemy łatwo to rozwiązać (zaadresować?) implementując `getCurrentWalletConnected`, który sprawdzi, czy adres jest już połączony z naszą dappką i odpowiednio zaktualizuje nasz interfejs użytkownika! + +#### Funkcja `getCurrentWalletConnected` {#the-getcurrentwalletconnected-function} + +Zaktualizuj swoją funkcję `getCurrentWalletConnected` w pliku `interact.js` do następującej postaci: + +```javascript +// interact.js + +export const getCurrentWalletConnected = async () => { + if (window.ethereum) { + try { + const addressArray = await window.ethereum.request({ + method: "eth_accounts", + }) + if (addressArray.length > 0) { + return { + address: addressArray[0], + status: "👆🏽 Wpisz wiadomość w polu tekstowym powyżej.", + } + } else { + return { + address: "", + status: "🦊 Połącz się z MetaMask za pomocą przycisku w prawym górnym rogu.", + } + } + } catch (err) { + return { + address: "", + status: "😥 " + err.message, + } + } + } else { + return { + address: "", + status: ( + +

+ {" "} + 🦊 + Musisz zainstalować MetaMask, wirtualny portfel Ethereum, w swojej + przeglądarce. + +

+
+ ), + } + } +} +``` + +Ten kod jest _bardzo_ podobny do funkcji `connectWallet`, którą właśnie napisaliśmy w poprzednim kroku. + +Główna różnica polega na tym, że zamiast wywoływać metodę `eth_requestAccounts`, która otwiera MetaMask, aby użytkownik mógł połączyć swój portfel, tutaj wywołujemy metodę `eth_accounts`, która po prostu zwraca tablicę zawierającą adresy MetaMask aktualnie połączone z naszą dapką. + +Aby zobaczyć tę funkcję w działaniu, wywołajmy ją w naszej funkcji `useEffect` naszego komponentu `HelloWorld.js`: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() + + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) +}, []) +``` + +Zauważ, że używamy odpowiedzi z naszego wywołania `getCurrentWalletConnected`, aby zaktualizować nasze zmienne stanu `walletAddress` i `status`. + +Teraz, gdy dodałeś ten kod, spróbujmy odświeżyć okno przeglądarki. + +Fajnieee! Przycisk powinien informować, że jesteś połączony i pokazywać podgląd adresu podłączonego portfela - nawet po odświeżeniu! + +#### Zaimplementuj `addWalletListener` {#implement-addwalletlistener} + +Ostatnim krokiem w konfiguracji portfela naszej dapki jest zaimplementowanie nasłuchiwacza portfela, aby nasz interfejs użytkownika aktualizował się, gdy zmieni się stan naszego portfela, na przykład gdy użytkownik się rozłączy lub zmieni konto. + +W pliku `HelloWorld.js` zmodyfikuj swoją funkcję `addWalletListener` w następujący sposób: + +```javascript +// HelloWorld.js + +function addWalletListener() { + if (window.ethereum) { + window.ethereum.on("accountsChanged", (accounts) => { + if (accounts.length > 0) { + setWallet(accounts[0]) + setStatus("👆🏽 Wpisz wiadomość w polu tekstowym powyżej.") + } else { + setWallet("") + setStatus("🦊 Połącz się z MetaMask za pomocą przycisku w prawym górnym rogu.") + } + }) + } else { + setStatus( +

+ {" "} + 🦊 + Musisz zainstalować MetaMask, wirtualny portfel Ethereum, w swojej przeglądarce. + +

+ ) + } +} +``` + +Założę się, że w tym momencie nie potrzebujesz nawet naszej pomocy, aby zrozumieć, co się tutaj dzieje, ale dla porządku szybko to omówmy: + +- Najpierw nasza funkcja sprawdza, czy `window.ethereum` jest włączone (tj. MetaMask jest zainstalowany). + - Jeśli nie jest, po prostu ustawiamy naszą zmienną stanu `status` na ciąg JSX, który prosi użytkownika o zainstalowanie MetaMask. + - Jeśli jest włączone, ustawiamy nasłuchiwacz `window.ethereum.on("accountsChanged")` w linii 3, który nasłuchuje zmian stanu w portfelu MetaMask, co obejmuje sytuacje, gdy użytkownik podłącza dodatkowe konto do dapki, zmienia konta lub odłącza konto. Jeśli co najmniej jedno konto jest połączone, zmienna stanu `walletAddress` jest aktualizowana jako pierwsze konto w tablicy `konta` zwróconej przez nasłuchiwacz. W przeciwnym razie `walletAddress` jest ustawiany jako pusty ciąg. + +Na koniec musimy wywołać ją w naszej funkcji `useEffect`: + +```javascript +// HelloWorld.js + +useEffect(async () => { + const message = await loadCurrentMessage() + setMessage(message) + addSmartContractListener() + + const { address, status } = await getCurrentWalletConnected() + setWallet(address) + setStatus(status) + + addWalletListener() +}, []) +``` + +I to wszystko! Pomyślnie ukończyliśmy programowanie całej naszej funkcjonalności portfela! Teraz przejdźmy do naszego ostatniego zadania: aktualizacji wiadomości zapisanej w naszym inteligentnym kontrakcie! + +### Krok 6: Zaimplementuj funkcję `updateMessage` {#step-6-implement-the-updateMessage-function} + +Dobra, ekipa, dotarliśmy do ostatniej prostej! W `updateMessage` w swoim pliku `interact.js`, zrobimy następujące rzeczy: + +1. Upewnij się, że wiadomość, którą chcemy opublikować w naszym inteligentnym kontakcie, jest ważna +2. Podpisz naszą transakcję za pomocą MetaMask +3. Wywołaj tę funkcję z naszego komponentu frontendowego `HelloWorld.js` + +To nie potrwa długo; dokończmy tę dappkę! + +#### Obsługa błędów wejściowych {#input-error-handling} + +Naturalnie, sensowne jest posiadanie jakiejś formy obsługi błędów wejściowych na początku funkcji. + +Będziemy chcieli, aby nasza funkcja zakończyła się wcześniej, jeśli nie ma zainstalowanego rozszerzenia MetaMask, nie jest podłączony portfel (tj. przekazany `address` jest pustym ciągiem znaków) lub `message` jest pustym ciągiem znaków. Dodajmy następującą obsługę błędów do `updateMessage`: + +```javascript +// interact.js + +export const updateMessage = async (address, message) => { + if (!window.ethereum || address === null) { + return { + status: + "💡 Połącz swój portfel MetaMask, aby zaktualizować wiadomość na blockchainie.", + } + } + + if (message.trim() === "") { + return { + status: "❌ Twoja wiadomość nie może być pustym ciągiem znaków.", + } + } +} +``` + +Teraz, gdy mamy właściwą obsługę błędów wejściowych, nadszedł czas, aby podpisać transakcję za pomocą MetaMask! + +#### Podpisywanie naszej transakcji {#signing-our-transaction} + +Jeśli jesteś już zaznajomiony z tradycyjnymi transakcjami Ethereum web3, kod, który napiszemy dalej, będzie bardzo znajomy. Poniżej kodu obsługi błędów wejściowych dodaj następujący kod do `updateMessage`: + +```javascript +// interact.js + +// ustaw parametry transakcji +const transactionParameters = { + to: contractAddress, // Wymagane z wyjątkiem publikacji kontraktów. + from: address, // musi pasować do aktywnego adresu użytkownika. + data: helloWorldContract.methods.update(message).encodeABI(), +} + +// podpisz transakcję +try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + status: ( + + ✅{" "} + + Zobacz status swojej transakcji na Etherscan! + +
+ ℹ️ Gdy transakcja zostanie zweryfikowana przez sieć, wiadomość zostanie + zaktualizowana automatycznie. +
+ ), + } +} catch (error) { + return { + status: "😥 " + error.message, + } +} +``` + +Przeanalizujmy, co się dzieje. Najpierw ustawiamy parametry naszej transakcji, gdzie: + +- `to` określa adres odbiorcy (nasz inteligentny kontrakt) +- `from` określa sygnatariusza transakcji, zmienną `address`, którą przekazaliśmy do naszej funkcji +- `data` zawiera wywołanie metody `update` naszego inteligentnego kontraktu Witaj Świecie, otrzymując jako dane wejściowe naszą zmienną ciągu znaków `message` + +Następnie wykonujemy wywołanie await, `window.ethereum.request`, w którym prosimy MetaMask o podpisanie transakcji. Zauważ, że w liniach 11 i 12 określamy naszą metodę eth, `eth_sendTransaction` i przekazujemy nasze `transactionParameters`. + +W tym momencie w przeglądarce otworzy się MetaMask i poprosi użytkownika o podpisanie lub odrzucenie transakcji. + +- Jeśli transakcja się powiedzie, funkcja zwróci obiekt JSON, w którym ciąg JSX `status` prosi użytkownika o sprawdzenie Etherscan w celu uzyskania dalszych informacji o transakcji. +- Jeśli transakcja się nie powiedzie, funkcja zwróci obiekt JSON, w którym ciąg znaków `status` przekaże komunikat o błędzie. + +W sumie nasza funkcja `updateMessage` powinna wyglądać tak: + +```javascript +// interact.js + +export const updateMessage = async (address, message) => { + // obsługa błędów wejściowych + if (!window.ethereum || address === null) { + return { + status: + "💡 Połącz swój portfel MetaMask, aby zaktualizować wiadomość na blockchainie.", + } + } + + if (message.trim() === "") { + return { + status: "❌ Twoja wiadomość nie może być pustym ciągiem znaków.", + } + } + + // ustaw parametry transakcji + const transactionParameters = { + to: contractAddress, // Wymagane z wyjątkiem publikacji kontraktów. + from: address, // musi pasować do aktywnego adresu użytkownika. + data: helloWorldContract.methods.update(message).encodeABI(), + } + + // podpisz transakcję + try { + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [transactionParameters], + }) + return { + status: ( + + ✅{" "} + + Zobacz status swojej transakcji na Etherscan! + +
+ ℹ️ Gdy transakcja zostanie zweryfikowana przez sieć, wiadomość zostanie + zaktualizowana automatycznie. +
+ ), + } + } catch (error) { + return { + status: "😥 " + error.message, + } + } +} +``` + +Na koniec musimy połączyć naszą funkcję `updateMessage` z naszym komponentem `HelloWorld.js`. + +#### Połącz `updateMessage` z frontendem `HelloWorld.js` {#connect-updatemessage-to-the-helloworld-js-frontend} + +Nasza funkcja `onUpdatePressed` powinna wykonać wywołanie await do zaimportowanej funkcji `updateMessage` i zmodyfikować zmienną stanu `status`, aby odzwierciedlić, czy nasza transakcja się powiodła, czy nie: + +```javascript +// HelloWorld.js + +const onUpdatePressed = async () => { + const { status } = await updateMessage(walletAddress, newMessage) + setStatus(status) +} +``` + +To super czyste i proste. I zgadnij co... TWOJA DAPPKA JEST UKOŃCZONA!!! + +Śmiało przetestuj przycisk **Aktualizuj**! + +### Stwórz swoją własną dappkę {#make-your-own-custom-dapp} + +Hura, dotarłeś do końca samouczka! Podsumowując, nauczyłeś się, jak: + +- Podłącz portfel MetaMask do swojego projektu dapp +- Odczytaj dane ze swojego inteligentnego kontraktu za pomocą interfejsu API [Alchemy Web3](https://docs.alchemy.com/alchemy/documentation/alchemy-web3) +- Podpisz transakcje Ethereum za pomocą MetaMask + +Teraz jesteś w pełni wyposażony, aby zastosować umiejętności z tego samouczka do zbudowania własnego, niestandardowego projektu dapp! Jak zawsze, jeśli masz jakieś pytania, nie wahaj się skontaktować z nami w celu uzyskania pomocy na [Discordzie Alchemy](https://discord.gg/gWuC7zB). 🧙‍♂️ + +Po ukończeniu tego samouczka daj nam znać, jak Ci poszło lub czy masz jakieś uwagi, oznaczając nas na Twitterze [@alchemyplatform](https://twitter.com/AlchemyPlatform)! diff --git a/public/content/translations/pl/developers/tutorials/hello-world-smart-contract/index.md b/public/content/translations/pl/developers/tutorials/hello-world-smart-contract/index.md new file mode 100644 index 00000000000..51f58532f80 --- /dev/null +++ b/public/content/translations/pl/developers/tutorials/hello-world-smart-contract/index.md @@ -0,0 +1,367 @@ +--- +title: "Inteligentny kontrakt Hello World dla początkujących" +description: "Samouczek wprowadzający na temat pisania i wdrażania prostego inteligentnego kontraktu na Ethereum." +author: "elanh" +tags: + [ + "solidity", + "hardhat", + "alchemy", + "smart kontrakty", + "wdrażanie" + ] +skill: beginner +lang: pl +published: 2021-03-31 +--- + +Jeśli dopiero zaczynasz przygodę z tworzeniem oprogramowania blockchain i nie wiesz, od czego zacząć, lub jeśli po prostu chcesz zrozumieć, jak wdrażać inteligentne kontrakty i wchodzić z nimi w interakcję, ten przewodnik jest dla Ciebie. Przeprowadzimy Cię przez proces tworzenia i wdrażania prostego inteligentnego kontraktu w sieci testowej Sepolia przy użyciu wirtualnego portfela [MetaMask](https://metamask.io/), [Solidity](https://docs.soliditylang.org/en/v0.8.0/), [Hardhat](https://hardhat.org/) i [Alchemy](https://www.alchemy.com/) (nie martw się, jeśli jeszcze nie rozumiesz, co to wszystko znaczy, wyjaśnimy to). + +W [części 2](https://docs.alchemy.com/docs/interacting-with-a-smart-contract) tego samouczka omówimy, jak wejść w interakcję z naszym inteligentnym kontraktem po jego wdrożeniu, a w [części 3](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan) omówimy, jak opublikować go na Etherscan. + +Jeśli w dowolnym momencie będziesz mieć pytania, śmiało odezwij się na [Discordzie Alchemy](https://discord.gg/gWuC7zB)! + +## Krok 1: Połącz się z siecią Ethereum {#step-1} + +Istnieje wiele sposobów na wysyłanie żądań do łańcucha Ethereum. Dla uproszczenia użyjemy darmowego konta na Alchemy, platformy deweloperskiej i interfejsu API blockchain, która pozwala nam komunikować się z łańcuchem Ethereum bez konieczności uruchamiania własnych węzłów. Platforma posiada również narzędzia deweloperskie do monitorowania i analityki, które wykorzystamy w tym samouczku, aby zrozumieć, co dzieje się „pod maską” podczas wdrażania naszego inteligentnego kontraktu. Jeśli nie masz jeszcze konta Alchemy, [możesz zarejestrować się za darmo tutaj](https://dashboard.alchemy.com/signup). + +## Krok 2: Stwórz swoją aplikację (i klucz API) {#step-2} + +Po utworzeniu konta Alchemy możesz wygenerować klucz API, tworząc aplikację. Pozwoli nam to na wysyłanie żądań do sieci testowej Sepolia. Jeśli nie znasz sieci testowych, sprawdź [tę stronę](/developers/docs/networks/). + +1. Przejdź na stronę "Utwórz nową aplikację" w pulpicie nawigacyjnym Alchemy, wybierając "Wybierz aplikację" na pasku nawigacyjnym i klikając "Utwórz nową aplikację" + +![Utwórz aplikację Hello world](./hello-world-create-app.png) + +2. Nazwij swoją aplikację "Hello World", podaj krótki opis i wybierz przypadek użycia, np. "Infrastruktura i narzędzia". Następnie wyszukaj "Ethereum" i wybierz sieć. + +![widok tworzenia aplikacji hello world](./create-app-view-hello-world.png) + +3. Kliknij "Dalej", aby kontynuować, następnie "Utwórz aplikację" i to wszystko! Twoja aplikacja powinna pojawić się w rozwijanym menu paska nawigacyjnego, z kluczem API dostępnym do skopiowania. + +## Krok 3: Utwórz konto Ethereum (adres) {#step-3} + +Potrzebujemy konta Ethereum do wysyłania i odbierania transakcji. W tym samouczku użyjemy MetaMask, wirtualnego portfela w przeglądarce, który służy do zarządzania adresem konta Ethereum. Więcej o [transakcjach](/developers/docs/transactions/). + +Możesz pobrać MetaMask i utworzyć darmowe konto Ethereum [tutaj](https://metamask.io/download). Podczas tworzenia konta lub jeśli już je posiadasz, pamiętaj, aby przełączyć się na sieć testową "Sepolia" za pomocą rozwijanego menu sieci (abyśmy nie mieli do czynienia z prawdziwymi pieniędzmi). + +Jeśli nie widzisz na liście sieci Sepolia, przejdź do menu, następnie do opcji Zaawansowane i przewiń w dół, aby włączyć opcję "Pokaż sieci testowe". W menu wyboru sieci wybierz kartę "Niestandardowe", aby znaleźć listę sieci testowych, i wybierz "Sepolia". + +![przykład metamask sepolia](./metamask-sepolia-example.png) + +## Krok 4: Dodaj ether z kranu (faucet) {#step-4} + +Aby wdrożyć nasz inteligentny kontrakt w sieci testowej, będziemy potrzebować trochę fałszywego ETH. Aby otrzymać Sepolia ETH, możesz przejść do [szczegółów sieci Sepolia](/developers/docs/networks/#sepolia), aby zobaczyć listę różnych kranów (faucetów). Jeśli jeden nie działa, spróbuj innego, ponieważ czasami mogą się wyczerpać. Otrzymanie fałszywego ETH może zająć trochę czasu ze względu na ruch w sieci. Wkrótce powinieneś zobaczyć ETH na swoim koncie Metamask! + +## Krok 5: Sprawdź saldo {#step-5} + +Aby upewnić się, że nasze saldo tam jest, wykonajmy żądanie [eth_getBalance](/developers/docs/apis/json-rpc/#eth_getbalance) za pomocą [narzędzia kompozytora Alchemy](https://sandbox.alchemy.com/?network=ETH_SEPOLIA&method=eth_getBalance&body.id=1&body.jsonrpc=2.0&body.method=eth_getBalance&body.params%5B0%5D=&body.params%5B1%5D=latest). Zwróci to ilość ETH w naszym portfelu. Po wprowadzeniu adresu konta MetaMask i kliknięciu „Wyślij żądanie” powinieneś zobaczyć następującą odpowiedź: + +```json +{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" } +``` + +> **UWAGA:** Ten wynik jest w wei, a nie w ETH. Wei jest używany jako najmniejsza jednostka etheru. Przeliczenie z wei na ETH wynosi: 1 eth = 1018 wei. Więc jeśli przekonwertujemy 0x2B5E3AF16B1880000 na liczbę dziesiętną, otrzymamy 5\*10¹⁸, co równa się 5 ETH. +> +> Uff! Nasze fałszywe pieniądze są na miejscu . + +## Krok 6: Zainicjuj nasz projekt {#step-6} + +Najpierw musimy utworzyć folder dla naszego projektu. Przejdź do wiersza poleceń i wpisz: + +``` +mkdir hello-world +cd hello-world +``` + +Teraz, gdy jesteśmy w folderze naszego projektu, użyjemy `npm init`, aby zainicjować projekt. Jeśli nie masz jeszcze zainstalowanego npm, postępuj zgodnie z [tymi instrukcjami](https://docs.alchemyapi.io/alchemy/guides/alchemy-for-macs#1-install-nodejs-and-npm) (będziemy również potrzebować Node.js, więc pobierz go również!). + +``` +npm init +``` + +Nie ma większego znaczenia, jak odpowiesz na pytania instalacyjne, dla porównania przedstawiamy, jak my to zrobiliśmy: + +``` +nazwa pakietu: (hello-world) +wersja: (1.0.0) +opis: inteligentny kontrakt hello world +punkt wejścia: (index.js) +polecenie testowe: +repozytorium git: +słowa kluczowe: +autor: +licencja: (ISC) +Zapisywanie do /Users/.../.../.../hello-world/package.json: + +{ + "name": "hello-world", + "version": "1.0.0", + "description": "inteligentny kontrakt hello world", + "main": "index.js", + "scripts": { + "test": "echo \"Błąd: nie określono testu\" && exit 1" + }, + "author": "", + "license": "ISC" +} +``` + +Zatwierdź plik package.json i gotowe! + +## Krok 7: Pobierz [Hardhat](https://hardhat.org/getting-started/#overview) {#step-7} + +Hardhat to środowisko programistyczne do kompilacji, wdrażania, testowania i debugowania oprogramowania Ethereum. Pomaga deweloperom w tworzeniu inteligentnych kontraktów i dapek lokalnie przed wdrożeniem ich na żywym łańcuchu. + +W naszym projekcie `hello-world` uruchom: + +``` +npm install --save-dev hardhat +``` + +Sprawdź tę stronę, aby uzyskać więcej szczegółów na temat [instrukcji instalacji](https://hardhat.org/getting-started/#overview). + +## Krok 8: Stwórz projekt Hardhat {#step-8} + +W folderze naszego projektu uruchom: + +``` +npx hardhat +``` + +Powinieneś wtedy zobaczyć wiadomość powitalną i opcję wyboru tego, co chcesz zrobić. Wybierz „utwórz pusty hardhat.config.js”: + +``` +888 888 888 888 888 +888 888 888 888 888 +888 888 888 888 888 +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 +888 888 "88b 888P" d88" 888 888 "88b "88b 888 +888 888 .d888888 888 888 888 888 888 .d888888 888 +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 + +👷 Witamy w Hardhat v2.0.11 👷‍? + +Co chcesz zrobić? … +Stwórz przykładowy projekt +❯ Stwórz pusty plik hardhat.config.js +Wyjdź +``` + +Spowoduje to wygenerowanie dla nas pliku `hardhat.config.js`, w którym określimy całą konfigurację naszego projektu (w kroku 13). + +## Krok 9: Dodaj foldery projektu {#step-9} + +Aby utrzymać porządek w naszym projekcie, utworzymy dwa nowe foldery. Przejdź do katalogu głównego projektu w wierszu poleceń i wpisz: + +``` +mkdir contracts +mkdir scripts +``` + +- w `contracts/` będziemy przechowywać plik z kodem naszego inteligentnego kontraktu hello world +- w `scripts/` będziemy przechowywać skrypty do wdrażania naszego kontraktu i interakcji z nim + +## Krok 10: Napisz nasz kontrakt {#step-10} + +Możesz zadawać sobie pytanie: kiedy, do licha, zaczniemy pisać kod?? Cóż, jesteśmy tutaj, w kroku 10. + +Otwórz projekt hello-world w swoim ulubionym edytorze (my lubimy [VSCode](https://code.visualstudio.com/)). Inteligentne kontrakty pisane są w języku o nazwie Solidity, którego użyjemy do napisania naszego inteligentnego kontraktu HelloWorld.sol.‌ + +1. Przejdź do folderu "contracts" i utwórz nowy plik o nazwie HelloWorld.sol +2. Poniżej znajduje się przykładowy inteligentny kontrakt Hello World z Ethereum Foundation, którego będziemy używać w tym samouczku. Skopiuj i wklej poniższą zawartość do pliku HelloWorld.sol i koniecznie przeczytaj komentarze, aby zrozumieć, co robi ten kontrakt: + +```solidity +// Określa wersję Solidity, używając wersjonowania semantycznego. +// Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma +pragma solidity ^0.7.0; + +// Definiuje kontrakt o nazwie `HelloWorld`. +// Kontrakt to zbiór funkcji i danych (jego stanu). Po wdrożeniu kontrakt znajduje się pod określonym adresem na blockchainie Ethereum. Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html +contract HelloWorld { + + // Deklaruje zmienną stanu `message` typu `string`. + // Zmienne stanu to zmienne, których wartości są trwale przechowywane w pamięci kontraktu. Słowo kluczowe `public` udostępnia zmienne z zewnątrz kontraktu i tworzy funkcję, którą inne kontrakty lub klienci mogą wywołać w celu uzyskania dostępu do wartości. + string public message; + + // Podobnie jak w wielu obiektowych językach programowania opartych na klasach, konstruktor jest specjalną funkcją, która jest wykonywana tylko podczas tworzenia kontraktu. + // Konstruktory służą do inicjalizacji danych kontraktu. Dowiedz się więcej:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors + constructor(string memory initMessage) { + + // Akceptuje argument typu string `initMessage` i ustawia jego wartość w zmiennej `message` w pamięci kontraktu). + message = initMessage; + } + + // Funkcja publiczna, która akceptuje argument typu string i aktualizuje zmienną `message` w pamięci. + function update(string memory newMessage) public { + message = newMessage; + } +} +``` + +To jest bardzo prosty inteligentny kontrakt, który przechowuje wiadomość po utworzeniu i może być aktualizowany przez wywołanie funkcji `update`. + +## Krok 11: Połącz MetaMask i Alchemy ze swoim projektem {#step-11} + +Stworzyliśmy portfel MetaMask, konto Alchemy i napisaliśmy nasz inteligentny kontrakt, teraz nadszedł czas, aby połączyć te trzy elementy. + +Każda transakcja wysłana z Twojego wirtualnego portfela wymaga podpisu przy użyciu Twojego unikalnego klucza prywatnego. Aby udzielić naszemu programowi tego uprawnienia, możemy bezpiecznie przechowywać nasz klucz prywatny (i klucz API Alchemy) w pliku środowiskowym. + +> Aby dowiedzieć się więcej o wysyłaniu transakcji, sprawdź [ten samouczek](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) o wysyłaniu transakcji przy użyciu web3. + +Najpierw zainstaluj pakiet dotenv w katalogu swojego projektu: + +``` +npm install dotenv --save +``` + +Następnie utwórz plik `.env` w katalogu głównym naszego projektu i dodaj do niego swój klucz prywatny MetaMask oraz adres URL HTTP API Alchemy. + +- Postępuj zgodnie z [tymi instrukcjami](https://support.metamask.io/configure/accounts/how-to-export-an-accounts-private-key/), aby wyeksportować swój klucz prywatny +- Poniżej dowiesz się, jak uzyskać adres URL interfejsu API HTTP Alchemy + +![uzyskaj klucz api alchemy](./get-alchemy-api-key.png) + +Skopiuj adres URL interfejsu API Alchemy + +Twój plik `.env` powinien wyglądać następująco: + +``` +API_URL = "https://eth-sepolia.g.alchemy.com/v2/twój-klucz-api" +PRIVATE_KEY = "twój-klucz-prywatny-metamask" +``` + +Aby faktycznie połączyć je z naszym kodem, odwołamy się do tych zmiennych w naszym pliku `hardhat.config.js` w kroku 13. + + + + +Nie commituj pliku .env! Upewnij się, że nigdy nie udostępniasz ani nie ujawniasz nikomu swojego pliku .env, ponieważ w ten sposób narażasz swoje poufne dane. Jeśli używasz kontroli wersji, dodaj swój plik .env do pliku gitignore. + + + + +## Krok 12: Zainstaluj Ethers.js {#step-12-install-ethersjs} + +Ethers.js to biblioteka, która ułatwia interakcję i wysyłanie żądań do Ethereum, opakowując [standardowe metody JSON-RPC](/developers/docs/apis/json-rpc/) w bardziej przyjazne dla użytkownika metody. + +Hardhat bardzo ułatwia integrację [wtyczek](https://hardhat.org/plugins/) w celu uzyskania dodatkowych narzędzi i rozszerzonej funkcjonalności. Skorzystamy z [wtyczki Ethers](https://hardhat.org/docs/plugins/official-plugins#hardhat-ethers) do wdrażania kontraktów ([Ethers.js](https://github.com/ethers-io/ethers.js/) ma kilka bardzo przejrzystych metod wdrażania kontraktów). + +W katalogu projektu wpisz: + +``` +npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0" +``` + +Będziemy również wymagać ethers w naszym pliku `hardhat.config.js` w następnym kroku. + +## Krok 13: Zaktualizuj hardhat.config.js {#step-13-update-hardhatconfigjs} + +Do tej pory dodaliśmy kilka zależności i wtyczek, teraz musimy zaktualizować `hardhat.config.js`, aby nasz projekt wiedział o wszystkich. + +Zaktualizuj swój `hardhat.config.js`, aby wyglądał następująco: + +``` +require('dotenv').config(); + +require("@nomiclabs/hardhat-ethers"); +const { API_URL, PRIVATE_KEY } = process.env; + +/** +* @type import('hardhat/config').HardhatUserConfig +*/ +module.exports = { + solidity: "0.7.3", + defaultNetwork: "sepolia", + networks: { + hardhat: {}, + sepolia: { + url: API_URL, + accounts: [`0x${PRIVATE_KEY}`] + } + }, +} +``` + +## Krok 14: Skompiluj nasz kontrakt {#step-14-compile-our-contracts} + +Aby upewnić się, że wszystko do tej pory działa, skompilujmy nasz kontrakt. Zadanie `compile` jest jednym z wbudowanych zadań hardhat. + +Z wiersza poleceń uruchom: + +``` +npx hardhat compile +``` + +Możesz otrzymać ostrzeżenie o `SPDX license identifier not provided in source file` (brak identyfikatora licencji SPDX w pliku źródłowym), ale nie musisz się tym martwić — miejmy nadzieję, że wszystko inne wygląda dobrze! Jeśli nie, zawsze możesz napisać wiadomość na [discordzie Alchemy](https://discord.gg/u72VCg3). + +## Krok 15: Napisz nasz skrypt wdrożeniowy {#step-15-write-our-deploy-scripts} + +Teraz, gdy nasz kontrakt jest napisany, a nasz plik konfiguracyjny jest gotowy, nadszedł czas, aby napisać nasz skrypt wdrażający kontrakt. + +Przejdź do folderu `scripts/` i utwórz nowy plik o nazwie `deploy.js`, dodając do niego następującą zawartość: + +``` +async function main() { + const HelloWorld = await ethers.getContractFactory("HelloWorld"); + + // Rozpocznij wdrażanie, zwracając obietnicę, która prowadzi do obiektu kontraktu + const hello_world = await HelloWorld.deploy("Hello World!"); + console.log("Kontrakt wdrożony pod adresem:", hello_world.address);} + +main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error); + process.exit(1); + }); +``` + +Hardhat wykonuje niesamowitą robotę, wyjaśniając, co robi każda z tych linii kodu w swoim [samouczku dotyczącym kontraktów](https://hardhat.org/tutorial/testing-contracts.html#writing-tests), a my przyjęliśmy ich wyjaśnienia tutaj. + +``` +const HelloWorld = await ethers.getContractFactory("HelloWorld"); +``` + +`ContractFactory` w ethers.js to abstrakcja używana do wdrażania nowych inteligentnych kontraktów, więc `HelloWorld` jest tutaj fabryką dla instancji naszego kontraktu hello world. Podczas korzystania z wtyczki `hardhat-ethers` instancje `ContractFactory` i `Contract` są domyślnie połączone z pierwszym sygnatariuszem. + +``` +const hello_world = await HelloWorld.deploy(); +``` + +Wywołanie `deploy()` na `ContractFactory` rozpocznie wdrażanie i zwróci `Promise`, które prowadzi do `Contract`. Jest to obiekt, który ma metodę dla każdej z naszych funkcji inteligentnego kontraktu. + +## Krok 16: Wdróż nasz kontrakt {#step-16-deploy-our-contract} + +Jesteśmy wreszcie gotowi do wdrożenia naszego inteligentnego kontraktu! Przejdź do wiersza poleceń i uruchom: + +``` +npx hardhat run scripts/deploy.js --network sepolia +``` + +Powinieneś wtedy zobaczyć coś takiego: + +``` +Kontrakt wdrożony pod adresem: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570 +``` + +Jeśli przejdziemy do [Etherscan Sepolia](https://sepolia.etherscan.io/) i wyszukamy adres naszego kontraktu, powinniśmy zobaczyć, że został on pomyślnie wdrożony. Transakcja będzie wyglądać mniej więcej tak: + +![kontrakt etherscan](./etherscan-contract.png) + +Adres `Od` powinien pasować do adresu Twojego konta MetaMask, a adres `Do` będzie miał napis „Tworzenie Kontraktu”, ale jeśli klikniemy w transakcję, zobaczymy adres naszego kontraktu w polu `Do`: + +![transakcja etherscan](./etherscan-transaction.png) + +Gratulacje! Właśnie wdrożyłeś inteligentny kontrakt w łańcuchu Ethereum 🎉 + +Aby zrozumieć, co dzieje się pod maską, przejdźmy do karty Explorer w naszym [panelu Alchemy](https://dashboard.alchemyapi.io/explorer). Jeśli masz wiele aplikacji Alchemy, pamiętaj, aby filtrować według aplikacji i wybrać „Hello World”. +![eksplorator hello world](./hello-world-explorer.png) + +Tutaj zobaczysz kilka wywołań JSON-RPC, które Hardhat/Ethers wykonały za nas „pod maską”, gdy wywołaliśmy funkcję `.deploy()`. Dwa ważne, które należy tu wymienić, to [`eth_sendRawTransaction`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-send-raw-transaction), czyli żądanie faktycznego zapisania naszego kontraktu w łańcuchu Sepolia, oraz [`eth_getTransactionByHash`](https://www.alchemy.com/docs/node/abstract/abstract-api-endpoints/eth-get-transaction-by-hash), czyli żądanie odczytania informacji o naszej transakcji na podstawie haszu (typowy wzorzec podczas +transakcji). Aby dowiedzieć się więcej o wysyłaniu transakcji, zapoznaj się z tym samouczkiem na temat [wysyłania transakcji za pomocą Web3](/developers/tutorials/sending-transactions-using-web3-and-alchemy/) + +To wszystko w części 1 tego samouczka, w części 2 faktycznie [wejdziemy w interakcję z naszym inteligentnym kontraktem](https://www.alchemy.com/docs/interacting-with-a-smart-contract) poprzez aktualizację naszej początkowej wiadomości, a w części 3 [opublikujemy nasz inteligentny kontrakt na Etherscan](https://www.alchemy.com/docs/submitting-your-smart-contract-to-etherscan), aby każdy wiedział, jak z nim wchodzić w interakcję. + +**Chcesz dowiedzieć się więcej o Alchemy?** Sprawdź naszą [stronę internetową](https://www.alchemy.com/eth). Nie chcesz przegapić żadnej aktualizacji? Zapisz się do naszego newslettera [tutaj](https://www.alchemy.com/newsletter)! Pamiętaj, aby dołączyć również do naszego [Discorda](https://discord.gg/u72VCg3).\*\*. diff --git a/public/content/translations/pl/developers/tutorials/how-to-implement-an-erc721-market/index.md b/public/content/translations/pl/developers/tutorials/how-to-implement-an-erc721-market/index.md index 4b8ddf84a6e..2effc79c82d 100644 --- a/public/content/translations/pl/developers/tutorials/how-to-implement-an-erc721-market/index.md +++ b/public/content/translations/pl/developers/tutorials/how-to-implement-an-erc721-market/index.md @@ -1,12 +1,8 @@ --- -title: Jak wdrożyć rynek ERC-721 -description: Jak umieścić tokenizowane przedmioty w celu sprzedaży w zdecentralizowanym serwisie ogłoszeniowym -author: "Alberto Cuesta Cañada" -tags: - - "inteligentne kontrakty" - - "erc-721" - - "solidity" - - "tokeny" +title: "Jak wdrożyć rynek ERC-721" +description: "Jak umieścić tokenizowane przedmioty w celu sprzedaży w zdecentralizowanym serwisie ogłoszeniowym" +author: "Alberto Cuesta Cañada" +tags: [ "smart kontrakty", "erc-721", "solidity", "tokeny" ] skill: intermediate lang: pl published: 2020-03-19 @@ -26,13 +22,13 @@ Blockchain ponownie zmieni te rynki — pozwolę sobie pokazać, w jaki sposób. Model biznesowy serwisu ogłoszeniowego w publicznym blockchainie będzie musiał różnić się od modelu Ebay i spółki. -Po pierwsze, jest [kąt decentralizacji](/developers/docs/web2-vs-web3/). Istniejące platformy muszą utrzymywać własne serwery. Zdecentralizowana platforma jest utrzymywana przez użytkowników, a zatem koszt obsługi głównej platformy spadnie do zera dla właściciela platformy. +Po pierwsze, jest [aspekt decentralizacji](/developers/docs/web2-vs-web3/). Istniejące platformy muszą utrzymywać własne serwery. Zdecentralizowana platforma jest utrzymywana przez użytkowników, a zatem koszt obsługi głównej platformy spadnie do zera dla właściciela platformy. -Następnie frontend — strona internetowa lub interfejs dający dostęp do platformy. Tutaj jest wiele możliwości. Właściciele platformy mogą ograniczyć dostęp i zmusić wszystkich do korzystania ze swojego interfejsu, pobierając opłaty. Właściciele platform mogą również zdecydować o otwarciu dostępu (Power to the People!) i pozwolić każdemu budować interfejsy na platformie. Lub właściciele mogliby wybrać jakiekolwiek pośrednie podejście. +Następnie frontend — strona internetowa lub interfejs dający dostęp do platformy. Tutaj jest wiele możliwości. Właściciele platformy mogą ograniczyć dostęp i zmusić wszystkich do korzystania ze swojego interfejsu, pobierając opłaty. Właściciele platformy mogą również zdecydować się na otwarcie dostępu (Władza w ręce ludu!) i pozwolić każdemu na budowanie interfejsów do platformy. Lub właściciele mogliby wybrać jakiekolwiek pośrednie podejście. _Liderzy biznesu, którzy mają więcej wizji niż ja, będą wiedzieć, jak na tym zarabiać. Widzę jedynie, że różni się to od status quo i prawdopodobnie jest opłacalne._ -Ponadto istnieje kąt automatyzacji i płatności. Niektóre rzeczy mogą być bardzo [skutecznie tokenizowane](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com) i sprzedawane w serwisach. Tokenizowane aktywa są łatwo przenoszone w blockchainie. Bardzo skomplikowane metody płatności mogą być łatwo wdrożone w blockchainie. +Ponadto istnieje kąt automatyzacji i płatności. Niektóre rzeczy można bardzo [skutecznie stokenizować](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com) i handlować nimi na tablicy ogłoszeń. Tokenizowane aktywa są łatwo przenoszone w blockchainie. Bardzo skomplikowane metody płatności mogą być łatwo wdrożone w blockchainie. Po prostu wyczuwam tutaj okazję biznesową. W łatwy sposób można wdrożyć serwis ogłoszeniowy bez kosztów bieżących, ze złożonymi ścieżkami płatności uwzględnionymi w każdej transakcji. Jestem pewien, że ktoś wymyśli, do czego to wykorzystać. @@ -40,9 +36,9 @@ Po prostu cieszę się, że to buduję. Rzućmy okiem na kod. ## Implementacja {#implementation} -Jakiś czas temu uruchomiliśmy [repozytorium open source](https://github.com/HQ20/contracts?ref=hackernoon.com) z implementacjami zastosowań biznesowych i innymi ciekawymi rzeczami. Rzuć okiem. +Jakiś czas temu uruchomiliśmy [repozytorium open source](https://github.com/HQ20/contracts?ref=hackernoon.com) z przykładowymi implementacjami biznesowymi i innymi dodatkami. Zachęcamy do zapoznania się. -Kod tego [serwisu ogłoszeniowego Ethereum](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com) jest dostępny, możesz go używać do woli. Pamiętaj tylko, że kod nie został poddany audytowi i musisz zrobić własną analizę due dililgence, zanim włożysz w niego pieniądze. +Kod tej [tablicy ogłoszeń Ethereum](https://github.com/HQ20/contracts/tree/master/contracts/classifieds?ref=hackernoon.com) jest tam dostępny, prosimy, korzystajcie z niego i testujcie go do woli. Pamiętaj tylko, że kod nie został poddany audytowi i musisz zrobić własną analizę due dililgence, zanim włożysz w niego pieniądze. Podstawy tablicy nie są skomplikowane. Wszystkie reklamy na tablicy będą tylko strukturą z kilkoma polami: @@ -67,7 +63,7 @@ Korzystanie z mapowania oznacza, że przed opublikowaniem tej wiadomości musimy Następnie pojawia się pytanie, z czym mamy do czynienia i jaka waluta jest wykorzystywana do zapłaty za transakcję. -Jeśli chodzi o przedmioty, zamierzamy tylko poprosić o implementację interfejsu [ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com), który naprawdę jest sposobem na reprezentowanie realnych pozycji świata w blockchain, chociaż [najlepiej współpracuje z cyfrowymi aktywami](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com). Zamierzamy określić nasz własny kontrakt ERC721 w konstruktorze, co oznacza, że wszelkie aktywa w naszym serwisie muszą być uprzednio tokenizowane. +W przypadku przedmiotów będziemy wymagać jedynie implementacji interfejsu [ERC-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol?ref=hackernoon.com), który jest po prostu sposobem reprezentacji rzeczywistych przedmiotów w blockchainie, chociaż [najlepiej działa w przypadku aktywów cyfrowych](https://hackernoon.com/tokenization-of-digital-assets-g0ffk3v8s?ref=hackernoon.com). Zamierzamy określić nasz własny kontrakt ERC721 w konstruktorze, co oznacza, że wszelkie aktywa w naszym serwisie muszą być uprzednio tokenizowane. W przypadku płatności zamierzamy zrobić coś podobnego. Większość projektów blockchain definiuje własną kryptowalutę [ERC-20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol?ref=hackernoon.com). Inni wolą korzystać z głównego nurtu takiego jak DAI. W tym serwisie ogłoszeniowym musisz tylko zdecydować podczas budowy, jaka będzie Twoja waluta. Łatwo. @@ -127,18 +123,18 @@ function cancelTrade(uint256 _trade) Trade memory trade = trades[_trade]; require( msg.sender == trade.poster, - "Trade can be cancelled only by poster." + "Ogłoszenie może być anulowane tylko przez wystawiającego." ); - require(trade.status == "Open", "Trade is not Open."); + require(trade.status == "Open", "Ogłoszenie nie jest otwarte."); itemToken.transferFrom(address(this), trade.poster, trade.item); trades[_trade].status = "Cancelled"; emit TradeStatusChange(_trade, "Cancelled"); } ``` -To już wszystko. Dotrwałeś do końca implementacji. To zaskakujące, jak kompaktowe są niektóre pojęcia biznesowe wyrażane w kodzie i jest to jeden z tych przypadków. Sprawdź pełną umowę [w naszym repozytorium](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol). +To już wszystko. Dotrwałeś do końca implementacji. To zaskakujące, jak kompaktowe są niektóre pojęcia biznesowe wyrażane w kodzie i jest to jeden z tych przypadków. Sprawdź pełny kontrakt [w naszym repozytorium](https://github.com/HQ20/contracts/blob/master/contracts/classifieds/Classifieds.sol). -## Podsumowanie {#conclusion} +## Wnioski {#conclusion} Serwisy ogłoszeniowe to powszechna konfiguracja rynku, która intensywnie rosła wraz z Internetem, stając się niezwykle popularnym modelem biznesowym z kilkoma monopolistycznymi zwycięzcami. @@ -146,4 +142,4 @@ Serwisy ogłoszeniowe stały się również łatwym narzędziem do replikacji w W tym artykule podjąłem próbę połączenia realiów biznesowych serwisów ogłoszeniowych z implementacją technologii. Ta wiedza powinna pomóc w stworzeniu wizji i planu implementacji, jeśli masz odpowiednie umiejętności. -Jak zawsze, jeśli chcesz zbudować coś fajnego i chciałbyś otrzymać jakąś radę, proszę [napisz do mnie](https://albertocuesta.es/)! Zawsze chętnie służę pomocą. +Jak zawsze, jeśli chcecie zbudować coś fajnego i przydałaby Wam się jakaś rada, [skontaktujcie się ze mną](https://albertocuesta.es/)! Zawsze chętnie służę pomocą.