Skip to content

Commit afe4769

Browse files
authored
Dynamic gas provider (#2142)
* initial commit Signed-off-by: Nischal Sharma <[email protected]> * PriorityGasProvider and DynamicGasProvider added Signed-off-by: Nischal Sharma <[email protected]> * Added DynamicEIP1559Gas Provider Signed-off-by: Nischal Sharma <[email protected]> * added intergration tests Signed-off-by: Nischal Sharma <[email protected]> * improved solution and fixed integration test Signed-off-by: Nischal Sharma <[email protected]> * integration tests combine and fix Signed-off-by: Nischal Sharma <[email protected]> * fixed maxFeePerGas Signed-off-by: Nischal Sharma <[email protected]> * resolved comments and added max gas limit Signed-off-by: Nischal Sharma <[email protected]> * resolved comments Signed-off-by: Nischal Sharma <[email protected]> --------- Signed-off-by: Nischal Sharma <[email protected]>
1 parent 639c1c4 commit afe4769

11 files changed

+405
-46
lines changed

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
77

88
### Bug Fixes
99

10+
* fix Transaction.getChainId when v=27 must return null [#2133](https://github.com/hyperledger-web3j/web3j/pull/2133)
11+
* Fix android scripts [#2138](https://github.com/hyperledger-web3j/web3j/pull/2138)
1012
* fixed subscription id conflict [#2127](https://github.com/hyperledger/web3j/pull/2127)
1113

1214
### Features
1315

1416
* bump snapshot version to 4.12.4 [#2132](https://github.com/hyperledger-web3j/web3j/pull/2132)
15-
* Added LineaEstimateGas api [#2150](https://github.com/hyperledger-web3j/web3j/pull/2150)
17+
* ENS - Label Hash function added [#2140](https://github.com/hyperledger-web3j/web3j/pull/2140)
18+
* Added Dynamic gas providers [#2142](https://github.com/hyperledger-web3j/web3j/pull/2142)
19+
* Added Linea RPC APIs [#2150](https://github.com/hyperledger-web3j/web3j/pull/2150)
1620

1721

1822
### BREAKING CHANGES

core/src/main/java/org/web3j/tx/Contract.java

+33-15
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.web3j.protocol.core.DefaultBlockParameterName;
4343
import org.web3j.protocol.core.RemoteCall;
4444
import org.web3j.protocol.core.RemoteFunctionCall;
45+
import org.web3j.protocol.core.methods.request.Transaction;
4546
import org.web3j.protocol.core.methods.response.EthGetCode;
4647
import org.web3j.protocol.core.methods.response.Log;
4748
import org.web3j.protocol.core.methods.response.TransactionReceipt;
@@ -72,7 +73,6 @@ public abstract class Contract extends ManagedTransaction {
7273

7374
public static final String BIN_NOT_PROVIDED = "Bin file was not provided";
7475
public static final String FUNC_DEPLOY = "deploy";
75-
7676
protected final String contractBinary;
7777
protected String contractAddress;
7878
protected ContractGasProvider gasProvider;
@@ -392,18 +392,18 @@ TransactionReceipt executeTransaction(
392392
if (gasProvider instanceof ContractEIP1559GasProvider) {
393393
ContractEIP1559GasProvider eip1559GasProvider =
394394
(ContractEIP1559GasProvider) gasProvider;
395-
if (eip1559GasProvider.isEIP1559Enabled()) {
396-
receipt =
397-
sendEIP1559(
398-
eip1559GasProvider.getChainId(),
399-
contractAddress,
400-
data,
401-
weiValue,
402-
eip1559GasProvider.getGasLimit(funcName),
403-
eip1559GasProvider.getMaxPriorityFeePerGas(funcName),
404-
eip1559GasProvider.getMaxFeePerGas(funcName),
405-
constructor);
406-
}
395+
396+
receipt =
397+
sendEIP1559(
398+
eip1559GasProvider.getChainId(),
399+
contractAddress,
400+
data,
401+
weiValue,
402+
eip1559GasProvider.getGasLimit(
403+
getGenericTransaction(data, constructor)),
404+
eip1559GasProvider.getMaxPriorityFeePerGas(),
405+
eip1559GasProvider.getMaxFeePerGas(),
406+
constructor);
407407
}
408408

409409
if (receipt == null) {
@@ -412,8 +412,8 @@ TransactionReceipt executeTransaction(
412412
contractAddress,
413413
data,
414414
weiValue,
415-
gasProvider.getGasPrice(funcName),
416-
gasProvider.getGasLimit(funcName),
415+
gasProvider.getGasPrice(),
416+
gasProvider.getGasLimit(getGenericTransaction(data, constructor)),
417417
constructor);
418418
}
419419
} catch (JsonRpcError error) {
@@ -447,6 +447,24 @@ TransactionReceipt executeTransaction(
447447
return receipt;
448448
}
449449

450+
protected Transaction getGenericTransaction(String data, boolean constructor) {
451+
if (constructor) {
452+
return Transaction.createContractTransaction(
453+
this.transactionManager.getFromAddress(),
454+
BigInteger.ONE,
455+
gasProvider.getGasPrice(),
456+
data);
457+
} else {
458+
return Transaction.createFunctionCallTransaction(
459+
this.transactionManager.getFromAddress(),
460+
BigInteger.ONE,
461+
gasProvider.getGasPrice(),
462+
gasProvider.getGasLimit(),
463+
contractAddress,
464+
data);
465+
}
466+
}
467+
450468
protected <T extends Type> RemoteFunctionCall<T> executeRemoteCallSingleValueReturn(
451469
Function function) {
452470
return new RemoteFunctionCall<>(function, () -> executeCallSingleValueReturn(function));

core/src/main/java/org/web3j/tx/gas/ContractEIP1559GasProvider.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@
1515
import java.math.BigInteger;
1616

1717
public interface ContractEIP1559GasProvider extends ContractGasProvider {
18-
boolean isEIP1559Enabled();
19-
2018
long getChainId();
2119

22-
BigInteger getMaxFeePerGas(String contractFunc);
20+
BigInteger getMaxFeePerGas();
2321

24-
BigInteger getMaxPriorityFeePerGas(String contractFunc);
22+
BigInteger getMaxPriorityFeePerGas();
2523
}

core/src/main/java/org/web3j/tx/gas/ContractGasProvider.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@
1414

1515
import java.math.BigInteger;
1616

17+
import org.web3j.protocol.core.methods.request.Transaction;
18+
1719
public interface ContractGasProvider {
18-
BigInteger getGasPrice(String contractFunc);
1920

20-
@Deprecated
2121
BigInteger getGasPrice();
2222

23-
BigInteger getGasLimit(String contractFunc);
23+
BigInteger getGasLimit(Transaction transaction);
2424

25-
@Deprecated
2625
BigInteger getGasLimit();
2726
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright 2025 Web3 Labs Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.web3j.tx.gas;
14+
15+
import java.io.IOException;
16+
import java.math.BigDecimal;
17+
import java.math.BigInteger;
18+
19+
import org.web3j.protocol.Web3j;
20+
import org.web3j.protocol.core.DefaultBlockParameterName;
21+
import org.web3j.protocol.core.methods.request.Transaction;
22+
import org.web3j.protocol.core.methods.response.EthEstimateGas;
23+
import org.web3j.protocol.core.methods.response.EthGasPrice;
24+
import org.web3j.protocol.core.methods.response.EthMaxPriorityFeePerGas;
25+
26+
public class DynamicEIP1559GasProvider implements ContractEIP1559GasProvider, PriorityGasProvider {
27+
private Web3j web3j;
28+
private long chainId;
29+
private final Priority priority;
30+
private final BigDecimal customMultiplier;
31+
private BigInteger maxGasLimit = BigInteger.valueOf(9_000_000);
32+
33+
public DynamicEIP1559GasProvider(Web3j web3j, long chainId) {
34+
this(web3j, chainId, Priority.NORMAL);
35+
}
36+
37+
public DynamicEIP1559GasProvider(Web3j web3j, long chainId, Priority priority) {
38+
this(web3j, chainId, priority, BigDecimal.ONE);
39+
}
40+
41+
public DynamicEIP1559GasProvider(
42+
Web3j web3j, long chainId, Priority priority, BigDecimal customMultiplier) {
43+
this.web3j = web3j;
44+
this.chainId = chainId;
45+
this.priority = priority;
46+
this.customMultiplier = customMultiplier;
47+
}
48+
49+
@Override
50+
public long getChainId() {
51+
return chainId;
52+
}
53+
54+
@Override
55+
public BigInteger getMaxFeePerGas() {
56+
try {
57+
BigInteger baseFee =
58+
web3j.ethGetBlockByNumber(DefaultBlockParameterName.LATEST, false)
59+
.send()
60+
.getBlock()
61+
.getBaseFeePerGas();
62+
63+
return baseFee.multiply(BigInteger.valueOf(2)).add(getMaxPriorityFeePerGas());
64+
} catch (IOException e) {
65+
throw new RuntimeException("Failed to get ethMaxFeePerGas");
66+
}
67+
}
68+
69+
@Override
70+
public BigInteger getMaxPriorityFeePerGas() {
71+
try {
72+
EthMaxPriorityFeePerGas ethMaxPriorityFeePerGas =
73+
web3j.ethMaxPriorityFeePerGas().send();
74+
if (ethMaxPriorityFeePerGas.hasError()) {
75+
throw new RuntimeException(
76+
"Error fetching ethMaxPriorityFeePerGas: "
77+
+ ethMaxPriorityFeePerGas.getError().getMessage());
78+
}
79+
return ethMaxPriorityFeePerGas.getMaxPriorityFeePerGas();
80+
} catch (IOException e) {
81+
throw new RuntimeException("Failed to get ethMaxPriorityFeePerGas");
82+
}
83+
}
84+
85+
@Override
86+
public BigInteger getGasPrice() {
87+
return applyPriority(fetchCurrentGasPrice(), priority, customMultiplier);
88+
}
89+
90+
@Override
91+
public BigInteger getGasLimit(Transaction transaction) {
92+
try {
93+
EthEstimateGas ethEstimateGas = web3j.ethEstimateGas(transaction).send();
94+
if (ethEstimateGas.hasError()) {
95+
throw new RuntimeException(
96+
"Error estimating gas limit: " + ethEstimateGas.getError().getMessage());
97+
}
98+
return ethEstimateGas.getAmountUsed();
99+
} catch (Exception e) {
100+
throw new RuntimeException("Failed to estimate gas limit", e);
101+
}
102+
}
103+
104+
@Override
105+
public BigInteger getGasLimit() {
106+
return maxGasLimit;
107+
}
108+
109+
public void setMaxGasLimit(BigInteger gasLimit) {
110+
maxGasLimit = gasLimit;
111+
}
112+
113+
private BigInteger fetchCurrentGasPrice() {
114+
try {
115+
EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
116+
if (ethGasPrice.hasError()) {
117+
throw new RuntimeException(
118+
"Error fetching gas price: " + ethGasPrice.getError().getMessage());
119+
}
120+
return ethGasPrice.getGasPrice();
121+
} catch (Exception e) {
122+
throw new RuntimeException("Failed to fetch gas price", e);
123+
}
124+
}
125+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2025 Web3 Labs Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.web3j.tx.gas;
14+
15+
import java.math.BigDecimal;
16+
import java.math.BigInteger;
17+
18+
import org.web3j.protocol.Web3j;
19+
import org.web3j.protocol.core.methods.request.Transaction;
20+
import org.web3j.protocol.core.methods.response.EthEstimateGas;
21+
import org.web3j.protocol.core.methods.response.EthGasPrice;
22+
23+
public class DynamicGasProvider implements ContractGasProvider, PriorityGasProvider {
24+
25+
private final Web3j web3j;
26+
private final Priority priority;
27+
private final BigDecimal customMultiplier;
28+
private BigInteger maxGasLimit = BigInteger.valueOf(9_000_000);
29+
30+
public DynamicGasProvider(Web3j web3j) {
31+
this(web3j, Priority.NORMAL);
32+
}
33+
34+
public DynamicGasProvider(Web3j web3j, Priority priority) {
35+
this(web3j, priority, BigDecimal.ONE);
36+
}
37+
38+
public DynamicGasProvider(Web3j web3j, Priority priority, BigDecimal customMultiplier) {
39+
this.web3j = web3j;
40+
this.priority = priority;
41+
this.customMultiplier = customMultiplier;
42+
}
43+
44+
@Override
45+
public BigInteger getGasPrice() {
46+
return applyPriority(fetchCurrentGasPrice(), priority, customMultiplier);
47+
}
48+
49+
@Override
50+
public BigInteger getGasLimit() {
51+
return maxGasLimit;
52+
}
53+
54+
public void setMaxGasLimit(BigInteger gasLimit) {
55+
maxGasLimit = gasLimit;
56+
}
57+
58+
public BigInteger getGasLimit(Transaction transaction) {
59+
try {
60+
EthEstimateGas ethEstimateGas = web3j.ethEstimateGas(transaction).send();
61+
if (ethEstimateGas.hasError()) {
62+
throw new RuntimeException(
63+
"Error estimating gas limit: " + ethEstimateGas.getError().getMessage());
64+
}
65+
return ethEstimateGas.getAmountUsed();
66+
} catch (Exception e) {
67+
throw new RuntimeException("Failed to estimate gas limit", e);
68+
}
69+
}
70+
71+
private BigInteger fetchCurrentGasPrice() {
72+
try {
73+
EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
74+
if (ethGasPrice.hasError()) {
75+
throw new RuntimeException(
76+
"Error fetching gas price: " + ethGasPrice.getError().getMessage());
77+
}
78+
return ethGasPrice.getGasPrice();
79+
} catch (Exception e) {
80+
throw new RuntimeException("Failed to fetch gas price", e);
81+
}
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2025 Web3 Labs Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.web3j.tx.gas;
14+
15+
import java.math.BigDecimal;
16+
import java.math.BigInteger;
17+
18+
public interface PriorityGasProvider {
19+
enum Priority {
20+
FAST,
21+
NORMAL,
22+
SLOW,
23+
CUSTOM
24+
}
25+
26+
default BigInteger applyPriority(
27+
BigInteger baseGasPrice, Priority priority, BigDecimal customMultiplier) {
28+
29+
return switch (priority) {
30+
case FAST -> baseGasPrice.multiply(BigInteger.valueOf(2)); // 2x for fast
31+
case NORMAL -> baseGasPrice; // Default gas price
32+
case SLOW -> baseGasPrice.divide(BigInteger.valueOf(2)); // 0.5x for slow
33+
case CUSTOM -> {
34+
if (customMultiplier == null || customMultiplier.compareTo(BigDecimal.ZERO) <= 0) {
35+
throw new IllegalArgumentException(
36+
"Custom multiplier must be a positive value");
37+
}
38+
yield new BigDecimal(baseGasPrice).multiply(customMultiplier).toBigInteger();
39+
}
40+
};
41+
}
42+
}

0 commit comments

Comments
 (0)