diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e5aeafd6c..0fc6add194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ### Features -* +- Add support for txpool_contentFrom JSON-RPC method to query transaction pool by address (#2262) ### BREAKING CHANGES diff --git a/core/src/main/java/org/web3j/protocol/admin/Admin.java b/core/src/main/java/org/web3j/protocol/admin/Admin.java index 491f0d8690..875f614b8f 100644 --- a/core/src/main/java/org/web3j/protocol/admin/Admin.java +++ b/core/src/main/java/org/web3j/protocol/admin/Admin.java @@ -24,6 +24,7 @@ import org.web3j.protocol.core.Request; import org.web3j.protocol.core.methods.request.Transaction; import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.protocol.core.methods.response.TxPoolContentFrom; /** * JSON-RPC Request object building factory for common Parity and Geth. Personal namespace has now @@ -58,4 +59,6 @@ public Request personalSendTransaction( Transaction transaction, String password); public Request txPoolContent(); + + Request txPoolContentFrom(String address); } diff --git a/core/src/main/java/org/web3j/protocol/core/Ethereum.java b/core/src/main/java/org/web3j/protocol/core/Ethereum.java index 1cb6fcf099..fedd0647df 100644 --- a/core/src/main/java/org/web3j/protocol/core/Ethereum.java +++ b/core/src/main/java/org/web3j/protocol/core/Ethereum.java @@ -78,6 +78,7 @@ import org.web3j.protocol.core.methods.response.ShhNewIdentity; import org.web3j.protocol.core.methods.response.ShhUninstallFilter; import org.web3j.protocol.core.methods.response.ShhVersion; +import org.web3j.protocol.core.methods.response.TxPoolContentFrom; import org.web3j.protocol.core.methods.response.TxPoolStatus; import org.web3j.protocol.core.methods.response.Web3ClientVersion; import org.web3j.protocol.core.methods.response.Web3Sha3; @@ -272,6 +273,8 @@ Request shhPost( Request txPoolStatus(); + Request txPoolContentFrom(String address); + Request lineaGetTransactionExclusionStatusV1( String transactionHash); } diff --git a/core/src/main/java/org/web3j/protocol/core/JsonRpc2_0Web3j.java b/core/src/main/java/org/web3j/protocol/core/JsonRpc2_0Web3j.java index 6df1a67c79..4c5b786e57 100644 --- a/core/src/main/java/org/web3j/protocol/core/JsonRpc2_0Web3j.java +++ b/core/src/main/java/org/web3j/protocol/core/JsonRpc2_0Web3j.java @@ -92,6 +92,7 @@ import org.web3j.protocol.core.methods.response.ShhNewIdentity; import org.web3j.protocol.core.methods.response.ShhUninstallFilter; import org.web3j.protocol.core.methods.response.ShhVersion; +import org.web3j.protocol.core.methods.response.TxPoolContentFrom; import org.web3j.protocol.core.methods.response.TxPoolStatus; import org.web3j.protocol.core.methods.response.Web3ClientVersion; import org.web3j.protocol.core.methods.response.Web3Sha3; @@ -809,6 +810,15 @@ public Request txPoolStatus() { "txpool_status", Collections.emptyList(), web3jService, TxPoolStatus.class); } + @Override + public Request txPoolContentFrom(String address) { + return new Request<>( + "txpool_contentFrom", + Arrays.asList(address), + web3jService, + TxPoolContentFrom.class); + } + @Override public Flowable newHeadsNotifications() { return web3jService.subscribe( diff --git a/core/src/main/java/org/web3j/protocol/core/methods/response/TxPoolContentFrom.java b/core/src/main/java/org/web3j/protocol/core/methods/response/TxPoolContentFrom.java new file mode 100644 index 0000000000..5081690905 --- /dev/null +++ b/core/src/main/java/org/web3j/protocol/core/methods/response/TxPoolContentFrom.java @@ -0,0 +1,74 @@ +/* + * Copyright 2024 Web3 Labs Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.web3j.protocol.core.methods.response; + +import java.math.BigInteger; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.web3j.protocol.core.Response; + +/** txpool_contentFrom. */ +public final class TxPoolContentFrom extends Response { + public static class TxPoolContentFromResult { + + private Map> pending; + private Map> queued; + + public TxPoolContentFromResult() {} + + public TxPoolContentFromResult( + Map> pending, + Map> queued) { + this.pending = immutableCopy(pending, val -> immutableCopy(val, Function.identity())); + this.queued = immutableCopy(queued, val -> immutableCopy(val, Function.identity())); + } + + public Map> getPending() { + return pending; + } + + public Map> getQueued() { + return queued; + } + + public List getPendingTransactions() { + return pending.values().stream() + .map(Map::values) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + public List getQueuedTransactions() { + return queued.values().stream() + .map(Map::values) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + private static Map immutableCopy(Map map, Function valueMapper) { + Map result = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + K key = entry.getKey(); + V value = entry.getValue(); + result.put(key, valueMapper.apply(value)); + } + return Collections.unmodifiableMap(result); + } + } +} diff --git a/core/src/test/java/org/web3j/protocol/core/RequestTest.java b/core/src/test/java/org/web3j/protocol/core/RequestTest.java index a8ab862523..d9d6d62c1a 100644 --- a/core/src/test/java/org/web3j/protocol/core/RequestTest.java +++ b/core/src/test/java/org/web3j/protocol/core/RequestTest.java @@ -148,6 +148,14 @@ void testEthHashrate() throws Exception { verifyResult("{\"jsonrpc\":\"2.0\",\"method\":\"eth_hashrate\",\"params\":[],\"id\":1}"); } + @Test + void testTxPoolContentFrom() throws Exception { + web3j.txPoolContentFrom("0x0032d05f320fa74c871e892f48f0e6387c0dfe95").send(); + + verifyResult( + "{\"jsonrpc\":\"2.0\",\"method\":\"txpool_contentFrom\",\"params\":[\"0x0032d05f320fa74c871e892f48f0e6387c0dfe95\"],\"id\":1}"); + } + @Test void testEthGasPrice() throws Exception { web3j.ethGasPrice().send(); diff --git a/core/src/test/java/org/web3j/protocol/core/methods/response/TxPoolContentFromTest.java b/core/src/test/java/org/web3j/protocol/core/methods/response/TxPoolContentFromTest.java new file mode 100644 index 0000000000..943ec61cd3 --- /dev/null +++ b/core/src/test/java/org/web3j/protocol/core/methods/response/TxPoolContentFromTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2024 Web3 Labs Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.web3j.protocol.core.methods.response; + +import org.junit.jupiter.api.Test; + +import org.web3j.protocol.ResponseTester; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TxPoolContentFromTest extends ResponseTester { + + @Test + public void testTxPoolContentFrom() { + buildResponse( + "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"id\": 1,\n" + + " \"result\": {\n" + + " \"pending\": {\n" + + " \"0x0032D05F320fa74C871E892F48F0e6387c0Dfe95\": {\n" + + " \"0\": {\n" + + " \"from\": \"0x0032d05f320fa74c871e892f48f0e6387c0dfe95\",\n" + + " \"gas\": \"0x63cad\",\n" + + " \"gasPrice\": \"0x1\",\n" + + " \"hash\": \"0x56cf53cbd377535c14b28cd373fa43d129f501b1a20b36903fd14b747c3f6cf5\",\n" + + " \"nonce\": \"0x0\",\n" + + " \"value\": \"0x0\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"queued\": {\n" + + " \"0x00Bf700CeB382877F8bFa38b05fcC81126f4f228\": {\n" + + " \"49\": {\n" + + " \"from\": \"0x00bf700ceb382877f8bfa38b05fcc81126f4f228\",\n" + + " \"gas\": \"0xfa00\",\n" + + " \"gasPrice\": \"0x3b9aca00\",\n" + + " \"hash\": \"0xa87ab980c4d277de6c4faf2670a2ec1b6e577482e582c8082d208b7e630cf395\",\n" + + " \"nonce\": \"0x31\",\n" + + " \"value\": \"0x0\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}"); + + TxPoolContentFrom content = deserialiseResponse(TxPoolContentFrom.class); + + assertEquals( + "0x0032d05f320fa74c871e892f48f0e6387c0dfe95", + content.getResult().getPendingTransactions().get(0).getFrom()); + assertEquals( + "0x00bf700ceb382877f8bfa38b05fcc81126f4f228", + content.getResult().getQueuedTransactions().get(0).getFrom()); + } +}