mev-share-java CI License Coverage Branches
Mev-share-java is a Java library for working with MEV-share.
Based on Specs
<dependency> <groupId>me.grapebaba</groupId> <artifactId>mev-share-java</artifactId> <version>0.1.0</version> </dependency>
implementation 'me.grapebaba:mev-share-java:0.1.0'To get started you need to install JDK17+, then run the following command:
GOERLI_RPC_URL=<GOERLI_RPC_URL> SIGNER_PRIVATE_KEY=<SIGNER_PRIVATE_KEY> ./gradlew clean build
For the latest javadocs for the main branch, run ./gradlew javadoc and open
the document under the build/docs/javadoc/index.html in your browser.
GOERLI_RPC_URL=<GOERLI_RPC_URL> SIGNER_PRIVATE_KEY=<SIGNER_PRIVATE_KEY> ./gradlew test
// Create a credential instance for authentication using Web3j Credentials authSigner = Credentials.create("<hex string of privateKey>"); // Create an ethereum provider instance using Web3j Web3j web3j = Web3j.build(new HttpService("<ethereum network rpc url>")); // Create a Mev share network options instance or use built-in options Network network = new Network() .setName("<network name>") .setChainId("<chain id>") .setRpcUrl("<mev share network rpc url>") .setStreamUrl("<mev share network stream url>"); Network network = Network.GOERLI; Network network = Network.MAINNET; // Create a Mev share instance MevShareClient mevShareClient = new MevShareClient(network, authSigner, web3j);
// Query the Event info CompletableFuture<EventHistoryInfo> historyInfoFuture = mevShareClient.getEventHistoryInfo(); // Query the Event List by params EventHistoryParams historyParams = new EventHistoryParams().setLimit(20).setBlockStart(BigInteger.valueOf(1_000_000L)); CompletableFuture<List<EventHistoryEntry>> eventHistory = mevShareClient.getEventHistory(historyParams);
// Create an event listener which handles the event Consumer<MevShareEvent> eventListener = mevShareEvent -> { // do something and do not block here... }; // Subscribe to events stream Disposable disposable = mevShareClient.subscribe(eventListener); // remember to release when no longer to subscribe events disposable.dispose();
// Listen to the event stream and capture the bundle hash CompletableFuture<MevShareEvent> future = new CompletableFuture<>(); Disposable eventSource = mevShareClient.subscribe(mevShareEvent -> { if (mevShareEvent.getHash() != null) { future.complete(mevShareEvent); } }); MevShareEvent mevShareEvent = future.get(); eventSource.dispose(); BigInteger number = web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false) .send() .getBlock() .getNumber(); Inclusion inclusion = new Inclusion().setBlock(number.add(BigInteger.ONE)).setMaxBlock(number.add(BigInteger.valueOf(4))); BundleItemType.HashItem bundleItem = new BundleItemType.HashItem().setHash(mevShareEvent.getHash()); // Create a backrun transaction Credentials signer = Credentials.create("<private key>"); BigInteger nonce = web3j.ethGetTransactionCount(signer.getAddress(), DefaultBlockParameterName.PENDING) .send() .getTransactionCount(); BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice(); BigInteger gasLimit = DefaultGasProvider.GAS_LIMIT; final String to = "<to address>"; final String amount = "<ether amount>"; RawTransaction rawTransaction = RawTransaction.createEtherTransaction( nonce, gasPrice, gasLimit, to, Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger()); byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, Network.GOERLI.chainId(), signer); String hexValue = Numeric.toHexString(signedMessage); BundleItemType.TxItem txItem = new BundleItemType.TxItem().setTx(hexValue).setCanRevert(true); // Construct the bundle with bundle hash item and backrun transaction item BundleParams bundleParams = new BundleParams().setInclusion(inclusion).setBody(List.of(bundleItem, txItem)); // Send the bundle CompletableFuture<SendBundleResponse> res = mevShareClient.sendBundle(bundleParams);
Bundles that only contain signed transactions can share hints about the transactions in their bundle by setting the privacy parameter
// Create a transaction Credentials signer = Credentials.create("<private key>"); BigInteger nonce = web3j.ethGetTransactionCount(signer.getAddress(), DefaultBlockParameterName.PENDING) .send() .getTransactionCount(); BigInteger gasPrice = web3j.ethGasPrice().send().getGasPrice(); BigInteger gasLimit = DefaultGasProvider.GAS_LIMIT; final String to = "<to address>"; final String amount = "<ether amount>"; RawTransaction rawTransaction = RawTransaction.createEtherTransaction( nonce, gasPrice, gasLimit, to, Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger()); byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, Network.GOERLI.chainId(), signer); String hexValue = Numeric.toHexString(signedMessage); BundleItemType.TxItem txItem = new BundleItemType.TxItem().setTx(hexValue).setCanRevert(true); // Set privacy parameter HintPreferences hintPreferences = new HintPreferences() .setCalldata(true) .setContractAddress(true) .setFunctionSelector(true) .setLogs(true) .setTxHash(true); List<String> builders = new ArrayList<>(); builders.add("flashbots"); BundlePrivacy bundlePrivacy = new BundlePrivacy().setHints(hintPreferences).setBuilders(builders); // Construct the bundle with transaction item BundleParams bundleParams = new BundleParams() .setInclusion(inclusion) .setBody(List.of(txItem)) .setPrivacy(bundlePrivacy); // Send the bundle CompletableFuture<SendBundleResponse> res = MEV_SHARE_CLIENT.sendBundle(bundleParams);
// Create a transaction var latestBlock = web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false) .send() .getBlock(); var parentBlock = web3j.ethGetBlockByNumber( DefaultBlockParameter.valueOf(latestBlock.getNumber().subtract(BigInteger.ONE)), false) .send() .getBlock(); Inclusion inclusion = new Inclusion() .setBlock(latestBlock.getNumber().subtract(BigInteger.ONE)) .setMaxBlock(latestBlock.getNumber().add(BigInteger.valueOf(10))); Credentials signer = Credentials.create("<private key>"); BigInteger nonce = web3j.ethGetTransactionCount(signer.getAddress(), DefaultBlockParameterName.PENDING) .send() .getTransactionCount(); final String to = "<to address>"; RawTransaction rawTransaction = RawTransaction.createEtherTransaction( nonce, web3j.ethGasPrice().send().getGasPrice(), DefaultGasProvider.GAS_LIMIT, to, Convert.toWei("0", Convert.Unit.ETHER).toBigInteger()); byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, Network.GOERLI.chainId(), signer); String hexValue = Numeric.toHexString(signedMessage); // Construct the bundle params with transaction item BundleItemType.TxItem bundleItem = new BundleItemType.TxItem().setTx(hexValue).setCanRevert(true); BundleParams bundleParams = new BundleParams().setInclusion(inclusion).setBody(List.of(bundleItem)); // Create a simbundle options SimBundleOptions options = new SimBundleOptions() .setParentBlock(latestBlock.getNumber().subtract(BigInteger.ONE)) .setBlockNumber(latestBlock.getNumber()) .setTimestamp(parentBlock.getTimestamp().add(BigInteger.valueOf(12))) .setGasLimit(parentBlock.getGasLimit()) .setBaseFee(parentBlock.getBaseFeePerGas()) .setTimeout(30); // Simulate the bundle CompletableFuture<SimBundleResponse> res = mevShareClient.simBundle(bundleParams, options);
// Create a transaction EthBlock.Block latest = web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false) .send() .getBlock(); BigInteger maxPriorityFeePerGas = BigInteger.valueOf(1_000_000_000L); Credentials signer = Credentials.create("<private key>"); BigInteger nonce = web3j.ethGetTransactionCount(signer.getAddress(), DefaultBlockParameterName.PENDING) .send() .getTransactionCount(); final String to = "<to address>"; RawTransaction rawTransaction = RawTransaction.createTransaction( 5L, nonce, latest.getGasLimit(), to, Convert.toWei("0", Convert.Unit.ETHER).toBigInteger(), Numeric.toHexString("<data>".getBytes(StandardCharsets.UTF_8)), maxPriorityFeePerGas, latest.getBaseFeePerGas().multiply(BigInteger.TWO).add(maxPriorityFeePerGas)); byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, Network.GOERLI.chainId(), signer); String signRawTx = Numeric.toHexString(signedMessage); // Create a private transaction options PrivateTxOptions txOptions = new PrivateTxOptions() .setHints(new HintPreferences() .setCalldata(true) .setContractAddress(true) .setFunctionSelector(true) .setLogs(true)); // Send the private transaction CompletableFuture<String> res = mevShareClient.sendPrivateTransaction(signRawTx, txOptions);
For more examples, you can see example
Examples can be run with the following commands:
./gradlew execute -PmainClassName=bundle.RpcSendPrivateTx ./gradlew execute -PmainClassName=event.SseHistorical
Examples require a
.envfile (or that you populate your environment directly with the appropriate variables).
cd src/examples
cp .env.example .env
vim .envTo help mev-share-java grow, follow Contributing to mev-share-java.