diff --git a/content/sdk/10.js/00.ethers/04.guides/02.accounts-l1-l2.md b/content/sdk/10.js/00.ethers/04.guides/02.accounts-l1-l2.md index bd03ecb2..e8c9c38a 100644 --- a/content/sdk/10.js/00.ethers/04.guides/02.accounts-l1-l2.md +++ b/content/sdk/10.js/00.ethers/04.guides/02.accounts-l1-l2.md @@ -13,7 +13,42 @@ If you need background information on how L1<->L2 interactions work on ZKsync, c `Wallet` and `L1Signer` objects allow you to deposit funds from L1 to L2. - **More Information**: See the method specification [`Deposit`](/sdk/js/ethers/api/v5/accounts/wallet#deposit). -- **Example**: [Deposit ETH and ERC20 token](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/01_deposit.ts). +- **Example**: Deposit ETH and ERC20 token + +::collapsible + + ```sh +import { Provider, types, utils, Wallet } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const PRIVATE_KEY = process.env.PRIVATE_KEY; +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +async function main() { + console.log(`L2 balance before deposit: ${await wallet.getBalance()}`); + console.log(`L1 balance before deposit: ${await wallet.getBalanceL1()}`); + + const tx = await wallet.deposit({ + token: utils.ETH_ADDRESS, + to: await wallet.getAddress(), + amount: ethers.parseEther("0.00020"), + refundRecipient: await wallet.getAddress(), + }); + const receipt = await tx.wait(); + console.log(`Tx: ${receipt.hash}`); + + console.log(`L2 balance after deposit: ${await wallet.getBalance()}`); + console.log(`L1 balance after deposit: ${await wallet.getBalanceL1()}`); +} + +main() + .then() + .catch((error) => { + console.log(`Error: ${error}`); + }); +:: ## Request execute @@ -44,4 +79,38 @@ If you need background information on how L1<->L2 interactions work on ZKsync, c `Wallet` and `Signer` objects enable you to withdraw funds from L2 to L1. - **More Information**: See the method specification [`Withdraw`](/sdk/js/ethers/api/v5/accounts/wallet#withdraw). -- **Example**: [Withdraw ETH and ERC20 token](https://github.com/zksync-sdk/zksync2-examples/blob/main/js/src/04_withdraw.ts). +- **Example**: Withdraw ETH and ERC20 token + +::collapsible + ``` + + import { Provider, types, utils, Wallet } from "zksync-ethers"; +import { ethers } from "ethers"; + +const provider = Provider.getDefaultProvider(types.Network.Sepolia); +const ethProvider = ethers.getDefaultProvider("sepolia"); +const PRIVATE_KEY = process.env.PRIVATE_KEY; +const wallet = new Wallet(PRIVATE_KEY, provider, ethProvider); + +async function main() { + console.log(`L2 balance before withdraw: ${await wallet.getBalance()}`); + console.log(`L1 balance before withdraw: ${await wallet.getBalanceL1()}`); + const tx = await wallet.withdraw({ + token: utils.ETH_ADDRESS, + to: await wallet.getAddress(), + amount: ethers.parseEther("0.00020"), + }); + const receipt = await tx.wait(); + console.log(`Tx: ${receipt.hash}`); + // The duration for submitting a withdrawal transaction to L1 can last up to 24 hours. For additional information, + // please refer to the documentation: https://era.zksync.io/docs/reference/troubleshooting/withdrawal-delay.html. + // Once the withdrawal transaction is submitted on L1, it needs to be finalized. + // To learn more about how to achieve this, please take a look at the 04_finalize_withdraw.ts script. +} + +main() + .then() + .catch((error) => { + console.log(`Error: ${error}`); + }); +:: diff --git a/content/sdk/20.go/00.introduction/01.why-zksync2-go.md b/content/sdk/20.go/00.introduction/01.why-zksync2-go.md index cbe12581..a7f116bb 100644 --- a/content/sdk/20.go/00.introduction/01.why-zksync2-go.md +++ b/content/sdk/20.go/00.introduction/01.why-zksync2-go.md @@ -1,6 +1,6 @@ --- title: Why zksync2-go -description: Benefits and Advantages of using `zksync2-go` +description: Benefits and Advantages of using zksync2-go tags: ["zksync", "zksync2-go", "ethereum", "layer-2", "zero-knowledge rollups", "go library"] --- diff --git a/content/sdk/20.go/01.guides/00.getting-started.md b/content/sdk/20.go/01.guides/00.getting-started.md index 611121b6..e9bd8512 100644 --- a/content/sdk/20.go/01.guides/00.getting-started.md +++ b/content/sdk/20.go/01.guides/00.getting-started.md @@ -91,14 +91,856 @@ fmt.Printf("%+v\n", transactionByHash) Also, the following examples demonstrate how to: -1. [Deposit ETH and tokens from Ethereum into ZKsync Era](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/01_deposit.go). -2. [Transfer ETH and tokens on ZKsync Era](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/02_transfer.go). -3. [Withdraw ETH and tokens from ZKsync Era to Ethereum](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/03_withdraw.go). -4. [Deploy a smart contract using CREATE method](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/08_deploy_create.go). -5. [Deploy a smart contract using CREATE2 method](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/11_deploy_create2.go). -6. [Deploy custom token on ZKsync Era](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/14_deploy_token_create.go). -7. [Deploy smart account](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/16_deploy_create_account.go). -8. [Use paymaster to pay fee with token](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/18_use_paymaster.go). - -Full code for all examples is available in the [documentation](https://github.com/zksync-sdk/zksync2-examples/tree/main/go). +1. Deposit ETH and tokens from Ethereum into ZKsync Era + +::collapsible + + ```sh +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "math/big" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + EthereumProvider = "https://rpc.ankr.com/eth_sepolia" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Connect to Ethereum network + ethClient, err := ethclient.Dial(EthereumProvider) + if err != nil { + log.Panic(err) + } + defer ethClient.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, ethClient) + if err != nil { + log.Panic(err) + } + + // Show balance before deposit + balance, err := wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Balance before deposit: ", balance) + + // Perform deposit + tx, err := wallet.Deposit(nil, accounts.DepositTransaction{ + Token: utils.EthAddress, + Amount: big.NewInt(1_000_000_000), + To: wallet.Address(), + }) + if err != nil { + log.Panic(err) + } + fmt.Println("L1 transaction: ", tx.Hash()) + + // Wait for deposit transaction to be finalized on L1 network + fmt.Println("Waiting for deposit transaction to be finalized on L1 network") + _, err = bind.WaitMined(context.Background(), ethClient, tx) + if err != nil { + log.Panic(err) + } + + // Get transaction receipt for deposit transaction on L1 network + l1Receipt, err := ethClient.TransactionReceipt(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + // Get deposit transaction on L2 network + l2Tx, err := client.L2TransactionFromPriorityOp(context.Background(), l1Receipt) + if err != nil { + log.Panic(err) + } + + fmt.Println("L2 transaction", l2Tx.Hash) + + // Wait for deposit transaction to be finalized on L2 network (5-7 minutes) + fmt.Println("Waiting for deposit transaction to be finalized on L2 network (5-7 minutes)") + _, err = client.WaitMined(context.Background(), l2Tx.Hash) + if err != nil { + log.Panic(err) + } + + balance, err = wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Balance after deposit: ", balance) + + /* + // ClaimFailedDeposit is used when transaction on L2 has failed. + cfdTx, err := wallet.ClaimFailedDeposit(nil, l2Tx.Hash) + if err != nil { + fmt.Println(err) // this should be triggered if deposit was successful + } + fmt.Println("ClaimFailedDeposit hash: ", cfdTx.Hash()) + */ +} +:: + +2. Transfer ETH and tokens on ZKsync Era + +::collapsible + + ```sh + +package main +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "math/big" + "os" +) + +func main() { + var ( + PrivateKey1 = os.Getenv("PRIVATE_KEY") + PublicKey2 = "0x81E9D85b65E9CC8618D85A1110e4b1DF63fA30d9" + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + ) + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey1), &client, nil) + if err != nil { + log.Panic(err) + } + // Show balances before transfer for both accounts + account1Balance, err := wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + account2Balance, err := client.BalanceAt(context.Background(), common.HexToAddress(PublicKey2), nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Account1 balance before transfer: ", account1Balance) + fmt.Println("Account2 balance before transfer: ", account2Balance) + // Perform transfer + tx, err := wallet.Transfer(nil, accounts.TransferTransaction{ + To: common.HexToAddress(PublicKey2), + Amount: big.NewInt(1_000_000_000), + Token: utils.EthAddress, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Transaction: ", tx.Hash()) + // Wait for transaction to be finalized on L2 network + _, err = client.WaitMined(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + // Show balances after transfer for both accounts + account1Balance, err = wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + account2Balance, err = client.BalanceAt(context.Background(), common.HexToAddress(PublicKey2), nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Account1 balance after transfer: ", account1Balance) + fmt.Println("Account2 balance after transfer: ", account2Balance) +} +:: +3. Withdraw ETH and tokens from ZKsync Era to Ethereum + +::collapsible + + ```sh + +package main + +import ( + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "math/big" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + EthereumProvider = "https://rpc.ankr.com/eth_sepolia" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Connect to Ethereum network + ethClient, err := ethclient.Dial(EthereumProvider) + if err != nil { + log.Panic(err) + } + defer ethClient.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, ethClient) + if err != nil { + log.Panic(err) + } + + // Perform withdrawal + tx, err := wallet.Withdraw(nil, accounts.WithdrawalTransaction{ + To: wallet.Address(), + Amount: big.NewInt(1_000_000_000), + Token: utils.EthAddress, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Withdraw transaction: ", tx.Hash()) + + // The duration for submitting a withdrawal transaction to L1 can last up to 24 hours. For additional information, + // please refer to the documentation: https://era.zksync.io/docs/reference/troubleshooting/withdrawal-delay.html. + // Once the withdrawal transaction is submitted on L1, it needs to be finalized. + // To learn more about how to achieve this, please take a look at the 04_finalize_withdraw.go script. +} +:: +4. Deploy a smart contract using CREATE method + +::collapsible + + ```sh + + package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/types" + "log" + "math/big" + "os" + "zksync2-examples/contracts/storage" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + // Read smart contract bytecode + bytecode, err := os.ReadFile("../solidity/storage/build/Storage.zbin") + if err != nil { + log.Panic(err) + } + + //Deploy smart contract + hash, err := wallet.DeployWithCreate(nil, accounts.CreateTransaction{Bytecode: bytecode}) + if err != nil { + panic(err) + } + fmt.Println("Transaction: ", hash) + + // Wait unit transaction is finalized + receipt, err := client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + contractAddress := receipt.ContractAddress + fmt.Println("Smart contract address", contractAddress.String()) + + // INTERACT WITH SMART CONTRACT + + // Create instance of Storage smart contract + storageContract, err := storage.NewStorage(contractAddress, client) + if err != nil { + log.Panic(err) + } + + abi, err := storage.StorageMetaData.GetAbi() + if err != nil { + log.Panic(err) + } + // Encode set function arguments + setArguments, err := abi.Pack("set", big.NewInt(700)) + if err != nil { + log.Panic(err) + } + gas, err := client.EstimateGasL2(context.Background(), types.CallMsg{ + CallMsg: ethereum.CallMsg{ + To: &contractAddress, + From: wallet.Address(), + Data: setArguments, + }, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Gas: ", gas) + + result, err := wallet.CallContract(context.Background(), accounts.CallMsg{ + To: &contractAddress, + Data: setArguments, + }, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Result: ", result) + + // Execute Get method from storage smart contract + value, err := storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value:", value) + + // Start configuring transaction parameters + opts, err := bind.NewKeyedTransactorWithChainID(wallet.Signer().PrivateKey(), wallet.Signer().Domain().ChainId) + if err != nil { + log.Panic(err) + } + + // Execute Set method from storage smart contract with configured transaction parameters + tx, err := storageContract.Set(opts, big.NewInt(200)) + if err != nil { + log.Panic(err) + } + // Wait for transaction to be finalized + _, err = client.WaitMined(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + + // Execute Get method again to check if state is changed + value, err = storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value after first Set method execution: ", value) + + // INTERACT WITH SMART CONTRACT USING EIP-712 TRANSACTIONS + abi, err = storage.StorageMetaData.GetAbi() + if err != nil { + log.Panic(err) + } + // Encode set function arguments + setArguments, err = abi.Pack("set", big.NewInt(500)) + if err != nil { + log.Panic(err) + } + // Execute set function + execute, err := wallet.SendTransaction(context.Background(), &accounts.Transaction{ + To: &contractAddress, + Data: setArguments, + }) + if err != nil { + log.Panic(err) + } + + _, err = client.WaitMined(context.Background(), execute) + if err != nil { + log.Panic(err) + } + + // Execute Get method again to check if state is changed + value, err = storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value after second Set method execution: ", value) +} +:: + +5. Deploy a smart contract using CREATE2 method + +::collapsible + + ```sh + +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "log" + "math/big" + "os" + "zksync2-examples/contracts/storage" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + // Read smart contract bytecode + bytecode, err := os.ReadFile("../solidity/storage/build/Storage.zbin") + if err != nil { + log.Panic(err) + } + + //Deploy smart contract + hash, err := wallet.Deploy(nil, accounts.Create2Transaction{Bytecode: bytecode}) + if err != nil { + log.Panic(err) + } + fmt.Println("Transaction: ", hash) + + // Wait unit transaction is finalized + receipt, err := client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + contractAddress := receipt.ContractAddress + fmt.Println("Smart contract address", contractAddress.String()) + + // INTERACT WITH SMART CONTRACT + + // Create instance of Storage smart contract + storageContract, err := storage.NewStorage(contractAddress, client) + if err != nil { + log.Panic(err) + } + + // Execute Get method from storage smart contract + value, err := storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value:", value) + + // Start configuring transaction parameters + opts, err := bind.NewKeyedTransactorWithChainID(wallet.Signer().PrivateKey(), wallet.Signer().Domain().ChainId) + if err != nil { + log.Panic(err) + } + + // Execute Set method from storage smart contract with configured transaction parameters + tx, err := storageContract.Set(opts, big.NewInt(200)) + if err != nil { + log.Panic(err) + } + // Wait for transaction to be finalized + _, err = client.WaitMined(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + + // Execute Get method again to check if state is changed + value, err = storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value after Set method execution: ", value) +} +:: +6. Deploy custom token on ZKsync Era + +::collapsible + + ```sh + +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "log" + "os" + "zksync2-examples/contracts/token" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + tokenAbi, err := token.TokenMetaData.GetAbi() + if err != nil { + log.Panic(err) + } + + constructor, err := tokenAbi.Pack("", "Crown", "Crown", uint8(18)) + if err != nil { + log.Panic(err) + } + + //Deploy smart contract + hash, err := wallet.DeployWithCreate(nil, accounts.CreateTransaction{ + Bytecode: common.FromHex(token.TokenMetaData.Bin), + Calldata: constructor, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Transaction: ", hash) + + // Wait unit transaction is finalized + receipt, err := client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + // Get address of deployed smart contract + tokenAddress := receipt.ContractAddress + fmt.Println("Token address", tokenAddress.String()) + + // Create instance of token contract + tokenContract, err := token.NewToken(tokenAddress, client) + if err != nil { + log.Panic(err) + } + + symbol, err := tokenContract.Symbol(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Symbol: ", symbol) + + decimals, err := tokenContract.Decimals(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Decimals: ", decimals) +} +:: +7. Deploy smart account + +::collapsible + + ```sh + +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + TokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F" // Crown token which can be minted for free + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + _, paymasterAbi, bytecode, err := utils.ReadStandardJson("../solidity/custom_paymaster/paymaster/build/Paymaster.json") + if err != nil { + log.Panic(err) + } + + constructor, err := paymasterAbi.Pack("", common.HexToAddress(TokenAddress)) + if err != nil { + log.Panic(err) + } + + // Deploy paymaster contract + hash, err := wallet.DeployAccountWithCreate(nil, accounts.CreateTransaction{ + Bytecode: bytecode, + Calldata: constructor, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Transaction: ", hash) + + // Wait unit transaction is finalized + receipt, err := client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + contractAddress := receipt.ContractAddress + fmt.Println("Paymaster address", contractAddress.String()) +} +:: +8. Use paymaster to pay fee with token + +::collapsible + + ```sh + +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/types" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "math/big" + "os" + "zksync2-examples/contracts/token" +) + +/* +This example demonstrates how to use a paymaster to facilitate fee payment with an ERC20 token. +The user initiates a mint transaction that is configured to be paid with an ERC20 token through the paymaster. +During transaction execution, the paymaster receives the ERC20 token from the user and covers the transaction fee using ETH. +*/ +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + TokenAddress = common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") // Crown tokenContract which can be minted for free + PaymasterAddress = common.HexToAddress("0x13D0D8550769f59aa241a41897D4859c87f7Dd46") // Paymaster for Crown tokenContract + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + // Get tokenContract contract + tokenContract, err := token.NewToken(TokenAddress, client) + if err != nil { + log.Panic(tokenContract) + } + + // Transfer some ETH to paymaster, so it can pay fee with ETH + transferTx, err := wallet.Transfer(nil, accounts.TransferTransaction{ + To: PaymasterAddress, + Amount: big.NewInt(2_000_000_000_000_000_000), + Token: utils.EthAddress, + }) + if err != nil { + log.Panic(err) + } + + _, err = client.WaitMined(context.Background(), transferTx.Hash()) + if err != nil { + log.Panic(err) + } + + // Also mint some tokens to user account, so it can offer to pay fee with it + opts, err := bind.NewKeyedTransactorWithChainID(wallet.Signer().PrivateKey(), wallet.Signer().Domain().ChainId) + if err != nil { + log.Panic(err) + } + tx, err := tokenContract.Mint(opts, wallet.Address(), big.NewInt(10)) + if err != nil { + log.Panic(err) + } + _, err = client.WaitMined(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + + // Read tokenContract and ETH balances from user and paymaster accounts + balance, err := wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Account balance before mint: ", balance) + + tokenBalance, err := tokenContract.BalanceOf(nil, wallet.Address()) + if err != nil { + log.Panic(err) + } + fmt.Println("Account tokenContract balance before mint: ", tokenBalance) + + balance, err = client.BalanceAt(context.Background(), PaymasterAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Paymaster balance before mint: ", balance) + + tokenBalance, err = tokenContract.BalanceOf(nil, TokenAddress) + if err != nil { + log.Panic(err) + } + fmt.Println("Paymaster tokenContract balance before mint: ", tokenBalance) + + abi, err := token.TokenMetaData.GetAbi() + if err != nil { + log.Panic(err) + } + + // Encode mint function from tokenContract contract + calldata, err := abi.Pack("mint", wallet.Address(), big.NewInt(7)) + if err != nil { + log.Panic(err) + } + + // Create paymaster parameters with encoded paymaster input + paymasterParams, err := utils.GetPaymasterParams( + PaymasterAddress, + &types.ApprovalBasedPaymasterInput{ + Token: TokenAddress, + MinimalAllowance: big.NewInt(1), + InnerInput: []byte{}, + }) + if err != nil { + log.Panic(err) + } + + // In order to use paymaster, EIP712 transaction + // need to be created with configured paymaster parameters + hash, err := wallet.SendTransaction(context.Background(), &accounts.Transaction{ + To: &TokenAddress, + Data: calldata, + Meta: &types.Eip712Meta{ + PaymasterParams: paymasterParams, + }, + }) + if err != nil { + log.Panic(err) + } + + _, err = client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + fmt.Println("Tx: ", hash) + + balance, err = wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Account balance after mint: ", balance) + + tokenBalance, err = tokenContract.BalanceOf(nil, wallet.Address()) + if err != nil { + log.Panic(err) + } + fmt.Println("Account tokenContract balance after mint: ", tokenBalance) + + balance, err = client.BalanceAt(context.Background(), PaymasterAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Paymaster balance after mint: ", balance) + + tokenBalance, err = tokenContract.BalanceOf(nil, TokenAddress) + if err != nil { + log.Panic(err) + } + fmt.Println("Paymaster tokenContract balance after mint: ", tokenBalance) + +} +:: + Examples are configured to interact with `ZKsync Era`, and `Sepolia` test networks. diff --git a/content/sdk/20.go/01.guides/02.accounts-l1-l2.md b/content/sdk/20.go/01.guides/02.accounts-l1-l2.md index 2bd753ae..41f13fd4 100644 --- a/content/sdk/20.go/01.guides/02.accounts-l1-l2.md +++ b/content/sdk/20.go/01.guides/02.accounts-l1-l2.md @@ -20,8 +20,251 @@ specification [`Deposit`](/sdk/go/api/accounts/walletl1#deposit). For a comprehensive example demonstrating the deposit workflow, refer to the following: -- [Deposit ETH](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/01_deposit.go). -- [Deposit ERC20 tokens](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/05_deposit_token.go). +- Deposit ETH + +::collapsible + + ```sh +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "math/big" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + EthereumProvider = "https://rpc.ankr.com/eth_sepolia" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Connect to Ethereum network + ethClient, err := ethclient.Dial(EthereumProvider) + if err != nil { + log.Panic(err) + } + defer ethClient.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, ethClient) + if err != nil { + log.Panic(err) + } + + // Show balance before deposit + balance, err := wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Balance before deposit: ", balance) + + // Perform deposit + tx, err := wallet.Deposit(nil, accounts.DepositTransaction{ + Token: utils.EthAddress, + Amount: big.NewInt(1_000_000_000), + To: wallet.Address(), + }) + if err != nil { + log.Panic(err) + } + fmt.Println("L1 transaction: ", tx.Hash()) + + // Wait for deposit transaction to be finalized on L1 network + fmt.Println("Waiting for deposit transaction to be finalized on L1 network") + _, err = bind.WaitMined(context.Background(), ethClient, tx) + if err != nil { + log.Panic(err) + } + + // Get transaction receipt for deposit transaction on L1 network + l1Receipt, err := ethClient.TransactionReceipt(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + // Get deposit transaction on L2 network + l2Tx, err := client.L2TransactionFromPriorityOp(context.Background(), l1Receipt) + if err != nil { + log.Panic(err) + } + + fmt.Println("L2 transaction", l2Tx.Hash) + + // Wait for deposit transaction to be finalized on L2 network (5-7 minutes) + fmt.Println("Waiting for deposit transaction to be finalized on L2 network (5-7 minutes)") + _, err = client.WaitMined(context.Background(), l2Tx.Hash) + if err != nil { + log.Panic(err) + } + + balance, err = wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Balance after deposit: ", balance) + + /* + // ClaimFailedDeposit is used when transaction on L2 has failed. + cfdTx, err := wallet.ClaimFailedDeposit(nil, l2Tx.Hash) + if err != nil { + fmt.Println(err) // this should be triggered if deposit was successful + } + fmt.Println("ClaimFailedDeposit hash: ", cfdTx.Hash()) + */ +} +:: +- Deposit ERC20 tokens + +::collapsible + ```sh + +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/contracts/erc20" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "math/big" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + EthereumProvider = "https://rpc.ankr.com/eth_sepolia" + TokenL1Address = common.HexToAddress("0x56E69Fa1BB0d1402c89E3A4E3417882DeA6B14Be") + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Connect to Ethereum network + ethClient, err := ethclient.Dial(EthereumProvider) + if err != nil { + log.Panic(err) + } + defer ethClient.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, ethClient) + if err != nil { + log.Panic(err) + } + + // Get token contract on Ethereum network + tokenL1, err := erc20.NewIERC20(TokenL1Address, ethClient) + if err != nil { + log.Panic(err) + } + + // Show balances before deposit + balance, err := wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + tokenBalance, err := tokenL1.BalanceOf(nil, wallet.Address()) + if err != nil { + log.Panic(err) + } + + fmt.Println("Balance before deposit on L1 network: ", balance) + fmt.Println("Token balance before deposit on L1 network: ", tokenBalance) + + tx, err := wallet.Deposit(nil, accounts.DepositTransaction{ + Token: TokenL1Address, + Amount: big.NewInt(5), + To: wallet.Address(), + ApproveERC20: true, + RefundRecipient: wallet.Address(), + }) + if err != nil { + log.Panic(err) + } + + fmt.Println("L1 deposit transaction: ", tx.Hash()) + + // Wait for deposit transaction to be finalized on L1 network + fmt.Println("Waiting for deposit transaction to be finalized on L1 network") + _, err = bind.WaitMined(context.Background(), ethClient, tx) + if err != nil { + log.Panic(err) + } + + // Get transaction receipt for deposit transaction on L1 network + l1Receipt, err := ethClient.TransactionReceipt(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + + // Get deposit transaction hash on L2 network + l2Tx, err := client.L2TransactionFromPriorityOp(context.Background(), l1Receipt) + if err != nil { + log.Panic(err) + } + fmt.Println("L2 transaction", l2Tx.Hash) + + // Wait for deposit transaction to be finalized on L2 network (5-7 minutes) + fmt.Println("Waiting for deposit transaction to be finalized on L2 network (5-7 minutes)") + _, err = client.WaitMined(context.Background(), l2Tx.Hash) + if err != nil { + log.Panic(err) + } + + balance, err = wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + tokenBalance, err = tokenL1.BalanceOf(nil, wallet.Address()) + if err != nil { + log.Panic(err) + } + + fmt.Println("Balance after deposit on L1 network: ", balance) + fmt.Println("Token balance after deposit on L1 network: ", tokenBalance) + + tokenL2Address, err := client.L2TokenAddress(context.Background(), TokenL1Address) + if err != nil { + log.Panic(err) + } + + fmt.Println("Token L2 address: ", tokenL2Address) + + tokenL2Balance, err := wallet.Balance(context.Background(), tokenL2Address, nil) + if err != nil { + log.Panic(err) + } + + fmt.Println("Token balance on L2 network: ", tokenL2Balance) +} +:: ## Request execute @@ -50,5 +293,129 @@ please refer to the method specification [`Deposit`](/sdk/go/api/accounts/wallet For a complete example of how to execute the deposit workflow, take a look at the following: -- [Withdraw ETH](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/03_withdraw.go). -- [Withdraw ERC20 token](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/07_withdraw_token.go). +- Withdraw ETH + +::collapsible + + ```sh +package main + +import ( + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "math/big" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + EthereumProvider = "https://rpc.ankr.com/eth_sepolia" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Connect to Ethereum network + ethClient, err := ethclient.Dial(EthereumProvider) + if err != nil { + log.Panic(err) + } + defer ethClient.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, ethClient) + if err != nil { + log.Panic(err) + } + + // Perform withdrawal + tx, err := wallet.Withdraw(nil, accounts.WithdrawalTransaction{ + To: wallet.Address(), + Amount: big.NewInt(1_000_000_000), + Token: utils.EthAddress, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Withdraw transaction: ", tx.Hash()) + + // The duration for submitting a withdrawal transaction to L1 can last up to 24 hours. For additional information, + // please refer to the documentation: https://era.zksync.io/docs/reference/troubleshooting/withdrawal-delay.html. + // Once the withdrawal transaction is submitted on L1, it needs to be finalized. + // To learn more about how to achieve this, please take a look at the 04_finalize_withdraw.go script. +} +:: +- Withdraw ERC20 token + +::collapsible + ```sh + +package main + +import ( + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "log" + "math/big" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + EthereumProvider = "https://rpc.ankr.com/eth_sepolia" + TokenL2Address = common.HexToAddress("0x6a4Fb925583F7D4dF82de62d98107468aE846FD1") + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Connect to Ethereum network + ethClient, err := ethclient.Dial(EthereumProvider) + if err != nil { + log.Panic(err) + } + defer ethClient.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, ethClient) + if err != nil { + log.Panic(err) + } + + // Perform withdraw + tx, err := wallet.Withdraw(nil, accounts.WithdrawalTransaction{ + To: wallet.Address(), + Amount: big.NewInt(1), + Token: TokenL2Address, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Withdraw transaction: ", tx.Hash()) + + // The duration for submitting a withdrawal transaction to L1 can last up to 24 hours. For additional information, + // please refer to the documentation: https://era.zksync.io/docs/reference/troubleshooting/withdrawal-delay.html. + // Once the withdrawal transaction is submitted on L1, it needs to be finalized. + // To learn more about how to achieve this, please take a look at the 04_finalize_withdraw.go script. +} +:: diff --git a/content/sdk/20.go/02.api/03.contracts/00.contracts.md b/content/sdk/20.go/02.api/03.contracts/00.contracts.md index 0c86db80..7af131d6 100644 --- a/content/sdk/20.go/02.api/03.contracts/00.contracts.md +++ b/content/sdk/20.go/02.api/03.contracts/00.contracts.md @@ -17,10 +17,407 @@ Contract instantiation is the same as in the [`geth`](https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings) library. For examples of how to deploy and instantiate contracts and accounts, refer to the following: -- [Deploy smart contracts using CREATE method](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/08_deploy_create.go). -- [Deploy smart contracts using CREATE2 method](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/11_deploy_create2.go). -- [Deploy smart accounts using CREATE method](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/16_deploy_create_account.go). -- [Deploy smart accounts using CREATE2 method](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/17_deploy_create2_account.go). +- Deploy smart contracts using `CREATE` method + +::collapsible + + ```sh +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/types" + "log" + "math/big" + "os" + "zksync2-examples/contracts/storage" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + // Read smart contract bytecode + bytecode, err := os.ReadFile("../solidity/storage/build/Storage.zbin") + if err != nil { + log.Panic(err) + } + + //Deploy smart contract + hash, err := wallet.DeployWithCreate(nil, accounts.CreateTransaction{Bytecode: bytecode}) + if err != nil { + panic(err) + } + fmt.Println("Transaction: ", hash) + + // Wait unit transaction is finalized + receipt, err := client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + contractAddress := receipt.ContractAddress + fmt.Println("Smart contract address", contractAddress.String()) + + // INTERACT WITH SMART CONTRACT + + // Create instance of Storage smart contract + storageContract, err := storage.NewStorage(contractAddress, client) + if err != nil { + log.Panic(err) + } + + abi, err := storage.StorageMetaData.GetAbi() + if err != nil { + log.Panic(err) + } + // Encode set function arguments + setArguments, err := abi.Pack("set", big.NewInt(700)) + if err != nil { + log.Panic(err) + } + gas, err := client.EstimateGasL2(context.Background(), types.CallMsg{ + CallMsg: ethereum.CallMsg{ + To: &contractAddress, + From: wallet.Address(), + Data: setArguments, + }, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Gas: ", gas) + + result, err := wallet.CallContract(context.Background(), accounts.CallMsg{ + To: &contractAddress, + Data: setArguments, + }, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Result: ", result) + + // Execute Get method from storage smart contract + value, err := storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value:", value) + + // Start configuring transaction parameters + opts, err := bind.NewKeyedTransactorWithChainID(wallet.Signer().PrivateKey(), wallet.Signer().Domain().ChainId) + if err != nil { + log.Panic(err) + } + + // Execute Set method from storage smart contract with configured transaction parameters + tx, err := storageContract.Set(opts, big.NewInt(200)) + if err != nil { + log.Panic(err) + } + // Wait for transaction to be finalized + _, err = client.WaitMined(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + + // Execute Get method again to check if state is changed + value, err = storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value after first Set method execution: ", value) + + // INTERACT WITH SMART CONTRACT USING EIP-712 TRANSACTIONS + abi, err = storage.StorageMetaData.GetAbi() + if err != nil { + log.Panic(err) + } + // Encode set function arguments + setArguments, err = abi.Pack("set", big.NewInt(500)) + if err != nil { + log.Panic(err) + } + // Execute set function + execute, err := wallet.SendTransaction(context.Background(), &accounts.Transaction{ + To: &contractAddress, + Data: setArguments, + }) + if err != nil { + log.Panic(err) + } + + _, err = client.WaitMined(context.Background(), execute) + if err != nil { + log.Panic(err) + } + + // Execute Get method again to check if state is changed + value, err = storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value after second Set method execution: ", value) +} +:: +- Deploy smart contracts using `CREATE2` method + +::collapsible + ```sh + +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "log" + "math/big" + "os" + "zksync2-examples/contracts/storage" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + // Read smart contract bytecode + bytecode, err := os.ReadFile("../solidity/storage/build/Storage.zbin") + if err != nil { + log.Panic(err) + } + + //Deploy smart contract + hash, err := wallet.Deploy(nil, accounts.Create2Transaction{Bytecode: bytecode}) + if err != nil { + log.Panic(err) + } + fmt.Println("Transaction: ", hash) + + // Wait unit transaction is finalized + receipt, err := client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + contractAddress := receipt.ContractAddress + fmt.Println("Smart contract address", contractAddress.String()) + + // INTERACT WITH SMART CONTRACT + + // Create instance of Storage smart contract + storageContract, err := storage.NewStorage(contractAddress, client) + if err != nil { + log.Panic(err) + } + + // Execute Get method from storage smart contract + value, err := storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value:", value) + + // Start configuring transaction parameters + opts, err := bind.NewKeyedTransactorWithChainID(wallet.Signer().PrivateKey(), wallet.Signer().Domain().ChainId) + if err != nil { + log.Panic(err) + } + + // Execute Set method from storage smart contract with configured transaction parameters + tx, err := storageContract.Set(opts, big.NewInt(200)) + if err != nil { + log.Panic(err) + } + // Wait for transaction to be finalized + _, err = client.WaitMined(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + + // Execute Get method again to check if state is changed + value, err = storageContract.Get(nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Value after Set method execution: ", value) +} +:: + +- Deploy smart accounts using `CREATE` method + +::collapsible + + ```sh +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + TokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F" // Crown token which can be minted for free + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + _, paymasterAbi, bytecode, err := utils.ReadStandardJson("../solidity/custom_paymaster/paymaster/build/Paymaster.json") + if err != nil { + log.Panic(err) + } + + constructor, err := paymasterAbi.Pack("", common.HexToAddress(TokenAddress)) + if err != nil { + log.Panic(err) + } + + // Deploy paymaster contract + hash, err := wallet.DeployAccountWithCreate(nil, accounts.CreateTransaction{ + Bytecode: bytecode, + Calldata: constructor, + }) + if err != nil { + log.Panic(err) + } + fmt.Println("Transaction: ", hash) + + // Wait unit transaction is finalized + receipt, err := client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + contractAddress := receipt.ContractAddress + fmt.Println("Paymaster address", contractAddress.String()) +} +:: +- Deploy smart accounts using `CREATE2` method + +::collapsible + ```sh + +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "os" +) + +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + TokenAddress = "0x927488F48ffbc32112F1fF721759649A89721F8F" // Crown token which can be minted for free + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + // Read paymaster contract from standard json + _, paymasterAbi, bytecode, err := utils.ReadStandardJson("../solidity/custom_paymaster/paymaster/build/Paymaster.json") + if err != nil { + log.Panic(err) + } + + // Encode paymaster constructor + constructor, err := paymasterAbi.Pack("", common.HexToAddress(TokenAddress)) + if err != nil { + log.Panic(err) + } + + // Deploy paymaster contract + hash, err := wallet.DeployAccount(nil, accounts.Create2Transaction{Bytecode: bytecode, Calldata: constructor}) + if err != nil { + log.Panic(err) + } + fmt.Println("Transaction: ", hash) + + // Wait unit transaction is finalized + receipt, err := client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + contractAddress := receipt.ContractAddress + fmt.Println("Paymaster address: ", contractAddress.String()) +} +:: ## Contracts interfaces diff --git a/content/sdk/20.go/02.api/04.utilities/01.paymaster-utils.md b/content/sdk/20.go/02.api/04.utilities/01.paymaster-utils.md index 28b678a2..fd1b00c8 100644 --- a/content/sdk/20.go/02.api/04.utilities/01.paymaster-utils.md +++ b/content/sdk/20.go/02.api/04.utilities/01.paymaster-utils.md @@ -72,4 +72,181 @@ func GetPaymasterParams(paymasterAddress common.Address, paymasterInput types.Pa ``` Find out more about the [`PaymasterInput` type](/sdk/go/api/types). -Check out the [example](https://github.com/zksync-sdk/zksync2-examples/blob/main/go/18_use_paymaster.go) how to use paymaster. + +Check out the example on how to use paymaster. + +::collapsible + + ```sh +package main + +import ( + "context" + "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/zksync-sdk/zksync2-go/accounts" + "github.com/zksync-sdk/zksync2-go/clients" + "github.com/zksync-sdk/zksync2-go/types" + "github.com/zksync-sdk/zksync2-go/utils" + "log" + "math/big" + "os" + "zksync2-examples/contracts/token" +) + +/* +This example demonstrates how to use a paymaster to facilitate fee payment with an ERC20 token. +The user initiates a mint transaction that is configured to be paid with an ERC20 token through the paymaster. +During transaction execution, the paymaster receives the ERC20 token from the user and covers the transaction fee using ETH. +*/ +func main() { + var ( + PrivateKey = os.Getenv("PRIVATE_KEY") + ZkSyncEraProvider = "https://sepolia.era.zksync.dev" + TokenAddress = common.HexToAddress("0x927488F48ffbc32112F1fF721759649A89721F8F") // Crown tokenContract which can be minted for free + PaymasterAddress = common.HexToAddress("0x13D0D8550769f59aa241a41897D4859c87f7Dd46") // Paymaster for Crown tokenContract + ) + + // Connect to ZKsync network + client, err := clients.Dial(ZkSyncEraProvider) + if err != nil { + log.Panic(err) + } + defer client.Close() + + // Create wallet + wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey), &client, nil) + if err != nil { + log.Panic(err) + } + + // Get tokenContract contract + tokenContract, err := token.NewToken(TokenAddress, client) + if err != nil { + log.Panic(tokenContract) + } + + // Transfer some ETH to paymaster, so it can pay fee with ETH + transferTx, err := wallet.Transfer(nil, accounts.TransferTransaction{ + To: PaymasterAddress, + Amount: big.NewInt(2_000_000_000_000_000_000), + Token: utils.EthAddress, + }) + if err != nil { + log.Panic(err) + } + + _, err = client.WaitMined(context.Background(), transferTx.Hash()) + if err != nil { + log.Panic(err) + } + + // Also mint some tokens to user account, so it can offer to pay fee with it + opts, err := bind.NewKeyedTransactorWithChainID(wallet.Signer().PrivateKey(), wallet.Signer().Domain().ChainId) + if err != nil { + log.Panic(err) + } + tx, err := tokenContract.Mint(opts, wallet.Address(), big.NewInt(10)) + if err != nil { + log.Panic(err) + } + _, err = client.WaitMined(context.Background(), tx.Hash()) + if err != nil { + log.Panic(err) + } + + // Read tokenContract and ETH balances from user and paymaster accounts + balance, err := wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Account balance before mint: ", balance) + + tokenBalance, err := tokenContract.BalanceOf(nil, wallet.Address()) + if err != nil { + log.Panic(err) + } + fmt.Println("Account tokenContract balance before mint: ", tokenBalance) + + balance, err = client.BalanceAt(context.Background(), PaymasterAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Paymaster balance before mint: ", balance) + + tokenBalance, err = tokenContract.BalanceOf(nil, TokenAddress) + if err != nil { + log.Panic(err) + } + fmt.Println("Paymaster tokenContract balance before mint: ", tokenBalance) + + abi, err := token.TokenMetaData.GetAbi() + if err != nil { + log.Panic(err) + } + + // Encode mint function from tokenContract contract + calldata, err := abi.Pack("mint", wallet.Address(), big.NewInt(7)) + if err != nil { + log.Panic(err) + } + + // Create paymaster parameters with encoded paymaster input + paymasterParams, err := utils.GetPaymasterParams( + PaymasterAddress, + &types.ApprovalBasedPaymasterInput{ + Token: TokenAddress, + MinimalAllowance: big.NewInt(1), + InnerInput: []byte{}, + }) + if err != nil { + log.Panic(err) + } + + // In order to use paymaster, EIP712 transaction + // need to be created with configured paymaster parameters + hash, err := wallet.SendTransaction(context.Background(), &accounts.Transaction{ + To: &TokenAddress, + Data: calldata, + Meta: &types.Eip712Meta{ + PaymasterParams: paymasterParams, + }, + }) + if err != nil { + log.Panic(err) + } + + _, err = client.WaitMined(context.Background(), hash) + if err != nil { + log.Panic(err) + } + + fmt.Println("Tx: ", hash) + + balance, err = wallet.Balance(context.Background(), utils.EthAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Account balance after mint: ", balance) + + tokenBalance, err = tokenContract.BalanceOf(nil, wallet.Address()) + if err != nil { + log.Panic(err) + } + fmt.Println("Account tokenContract balance after mint: ", tokenBalance) + + balance, err = client.BalanceAt(context.Background(), PaymasterAddress, nil) + if err != nil { + log.Panic(err) + } + fmt.Println("Paymaster balance after mint: ", balance) + + tokenBalance, err = tokenContract.BalanceOf(nil, TokenAddress) + if err != nil { + log.Panic(err) + } + fmt.Println("Paymaster tokenContract balance after mint: ", tokenBalance) + +} +:: diff --git a/content/sdk/30.python/00.introduction/00.overview.md b/content/sdk/30.python/00.introduction/00.overview.md index c6c91b6a..d5514229 100644 --- a/content/sdk/30.python/00.introduction/00.overview.md +++ b/content/sdk/30.python/00.introduction/00.overview.md @@ -18,11 +18,6 @@ To make it easy to use all ZKsync Era features, we created the `zksync2` Python to [web3.py](https://web3py.readthedocs.io/en/latest/index.html). In fact, `web3.py` is a peer dependency of our library. Most objects exported by `zksync2` inherit from `web3.py` objects and only change the fields that need adjustments. -::callout{icon="i-heroicons-light-bulb"} -Explore the [Python SDK examples](https://github.com/zksync-sdk/zksync2-examples/tree/main/python) to quickly and -efficiently develop applications with the ZKsync protocol. -:: - ## Key features - **Transaction management**: Easily create, sign, and send transactions on the ZKsync network. diff --git a/content/sdk/30.python/01.guides/00.getting-started.md b/content/sdk/30.python/01.guides/00.getting-started.md index 8bac4824..9de35759 100644 --- a/content/sdk/30.python/01.guides/00.getting-started.md +++ b/content/sdk/30.python/01.guides/00.getting-started.md @@ -47,5 +47,4 @@ Also, the following examples demonstrate how to: 3. [Withdraw ETH and tokens from ZKsync Era to Ethereum](https://github.com/zksync-sdk/zksync2-examples/blob/main/python/09_withdrawal.py) 4. [Use paymaster to pay fees with tokens](https://github.com/zksync-sdk/zksync2-examples/blob/main/python/15_use_paymaster.py) -Full code for all examples is available in the [Python ZKsync SDK](https://github.com/zksync-sdk/zksync2-examples/tree/main/python). Examples are configured to interact with `ZKsync Era` and `Sepolia` test networks.