From 4723c670bec171cae0df42dde196eeb67ad65e5b Mon Sep 17 00:00:00 2001 From: kopestinskij Date: Thu, 25 May 2023 13:47:01 +0200 Subject: [PATCH 1/3] initial --- build.gradle | 1 + src/main/java/io/zksync/ZkSyncWallet.java | 51 ++++--- .../io/zksync/methods/response/L2toL1Log.java | 107 ++++++++++++++ .../response/ZkTransactionReceipt.java | 16 ++- .../io/zksync/protocol/JsonRpc2_0ZkSync.java | 5 + src/main/java/io/zksync/protocol/ZkSync.java | 1 + .../provider/DefaultEthereumProvider.java | 75 ++++++++-- .../protocol/provider/EthereumProvider.java | 3 +- .../java/io/zksync/wrappers/L1Messenger.java | 134 ++++++++++++++++++ .../crypto/signer/SelfTransferTest.java | 46 ++++-- 10 files changed, 401 insertions(+), 38 deletions(-) create mode 100644 src/main/java/io/zksync/methods/response/L2toL1Log.java create mode 100644 src/main/java/io/zksync/wrappers/L1Messenger.java diff --git a/build.gradle b/build.gradle index 1149d9d..049bb24 100755 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,7 @@ publishing { name = 'ZkSync2 SDK' description = 'This repository provides a Java SDK for zkSync developers, which can be used either on PC or Android.' url = 'https://github.com/zksync-sdk/zksync2-java' + version = '0.1.0' organization { name = 'Matter Labs' url = 'https://matter-labs.io' diff --git a/src/main/java/io/zksync/ZkSyncWallet.java b/src/main/java/io/zksync/ZkSyncWallet.java index 64a8afe..88aab09 100644 --- a/src/main/java/io/zksync/ZkSyncWallet.java +++ b/src/main/java/io/zksync/ZkSyncWallet.java @@ -19,9 +19,11 @@ import org.web3j.abi.datatypes.Function; import org.web3j.abi.datatypes.generated.Uint256; import org.web3j.crypto.Credentials; +import org.web3j.crypto.Hash; import org.web3j.protocol.core.DefaultBlockParameter; import org.web3j.protocol.core.RemoteCall; import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.protocol.core.methods.response.Log; import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.protocol.exceptions.TransactionException; import org.web3j.tx.ReadonlyTransactionManager; @@ -183,28 +185,43 @@ public RemoteCall withdraw(String to, BigInteger amount, @Nu String l2Bridge; ArrayList parameters = new ArrayList(); parameters.add(new Address(to)); - + Transaction estimate; if (tokenToUse.isETH()) { + Function function = new Function( + IL2Bridge.FUNC_WITHDRAW, + Arrays.asList(new Address(to)), + Collections.emptyList()); + + String calldata = FunctionEncoder.encode(function); l2Bridge = L2_ETH_TOKEN_ADDRESS; + + estimate = Transaction.createFunctionCallTransaction( + signer.getAddress(), + l2Bridge, + BigInteger.ZERO, + BigInteger.ZERO, + amount, + calldata + ); } else { - parameters.add(new Address(tokenToUse.getL2Address())); - parameters.add(new Uint256(amount)); + Function function = new Function( + IL2Bridge.FUNC_WITHDRAW, + Arrays.asList(new Address(to), + new Address(tokenToUse.getL2Address()), + new Uint256(amount)), + Collections.emptyList()); + + String calldata = FunctionEncoder.encode(function); l2Bridge = zksync.zksGetBridgeContracts().send().getResult().getL2Erc20DefaultBridge(); - } - Function function = new Function( - IL2Bridge.FUNC_WITHDRAW, - parameters, - Collections.emptyList()); - String calldata = FunctionEncoder.encode(function); - - Transaction estimate = Transaction.createFunctionCallTransaction( - signer.getAddress(), - l2Bridge, - BigInteger.ZERO, - BigInteger.ZERO, - calldata - ); + estimate = Transaction.createFunctionCallTransaction( + signer.getAddress(), + l2Bridge, + BigInteger.ZERO, + BigInteger.ZERO, + calldata + ); + } EthSendTransaction sent = estimateAndSend(estimate, nonceToUse).join(); diff --git a/src/main/java/io/zksync/methods/response/L2toL1Log.java b/src/main/java/io/zksync/methods/response/L2toL1Log.java new file mode 100644 index 0000000..ca33a6f --- /dev/null +++ b/src/main/java/io/zksync/methods/response/L2toL1Log.java @@ -0,0 +1,107 @@ +package io.zksync.methods.response; + +import org.web3j.abi.datatypes.Address; + +import java.math.BigInteger; + +public class L2toL1Log { + public String BlockNumber; + public String BlockHash; + public String L1BatchNumber; + public String TransactionIndex; + public String ShardId; + public boolean IsService; + public Address Sender; + public String Key; + public String Value; + public String TxHash; + public int Index; + + public String getBlockNumber() { + return BlockNumber; + } + + public void setBlockNumber(String blockNumber) { + BlockNumber = blockNumber; + } + + public String getBlockHash() { + return BlockHash; + } + + public void setBlockHash(String blockHash) { + BlockHash = blockHash; + } + + public String getL1BatchNumber() { + return L1BatchNumber; + } + + public void setL1BatchNumber(String l1BatchNumber) { + L1BatchNumber = l1BatchNumber; + } + + public String getTransactionIndex() { + return TransactionIndex; + } + + public void setTransactionIndex(String transactionIndex) { + TransactionIndex = transactionIndex; + } + + public String getShardId() { + return ShardId; + } + + public void setShardId(String shardId) { + ShardId = shardId; + } + + public boolean isService() { + return IsService; + } + + public void setService(boolean service) { + IsService = service; + } + + public Address getSender() { + return Sender; + } + + public void setSender(Address sender) { + Sender = sender; + } + + public String getKey() { + return Key; + } + + public void setKey(String key) { + Key = key; + } + + public String getValue() { + return Value; + } + + public void setValue(String value) { + Value = value; + } + + public String getTxHash() { + return TxHash; + } + + public void setTxHash(String txHash) { + TxHash = txHash; + } + + public int getIndex() { + return Index; + } + + public void setIndex(int index) { + Index = index; + } +} diff --git a/src/main/java/io/zksync/methods/response/ZkTransactionReceipt.java b/src/main/java/io/zksync/methods/response/ZkTransactionReceipt.java index c28f69b..eb77efb 100644 --- a/src/main/java/io/zksync/methods/response/ZkTransactionReceipt.java +++ b/src/main/java/io/zksync/methods/response/ZkTransactionReceipt.java @@ -1,10 +1,13 @@ package io.zksync.methods.response; +import kotlin.OverloadResolutionByLambdaReturnType; +import kotlin.jvm.JvmOverloads; import org.web3j.protocol.core.methods.response.Log; import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.utils.Numeric; import java.math.BigInteger; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -12,13 +15,15 @@ public class ZkTransactionReceipt extends TransactionReceipt { private String l1BatchNumber; private String l1BatchTxIndex; + private List l2ToL1Logs; public ZkTransactionReceipt() { } - public ZkTransactionReceipt(String transactionHash, String transactionIndex, String blockHash, String blockNumber, String cumulativeGasUsed, String gasUsed, String contractAddress, String root, String status, String from, String to, List logs, String logsBloom, String revertReason, String type, String effectiveGasPrice, String l1BatchNumber, String l1BatchTxIndex) { + public ZkTransactionReceipt(String transactionHash, String transactionIndex, String blockHash, String blockNumber, String cumulativeGasUsed, String gasUsed, String contractAddress, String root, String status, String from, String to, List logs, String logsBloom, String revertReason, String type, String effectiveGasPrice, String l1BatchNumber, String l1BatchTxIndex, List l2ToL1Logs) { super(transactionHash, transactionIndex, blockHash, blockNumber, cumulativeGasUsed, gasUsed, contractAddress, root, status, from, to, logs, logsBloom, revertReason, type, effectiveGasPrice); this.l1BatchNumber = l1BatchNumber; this.l1BatchTxIndex = l1BatchTxIndex; + this.l2ToL1Logs = l2ToL1Logs; } public String getL1BatchNumberRaw() { @@ -29,7 +34,6 @@ public BigInteger getL1BatchNumber() { return Numeric.decodeQuantity(l1BatchNumber); } - public void setL1BatchNumber(String l1BatchNumber) { this.l1BatchNumber = l1BatchNumber; } @@ -46,6 +50,14 @@ public void setL1BatchTxIndex(String l1BatchTxIndex) { this.l1BatchTxIndex = l1BatchTxIndex; } + public void setL2ToL1Logs(List l2ToL1Logs) { + this.l2ToL1Logs = l2ToL1Logs; + } + + public List getl2ToL1Logs() { + return l2ToL1Logs; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/io/zksync/protocol/JsonRpc2_0ZkSync.java b/src/main/java/io/zksync/protocol/JsonRpc2_0ZkSync.java index 983ccab..7a508af 100755 --- a/src/main/java/io/zksync/protocol/JsonRpc2_0ZkSync.java +++ b/src/main/java/io/zksync/protocol/JsonRpc2_0ZkSync.java @@ -76,6 +76,11 @@ public Request zksGetL2ToL1MsgProof(Integer block, String se return new Request<>("zks_getL2ToL1MsgProof", Arrays.asList(block, sender, message), web3jService, ZksMessageProof.class); } + @Override + public Request zksGetL2ToL1LogProof(String txHash, int index) { + return new Request<>("zks_getL2ToL1LogProof", Arrays.asList(txHash, index), web3jService, ZksMessageProof.class); + } + @Override public Request ethEstimateGas(Transaction transaction) { return new Request<>( diff --git a/src/main/java/io/zksync/protocol/ZkSync.java b/src/main/java/io/zksync/protocol/ZkSync.java index c289b14..0480d23 100755 --- a/src/main/java/io/zksync/protocol/ZkSync.java +++ b/src/main/java/io/zksync/protocol/ZkSync.java @@ -86,6 +86,7 @@ static ZkSync build(Web3jService web3jService) { */ Request zksGetL2ToL1MsgProof(Integer block, String sender, String message, @Nullable Long l2LogPosition); + Request zksGetL2ToL1LogProof(String txHash, int index); /** * Generates and returns an estimate of how much gas is necessary to allow the transaction to complete. The transaction will not be added to the blockchain. * diff --git a/src/main/java/io/zksync/protocol/provider/DefaultEthereumProvider.java b/src/main/java/io/zksync/protocol/provider/DefaultEthereumProvider.java index 6eece7e..2b27d4a 100755 --- a/src/main/java/io/zksync/protocol/provider/DefaultEthereumProvider.java +++ b/src/main/java/io/zksync/protocol/provider/DefaultEthereumProvider.java @@ -1,24 +1,45 @@ package io.zksync.protocol.provider; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.*; -import java.util.concurrent.CompletableFuture; - -import io.zksync.wrappers.*; +import io.zksync.methods.response.L2toL1Log; +import io.zksync.methods.response.ZkLog; +import io.zksync.methods.response.ZkTransactionReceipt; +import io.zksync.methods.response.ZksMessageProof; +import io.zksync.protocol.ZkSync; +import io.zksync.protocol.core.L2ToL1MessageProof; +import io.zksync.protocol.core.Token; +import io.zksync.wrappers.ERC20; +import io.zksync.wrappers.IL1Bridge; +import io.zksync.wrappers.ZkSyncContract; +import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.Nullable; -import org.web3j.abi.datatypes.DynamicBytes; +import org.web3j.abi.EventValues; +import org.web3j.abi.FunctionEncoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.Utils; +import org.web3j.abi.datatypes.*; +import org.web3j.abi.datatypes.generated.Bytes32; +import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.crypto.Hash; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.methods.response.EthGasPrice; +import org.web3j.protocol.core.methods.response.Log; import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tx.Contract; import org.web3j.tx.TransactionManager; import org.web3j.tx.Transfer; import org.web3j.tx.gas.ContractGasProvider; import org.web3j.tx.gas.StaticGasProvider; import org.web3j.utils.Convert.Unit; +import org.web3j.utils.Numeric; -import io.zksync.protocol.core.Token; -import lombok.RequiredArgsConstructor; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import static io.zksync.utils.ZkSyncAddresses.MESSENGER_ADDRESS; +import static io.zksync.wrappers.L1Messenger.L1MESSAGESENT_EVENT; @RequiredArgsConstructor public class DefaultEthereumProvider implements EthereumProvider { @@ -33,6 +54,7 @@ public class DefaultEthereumProvider implements EthereumProvider { private static final BigInteger L1_TO_L2_GAS_PER_PUBDATA = BigInteger.valueOf(800); private final Web3j web3j; + private final ZkSync zkSync; private final TransactionManager transactionManager; private final ContractGasProvider gasProvider; private final ZkSyncContract contract; @@ -106,6 +128,41 @@ public CompletableFuture withdraw(Token token, BigInteger am throw new UnsupportedOperationException(); } + @Override + public TransactionReceipt finalizeWithdraw(String txHash, int index) throws IOException { + ZkTransactionReceipt receipt = zkSync.zksGetTransactionReceipt(txHash).sendAsync().join().getResult(); + + String topic = "L1MessageSent(address,bytes32,bytes)"; + List logs = receipt.getLogs(); + List msgsIndex = new ArrayList<>(); + for (int i = 0 ; i < logs.size() ; i++){ + if (logs.get(i).getAddress().equals(MESSENGER_ADDRESS) && Arrays.equals(logs.get(i).getTopics().get(0).getBytes(), Hash.sha3String(topic).getBytes())){ + msgsIndex.add(i); + } + } + + List msgsL2Index = new ArrayList<>(); + List l2toL1Logs = receipt.getl2ToL1Logs(); + for (int i = 0 ; i < l2toL1Logs.size() ; i++){ + if (l2toL1Logs.get(i).getSender().getValue().equals(MESSENGER_ADDRESS)){ + msgsL2Index.add(i); + } + } + + L2ToL1MessageProof l2ToL1MessageProof = zkSync.zksGetL2ToL1LogProof(txHash, msgsL2Index.get(index)).sendAsync().join().getResult(); + Log log = logs.get(msgsIndex.get(index)); + EventValues eventValues = Contract.staticExtractEventParameters(L1MESSAGESENT_EVENT, log); + byte[] bytes_data = eventValues.getIndexedValues().toString().getBytes(); + BigInteger l1BatchNumber = receipt.getL1BatchNumber(); + + List merkle_proof = new ArrayList<>(); + for (int i = 0 ; i < l2ToL1MessageProof.getProof().size() ; i++){ + merkle_proof.add(Numeric.hexStringToByteArray(l2ToL1MessageProof.getProof().get(i))); + } + + return contract.finalizeEthWithdrawal(l1BatchNumber, BigInteger.valueOf(l2ToL1MessageProof.getId()), receipt.getL1BatchTxIndex(), bytes_data, merkle_proof).sendAsync().join(); + } + @Override public CompletableFuture isDepositApproved(Token token, String to, Optional threshold) { ERC20 tokenContract = ERC20.load(token.getL1Address(), web3j, transactionManager, diff --git a/src/main/java/io/zksync/protocol/provider/EthereumProvider.java b/src/main/java/io/zksync/protocol/provider/EthereumProvider.java index c5f85b4..1875cac 100755 --- a/src/main/java/io/zksync/protocol/provider/EthereumProvider.java +++ b/src/main/java/io/zksync/protocol/provider/EthereumProvider.java @@ -85,6 +85,7 @@ public interface EthereumProvider { * @return CompletableFuture for waiting for transaction mine */ CompletableFuture withdraw(Token token, BigInteger amount, String userAddress); + TransactionReceipt finalizeWithdraw(String txHash, int index) throws Exception; /** * Check if deposit is approved in enough amount @@ -109,7 +110,7 @@ static CompletableFuture load(ZkSync zksync, Web3j ethereum, T String mainContract = zksync.zksMainContract().sendAsync().join().getResult(); IL1Bridge erc20Bridge = IL1Bridge.load(bridgeAddresses.getL1Erc20DefaultBridge(), ethereum, transactionManager, gasProvider); ZkSyncContract zkSyncContract = ZkSyncContract.load(mainContract, ethereum, transactionManager, gasProvider); - return new DefaultEthereumProvider(ethereum, transactionManager, gasProvider, zkSyncContract, erc20Bridge); + return new DefaultEthereumProvider(ethereum,zksync, transactionManager, gasProvider, zkSyncContract, erc20Bridge); }); } diff --git a/src/main/java/io/zksync/wrappers/L1Messenger.java b/src/main/java/io/zksync/wrappers/L1Messenger.java new file mode 100644 index 0000000..0e40523 --- /dev/null +++ b/src/main/java/io/zksync/wrappers/L1Messenger.java @@ -0,0 +1,134 @@ +package io.zksync.wrappers; + +import io.reactivex.Flowable; +import io.reactivex.functions.Function; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.web3j.abi.EventEncoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.DynamicBytes; +import org.web3j.abi.datatypes.Event; +import org.web3j.abi.datatypes.Type; +import org.web3j.abi.datatypes.generated.Bytes32; +import org.web3j.crypto.Credentials; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.RemoteFunctionCall; +import org.web3j.protocol.core.methods.request.EthFilter; +import org.web3j.protocol.core.methods.response.BaseEventResponse; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tx.Contract; +import org.web3j.tx.TransactionManager; +import org.web3j.tx.gas.ContractGasProvider; + +/** + *

Auto generated code. + *

Do not modify! + *

Please use the web3j command line tools, + * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the + * codegen module to update. + * + *

Generated with web3j version 1.4.2. + */ +@SuppressWarnings("rawtypes") +public class L1Messenger extends Contract { + public static final String BINARY = "Bin file was not provided"; + + public static final String FUNC_SENDTOL1 = "sendToL1"; + + public static final Event L1MESSAGESENT_EVENT = new Event("L1MessageSent", + Arrays.>asList(new TypeReference

(true) {}, new TypeReference(true) {}, new TypeReference() {})); + ; + + @Deprecated + protected L1Messenger(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + protected L1Messenger(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, credentials, contractGasProvider); + } + + @Deprecated + protected L1Messenger(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + protected L1Messenger(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static List getL1MessageSentEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(L1MESSAGESENT_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (Contract.EventValuesWithLog eventValues : valueList) { + L1MessageSentEventResponse typedResponse = new L1MessageSentEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse._sender = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse._hash = (byte[]) eventValues.getIndexedValues().get(1).getValue(); + typedResponse._message = (byte[]) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public Flowable l1MessageSentEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(new Function() { + @Override + public L1MessageSentEventResponse apply(Log log) { + Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(L1MESSAGESENT_EVENT, log); + L1MessageSentEventResponse typedResponse = new L1MessageSentEventResponse(); + typedResponse.log = log; + typedResponse._sender = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse._hash = (byte[]) eventValues.getIndexedValues().get(1).getValue(); + typedResponse._message = (byte[]) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + }); + } + + public Flowable l1MessageSentEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(L1MESSAGESENT_EVENT)); + return l1MessageSentEventFlowable(filter); + } + + public RemoteFunctionCall sendToL1(byte[] _message) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_SENDTOL1, + Arrays.asList(new org.web3j.abi.datatypes.DynamicBytes(_message)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + @Deprecated + public static L1Messenger load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + return new L1Messenger(contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + @Deprecated + public static L1Messenger load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + return new L1Messenger(contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + public static L1Messenger load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + return new L1Messenger(contractAddress, web3j, credentials, contractGasProvider); + } + + public static L1Messenger load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + return new L1Messenger(contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static class L1MessageSentEventResponse extends BaseEventResponse { + public String _sender; + + public byte[] _hash; + + public byte[] _message; + } +} diff --git a/src/test/java/io/zksync/crypto/signer/SelfTransferTest.java b/src/test/java/io/zksync/crypto/signer/SelfTransferTest.java index b2a943d..b54988d 100644 --- a/src/test/java/io/zksync/crypto/signer/SelfTransferTest.java +++ b/src/test/java/io/zksync/crypto/signer/SelfTransferTest.java @@ -1,12 +1,19 @@ package io.zksync.crypto.signer; import io.zksync.ZkSyncWallet; +import io.zksync.abi.TransactionEncoder; import io.zksync.crypto.eip712.Eip712Domain; +import io.zksync.methods.request.Eip712Meta; +import io.zksync.methods.response.ZkTransactionReceipt; +import io.zksync.methods.response.ZksEstimateFee; +import io.zksync.methods.response.ZksMessageProof; import io.zksync.protocol.ZkSync; import io.zksync.protocol.core.Token; import io.zksync.protocol.core.ZkBlockParameterName; import io.zksync.protocol.provider.EthereumProvider; +import io.zksync.transaction.fee.Fee; import io.zksync.transaction.type.Transaction712; +import io.zksync.wrappers.IL2Bridge; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -16,9 +23,10 @@ import org.web3j.abi.datatypes.*; import org.web3j.abi.datatypes.generated.Uint256; import org.web3j.crypto.Credentials; +import org.web3j.crypto.Hash; import org.web3j.protocol.Web3j; -import org.web3j.protocol.core.methods.response.EthCall; -import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.protocol.core.DefaultBlockParameterName; +import org.web3j.protocol.core.methods.response.*; import org.web3j.protocol.http.HttpService; import org.web3j.tx.RawTransactionManager; import org.web3j.tx.TransactionManager; @@ -34,7 +42,10 @@ import java.util.Collections; import java.util.List; -@Disabled +import static io.zksync.integration.BaseIntegrationEnv.ETH; +import static io.zksync.utils.ZkSyncAddresses.L2_ETH_TOKEN_ADDRESS; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class SelfTransferTest { private static Credentials credentials; @@ -50,14 +61,14 @@ public class SelfTransferTest { @BeforeAll public static void setUp() throws IOException { - final String privateKey = "PRIVATE_KEY"; - zksync = ZkSync.build(new HttpService("https://zksync2-testnet.zksync.dev")); + final String privateKey = "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"; + zksync = ZkSync.build(new HttpService("http://localhost:3050/")); credentials = Credentials.create(privateKey); - key = new PrivateKeyEthSigner(credentials, 280L); + key = new PrivateKeyEthSigner(credentials, 270L); wallet = new ZkSyncWallet(zksync, key, Token.ETH); - web3j = Web3j.build(new HttpService("https://goerli.infura.io/v3/API_KEY")); + web3j = Web3j.build(new HttpService("http://localhost:8545/")); chainId = web3j.ethChainId().send().getChainId(); - domain = Eip712Domain.defaultDomain(280L); + domain = Eip712Domain.defaultDomain(270L); } @Test @@ -73,10 +84,27 @@ public void SelfTransfer() throws IOException { } + @Test + public void withdraw() throws Exception{ + EthGetBalance getBalance = web3j + .ethGetBalance(credentials.getAddress(), DefaultBlockParameterName.LATEST) + .send(); + + + + + } @Test public void withdrawTest() throws Exception { - TransactionReceipt receipt = wallet.withdraw(credentials.getAddress(), Convert.toWei("0.117649", Convert.Unit.ETHER).toBigInteger()).send(); + TransactionReceipt tx = wallet.withdraw(credentials.getAddress(), Convert.toWei("0.117649", Convert.Unit.ETHER).toBigInteger()).send(); + TransactionManager manager = new RawTransactionManager(web3j, credentials, chainId.longValue()); + BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice(); + ContractGasProvider gasProvider = new StaticGasProvider(gasPrice, BigInteger.valueOf(300_000L)); + TransactionReceipt receipt = EthereumProvider + .load(wallet.getZksync(), web3j, manager, gasProvider).join() + .finalizeWithdraw(tx.getTransactionHash(), 0); + System.out.println(receipt); } From 14f8a7008f03836551ed982a88e939ebbca50275 Mon Sep 17 00:00:00 2001 From: kopestinskij Date: Wed, 31 May 2023 14:25:24 +0200 Subject: [PATCH 2/3] DefaultEthereumProvider finalize withdraw --- .../provider/DefaultEthereumProvider.java | 76 ++++++++++++------- .../crypto/signer/SelfTransferTest.java | 48 +++--------- 2 files changed, 57 insertions(+), 67 deletions(-) diff --git a/src/main/java/io/zksync/protocol/provider/DefaultEthereumProvider.java b/src/main/java/io/zksync/protocol/provider/DefaultEthereumProvider.java index 2b27d4a..a9d7795 100755 --- a/src/main/java/io/zksync/protocol/provider/DefaultEthereumProvider.java +++ b/src/main/java/io/zksync/protocol/provider/DefaultEthereumProvider.java @@ -1,24 +1,18 @@ package io.zksync.protocol.provider; import io.zksync.methods.response.L2toL1Log; -import io.zksync.methods.response.ZkLog; import io.zksync.methods.response.ZkTransactionReceipt; -import io.zksync.methods.response.ZksMessageProof; import io.zksync.protocol.ZkSync; import io.zksync.protocol.core.L2ToL1MessageProof; import io.zksync.protocol.core.Token; import io.zksync.wrappers.ERC20; import io.zksync.wrappers.IL1Bridge; +import io.zksync.wrappers.IL2Bridge; import io.zksync.wrappers.ZkSyncContract; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.Nullable; import org.web3j.abi.EventValues; -import org.web3j.abi.FunctionEncoder; -import org.web3j.abi.TypeReference; -import org.web3j.abi.Utils; -import org.web3j.abi.datatypes.*; -import org.web3j.abi.datatypes.generated.Bytes32; -import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.abi.datatypes.DynamicBytes; import org.web3j.crypto.Hash; import org.web3j.protocol.Web3j; import org.web3j.protocol.core.methods.response.EthGasPrice; @@ -32,12 +26,12 @@ import org.web3j.utils.Convert.Unit; import org.web3j.utils.Numeric; -import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.*; import java.util.concurrent.CompletableFuture; +import static io.zksync.utils.ZkSyncAddresses.L2_ETH_TOKEN_ADDRESS; import static io.zksync.utils.ZkSyncAddresses.MESSENGER_ADDRESS; import static io.zksync.wrappers.L1Messenger.L1MESSAGESENT_EVENT; @@ -129,40 +123,64 @@ public CompletableFuture withdraw(Token token, BigInteger am } @Override - public TransactionReceipt finalizeWithdraw(String txHash, int index) throws IOException { + public TransactionReceipt finalizeWithdraw(String txHash, int index) throws Exception { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } ZkTransactionReceipt receipt = zkSync.zksGetTransactionReceipt(txHash).sendAsync().join().getResult(); - String topic = "L1MessageSent(address,bytes32,bytes)"; - List logs = receipt.getLogs(); - List msgsIndex = new ArrayList<>(); - for (int i = 0 ; i < logs.size() ; i++){ - if (logs.get(i).getAddress().equals(MESSENGER_ADDRESS) && Arrays.equals(logs.get(i).getTopics().get(0).getBytes(), Hash.sha3String(topic).getBytes())){ - msgsIndex.add(i); - } - } + int logIndex = getWithdrawalLogIndex(receipt.getLogs(), index); + Log log = receipt.getLogs().get(logIndex); - List msgsL2Index = new ArrayList<>(); - List l2toL1Logs = receipt.getl2ToL1Logs(); - for (int i = 0 ; i < l2toL1Logs.size() ; i++){ - if (l2toL1Logs.get(i).getSender().getValue().equals(MESSENGER_ADDRESS)){ - msgsL2Index.add(i); - } - } + int l2ToL1LogIndex = getWithdrawalL2ToL1LogIndex(receipt.getl2ToL1Logs(), index); + L2ToL1MessageProof l2ToL1MessageProof = zkSync.zksGetL2ToL1LogProof(txHash, l2ToL1LogIndex).sendAsync().join().getResult(); - L2ToL1MessageProof l2ToL1MessageProof = zkSync.zksGetL2ToL1LogProof(txHash, msgsL2Index.get(index)).sendAsync().join().getResult(); - Log log = logs.get(msgsIndex.get(index)); EventValues eventValues = Contract.staticExtractEventParameters(L1MESSAGESENT_EVENT, log); - byte[] bytes_data = eventValues.getIndexedValues().toString().getBytes(); + byte[] bytes_data = (byte[]) eventValues.getNonIndexedValues().get(0).getValue(); + BigInteger l1BatchNumber = receipt.getL1BatchNumber(); List merkle_proof = new ArrayList<>(); for (int i = 0 ; i < l2ToL1MessageProof.getProof().size() ; i++){ merkle_proof.add(Numeric.hexStringToByteArray(l2ToL1MessageProof.getProof().get(i))); } + String sender = log.getTopics().get(1); + + if (sender.equals(L2_ETH_TOKEN_ADDRESS)){ + return contract.finalizeEthWithdrawal(l1BatchNumber, BigInteger.valueOf(l2ToL1MessageProof.getId()), receipt.getL1BatchTxIndex(), bytes_data, merkle_proof).sendAsync().join(); + } + IL2Bridge il2Bridge = IL2Bridge.load(sender, web3j, transactionManager, gasProvider); + IL1Bridge il1Bridge = IL1Bridge.load(il2Bridge.l1Bridge().send(), web3j, transactionManager, gasProvider); + + return il1Bridge.finalizeWithdrawal(l1BatchNumber, BigInteger.valueOf(l2ToL1MessageProof.getId()), receipt.getL1BatchTxIndex(), bytes_data, merkle_proof).sendAsync().join(); + } + + public int getWithdrawalLogIndex(List logs, int index){ + String topic = "L1MessageSent(address,bytes32,bytes)"; + List logIndex = new ArrayList<>(); + + for (int i = 0 ; i < logs.size() ; i++){ + if (logs.get(i).getAddress().equals(MESSENGER_ADDRESS) && Arrays.equals(logs.get(i).getTopics().get(0).getBytes(), Hash.sha3String(topic).getBytes())){ + logIndex.add(i); + } + } - return contract.finalizeEthWithdrawal(l1BatchNumber, BigInteger.valueOf(l2ToL1MessageProof.getId()), receipt.getL1BatchTxIndex(), bytes_data, merkle_proof).sendAsync().join(); + return logIndex.get(index); } + public int getWithdrawalL2ToL1LogIndex(List logs, int index){ + List logIndex = new ArrayList<>(); + + for (int i = 0 ; i < logs.size() ; i++){ + if (logs.get(i).getSender().getValue().equals(MESSENGER_ADDRESS)){ + logIndex.add(i); + } + } + + return logIndex.get(index); + } @Override public CompletableFuture isDepositApproved(Token token, String to, Optional threshold) { ERC20 tokenContract = ERC20.load(token.getL1Address(), web3j, transactionManager, diff --git a/src/test/java/io/zksync/crypto/signer/SelfTransferTest.java b/src/test/java/io/zksync/crypto/signer/SelfTransferTest.java index b54988d..6fa1892 100644 --- a/src/test/java/io/zksync/crypto/signer/SelfTransferTest.java +++ b/src/test/java/io/zksync/crypto/signer/SelfTransferTest.java @@ -1,19 +1,12 @@ package io.zksync.crypto.signer; import io.zksync.ZkSyncWallet; -import io.zksync.abi.TransactionEncoder; import io.zksync.crypto.eip712.Eip712Domain; -import io.zksync.methods.request.Eip712Meta; -import io.zksync.methods.response.ZkTransactionReceipt; -import io.zksync.methods.response.ZksEstimateFee; -import io.zksync.methods.response.ZksMessageProof; import io.zksync.protocol.ZkSync; import io.zksync.protocol.core.Token; import io.zksync.protocol.core.ZkBlockParameterName; import io.zksync.protocol.provider.EthereumProvider; -import io.zksync.transaction.fee.Fee; import io.zksync.transaction.type.Transaction712; -import io.zksync.wrappers.IL2Bridge; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -23,10 +16,9 @@ import org.web3j.abi.datatypes.*; import org.web3j.abi.datatypes.generated.Uint256; import org.web3j.crypto.Credentials; -import org.web3j.crypto.Hash; import org.web3j.protocol.Web3j; -import org.web3j.protocol.core.DefaultBlockParameterName; -import org.web3j.protocol.core.methods.response.*; +import org.web3j.protocol.core.methods.response.EthCall; +import org.web3j.protocol.core.methods.response.TransactionReceipt; import org.web3j.protocol.http.HttpService; import org.web3j.tx.RawTransactionManager; import org.web3j.tx.TransactionManager; @@ -42,10 +34,7 @@ import java.util.Collections; import java.util.List; -import static io.zksync.integration.BaseIntegrationEnv.ETH; -import static io.zksync.utils.ZkSyncAddresses.L2_ETH_TOKEN_ADDRESS; -import static org.junit.jupiter.api.Assertions.assertTrue; - +@Disabled public class SelfTransferTest { private static Credentials credentials; @@ -61,14 +50,14 @@ public class SelfTransferTest { @BeforeAll public static void setUp() throws IOException { - final String privateKey = "0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"; - zksync = ZkSync.build(new HttpService("http://localhost:3050/")); + final String privateKey = "PRIVATE_KEY"; + zksync = ZkSync.build(new HttpService("https://zksync2-testnet.zksync.dev")); credentials = Credentials.create(privateKey); - key = new PrivateKeyEthSigner(credentials, 270L); + key = new PrivateKeyEthSigner(credentials, 280L); wallet = new ZkSyncWallet(zksync, key, Token.ETH); - web3j = Web3j.build(new HttpService("http://localhost:8545/")); + web3j = Web3j.build(new HttpService("https://goerli.infura.io/v3/API_KEY")); chainId = web3j.ethChainId().send().getChainId(); - domain = Eip712Domain.defaultDomain(270L); + domain = Eip712Domain.defaultDomain(280L); } @Test @@ -84,27 +73,10 @@ public void SelfTransfer() throws IOException { } - @Test - public void withdraw() throws Exception{ - EthGetBalance getBalance = web3j - .ethGetBalance(credentials.getAddress(), DefaultBlockParameterName.LATEST) - .send(); - - - - - } @Test public void withdrawTest() throws Exception { - TransactionReceipt tx = wallet.withdraw(credentials.getAddress(), Convert.toWei("0.117649", Convert.Unit.ETHER).toBigInteger()).send(); - TransactionManager manager = new RawTransactionManager(web3j, credentials, chainId.longValue()); - BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice(); - ContractGasProvider gasProvider = new StaticGasProvider(gasPrice, BigInteger.valueOf(300_000L)); - TransactionReceipt receipt = EthereumProvider - .load(wallet.getZksync(), web3j, manager, gasProvider).join() - .finalizeWithdraw(tx.getTransactionHash(), 0); - + TransactionReceipt receipt = wallet.withdraw(credentials.getAddress(), Convert.toWei("0.117649", Convert.Unit.ETHER).toBigInteger()).send(); System.out.println(receipt); } @@ -183,4 +155,4 @@ public void testDeposit() throws IOException { } -} +} \ No newline at end of file From 964230da27a33880e68c8df0a5c43c9f97f9eb82 Mon Sep 17 00:00:00 2001 From: kopestinskij Date: Thu, 1 Jun 2023 15:53:04 +0200 Subject: [PATCH 3/3] Updating gradle --- build.gradle | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 049bb24..097763d 100755 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java-library' id 'maven-publish' id 'signing' + id 'io.codearte.nexus-staging' version '0.30.0' } group = 'io.zksync' @@ -40,7 +41,6 @@ publishing { name = 'ZkSync2 SDK' description = 'This repository provides a Java SDK for zkSync developers, which can be used either on PC or Android.' url = 'https://github.com/zksync-sdk/zksync2-java' - version = '0.1.0' organization { name = 'Matter Labs' url = 'https://matter-labs.io' @@ -53,9 +53,9 @@ publishing { } developers { developer { - id = 'mfischuk' - name = 'Maxim Fischuk' - email = 'mfischuk@vareger.com' + id = 'matterLabs' + name = 'Matter Labs' + email = 'hello@matterlabs.io' } } scm { @@ -78,6 +78,11 @@ publishing { } } +nexusStaging { + packageGroup = "io.zksync.zksync2" //optional if packageGroup == project.getGroup() + stagingProfileId findProperty("OSSRH_STAGING_PROFILE_ID") +} + signing { def signingKeyId = findProperty("signingKeyId")