Skip to content

Commit

Permalink
solution: implement RPC mapping for eth_simulateV1
Browse files Browse the repository at this point in the history
  • Loading branch information
splix committed Nov 21, 2024
1 parent daf8d21 commit e4c98bf
Show file tree
Hide file tree
Showing 19 changed files with 1,112 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public ContractData getData() {
public TransactionCallJson toJson() {
TransactionCallJson json = new TransactionCallJson();
json.setTo(contract);
json.setData(data.toData());
json.setInput(data.toData());
return json;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,17 @@ public RpcCall<String, HexData> call(TransactionCallJson call, BlockTag block) {
return RpcCall.create("eth_call", call, block.getCode()).converted(HexData.class, HexData::from);
}

/**
* The eth_simulateV1 method allows the simulation of multiple blocks and transactions without creating transactions or blocks on the blockchain. It functions similarly to eth_call, but offers more control.
*
* @param payload the simulation payload
* @param block The simulated blocks will be built on top of this. If null, the current block is used.
* @return simulated block
*/
public RpcCall<BlockSimulatedJson, BlockSimulatedJson> simulateV1(SimulateJson payload, BlockTag block) {
return RpcCall.create("eth_simulateV1", BlockSimulatedJson.class, payload, block.getCode());
}

/**
* Executes a new message call immediately without creating a transaction on the block chain.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ public EtherjarModule() {
addDeserializer(Address.class, new AddressDeserializer());
addDeserializer(MethodId.class, new MethodIdDeserializer());
addDeserializer(Bloom.class, new BloomDeserializer());

addKeySerializer(Address.class, new HexDataSerializer.AsKey());

addKeyDeserializer(Address.class, new AddressDeserializer.FromKey());
addKeyDeserializer(Hex32.class, new Hex32Deserializer.FromKey());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import io.emeraldpay.etherjar.domain.Address;

Expand All @@ -31,4 +32,11 @@ public Address deserialize(JsonParser p, DeserializationContext ctxt) throws IOE
}
throw JsonMappingException.from(p,"Invalid Address type: " + token);
}

public static class FromKey extends KeyDeserializer {
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return Address.from(key);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -466,34 +466,33 @@ public BlockJson<T> copy() {

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BlockJson)) return false;

BlockJson<?> blockJson = (BlockJson<?>) o;

if (!Objects.equals(number, blockJson.number)) return false;
if (!Objects.equals(hash, blockJson.hash)) return false;
if (!Objects.equals(parentHash, blockJson.parentHash)) return false;
if (!Objects.equals(sha3Uncles, blockJson.sha3Uncles)) return false;
if (!Objects.equals(logsBloom, blockJson.logsBloom)) return false;
if (!Objects.equals(transactionsRoot, blockJson.transactionsRoot)) return false;
if (!Objects.equals(stateRoot, blockJson.stateRoot)) return false;
if (!Objects.equals(receiptsRoot, blockJson.receiptsRoot)) return false;
if (!Objects.equals(miner, blockJson.miner)) return false;
if (!Objects.equals(difficulty, blockJson.difficulty)) return false;
if (!Objects.equals(totalDifficulty, blockJson.totalDifficulty)) return false;
if (!Objects.equals(extraData, blockJson.extraData)) return false;
if (!Objects.equals(size, blockJson.size)) return false;
if (!Objects.equals(gasLimit, blockJson.gasLimit)) return false;
if (!Objects.equals(gasUsed, blockJson.gasUsed)) return false;
if (!Objects.equals(timestamp, blockJson.timestamp)) return false;
if (!Objects.equals(transactions, blockJson.transactions)) return false;
if (!Objects.equals(baseFeePerGas, blockJson.baseFeePerGas)) return false;
if (!Objects.equals(mixHash, blockJson.mixHash)) return false;
if (!Objects.equals(nonce, blockJson.nonce)) return false;
if (!Objects.equals(withdrawalsRoot, blockJson.withdrawalsRoot)) return false;
if (!Objects.equals(withdrawals, blockJson.withdrawals)) return false;
return Objects.equals(uncles, blockJson.uncles);
if (!(o instanceof BlockJson<?> blockJson)) return false;
return Objects.equals(number, blockJson.number)
&& Objects.equals(hash, blockJson.hash)
&& Objects.equals(parentHash, blockJson.parentHash)
&& Objects.equals(sha3Uncles, blockJson.sha3Uncles)
&& Objects.equals(logsBloom, blockJson.logsBloom)
&& Objects.equals(transactionsRoot, blockJson.transactionsRoot)
&& Objects.equals(stateRoot, blockJson.stateRoot)
&& Objects.equals(receiptsRoot, blockJson.receiptsRoot)
&& Objects.equals(miner, blockJson.miner)
&& Objects.equals(difficulty, blockJson.difficulty)
&& Objects.equals(totalDifficulty, blockJson.totalDifficulty)
&& Objects.equals(extraData, blockJson.extraData)
&& Objects.equals(mixHash, blockJson.mixHash)
&& Objects.equals(nonce, blockJson.nonce)
&& Objects.equals(size, blockJson.size)
&& Objects.equals(gasLimit, blockJson.gasLimit)
&& Objects.equals(gasUsed, blockJson.gasUsed)
&& Objects.equals(timestamp, blockJson.timestamp)
&& Objects.equals(transactions, blockJson.transactions)
&& Objects.equals(uncles, blockJson.uncles)
&& Objects.equals(baseFeePerGas, blockJson.baseFeePerGas)
&& Objects.equals(withdrawalsRoot, blockJson.withdrawalsRoot)
&& Objects.equals(withdrawals, blockJson.withdrawals)
&& Objects.equals(blobGasUsed, blockJson.blobGasUsed)
&& Objects.equals(excessBlobGas, blockJson.excessBlobGas)
&& Objects.equals(parentBeaconBlockRoot, blockJson.parentBeaconBlockRoot);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package io.emeraldpay.etherjar.rpc.json;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.emeraldpay.etherjar.domain.Address;
import io.emeraldpay.etherjar.domain.Wei;
import io.emeraldpay.etherjar.hex.Hex32;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
* The fields of this object customize the block as part of which a call is simulated. This object can be passed to <code>eth_call</code>, <code>eth_simulateV1</code> as well as <code>debug_traceCall</code> methods.
*
* @see <a href="https://geth.ethereum.org/docs/interacting-with-geth/rpc/objects#block-overrides">eth_simulateV1</a>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class BlockOverridesJson {

/**
* Block number
*/
@JsonDeserialize(using = HexLongDeserializer.class)
@JsonSerialize(using = HexLongSerializer.class)
private Long number;

/**
* The previous value of randomness beacon
*/
private Hex32 prevRandao;

/**
* Block timestamp
*/
@JsonDeserialize(using = HexLongDeserializer.class)
@JsonSerialize(using = HexLongSerializer.class)
private Long time;

/**
* Gas limit.
*/
@JsonDeserialize(using = HexLongDeserializer.class)
@JsonSerialize(using = HexLongSerializer.class)
private Long gasLimit;

/**
* Fee recipient (also known as coinbase).
*/
private Address feeRecipient;

/**
* Withdrawals made by validators.
*/
private List<WithdrawalJson> withdrawals;

/**
* Base fee per unit of gas (see EIP-1559).
*/
private Wei baseFeePerGas;

/**
* Base fee per unit of blob gas (see EIP-4844).
*/
private Wei blobBaseFee;

public Long getNumber() {
return number;
}

public void setNumber(Long number) {
this.number = number;
}

public Hex32 getPrevRandao() {
return prevRandao;
}

public void setPrevRandao(Hex32 prevRandao) {
this.prevRandao = prevRandao;
}

public Long getTime() {
return time;
}

public void setTime(Long time) {
this.time = time;
}

public Long getGasLimit() {
return gasLimit;
}

public void setGasLimit(Long gasLimit) {
this.gasLimit = gasLimit;
}

public Address getFeeRecipient() {
return feeRecipient;
}

public void setFeeRecipient(Address feeRecipient) {
this.feeRecipient = feeRecipient;
}

public List<WithdrawalJson> getWithdrawals() {
return withdrawals;
}

public void setWithdrawals(List<WithdrawalJson> withdrawals) {
this.withdrawals = withdrawals;
}

public BlockOverridesJson appendWithdrawal(WithdrawalJson withdrawal) {
if (withdrawals == null) {
withdrawals = new ArrayList<>();
}
withdrawals.add(withdrawal);
return this;
}

public Wei getBaseFeePerGas() {
return baseFeePerGas;
}

public void setBaseFeePerGas(Wei baseFeePerGas) {
this.baseFeePerGas = baseFeePerGas;
}

public Wei getBlobBaseFee() {
return blobBaseFee;
}

public void setBlobBaseFee(Wei blobBaseFee) {
this.blobBaseFee = blobBaseFee;
}

@Override
public boolean equals(Object o) {
if (!(o instanceof BlockOverridesJson that)) return false;
return Objects.equals(number, that.number) && Objects.equals(prevRandao, that.prevRandao) && Objects.equals(time, that.time) && Objects.equals(gasLimit, that.gasLimit) && Objects.equals(feeRecipient, that.feeRecipient) && Objects.equals(withdrawals, that.withdrawals) && Objects.equals(baseFeePerGas, that.baseFeePerGas) && Objects.equals(blobBaseFee, that.blobBaseFee);
}

@Override
public int hashCode() {
return Objects.hash(number, prevRandao, time, gasLimit, feeRecipient, withdrawals, baseFeePerGas, blobBaseFee);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package io.emeraldpay.etherjar.rpc.json;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.emeraldpay.etherjar.domain.Wei;
import io.emeraldpay.etherjar.hex.HexData;
import io.emeraldpay.etherjar.hex.HexQuantity;

import java.util.List;
import java.util.Objects;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class BlockSimulatedJson extends BlockJson<TransactionRefJson> {

/**
* Log events emitted during call. This includes ETH logs, if <code>traceTransfers</code> is enabled
*/
private List<CallResultLog> calls;

public List<CallResultLog> getCalls() {
return calls;
}

public void setCalls(List<CallResultLog> calls) {
this.calls = calls;
}

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class CallResultLog {

/**
* Transactions return data
*/
private HexData returnData;

/**
* Log events emitted during call. This includes ETH logs, if traceTransfers is enabled
*/
private List<TransactionLogJson> logs;

/**
* Gas used by the transaction
*/
private HexQuantity gasUsed;

/**
* Status indicating that the transaction succeeded
* 1 is success
*/
@JsonDeserialize(using = HexLongDeserializer.class)
@JsonSerialize(using = HexLongSerializer.class)
private Long status;

public HexData getReturnData() {
return returnData;
}

public void setReturnData(HexData returnData) {
this.returnData = returnData;
}

public List<TransactionLogJson> getLogs() {
return logs;
}

public void setLogs(List<TransactionLogJson> logs) {
this.logs = logs;
}

public HexQuantity getGasUsed() {
return gasUsed;
}

public void setGasUsed(HexQuantity gasUsed) {
this.gasUsed = gasUsed;
}

public Long getStatus() {
return status;
}

public void setStatus(Long status) {
this.status = status;
}

@JsonIgnore
public boolean isSuccessful() {
return status == 1;
}

@Override
public boolean equals(Object o) {
if (!(o instanceof CallResultLog that)) return false;
return Objects.equals(returnData, that.returnData)
&& Objects.equals(logs, that.logs)
&& Objects.equals(gasUsed, that.gasUsed)
&& Objects.equals(status, that.status);
}

@Override
public int hashCode() {
return Objects.hash(returnData, logs, gasUsed, status);
}
}

@Override
public boolean equals(Object o) {
if (!(o instanceof BlockSimulatedJson that)) return false;
if (!super.equals(o)) return false;
return Objects.equals(calls, that.calls);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), calls);
}
}

Loading

0 comments on commit e4c98bf

Please sign in to comment.