defmodule Etherex.Time do @moduledoc """ Some Ethereum clients support jumping through time simulating time events like advance the time and the blocks or set and revert to a snapshot. Ganache-cli, for example, support the following non-standard JSON RPC methods: - evm_snapshot : Snapshot the state of the blockchain at the current block. Takes no parameters. Returns the integer id of the snapshot created. A snapshot can only be used (reverted) once. - evm_revert : Revert the state of the blockchain to a previous snapshot. Takes a single parameter, which is the snapshot id to revert to. This deletes the given snapshot, as well as any snapshots taken after. - evm_increaseTime : Jump forward in time. Takes one parameter, which is the amount of time to increase in seconds. Returns the total time adjustment, in seconds. - evm_mine : Force a block to be mined. Takes one optional parameter, which is the timestamp a block should setup as the mining time. Mines a block independent of whether or not mining is started or stopped. """ require Logger alias Ethereumex.HttpClient, as: JsonRPC alias Etherex import Etherex.Helpers, only: [decode_json_rpc_error: 1, encode_quantity: 1, decode_quantity: 1] @doc """ Forces a block to be mined. Takes one optional parameter, which is the timestamp a block should setup as the mining time. Returns the block mined. """ @spec mine(integer() | nil) :: {:ok, Etherex.quantity()} | {:error, Etherex.error()} def mine(time \\ nil) do params = if is_nil(time), do: [], else: [encode_quantity(time)] case JsonRPC.request("evm_mine", params, []) do {:ok, _} -> block_number = Etherex.block_number!() Logger.info("Block #{block_number} mined") {:ok, block_number} error -> decode_json_rpc_error(error) end end @doc """ Enables automatic mining. """ @spec miner_start() :: :ok | {:error, Etherex.error()} def miner_start() do case JsonRPC.request("miner_start", [], []) do {:ok, _} -> :ok error -> decode_json_rpc_error(error) end end @doc """ Disables automatic mining. """ @spec miner_stop() :: :ok | {:error, Etherex.error()} def miner_stop() do case JsonRPC.request("miner_stop", [], []) do {:ok, _} -> :ok error -> decode_json_rpc_error(error) end end @spec snapshot() :: {:ok, non_neg_integer()} | {:error, Etherex.error()} def snapshot() do case JsonRPC.request("evm_snapshot", [], []) do {:ok, id} -> {:ok, decode_quantity(id)} error -> decode_json_rpc_error(error) end end @spec revert(id :: non_neg_integer()) :: :ok | {:error, Etherex.error()} def revert(id) do case JsonRPC.request("evm_revert", [encode_quantity(id)], []) do {:ok, _} -> :ok error -> decode_json_rpc_error(error) end end end