diff --git a/CHANGELOG.md b/CHANGELOG.md index 0289b64..eb9524b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to `citrate-labs-sdk` are documented here. This project adheres to [Semantic Versioning](https://semver.org/). +## [Unreleased] + +### Added + +- `citrate_sdk.verify`: `verify_contract` and `deploy_and_verify` verify Solidity source on + CitrateScan through its Etherscan-compatible API (`verifysourcecode`, `checkverifystatus`) from + forge or hardhat build-info, a hardhat artifact, or a standard JSON input, with polling and typed + errors (`VerificationError` and subclasses). Also `load_build_info`, `parse_build_info`, + `prepare_standard_json`, `check_verify_status`, `wait_for_verification`. +- `CitrateClient.deploy_contract(data, value=0, gas_limit=None, receipt_timeout=120)`: sign and + send a contract-creation transaction (same genesis and chain-id checks) and wait for the receipt + (`DEPLOY_FAILED` on a status-0 receipt). +- Verification details: `chain_id` is optional (sent as `chainid` on the submission and every + status call only when given, as `citrate contract verify --chain-id`); transient explorer errors + (transport, timeout, HTTP 5xx and 429) are retried while waiting, with a backoff capped by + `transient_backoff_max`, and flagged by `ExplorerResponseError.transient`; not-indexed + resubmissions wait at least `rate_limit_backoff`; transport-error messages never include the API + key or the request's query string. + ## [0.7.0] - 2026-10-27 — r1006 address book and a genesis guard Held for the r1006 public cutover: publish only after rpc.citrate.ai serves r1006. diff --git a/README.md b/README.md index 28ca2f3..f3edb52 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,87 @@ Point the SDK at a local Citrate stack on one machine instead of the public test For the full multi-repo bring-up (chain → identity → bundler → gateway → SDKs), see the LOCAL_STACK guide at . +## Verify contracts on CitrateScan +`citrate_sdk.verify` submits Solidity source to CitrateScan, the explorer for chain 40204, through +its Etherscan-compatible API (`https://explorer.citrate.ai/api`). It POSTs `verifysourcecode`, +polls `checkverifystatus`, and returns when the explorer has recompiled the source and matched it +to the code on chain. Input is a forge or hardhat build-info, a hardhat contract artifact, or a +solc standard JSON input. Only the sources the contract's metadata lists are sent, as forge and +hardhat-verify do. + +```python +import os + +from citrate_sdk import CitrateClient, deploy_and_verify, load_build_info, verify_contract + +# forge: `forge build --build-info` writes out/build-info/.json with the compiler input. +build_info, _ = load_build_info("out/build-info/.json") +result = verify_contract( + chain_id=40204, # optional: sent as Etherscan v2 `chainid`; omit it for a local devnet explorer + address="0xYourContractAddress", + build_info=build_info, + contract="src/MyContract.sol:MyContract", # or "MyContract" when the name is unique + constructor_args="0x...", # optional: the explorer reads them from the creation transaction +) +print(result.status) # "verified" or "already_verified" + +# hardhat: pass the contract artifact; load_build_info follows its .dbg.json. +bi, contract = load_build_info("artifacts/contracts/MyContract.sol/MyContract.json") +verify_contract(chain_id=40204, address="0xYourContractAddress", build_info=bi, contract=contract) + +# Deploy from the build-info with the client's key (same genesis/chain-id checks as every +# transaction), then verify with the creation transaction hash. +client = CitrateClient(rpc_url="https://rpc.citrate.ai", private_key=os.environ["CITRATE_PRIVATE_KEY"]) +deployed = deploy_and_verify( + client=client, chain_id=40204, build_info=build_info, + contract="src/MyContract.sol:MyContract", args=[7, "hello"], +) +print(deployed.address, deployed.tx_hash, deployed.verification.status) +``` + +Keywords: +- `chain_id`: optional. When given, it is sent as Etherscan v2 `chainid` on the submission and on + every status call. When omitted, no `chainid` is sent, so an explorer for a local devnet with + another chain id works. This matches `citrate contract verify --chain-id`. +- `explorer_url` (default `https://explorer.citrate.ai/api`), `api_key` (sent as `apikey`; not + required, and never included in error messages), `compiler_version` (otherwise read from the + contract metadata), `creation_tx_hash`. +- `wait=False`: return the guid without polling. +- `poll_interval` (3 s) and `timeout` (300 s) for the status polling. A transient error while + polling (connection reset, request timeout, HTTP 5xx or 429) does not end the wait: the poll is + retried with a backoff that doubles from `poll_interval` up to `transient_backoff_max` (30 s), + within `timeout`. A 4xx other than 429, or a `Fail` verdict, ends it. +- `rate_limit_retries` (3) and `rate_limit_backoff` (5 s) for `Max rate limit reached`. The + explorer takes one submission per 5 s per IP, so while it has not indexed a just-deployed + contract, `deploy_and_verify` resubmits at most every `max(poll_interval, rate_limit_backoff)` + seconds, for up to `index_timeout` (120 s). + +`check_verify_status(guid)` and `wait_for_verification(guid)` poll a job you submitted earlier; +pass the same `chain_id` (or none) as the submission. `CitrateClient.deploy_contract` waits up to +`receipt_timeout` (120 s) for the receipt and raises `CitrateError` with code `DEPLOY_FAILED` +when the receipt has status 0. + +Errors subclass `VerificationError` (a `CitrateError`): +- `BuildInfoError`: bad input, nothing sent. A plain `forge build` without `--build-info` gets + this error, with the fix in the message. +- `VerificationFailedError`: `Fail - Unable to verify`. +- `VerificationRejectedError`: the explorer's refusal string. +- `VerificationRateLimitedError`: still rate limited after retries. +- `VerificationTimeoutError`: no verdict yet. The job may still finish. +- `ExplorerResponseError`: the explorer could not be reached or gave an unexpected answer. + `transient` is true for a transport failure, a timeout, HTTP 5xx and 429. + +The explorer accepts at most 64 files and 2 MiB, inline content only. + +forge and hardhat can verify against the same API directly. Two details matter on chain 40204: +- **forge:** use `--verifier custom --verifier-url https://explorer.citrate.ai/api --verifier-api-key citrate`. + With forge 1.5.1, `--verifier etherscan` stops before sending anything ("No known Etherscan API + URL for chain `40204`"). `--verifier-api-key` must be present, and any value works. +- **hardhat-verify:** give `etherscan.apiKey` as a per-network object (`{ citrate: "citrate" }`) + with a `customChains` entry for chainId 40204 (`apiURL: "https://explorer.citrate.ai/api"`). + With hardhat-verify 2.1.3 a single string `apiKey` switches it to Etherscan's v2 mode, which + ignores `customChains` and sends the request to api.etherscan.io. + ## Configuration | Env var | Default | Purpose | |---------|---------|---------| diff --git a/citrate_sdk/__init__.py b/citrate_sdk/__init__.py index 8bda555..c424ded 100644 --- a/citrate_sdk/__init__.py +++ b/citrate_sdk/__init__.py @@ -48,6 +48,22 @@ StakingInfo, ) +# Contract source verification on CitrateScan (Etherscan-compatible explorer API) +from .verify import ( + BuildInfoError, + ExplorerResponseError, + VerificationError, + VerificationFailedError, + VerificationRateLimitedError, + VerificationRejectedError, + VerificationTimeoutError, + check_verify_status, + deploy_and_verify, + load_build_info, + verify_contract, + wait_for_verification, +) + __version__ = "0.7.0" __author__ = "Citrate Team" @@ -81,6 +97,19 @@ "MemoryClient", "ByomMemoryClient", "MemoryError", + # Contract verification + "verify_contract", + "deploy_and_verify", + "load_build_info", + "check_verify_status", + "wait_for_verification", + "VerificationError", + "BuildInfoError", + "VerificationFailedError", + "VerificationRejectedError", + "VerificationRateLimitedError", + "VerificationTimeoutError", + "ExplorerResponseError", # Data types "LearningPool", "CycleStatus", diff --git a/citrate_sdk/client.py b/citrate_sdk/client.py index 1e0bf99..a3345b7 100644 --- a/citrate_sdk/client.py +++ b/citrate_sdk/client.py @@ -502,6 +502,16 @@ def _send_transaction( JSON string, which is sent byte-for-byte (see ``deploy_model``). """ encoded = data if isinstance(data, str) else json.dumps(data) + return self._sign_and_send(to_address, "0x" + encoded.encode().hex(), value, gas_limit) + + def _sign_and_send( + self, to_address: str | None, data_hex: str, value: int, gas_limit: int + ) -> str: + """Sign ``data_hex`` (0x-prefixed calldata or creation code) and broadcast it. + + ``to_address=None`` is a contract creation. Data source: ``eth_getTransactionCount``, + ``eth_chainId`` and ``eth_sendRawTransaction`` on ``rpc_url``. + """ if not self.key_manager: raise CitrateError("Private key required for transactions") @@ -515,16 +525,17 @@ def _send_transaction( # Build transaction. RM-G.4: bind chainId so the signature is # EIP-155 (domain-separated) and cannot be replayed on another # Citrate network — the pre-fix tx omitted chainId (pre-EIP-155). - tx = { + tx: dict[str, Any] = { "from": from_address, - "to": to_address, "value": hex(value), "gas": hex(gas_limit), "gasPrice": hex(20_000_000_000), # 20 gwei "nonce": hex(nonce), - "data": "0x" + encoded.encode().hex(), + "data": data_hex, "chainId": self._eip155_chain_id(), } + if to_address is not None: + tx["to"] = to_address # Sign transaction signed_tx = self.key_manager.sign_transaction(tx) @@ -532,6 +543,48 @@ def _send_transaction( # Send raw transaction return cast(str, self._rpc_call("eth_sendRawTransaction", [signed_tx])) + def deploy_contract( + self, data: str, value: int = 0, gas_limit: int | None = None, receipt_timeout: int = 120 + ) -> dict[str, Any]: + """Deploy an EVM contract and wait for it to be mined. + + ``data`` is the 0x-prefixed creation code with the ABI-encoded constructor arguments + appended. The transaction is signed with this client's key after the genesis and + chain-id checks every transaction gets. ``gas_limit`` defaults to ``eth_estimateGas`` + plus 20%. Returns ``{"address", "tx_hash", "block_number"}``. + Data source: ``eth_estimateGas``, ``eth_sendRawTransaction`` and + ``eth_getTransactionReceipt`` on ``rpc_url``. Used by + :func:`citrate_sdk.verify.deploy_and_verify`. + """ + if not isinstance(data, str) or not data.startswith("0x") or len(data) <= 2: + raise CitrateError("deploy_contract: data must be non-empty 0x-prefixed hex") + try: + bytes.fromhex(data[2:]) + except ValueError as e: + raise CitrateError("deploy_contract: data must be non-empty 0x-prefixed hex") from e + if not self.key_manager: + raise CitrateError("Private key required for contract deployment") + if gas_limit is None: + estimate = self._rpc_call( + "eth_estimateGas", + [{"from": self.key_manager.get_address(), "data": data, "value": hex(value)}], + ) + gas_limit = int(estimate, 16) * 6 // 5 + tx_hash = self._sign_and_send(None, data, value, gas_limit) + receipt = self._wait_for_receipt(tx_hash, timeout=receipt_timeout) + status = receipt.get("status") + if status is not None and int(str(status), 16) != 1: + raise CitrateError(f"Contract creation failed (tx {tx_hash})", "DEPLOY_FAILED", {"tx_hash": tx_hash}) + address = receipt.get("contractAddress") + if not address: + raise CitrateError(f"Receipt has no contractAddress (tx {tx_hash})", "DEPLOY_FAILED", {"tx_hash": tx_hash}) + block = receipt.get("blockNumber") + return { + "address": address, + "tx_hash": tx_hash, + "block_number": int(str(block), 16) if block is not None else None, + } + def _wait_for_receipt(self, tx_hash: str, timeout: int = 60) -> dict[str, Any]: """Wait for transaction receipt""" start_time = time.time() diff --git a/citrate_sdk/verify.py b/citrate_sdk/verify.py new file mode 100644 index 0000000..5214ae7 --- /dev/null +++ b/citrate_sdk/verify.py @@ -0,0 +1,760 @@ +""" +Contract source verification on CitrateScan (chain 40204). + +Data source: the explorer's Etherscan-compatible API, ``GET|POST `` (default +``https://explorer.citrate.ai/api``), ``module=contract`` with the actions ``verifysourcecode`` +(form-encoded POST) and ``checkverifystatus`` (GET). Every answer is Etherscan's +``{"status": "1" | "0", "message": ..., "result": ...}`` envelope. + +The request shape is the one forge 1.5.1 (``--verifier custom``) and hardhat-verify 2.1.3 send; +``tests/fixtures/verify/`` holds their recorded requests and the tests compare against them. + +Input is a compiler build-info (forge ``out/build-info/.json`` written with +``forge build --build-info``, or hardhat ``artifacts/build-info/.json``), a hardhat contract +artifact (via :func:`load_build_info`) or a solc standard JSON input. As forge and hardhat-verify +do, only the sources listed in the contract's metadata are submitted. +""" + +from __future__ import annotations + +import json +import re +import time +from collections.abc import Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Literal, Protocol +from urllib.parse import quote, quote_plus, urlencode + +import requests + +from ._url_security import enforce_transport_security +from .errors import CitrateError + +DEFAULT_EXPLORER_URL = "https://explorer.citrate.ai/api" +#: Explorer limits (CitrateScan docs, "Limits"). +MAX_SOURCE_BYTES = 2 * 1024 * 1024 +MAX_SOURCE_FILES = 64 + +_HEX = re.compile(r"^[0-9a-fA-F]*$") +_ADDRESS = re.compile(r"^0x[0-9a-fA-F]{40}$") + + +# --------------------------------------------------------------------------- +# Errors +# --------------------------------------------------------------------------- + + +class VerificationError(CitrateError): + """Base class for verification errors.""" + + +class BuildInfoError(VerificationError): + """The build-info or parameters cannot produce a submission. Nothing was sent.""" + + def __init__(self, message: str): + super().__init__(message, "VERIFY_BAD_INPUT") + + +class VerificationRejectedError(VerificationError): + """The explorer refused the submission; ``result`` is Etherscan's string.""" + + def __init__(self, result: str): + super().__init__(f"explorer refused the submission: {result}", "VERIFY_REJECTED", {"result": result}) + self.result = result + + +class VerificationRateLimitedError(VerificationError): + """``Max rate limit reached``, after the configured retries.""" + + def __init__(self, result: str): + super().__init__(f"explorer rate limit: {result}", "VERIFY_RATE_LIMITED", {"result": result}) + self.result = result + + +class VerificationFailedError(VerificationError): + """The explorer compiled the source and it does not match the code on chain.""" + + def __init__(self, guid: str, result: str): + super().__init__( + f"verification failed: {result} (guid {guid})", "VERIFY_FAILED", {"guid": guid, "result": result} + ) + self.guid = guid + self.result = result + + +class VerificationTimeoutError(VerificationError): + """No verdict before the deadline. The job may still finish; poll ``guid`` later.""" + + def __init__(self, guid: str, last_status: str): + super().__init__( + f"no verdict for {guid} before the deadline (last: {last_status})", + "VERIFY_TIMEOUT", + {"guid": guid, "last_status": last_status}, + ) + self.guid = guid + self.last_status = last_status + + +class ExplorerResponseError(VerificationError): + """The explorer could not be reached, or answered outside the documented envelope. + + ``transient`` is true for a transport failure (connection reset, timeout) and for HTTP 5xx and + 429: worth retrying later. :func:`wait_for_verification` retries those. + """ + + def __init__(self, message: str, http_status: int | None = None, transient: bool = False): + super().__init__( + message, "VERIFY_EXPLORER_RESPONSE", {"http_status": http_status, "transient": transient} + ) + self.http_status = http_status + self.transient = transient + + +# --------------------------------------------------------------------------- +# Types +# --------------------------------------------------------------------------- + + +@dataclass(frozen=True) +class PreparedSubmission: + standard_json_input: dict[str, Any] + #: Fully qualified, ``path/To.sol:Name``. + contract_name: str + #: ``v0.8.26+commit.8a97fa7a``. + compiler_version: str + + +@dataclass(frozen=True) +class VerifyStatus: + state: Literal["pending", "verified", "failed"] + message: str + + +@dataclass(frozen=True) +class VerifyResult: + #: ``verified`` after ``Pass - Verified``; ``already_verified`` when the explorer already has a + #: record; ``pending`` when called with ``wait=False``. + status: Literal["verified", "already_verified", "pending"] + message: str + contract_name: str + compiler_version: str + guid: str | None = None + + +@dataclass(frozen=True) +class DeployAndVerifyResult: + address: str + tx_hash: str + verification: VerifyResult + + +class ContractDeployer(Protocol): + """What :func:`deploy_and_verify` needs; :class:`citrate_sdk.CitrateClient` provides it.""" + + def deploy_contract( + self, data: str, value: int = 0, gas_limit: int | None = None + ) -> dict[str, Any]: ... + + +# --------------------------------------------------------------------------- +# Build-info parsing +# --------------------------------------------------------------------------- + + +def parse_build_info(obj: Any) -> dict[str, Any]: + """Accept a forge or hardhat build-info, or a bare standard JSON input, as a build-info dict. + + A bare input is returned as ``{"input": obj}`` (no ``output``). + """ + if not isinstance(obj, dict): + raise BuildInfoError("build-info is not a JSON object") + if obj.get("_format") == "hh-sol-artifact-1": + raise BuildInfoError( + "this is a hardhat contract artifact; pass the build-info its .dbg.json names " + "(or use load_build_info(path))" + ) + if isinstance(obj.get("input"), dict): + return obj + if "source_id_to_path" in obj: + raise BuildInfoError( + "this forge build-info has no compiler input; rebuild with `forge build --build-info` " + "(or build_info = true in foundry.toml)" + ) + if isinstance(obj.get("sources"), dict) and isinstance(obj.get("language"), str): + return {"input": obj} + raise BuildInfoError("not a forge or hardhat build-info or a standard JSON input") + + +def _read_json(path: Path) -> Any: + try: + return json.loads(path.read_text(encoding="utf-8")) + except (OSError, ValueError) as e: + raise BuildInfoError(f"cannot read {path}: {e}") from e + + +def load_build_info(path: str | Path) -> tuple[dict[str, Any], str | None]: + """Read a build-info from disk. + + Also accepts a hardhat contract artifact (``artifacts//.json``) and follows its + ``.dbg.json`` to the build-info. Returns ``(build_info, contract)``; ``contract`` is the + artifact's ``sourceName:contractName`` for an artifact, else ``None``. + """ + p = Path(path) + obj = _read_json(p) + if isinstance(obj, dict) and obj.get("_format") == "hh-sol-artifact-1": + source_name, contract_name = obj.get("sourceName"), obj.get("contractName") + if not isinstance(source_name, str) or not isinstance(contract_name, str): + raise BuildInfoError("hardhat artifact has no sourceName/contractName") + dbg_path = p.with_name(p.stem + ".dbg.json") + dbg = _read_json(dbg_path) + rel = dbg.get("buildInfo") if isinstance(dbg, dict) else None + if not isinstance(rel, str): + raise BuildInfoError(f"{dbg_path} names no buildInfo") + bi = parse_build_info(_read_json((dbg_path.parent / rel).resolve())) + return bi, f"{source_name}:{contract_name}" + return parse_build_info(obj), None + + +def normalize_compiler_version(version: str) -> str: + """``0.8.26+commit.8a97fa7a`` or ``v0.8.26+commit.8a97fa7a`` → ``v0.8.26+commit.8a97fa7a``.""" + t = version.strip().removeprefix("v") + if "+commit." not in t: + raise BuildInfoError( + f"compiler version {version!r} lacks the commit; use e.g. v0.8.26+commit.8a97fa7a" + ) + return "v" + t + + +def normalize_constructor_args(args: str | bytes) -> str: + """Validate ABI-encoded constructor arguments; return lowercase hex without ``0x``.""" + if isinstance(args, bytes): + return args.hex() + t = args.strip() + if t[:2].lower() == "0x": + t = t[2:] + if len(t) % 2 or not _HEX.match(t): + raise BuildInfoError("constructor_args must be ABI-encoded hex") + return t.lower() + + +def _contracts(bi: dict[str, Any]) -> dict[str, Any] | None: + out = bi.get("output") + c = out.get("contracts") if isinstance(out, dict) else None + return c if isinstance(c, dict) else None + + +def _resolve_contract(bi: dict[str, Any], wanted: str) -> tuple[str, str]: + contracts = _contracts(bi) + if ":" in wanted: + path, name = wanted.rsplit(":", 1) + if contracts is not None and name not in (contracts.get(path) or {}): + raise BuildInfoError(f"contract {wanted} is not in this build-info") + return path, name + if contracts is None: + raise BuildInfoError("a standard JSON input carries no contract list; pass contract as path:Name") + hits = [(p, n) for p, f in contracts.items() if isinstance(f, dict) for n in f if n == wanted] + if len(hits) == 1: + return hits[0] + if not hits: + raise BuildInfoError(f"contract {wanted} is not in this build-info") + raise BuildInfoError(f"several contracts are named {wanted}: " + ", ".join(f"{p}:{n}" for p, n in hits)) + + +def _metadata(bi: dict[str, Any], path: str, name: str) -> dict[str, Any] | None: + raw = ((_contracts(bi) or {}).get(path) or {}).get(name, {}).get("metadata") + if isinstance(raw, str): + try: + parsed = json.loads(raw) + except ValueError as e: + raise BuildInfoError(f"metadata of {path}:{name} is not JSON") from e + return parsed if isinstance(parsed, dict) else None + return raw if isinstance(raw, dict) else None + + +def prepare_standard_json( + build_info: dict[str, Any], contract: str, compiler_version: str | None = None +) -> PreparedSubmission: + """The standard JSON input, fully qualified name and compiler version for one contract. + + The compiler version comes from the contract metadata (forge writes ``solcLongVersion`` + without the commit) unless ``compiler_version`` is given. + """ + bi = parse_build_info(build_info) + path, name = _resolve_contract(bi, contract) + meta = _metadata(bi, path, name) + meta_compiler = (meta or {}).get("compiler") + version = compiler_version or ( + meta_compiler.get("version") if isinstance(meta_compiler, dict) else None + ) or bi.get("solcLongVersion") + if not isinstance(version, str) or not version: + raise BuildInfoError("no compiler version in the build-info; pass compiler_version") + + inp = bi["input"] + all_sources = inp.get("sources") + if not isinstance(all_sources, dict): + raise BuildInfoError("standard JSON input has no sources") + meta_sources = (meta or {}).get("sources") + keep = list(meta_sources) if isinstance(meta_sources, dict) else list(all_sources) + sources: dict[str, Any] = {} + for key in keep: + src = all_sources.get(key) + if not isinstance(src, dict): + raise BuildInfoError(f"source {key} is in the metadata but not in the input") + if not isinstance(src.get("content"), str): + raise BuildInfoError(f"source {key} has no inline content (the explorer does not fetch urls)") + sources[key] = src + if len(sources) > MAX_SOURCE_FILES: + raise BuildInfoError(f"{len(sources)} source files; the explorer accepts at most {MAX_SOURCE_FILES}") + + settings = dict(inp.get("settings") or {}) + sel = settings.get("outputSelection") + if isinstance(sel, dict) and "*" not in sel and sel: + # forge keys outputSelection by file; the pruned input uses one "*" entry. + settings["outputSelection"] = {"*": next(iter(sel.values()))} + settings.setdefault("libraries", {}) + + std = { + "language": inp.get("language") if isinstance(inp.get("language"), str) else "Solidity", + "sources": sources, + "settings": settings, + } + size = len(json.dumps(std, separators=(",", ":")).encode("utf-8")) + if size > MAX_SOURCE_BYTES: + raise BuildInfoError(f"standard JSON input is {size} bytes; the explorer accepts at most {MAX_SOURCE_BYTES}") + return PreparedSubmission(std, f"{path}:{name}", normalize_compiler_version(version)) + + +# --------------------------------------------------------------------------- +# Request shape +# --------------------------------------------------------------------------- + + +def build_verify_source_code_form( + *, + address: str, + submission: PreparedSubmission, + constructor_args: str | bytes | None = None, + creation_tx_hash: str | None = None, + chain_id: int | None = None, + api_key: str | None = None, +) -> list[tuple[str, str]]: + """The ``verifysourcecode`` form fields, in the order forge sends them. + + Constructor arguments go out as both ``constructorArguements`` (Etherscan's spelling) and + ``constructorArguments``. + """ + if not _ADDRESS.match(address): + raise BuildInfoError(f"address {address!r} is not 20 bytes of hex") + f: list[tuple[str, str]] = [] + if api_key: + f.append(("apikey", api_key)) + if chain_id is not None: + f.append(("chainid", str(chain_id))) + f += [ + ("module", "contract"), + ("action", "verifysourcecode"), + ("contractaddress", address), + ("sourceCode", json.dumps(submission.standard_json_input, separators=(",", ":"))), + ("codeformat", "solidity-standard-json-input"), + ("contractname", submission.contract_name), + ("compilerversion", normalize_compiler_version(submission.compiler_version)), + ] + if constructor_args: + hx = normalize_constructor_args(constructor_args) + f += [("constructorArguements", hx), ("constructorArguments", hx)] + if creation_tx_hash: + f.append(("creationtxhash", creation_tx_hash)) + return f + + +# --------------------------------------------------------------------------- +# Explorer calls +# --------------------------------------------------------------------------- + + +@dataclass(frozen=True) +class _Envelope: + ok: bool + message: str + result: Any + + @property + def text(self) -> str: + return self.result if isinstance(self.result, str) else json.dumps(self.result) + + +def _is_rate_limit(text: str) -> bool: + return "rate limit" in text.lower() + + +_URL_QUERY = re.compile(r"\?[^\s'\"()]+") + + +def _transport_error(e: requests.RequestException, api_key: str | None) -> ExplorerResponseError: + """An :class:`ExplorerResponseError` for a failed request, without the API key. + + requests names the request URL in its message (``/api?apikey=&...``), so the message is + scrubbed: every query string is cut and the key is removed wherever it appears. The caller + raises it ``from None``, so the original exception, which carries the same URL, is not chained + into the traceback. + """ + text = _URL_QUERY.sub("?", str(e)) + if api_key: + for form in {api_key, quote_plus(api_key), quote(api_key, safe="")}: + text = text.replace(form, "") + return ExplorerResponseError( + f"could not reach the explorer ({type(e).__name__}): {text}", transient=_is_transient(e) + ) + + +def _is_transient(e: requests.RequestException) -> bool: + """Connection reset or refused, a timeout, or a body cut short: worth retrying.""" + return isinstance(e, (requests.ConnectionError, requests.Timeout, requests.exceptions.ChunkedEncodingError)) + + +class _Explorer: + def __init__( + self, + explorer_url: str, + api_key: str | None, + chain_id: int | None, + allow_insecure_http: bool, + session: requests.Session | None, + request_timeout: float, + ): + self.url = enforce_transport_security(explorer_url, allow_insecure_http=allow_insecure_http) + self.api_key = api_key or None + self.chain_id = chain_id + self.session = session or requests.Session() + self.request_timeout = request_timeout + + def _parse(self, resp: requests.Response) -> _Envelope: + try: + body = resp.json() + except ValueError: + body = None + if isinstance(body, dict) and isinstance(body.get("status"), (str, int)): + return _Envelope(str(body["status"]) == "1", str(body.get("message") or ""), body.get("result")) + if not resp.ok: + transient = resp.status_code >= 500 or resp.status_code == 429 + raise ExplorerResponseError( + f"explorer answered HTTP {resp.status_code}: {resp.text[:300]}", resp.status_code, transient + ) + raise ExplorerResponseError(f"unexpected explorer answer: {resp.text[:300]}", resp.status_code) + + def post_form(self, form: list[tuple[str, str]]) -> _Envelope: + try: + resp = self.session.post( + self.url, + data=urlencode(form), + headers={"content-type": "application/x-www-form-urlencoded"}, + timeout=self.request_timeout, + ) + except requests.RequestException as e: + form_key = next((v for k, v in form if k == "apikey"), None) + raise _transport_error(e, self.api_key or form_key) from None + return self._parse(resp) + + def get(self, pairs: list[tuple[str, str]]) -> _Envelope: + q: list[tuple[str, str]] = [] + if self.api_key: + q.append(("apikey", self.api_key)) + if self.chain_id is not None: + q.append(("chainid", str(self.chain_id))) + q += pairs + try: + resp = self.session.get(self.url + "?" + urlencode(q), timeout=self.request_timeout) + except requests.RequestException as e: + raise _transport_error(e, self.api_key) from None + return self._parse(resp) + + +def _explorer( + explorer_url: str = DEFAULT_EXPLORER_URL, + api_key: str | None = None, + chain_id: int | None = None, + allow_insecure_http: bool = False, + session: requests.Session | None = None, + request_timeout: float = 60.0, +) -> _Explorer: + return _Explorer(explorer_url, api_key, chain_id, allow_insecure_http, session, request_timeout) + + +def submit_verification( + form: list[tuple[str, str]], + *, + explorer_url: str = DEFAULT_EXPLORER_URL, + rate_limit_retries: int = 3, + rate_limit_backoff: float = 5.0, + allow_insecure_http: bool = False, + session: requests.Session | None = None, +) -> str: + """POST ``verifysourcecode`` once (retrying after ``Max rate limit reached``); return the guid.""" + ex = _explorer(explorer_url, allow_insecure_http=allow_insecure_http, session=session) + attempt = 0 + while True: + env = ex.post_form(form) + if env.ok: + if not env.text: + raise ExplorerResponseError("empty guid") + return env.text + if _is_rate_limit(env.text) and attempt < rate_limit_retries: + attempt += 1 + time.sleep(rate_limit_backoff) + continue + if _is_rate_limit(env.text): + raise VerificationRateLimitedError(env.text) + raise VerificationRejectedError(env.text) + + +def check_verify_status( + guid: str, + *, + explorer_url: str = DEFAULT_EXPLORER_URL, + api_key: str | None = None, + chain_id: int | None = None, + allow_insecure_http: bool = False, + session: requests.Session | None = None, +) -> VerifyStatus: + """One ``checkverifystatus`` call.""" + ex = _explorer(explorer_url, api_key, chain_id, allow_insecure_http, session) + env = ex.get([("module", "contract"), ("action", "checkverifystatus"), ("guid", guid)]) + text = env.text + if env.ok or "already verified" in text.lower(): + return VerifyStatus("verified", text) + if text.lower().startswith("pending") or _is_rate_limit(text): + return VerifyStatus("pending", text) + return VerifyStatus("failed", text) + + +def wait_for_verification( + guid: str, + *, + poll_interval: float = 3.0, + timeout: float = 300.0, + explorer_url: str = DEFAULT_EXPLORER_URL, + api_key: str | None = None, + chain_id: int | None = None, + allow_insecure_http: bool = False, + session: requests.Session | None = None, + transient_backoff_max: float = 30.0, +) -> VerifyStatus: + """Poll until ``Pass - Verified`` (returns) or ``Fail - ...`` (raises :class:`VerificationFailedError`). + + A transient :class:`ExplorerResponseError` (transport failure, HTTP 5xx or 429) does not end the + wait: the poll is retried after a backoff that doubles from ``poll_interval`` up to + ``transient_backoff_max`` seconds, within ``timeout``. Any other error ends it. + """ + deadline = time.monotonic() + timeout + transient_errors = 0 + while True: + delay = poll_interval + try: + s = check_verify_status( + guid, + explorer_url=explorer_url, + api_key=api_key, + chain_id=chain_id, + allow_insecure_http=allow_insecure_http, + session=session, + ) + except ExplorerResponseError as e: + if not e.transient: + raise + transient_errors += 1 + delay = min(poll_interval * 2 ** (transient_errors - 1), max(transient_backoff_max, poll_interval)) + last = str(e) + else: + if s.state == "verified": + return s + if s.state == "failed": + raise VerificationFailedError(guid, s.message) + transient_errors = 0 + last = s.message + if time.monotonic() + delay > deadline: + raise VerificationTimeoutError(guid, last) + time.sleep(delay) + + +def verify_contract( + *, + address: str, + contract: str, + build_info: dict[str, Any] | None = None, + standard_json_input: dict[str, Any] | None = None, + constructor_args: str | bytes | None = None, + explorer_url: str = DEFAULT_EXPLORER_URL, + api_key: str | None = None, + compiler_version: str | None = None, + creation_tx_hash: str | None = None, + wait: bool = True, + poll_interval: float = 3.0, + timeout: float = 300.0, + rate_limit_retries: int = 3, + rate_limit_backoff: float = 5.0, + allow_insecure_http: bool = False, + session: requests.Session | None = None, + chain_id: int | None = None, + transient_backoff_max: float = 30.0, +) -> VerifyResult: + """Verify a deployed contract's source on CitrateScan. + + Submits ``verifysourcecode`` and, unless ``wait=False``, polls ``checkverifystatus`` until a + verdict. Returns ``verified`` or ``already_verified``; raises + :class:`VerificationFailedError` on ``Fail - Unable to verify`` and the other + :class:`VerificationError` subclasses otherwise. + + ``contract`` is ``path/To.sol:Name`` or a bare ``Name`` when unique in the build-info. + ``constructor_args`` (ABI-encoded) is optional: the explorer reads them from the creation + transaction. + + ``chain_id``, when given, is sent as Etherscan v2 ``chainid`` on the submission and on every + status call (CitrateScan accepts 40204). When omitted nothing is sent, so an explorer for a + local devnet with another chain id works. + """ + if chain_id is not None and (not isinstance(chain_id, int) or isinstance(chain_id, bool) or chain_id <= 0): + raise BuildInfoError("chain_id must be a positive integer") + source = build_info if build_info is not None else ( + {"input": standard_json_input} if standard_json_input is not None else None + ) + if source is None: + raise BuildInfoError("pass build_info or standard_json_input") + submission = prepare_standard_json(source, contract, compiler_version) + form = build_verify_source_code_form( + address=address, + submission=submission, + constructor_args=constructor_args, + creation_tx_hash=creation_tx_hash, + chain_id=chain_id, + api_key=api_key, + ) + base = {"contract_name": submission.contract_name, "compiler_version": submission.compiler_version} + try: + guid = submit_verification( + form, + explorer_url=explorer_url, + rate_limit_retries=rate_limit_retries, + rate_limit_backoff=rate_limit_backoff, + allow_insecure_http=allow_insecure_http, + session=session, + ) + except VerificationRejectedError as e: + if "already verified" in e.result.lower(): + return VerifyResult(status="already_verified", message=e.result, **base) + raise + if not wait: + return VerifyResult(status="pending", message="submitted", guid=guid, **base) + s = wait_for_verification( + guid, + poll_interval=poll_interval, + timeout=timeout, + explorer_url=explorer_url, + api_key=api_key, + chain_id=chain_id, + allow_insecure_http=allow_insecure_http, + session=session, + transient_backoff_max=transient_backoff_max, + ) + return VerifyResult(status="verified", message=s.message, guid=guid, **base) + + +# --------------------------------------------------------------------------- +# Deploy, then verify +# --------------------------------------------------------------------------- + + +def encode_constructor_args(abi: list[dict[str, Any]], args: Sequence[Any]) -> str: + """ABI-encode constructor arguments with the contract ABI; hex without ``0x``.""" + from eth_abi import encode + from eth_utils import collapse_if_tuple + + ctor = next((e for e in abi if e.get("type") == "constructor"), None) + inputs = ctor.get("inputs", []) if ctor else [] + if len(inputs) != len(args): + raise BuildInfoError(f"constructor takes {len(inputs)} argument(s), got {len(args)}") + types = [collapse_if_tuple(i) for i in inputs] + return bytes(encode(types, list(args))).hex() + + +def deploy_and_verify( + *, + client: ContractDeployer, + build_info: dict[str, Any], + contract: str, + args: Sequence[Any] | None = None, + constructor_args: str | bytes | None = None, + value: int = 0, + gas_limit: int | None = None, + index_timeout: float = 120.0, + explorer_url: str = DEFAULT_EXPLORER_URL, + api_key: str | None = None, + compiler_version: str | None = None, + wait: bool = True, + poll_interval: float = 3.0, + timeout: float = 300.0, + rate_limit_retries: int = 3, + rate_limit_backoff: float = 5.0, + allow_insecure_http: bool = False, + session: requests.Session | None = None, + chain_id: int | None = None, + transient_backoff_max: float = 30.0, +) -> DeployAndVerifyResult: + """Deploy a contract from a build-info through the client's signer, then verify it. + + The creation code is the build-info's ``evm.bytecode.object`` (unlinked libraries are + refused) followed by the constructor arguments (``args`` encoded with the contract ABI, or + already-encoded ``constructor_args``). The creation transaction hash is passed to the + explorer. While the explorer answers ``Unable to locate ContractCode`` (not indexed yet), the + submission is retried until ``index_timeout`` seconds, waiting the larger of ``poll_interval`` + and ``rate_limit_backoff`` between submissions. + """ + bi = parse_build_info(build_info) + path, name = _resolve_contract(bi, contract) + compiled = ((_contracts(bi) or {}).get(path) or {}).get(name) or {} + bytecode = ((compiled.get("evm") or {}).get("bytecode") or {}).get("object") + if not isinstance(bytecode, str) or not bytecode: + raise BuildInfoError(f"{path}:{name} has no evm.bytecode.object in the build-info") + if "__$" in bytecode: + raise BuildInfoError(f"{path}:{name} has unlinked library placeholders; link them first") + if args is not None and constructor_args is not None: + raise BuildInfoError("pass args or constructor_args, not both") + encoded = normalize_constructor_args(constructor_args) if constructor_args is not None else "" + if args is not None: + abi = compiled.get("abi") + if not isinstance(abi, list): + raise BuildInfoError(f"{path}:{name} has no ABI to encode args with") + encoded = encode_constructor_args(abi, args) + + data = "0x" + bytecode.removeprefix("0x") + encoded + deployed = client.deploy_contract(data, value=value, gas_limit=gas_limit) + address, tx_hash = str(deployed["address"]), str(deployed["tx_hash"]) + + deadline = time.monotonic() + index_timeout + while True: + try: + verification = verify_contract( + chain_id=chain_id, + address=address, + contract=f"{path}:{name}", + build_info=bi, + constructor_args=encoded or None, + explorer_url=explorer_url, + api_key=api_key, + compiler_version=compiler_version, + creation_tx_hash=tx_hash, + wait=wait, + poll_interval=poll_interval, + timeout=timeout, + rate_limit_retries=rate_limit_retries, + rate_limit_backoff=rate_limit_backoff, + allow_insecure_http=allow_insecure_http, + session=session, + transient_backoff_max=transient_backoff_max, + ) + return DeployAndVerifyResult(address, tx_hash, verification) + except VerificationRejectedError as e: + if "unable to locate contractcode" not in e.result.lower() or time.monotonic() >= deadline: + raise + # A resubmission is a verifysourcecode POST: the explorer takes one per 5 s per IP. + time.sleep(max(poll_interval, rate_limit_backoff)) diff --git a/tests/fixtures/verify/README.md b/tests/fixtures/verify/README.md new file mode 100644 index 0000000..c9be17b --- /dev/null +++ b/tests/fixtures/verify/README.md @@ -0,0 +1,14 @@ +Fixtures generated by real tools on 2026-11-07, not written by hand. + +- `forge/`: Foundry forge 1.5.1-stable (b0a9dd9c), `forge build --build-info` over `forge/src/` + (solc 0.8.26, optimizer 200 runs, evm cancun). The build-info is copied to `forge/build-info/` + because `out/` is git-ignored. + - `standard-json-input.json` is `forge verify-contract --show-standard-json-input src/Greeter.sol:Greeter`. + - `minimal-build-info.json` is what a plain `forge build` (no `--build-info`) writes. + - `forge-1.5.1-custom-verifier-requests.jsonl` holds the two requests + `forge verify-contract --verifier custom --watch --constructor-args 0x…07` sent to a local + recording server. +- `hardhat/`: Hardhat 2.29.1, `npx hardhat compile` over the same three sources under `contracts/`. + - `hardhat-verify-2.1.3-requests.jsonl` holds the requests `npx hardhat verify --network citrate + 7 hello` (hardhat-verify 2.1.3, per-network apiKey `citrate`, customChains 40204) sent + to a local recording server, against a Greeter deployed on a local anvil. diff --git a/tests/fixtures/verify/forge/build-info/970692f6b0ae96cc.json b/tests/fixtures/verify/forge/build-info/970692f6b0ae96cc.json new file mode 100644 index 0000000..96a2820 --- /dev/null +++ b/tests/fixtures/verify/forge/build-info/970692f6b0ae96cc.json @@ -0,0 +1 @@ +{"id":"970692f6b0ae96cc","source_id_to_path":{"0":"src/Counter.sol","1":"src/Greeter.sol","2":"src/Unrelated.sol"},"language":"Solidity","_format":"ethers-rs-sol-build-info-1","input":{"version":"0.8.26","language":"Solidity","sources":{"src/Counter.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\ncontract Counter {\n uint256 public number;\n\n function increment() external {\n number += 1;\n }\n}\n"},"src/Greeter.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Counter} from \"./Counter.sol\";\n\ncontract Greeter {\n string public greeting;\n uint256 public immutable seed;\n\n constructor(uint256 seed_, string memory greeting_) {\n seed = seed_;\n greeting = greeting_;\n }\n}\n"},"src/Unrelated.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\ncontract Unrelated {\n function ping() external pure returns (uint256) {\n return 1;\n }\n}\n"}},"settings":{"optimizer":{"enabled":true,"runs":200},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"src/Counter.sol":{"":["ast"],"*":["abi","evm.bytecode.object","evm.bytecode.sourceMap","evm.bytecode.linkReferences","evm.deployedBytecode.object","evm.deployedBytecode.sourceMap","evm.deployedBytecode.linkReferences","evm.deployedBytecode.immutableReferences","evm.methodIdentifiers","metadata"]},"src/Greeter.sol":{"":["ast"],"*":["abi","evm.bytecode.object","evm.bytecode.sourceMap","evm.bytecode.linkReferences","evm.deployedBytecode.object","evm.deployedBytecode.sourceMap","evm.deployedBytecode.linkReferences","evm.deployedBytecode.immutableReferences","evm.methodIdentifiers","metadata"]},"src/Unrelated.sol":{"":["ast"],"*":["abi","evm.bytecode.object","evm.bytecode.sourceMap","evm.bytecode.linkReferences","evm.deployedBytecode.object","evm.deployedBytecode.sourceMap","evm.deployedBytecode.linkReferences","evm.deployedBytecode.immutableReferences","evm.methodIdentifiers","metadata"]}},"evmVersion":"cancun","viaIR":false,"libraries":{}},"allowPaths":["/private/tmp/claude-501/-Users-learnlikelarry-Projects-citrate-labs/184324b5-d0b1-45d5-a1b6-5f029127bc0f/scratchpad/fx-forge","/private/tmp/claude-501/-Users-learnlikelarry-Projects-citrate-labs/184324b5-d0b1-45d5-a1b6-5f029127bc0f/scratchpad/fx-forge/lib"],"basePath":"/private/tmp/claude-501/-Users-learnlikelarry-Projects-citrate-labs/184324b5-d0b1-45d5-a1b6-5f029127bc0f/scratchpad/fx-forge","includePaths":["/private/tmp/claude-501/-Users-learnlikelarry-Projects-citrate-labs/184324b5-d0b1-45d5-a1b6-5f029127bc0f/scratchpad/fx-forge"]},"output":{"contracts":{"src/Counter.sol":{"Counter":{"abi":[{"type":"function","name":"increment","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"number","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}],"metadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"increment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"number\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/Counter.sol\":\"Counter\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"src/Counter.sol\":{\"keccak256\":\"0xc2db2c51f9be330b16e281a99dfe043e18dc2a60c40eea193802acb2aa612584\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://df36ff1caedb811d7f0b735d3a2739c75e7bba3361b2be8ae9b92320aac69395\",\"dweb:/ipfs/QmQz644QUD1bWRX8QcEa4CcQknRpLFrLZiJNXT2rZ9PcSu\"]}},\"version\":1}","userdoc":{},"devdoc":{},"evm":{"bytecode":{"object":"6080604052348015600e575f80fd5b5060c580601a5f395ff3fe6080604052348015600e575f80fd5b50600436106030575f3560e01c80638381f58a146034578063d09de08a14604d575b5f80fd5b603b5f5481565b60405190815260200160405180910390f35b60536055565b005b60015f8082825460649190606b565b9091555050565b80820180821115608957634e487b7160e01b5f52601160045260245ffd5b9291505056fea264697066735822122003e0e26c39beea7c8c7aeb4ea7b01294b44b8111cd7c2aa40ca407bd6babf18864736f6c634300081a0033","sourceMap":"57:111:0:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"6080604052348015600e575f80fd5b50600436106030575f3560e01c80638381f58a146034578063d09de08a14604d575b5f80fd5b603b5f5481565b60405190815260200160405180910390f35b60536055565b005b60015f8082825460649190606b565b9091555050565b80820180821115608957634e487b7160e01b5f52601160045260245ffd5b9291505056fea264697066735822122003e0e26c39beea7c8c7aeb4ea7b01294b44b8111cd7c2aa40ca407bd6babf18864736f6c634300081a0033","sourceMap":"57:111:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;80:21;;;;;;;;;160:25:3;;;148:2;133:18;80:21:0;;;;;;;108:58;;;:::i;:::-;;;158:1;148:6;;:11;;;;;;;:::i;:::-;;;;-1:-1:-1;;108:58:0:o;196:222:3:-;261:9;;;282:10;;;279:133;;;334:10;329:3;325:20;322:1;315:31;369:4;366:1;359:15;397:4;394:1;387:15;279:133;196:222;;;;:::o","linkReferences":{}},"methodIdentifiers":{"increment()":"d09de08a","number()":"8381f58a"}}}},"src/Greeter.sol":{"Greeter":{"abi":[{"type":"constructor","inputs":[{"name":"seed_","type":"uint256","internalType":"uint256"},{"name":"greeting_","type":"string","internalType":"string"}],"stateMutability":"nonpayable"},{"type":"function","name":"greeting","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"seed","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"}],"metadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"seed_\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"greeting_\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"greeting\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"seed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/Greeter.sol\":\"Greeter\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"src/Counter.sol\":{\"keccak256\":\"0xc2db2c51f9be330b16e281a99dfe043e18dc2a60c40eea193802acb2aa612584\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://df36ff1caedb811d7f0b735d3a2739c75e7bba3361b2be8ae9b92320aac69395\",\"dweb:/ipfs/QmQz644QUD1bWRX8QcEa4CcQknRpLFrLZiJNXT2rZ9PcSu\"]},\"src/Greeter.sol\":{\"keccak256\":\"0x1758099b79a52734a12d37366662fc7fdacf74767ca04a0b741976e2bcc754f0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f5b995a3c5ae7000f3025326c412a1a08bfa89aea899d84a0751b8a84b16aefd\",\"dweb:/ipfs/QmbfaqxQmtcZVFXKEv7RDjnxcGhJ1cLHLhjemCKrVEjPZk\"]}},\"version\":1}","userdoc":{},"devdoc":{},"evm":{"bytecode":{"object":"60a060405234801561000f575f80fd5b5060405161041f38038061041f83398101604081905261002e9161005a565b60808290525f61003e8282610199565b505050610253565b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561006b575f80fd5b825160208401519092506001600160401b03811115610088575f80fd5b8301601f81018513610098575f80fd5b80516001600160401b038111156100b1576100b1610046565b604051601f8201601f19908116603f011681016001600160401b03811182821017156100df576100df610046565b6040528181528282016020018710156100f6575f80fd5b8160208401602083015e5f602083830101528093505050509250929050565b600181811c9082168061012957607f821691505b60208210810361014757634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561019457805f5260205f20601f840160051c810160208510156101725750805b601f840160051c820191505b81811015610191575f815560010161017e565b50505b505050565b81516001600160401b038111156101b2576101b2610046565b6101c6816101c08454610115565b8461014d565b6020601f8211600181146101f8575f83156101e15750848201515b5f19600385901b1c1916600184901b178455610191565b5f84815260208120601f198516915b828110156102275787850151825560209485019460019092019101610207565b508482101561024457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6080516101b561026a5f395f603d01526101b55ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80637d94792a14610038578063ef690cc014610072575b5f80fd5b61005f7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b61007a610087565b6040516100699190610112565b5f805461009390610147565b80601f01602080910402602001604051908101604052809291908181526020018280546100bf90610147565b801561010a5780601f106100e15761010080835404028352916020019161010a565b820191905f5260205f20905b8154815290600101906020018083116100ed57829003601f168201915b505050505081565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b600181811c9082168061015b57607f821691505b60208210810361017957634e487b7160e01b5f52602260045260245ffd5b5091905056fea2646970667358221220bc6dcc81ff4bc49f02a4024086a8540e720924583e9db263fae57e294698cfdf64736f6c634300081a0033","sourceMap":"97:200:1:-:0;;;184:111;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;246:12;;;;268:8;:20;279:9;268:8;:20;:::i;:::-;;184:111;;97:200;;14:127:3;75:10;70:3;66:20;63:1;56:31;106:4;103:1;96:15;130:4;127:1;120:15;146:996;235:6;243;296:2;284:9;275:7;271:23;267:32;264:52;;;312:1;309;302:12;264:52;335:16;;395:2;380:18;;374:25;335:16;;-1:-1:-1;;;;;;411:30:3;;408:50;;;454:1;451;444:12;408:50;477:22;;530:4;522:13;;518:27;-1:-1:-1;508:55:3;;559:1;556;549:12;508:55;586:9;;-1:-1:-1;;;;;607:30:3;;604:56;;;640:18;;:::i;:::-;689:2;683:9;781:2;743:17;;-1:-1:-1;;739:31:3;;;772:2;735:40;731:54;719:67;;-1:-1:-1;;;;;801:34:3;;837:22;;;798:62;795:88;;;863:18;;:::i;:::-;899:2;892:22;923;;;964:15;;;981:2;960:24;957:37;-1:-1:-1;954:57:3;;;1007:1;1004;997:12;954:57;1056:6;1051:2;1047;1043:11;1038:2;1030:6;1026:15;1020:43;1109:1;1104:2;1095:6;1087;1083:19;1079:28;1072:39;1130:6;1120:16;;;;;146:996;;;;;:::o;1147:380::-;1226:1;1222:12;;;;1269;;;1290:61;;1344:4;1336:6;1332:17;1322:27;;1290:61;1397:2;1389:6;1386:14;1366:18;1363:38;1360:161;;1443:10;1438:3;1434:20;1431:1;1424:31;1478:4;1475:1;1468:15;1506:4;1503:1;1496:15;1360:161;;1147:380;;;:::o;1658:518::-;1760:2;1755:3;1752:11;1749:421;;;1796:5;1793:1;1786:16;1840:4;1837:1;1827:18;1910:2;1898:10;1894:19;1891:1;1887:27;1881:4;1877:38;1946:4;1934:10;1931:20;1928:47;;;-1:-1:-1;1969:4:3;1928:47;2024:2;2019:3;2015:12;2012:1;2008:20;2002:4;1998:31;1988:41;;2079:81;2097:2;2090:5;2087:13;2079:81;;;2156:1;2142:16;;2123:1;2112:13;2079:81;;;2083:3;;1749:421;1658:518;;;:::o;2352:1299::-;2472:10;;-1:-1:-1;;;;;2494:30:3;;2491:56;;;2527:18;;:::i;:::-;2556:97;2646:6;2606:38;2638:4;2632:11;2606:38;:::i;:::-;2600:4;2556:97;:::i;:::-;2702:4;2733:2;2722:14;;2750:1;2745:649;;;;3438:1;3455:6;3452:89;;;-1:-1:-1;3507:19:3;;;3501:26;3452:89;-1:-1:-1;;2309:1:3;2305:11;;;2301:24;2297:29;2287:40;2333:1;2329:11;;;2284:57;3554:81;;2715:930;;2745:649;1605:1;1598:14;;;1642:4;1629:18;;-1:-1:-1;;2781:20:3;;;2899:222;2913:7;2910:1;2907:14;2899:222;;;2995:19;;;2989:26;2974:42;;3102:4;3087:20;;;;3055:1;3043:14;;;;2929:12;2899:222;;;2903:3;3149:6;3140:7;3137:19;3134:201;;;3210:19;;;3204:26;-1:-1:-1;;3293:1:3;3289:14;;;3305:3;3285:24;3281:37;3277:42;3262:58;3247:74;;3134:201;-1:-1:-1;;;;3381:1:3;3365:14;;;3361:22;3348:36;;-1:-1:-1;2352:1299:3:o;:::-;97:200:1;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"608060405234801561000f575f80fd5b5060043610610034575f3560e01c80637d94792a14610038578063ef690cc014610072575b5f80fd5b61005f7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b61007a610087565b6040516100699190610112565b5f805461009390610147565b80601f01602080910402602001604051908101604052809291908181526020018280546100bf90610147565b801561010a5780601f106100e15761010080835404028352916020019161010a565b820191905f5260205f20905b8154815290600101906020018083116100ed57829003601f168201915b505050505081565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b600181811c9082168061015b57607f821691505b60208210810361017957634e487b7160e01b5f52602260045260245ffd5b5091905056fea2646970667358221220bc6dcc81ff4bc49f02a4024086a8540e720924583e9db263fae57e294698cfdf64736f6c634300081a0033","sourceMap":"97:200:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;148:29;;;;;;;;160:25:3;;;148:2;133:18;148:29:1;;;;;;;;120:22;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;196:418:3:-;345:2;334:9;327:21;308:4;377:6;371:13;420:6;415:2;404:9;400:18;393:34;479:6;474:2;466:6;462:15;457:2;446:9;442:18;436:50;535:1;530:2;521:6;510:9;506:22;502:31;495:42;605:2;598;594:7;589:2;581:6;577:15;573:29;562:9;558:45;554:54;546:62;;;196:418;;;;:::o;619:380::-;698:1;694:12;;;;741;;;762:61;;816:4;808:6;804:17;794:27;;762:61;869:2;861:6;858:14;838:18;835:38;832:161;;915:10;910:3;906:20;903:1;896:31;950:4;947:1;940:15;978:4;975:1;968:15;832:161;;619:380;;;:::o","linkReferences":{},"immutableReferences":{"20":[{"start":61,"length":32}]}},"methodIdentifiers":{"greeting()":"ef690cc0","seed()":"7d94792a"}}}},"src/Unrelated.sol":{"Unrelated":{"abi":[{"type":"function","name":"ping","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"pure"}],"metadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"ping\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/Unrelated.sol\":\"Unrelated\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"src/Unrelated.sol\":{\"keccak256\":\"0x5cc667db0b908cd4179177879592dc9346bea9887b091554a9d9d82fe9a759b6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://04e1c4dce1fb8a75bce1b5b0e1ef9457e864dbe2ed7bb23df7603e151483c549\",\"dweb:/ipfs/QmRwLY8VjfogJj19xdFRdmZYLC5rqdotUnSjxfnPQ6MC8Y\"]}},\"version\":1}","userdoc":{},"devdoc":{},"evm":{"bytecode":{"object":"6080604052348015600e575f80fd5b50607480601a5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c80635c36b18614602a575b5f80fd5b600160405190815260200160405180910390f3fea2646970667358221220043ee5a12d3c044a8f7d6df2820ba1d938ae80ad4c01e80809d615e3462e8e4b64736f6c634300081a0033","sourceMap":"57:100:2:-:0;;;;;;;;;;;;;;;;;;;","linkReferences":{}},"deployedBytecode":{"object":"6080604052348015600e575f80fd5b50600436106026575f3560e01c80635c36b18614602a575b5f80fd5b600160405190815260200160405180910390f3fea2646970667358221220043ee5a12d3c044a8f7d6df2820ba1d938ae80ad4c01e80809d615e3462e8e4b64736f6c634300081a0033","sourceMap":"57:100:2:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;82:73;147:1;82:73;;160:25:3;;;148:2;133:18;82:73:2;;;;;;","linkReferences":{}},"methodIdentifiers":{"ping()":"5c36b186"}}}}},"sources":{"src/Counter.sol":{"id":0,"ast":{"absolutePath":"src/Counter.sol","id":13,"exportedSymbols":{"Counter":[12]},"nodeType":"SourceUnit","src":"32:137:0","nodes":[{"id":1,"nodeType":"PragmaDirective","src":"32:23:0","nodes":[],"literals":["solidity","0.8",".26"]},{"id":12,"nodeType":"ContractDefinition","src":"57:111:0","nodes":[{"id":3,"nodeType":"VariableDeclaration","src":"80:21:0","nodes":[],"constant":false,"functionSelector":"8381f58a","mutability":"mutable","name":"number","nameLocation":"95:6:0","scope":12,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":2,"name":"uint256","nodeType":"ElementaryTypeName","src":"80:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":11,"nodeType":"FunctionDefinition","src":"108:58:0","nodes":[],"body":{"id":10,"nodeType":"Block","src":"138:28:0","nodes":[],"statements":[{"expression":{"id":8,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":6,"name":"number","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":3,"src":"148:6:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"+=","rightHandSide":{"hexValue":"31","id":7,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"158:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"148:11:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":9,"nodeType":"ExpressionStatement","src":"148:11:0"}]},"functionSelector":"d09de08a","implemented":true,"kind":"function","modifiers":[],"name":"increment","nameLocation":"117:9:0","parameters":{"id":4,"nodeType":"ParameterList","parameters":[],"src":"126:2:0"},"returnParameters":{"id":5,"nodeType":"ParameterList","parameters":[],"src":"138:0:0"},"scope":12,"stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Counter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[12],"name":"Counter","nameLocation":"66:7:0","scope":13,"usedErrors":[],"usedEvents":[]}],"license":"MIT"}},"src/Greeter.sol":{"id":1,"ast":{"absolutePath":"src/Greeter.sol","id":38,"exportedSymbols":{"Counter":[12],"Greeter":[37]},"nodeType":"SourceUnit","src":"32:266:1","nodes":[{"id":14,"nodeType":"PragmaDirective","src":"32:23:1","nodes":[],"literals":["solidity","0.8",".26"]},{"id":16,"nodeType":"ImportDirective","src":"57:38:1","nodes":[],"absolutePath":"src/Counter.sol","file":"./Counter.sol","nameLocation":"-1:-1:-1","scope":38,"sourceUnit":13,"symbolAliases":[{"foreign":{"id":15,"name":"Counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":12,"src":"65:7:1","typeDescriptions":{}},"nameLocation":"-1:-1:-1"}],"unitAlias":""},{"id":37,"nodeType":"ContractDefinition","src":"97:200:1","nodes":[{"id":18,"nodeType":"VariableDeclaration","src":"120:22:1","nodes":[],"constant":false,"functionSelector":"ef690cc0","mutability":"mutable","name":"greeting","nameLocation":"134:8:1","scope":37,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_string_storage","typeString":"string"},"typeName":{"id":17,"name":"string","nodeType":"ElementaryTypeName","src":"120:6:1","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"public"},{"id":20,"nodeType":"VariableDeclaration","src":"148:29:1","nodes":[],"constant":false,"functionSelector":"7d94792a","mutability":"immutable","name":"seed","nameLocation":"173:4:1","scope":37,"stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":19,"name":"uint256","nodeType":"ElementaryTypeName","src":"148:7:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"id":36,"nodeType":"FunctionDefinition","src":"184:111:1","nodes":[],"body":{"id":35,"nodeType":"Block","src":"236:59:1","nodes":[],"statements":[{"expression":{"id":29,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":27,"name":"seed","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":20,"src":"246:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":28,"name":"seed_","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":22,"src":"253:5:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"246:12:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":30,"nodeType":"ExpressionStatement","src":"246:12:1"},{"expression":{"id":33,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":31,"name":"greeting","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":18,"src":"268:8:1","typeDescriptions":{"typeIdentifier":"t_string_storage","typeString":"string storage ref"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":32,"name":"greeting_","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":24,"src":"279:9:1","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},"src":"268:20:1","typeDescriptions":{"typeIdentifier":"t_string_storage","typeString":"string storage ref"}},"id":34,"nodeType":"ExpressionStatement","src":"268:20:1"}]},"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","parameters":{"id":25,"nodeType":"ParameterList","parameters":[{"constant":false,"id":22,"mutability":"mutable","name":"seed_","nameLocation":"204:5:1","nodeType":"VariableDeclaration","scope":36,"src":"196:13:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":21,"name":"uint256","nodeType":"ElementaryTypeName","src":"196:7:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"constant":false,"id":24,"mutability":"mutable","name":"greeting_","nameLocation":"225:9:1","nodeType":"VariableDeclaration","scope":36,"src":"211:23:1","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"211:6:1","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"195:40:1"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[],"src":"236:0:1"},"scope":37,"stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"abstract":false,"baseContracts":[],"canonicalName":"Greeter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[37],"name":"Greeter","nameLocation":"106:7:1","scope":38,"usedErrors":[],"usedEvents":[]}],"license":"MIT"}},"src/Unrelated.sol":{"id":2,"ast":{"absolutePath":"src/Unrelated.sol","id":49,"exportedSymbols":{"Unrelated":[48]},"nodeType":"SourceUnit","src":"32:126:2","nodes":[{"id":39,"nodeType":"PragmaDirective","src":"32:23:2","nodes":[],"literals":["solidity","0.8",".26"]},{"id":48,"nodeType":"ContractDefinition","src":"57:100:2","nodes":[{"id":47,"nodeType":"FunctionDefinition","src":"82:73:2","nodes":[],"body":{"id":46,"nodeType":"Block","src":"130:25:2","nodes":[],"statements":[{"expression":{"hexValue":"31","id":44,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"147:1:2","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"functionReturnParameters":43,"id":45,"nodeType":"Return","src":"140:8:2"}]},"functionSelector":"5c36b186","implemented":true,"kind":"function","modifiers":[],"name":"ping","nameLocation":"91:4:2","parameters":{"id":40,"nodeType":"ParameterList","parameters":[],"src":"95:2:2"},"returnParameters":{"id":43,"nodeType":"ParameterList","parameters":[{"constant":false,"id":42,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":47,"src":"121:7:2","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":41,"name":"uint256","nodeType":"ElementaryTypeName","src":"121:7:2","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"120:9:2"},"scope":48,"stateMutability":"pure","virtual":false,"visibility":"external"}],"abstract":false,"baseContracts":[],"canonicalName":"Unrelated","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"linearizedBaseContracts":[48],"name":"Unrelated","nameLocation":"66:9:2","scope":49,"usedErrors":[],"usedEvents":[]}],"license":"MIT"}}}},"solcLongVersion":"0.8.26","solcVersion":"0.8.26"} \ No newline at end of file diff --git a/tests/fixtures/verify/forge/forge-1.5.1-custom-verifier-requests.jsonl b/tests/fixtures/verify/forge/forge-1.5.1-custom-verifier-requests.jsonl new file mode 100644 index 0000000..5e87131 --- /dev/null +++ b/tests/fixtures/verify/forge/forge-1.5.1-custom-verifier-requests.jsonl @@ -0,0 +1,2 @@ +{"method": "POST", "path": "/api", "ctype": "application/x-www-form-urlencoded", "body": "module=contract&action=verifysourcecode&contractaddress=0x5fbdb2315678afecb367f032d99f2f9fc9a2f74b&sourceCode=%7B%22language%22%3A%22Solidity%22%2C%22sources%22%3A%7B%22src%2FGreeter.sol%22%3A%7B%22content%22%3A%22%2F%2F+SPDX-License-Identifier%3A+MIT%5Cnpragma+solidity+0.8.26%3B%5Cn%5Cnimport+%7BCounter%7D+from+%5C%22.%2FCounter.sol%5C%22%3B%5Cn%5Cncontract+Greeter+%7B%5Cn++++string+public+greeting%3B%5Cn++++uint256+public+immutable+seed%3B%5Cn%5Cn++++constructor%28uint256+seed_%2C+string+memory+greeting_%29+%7B%5Cn++++++++seed+%3D+seed_%3B%5Cn++++++++greeting+%3D+greeting_%3B%5Cn++++%7D%5Cn%7D%5Cn%22%7D%2C%22src%2FCounter.sol%22%3A%7B%22content%22%3A%22%2F%2F+SPDX-License-Identifier%3A+MIT%5Cnpragma+solidity+0.8.26%3B%5Cn%5Cncontract+Counter+%7B%5Cn++++uint256+public+number%3B%5Cn%5Cn++++function+increment%28%29+external+%7B%5Cn++++++++number+%2B%3D+1%3B%5Cn++++%7D%5Cn%7D%5Cn%22%7D%7D%2C%22settings%22%3A%7B%22optimizer%22%3A%7B%22enabled%22%3Atrue%2C%22runs%22%3A200%7D%2C%22metadata%22%3A%7B%22useLiteralContent%22%3Afalse%2C%22bytecodeHash%22%3A%22ipfs%22%2C%22appendCBOR%22%3Atrue%7D%2C%22outputSelection%22%3A%7B%22*%22%3A%7B%22%22%3A%5B%22ast%22%5D%2C%22*%22%3A%5B%22abi%22%2C%22evm.bytecode.object%22%2C%22evm.bytecode.sourceMap%22%2C%22evm.bytecode.linkReferences%22%2C%22evm.deployedBytecode.object%22%2C%22evm.deployedBytecode.sourceMap%22%2C%22evm.deployedBytecode.linkReferences%22%2C%22evm.deployedBytecode.immutableReferences%22%2C%22evm.methodIdentifiers%22%2C%22metadata%22%5D%7D%7D%2C%22evmVersion%22%3A%22cancun%22%2C%22viaIR%22%3Afalse%2C%22libraries%22%3A%7B%7D%7D%7D&codeformat=solidity-standard-json-input&contractname=src%2FGreeter.sol%3AGreeter&compilerversion=v0.8.26%2Bcommit.8a97fa7a&constructorArguements=0000000000000000000000000000000000000000000000000000000000000007&constructorArguments=0000000000000000000000000000000000000000000000000000000000000007"} +{"method": "POST", "path": "/api", "ctype": "application/x-www-form-urlencoded", "body": "module=contract&action=checkverifystatus&guid=abc-guid"} diff --git a/tests/fixtures/verify/forge/minimal-build-info.json b/tests/fixtures/verify/forge/minimal-build-info.json new file mode 100644 index 0000000..5bb8914 --- /dev/null +++ b/tests/fixtures/verify/forge/minimal-build-info.json @@ -0,0 +1 @@ +{"id":"83136ca9bf880236","source_id_to_path":{"0":"src/Counter.sol","1":"src/Greeter.sol","2":"src/Unrelated.sol"},"language":"Solidity"} \ No newline at end of file diff --git a/tests/fixtures/verify/forge/src/Counter.sol b/tests/fixtures/verify/forge/src/Counter.sol new file mode 100644 index 0000000..512bf1e --- /dev/null +++ b/tests/fixtures/verify/forge/src/Counter.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +contract Counter { + uint256 public number; + + function increment() external { + number += 1; + } +} diff --git a/tests/fixtures/verify/forge/src/Greeter.sol b/tests/fixtures/verify/forge/src/Greeter.sol new file mode 100644 index 0000000..21e4796 --- /dev/null +++ b/tests/fixtures/verify/forge/src/Greeter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {Counter} from "./Counter.sol"; + +contract Greeter { + string public greeting; + uint256 public immutable seed; + + constructor(uint256 seed_, string memory greeting_) { + seed = seed_; + greeting = greeting_; + } +} diff --git a/tests/fixtures/verify/forge/src/Unrelated.sol b/tests/fixtures/verify/forge/src/Unrelated.sol new file mode 100644 index 0000000..3128f80 --- /dev/null +++ b/tests/fixtures/verify/forge/src/Unrelated.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +contract Unrelated { + function ping() external pure returns (uint256) { + return 1; + } +} diff --git a/tests/fixtures/verify/forge/standard-json-input.json b/tests/fixtures/verify/forge/standard-json-input.json new file mode 100644 index 0000000..e025126 --- /dev/null +++ b/tests/fixtures/verify/forge/standard-json-input.json @@ -0,0 +1 @@ +{"language":"Solidity","sources":{"src/Greeter.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Counter} from \"./Counter.sol\";\n\ncontract Greeter {\n string public greeting;\n uint256 public immutable seed;\n\n constructor(uint256 seed_, string memory greeting_) {\n seed = seed_;\n greeting = greeting_;\n }\n}\n"},"src/Counter.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\ncontract Counter {\n uint256 public number;\n\n function increment() external {\n number += 1;\n }\n}\n"}},"settings":{"optimizer":{"enabled":true,"runs":200},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"":["ast"],"*":["abi","evm.bytecode.object","evm.bytecode.sourceMap","evm.bytecode.linkReferences","evm.deployedBytecode.object","evm.deployedBytecode.sourceMap","evm.deployedBytecode.linkReferences","evm.deployedBytecode.immutableReferences","evm.methodIdentifiers","metadata"]}},"evmVersion":"cancun","viaIR":false,"libraries":{}}} diff --git a/tests/fixtures/verify/hardhat/artifacts/build-info/dd99b6a8e0696988f72849c906423ccc.json b/tests/fixtures/verify/hardhat/artifacts/build-info/dd99b6a8e0696988f72849c906423ccc.json new file mode 100644 index 0000000..56926e0 --- /dev/null +++ b/tests/fixtures/verify/hardhat/artifacts/build-info/dd99b6a8e0696988f72849c906423ccc.json @@ -0,0 +1 @@ +{"id":"dd99b6a8e0696988f72849c906423ccc","_format":"hh-sol-build-info-1","solcVersion":"0.8.26","solcLongVersion":"0.8.26+commit.8a97fa7a","input":{"language":"Solidity","sources":{"contracts/Counter.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\ncontract Counter {\n uint256 public number;\n\n function increment() external {\n number += 1;\n }\n}\n"},"contracts/Greeter.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Counter} from \"./Counter.sol\";\n\ncontract Greeter {\n string public greeting;\n uint256 public immutable seed;\n\n constructor(uint256 seed_, string memory greeting_) {\n seed = seed_;\n greeting = greeting_;\n }\n}\n"},"contracts/Unrelated.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\ncontract Unrelated {\n function ping() external pure returns (uint256) {\n return 1;\n }\n}\n"}},"settings":{"optimizer":{"enabled":true,"runs":200},"evmVersion":"cancun","outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}}}},"output":{"sources":{"contracts/Counter.sol":{"ast":{"absolutePath":"contracts/Counter.sol","exportedSymbols":{"Counter":[12]},"id":13,"license":"MIT","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity","0.8",".26"],"nodeType":"PragmaDirective","src":"32:23:0"},{"abstract":false,"baseContracts":[],"canonicalName":"Counter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":12,"linearizedBaseContracts":[12],"name":"Counter","nameLocation":"66:7:0","nodeType":"ContractDefinition","nodes":[{"constant":false,"functionSelector":"8381f58a","id":3,"mutability":"mutable","name":"number","nameLocation":"95:6:0","nodeType":"VariableDeclaration","scope":12,"src":"80:21:0","stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":2,"name":"uint256","nodeType":"ElementaryTypeName","src":"80:7:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"body":{"id":10,"nodeType":"Block","src":"138:28:0","statements":[{"expression":{"id":8,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":6,"name":"number","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":3,"src":"148:6:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"+=","rightHandSide":{"hexValue":"31","id":7,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"158:1:0","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"src":"148:11:0","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":9,"nodeType":"ExpressionStatement","src":"148:11:0"}]},"functionSelector":"d09de08a","id":11,"implemented":true,"kind":"function","modifiers":[],"name":"increment","nameLocation":"117:9:0","nodeType":"FunctionDefinition","parameters":{"id":4,"nodeType":"ParameterList","parameters":[],"src":"126:2:0"},"returnParameters":{"id":5,"nodeType":"ParameterList","parameters":[],"src":"138:0:0"},"scope":12,"src":"108:58:0","stateMutability":"nonpayable","virtual":false,"visibility":"external"}],"scope":13,"src":"57:111:0","usedErrors":[],"usedEvents":[]}],"src":"32:137:0"},"id":0},"contracts/Greeter.sol":{"ast":{"absolutePath":"contracts/Greeter.sol","exportedSymbols":{"Counter":[12],"Greeter":[37]},"id":38,"license":"MIT","nodeType":"SourceUnit","nodes":[{"id":14,"literals":["solidity","0.8",".26"],"nodeType":"PragmaDirective","src":"32:23:1"},{"absolutePath":"contracts/Counter.sol","file":"./Counter.sol","id":16,"nameLocation":"-1:-1:-1","nodeType":"ImportDirective","scope":38,"sourceUnit":13,"src":"57:38:1","symbolAliases":[{"foreign":{"id":15,"name":"Counter","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":12,"src":"65:7:1","typeDescriptions":{}},"nameLocation":"-1:-1:-1"}],"unitAlias":""},{"abstract":false,"baseContracts":[],"canonicalName":"Greeter","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":37,"linearizedBaseContracts":[37],"name":"Greeter","nameLocation":"106:7:1","nodeType":"ContractDefinition","nodes":[{"constant":false,"functionSelector":"ef690cc0","id":18,"mutability":"mutable","name":"greeting","nameLocation":"134:8:1","nodeType":"VariableDeclaration","scope":37,"src":"120:22:1","stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_string_storage","typeString":"string"},"typeName":{"id":17,"name":"string","nodeType":"ElementaryTypeName","src":"120:6:1","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"public"},{"constant":false,"functionSelector":"7d94792a","id":20,"mutability":"immutable","name":"seed","nameLocation":"173:4:1","nodeType":"VariableDeclaration","scope":37,"src":"148:29:1","stateVariable":true,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":19,"name":"uint256","nodeType":"ElementaryTypeName","src":"148:7:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"public"},{"body":{"id":35,"nodeType":"Block","src":"236:59:1","statements":[{"expression":{"id":29,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":27,"name":"seed","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":20,"src":"246:4:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":28,"name":"seed_","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":22,"src":"253:5:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"src":"246:12:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"id":30,"nodeType":"ExpressionStatement","src":"246:12:1"},{"expression":{"id":33,"isConstant":false,"isLValue":false,"isPure":false,"lValueRequested":false,"leftHandSide":{"id":31,"name":"greeting","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":18,"src":"268:8:1","typeDescriptions":{"typeIdentifier":"t_string_storage","typeString":"string storage ref"}},"nodeType":"Assignment","operator":"=","rightHandSide":{"id":32,"name":"greeting_","nodeType":"Identifier","overloadedDeclarations":[],"referencedDeclaration":24,"src":"279:9:1","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string memory"}},"src":"268:20:1","typeDescriptions":{"typeIdentifier":"t_string_storage","typeString":"string storage ref"}},"id":34,"nodeType":"ExpressionStatement","src":"268:20:1"}]},"id":36,"implemented":true,"kind":"constructor","modifiers":[],"name":"","nameLocation":"-1:-1:-1","nodeType":"FunctionDefinition","parameters":{"id":25,"nodeType":"ParameterList","parameters":[{"constant":false,"id":22,"mutability":"mutable","name":"seed_","nameLocation":"204:5:1","nodeType":"VariableDeclaration","scope":36,"src":"196:13:1","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":21,"name":"uint256","nodeType":"ElementaryTypeName","src":"196:7:1","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"},{"constant":false,"id":24,"mutability":"mutable","name":"greeting_","nameLocation":"225:9:1","nodeType":"VariableDeclaration","scope":36,"src":"211:23:1","stateVariable":false,"storageLocation":"memory","typeDescriptions":{"typeIdentifier":"t_string_memory_ptr","typeString":"string"},"typeName":{"id":23,"name":"string","nodeType":"ElementaryTypeName","src":"211:6:1","typeDescriptions":{"typeIdentifier":"t_string_storage_ptr","typeString":"string"}},"visibility":"internal"}],"src":"195:40:1"},"returnParameters":{"id":26,"nodeType":"ParameterList","parameters":[],"src":"236:0:1"},"scope":37,"src":"184:111:1","stateMutability":"nonpayable","virtual":false,"visibility":"public"}],"scope":38,"src":"97:200:1","usedErrors":[],"usedEvents":[]}],"src":"32:266:1"},"id":1},"contracts/Unrelated.sol":{"ast":{"absolutePath":"contracts/Unrelated.sol","exportedSymbols":{"Unrelated":[48]},"id":49,"license":"MIT","nodeType":"SourceUnit","nodes":[{"id":39,"literals":["solidity","0.8",".26"],"nodeType":"PragmaDirective","src":"32:23:2"},{"abstract":false,"baseContracts":[],"canonicalName":"Unrelated","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":48,"linearizedBaseContracts":[48],"name":"Unrelated","nameLocation":"66:9:2","nodeType":"ContractDefinition","nodes":[{"body":{"id":46,"nodeType":"Block","src":"130:25:2","statements":[{"expression":{"hexValue":"31","id":44,"isConstant":false,"isLValue":false,"isPure":true,"kind":"number","lValueRequested":false,"nodeType":"Literal","src":"147:1:2","typeDescriptions":{"typeIdentifier":"t_rational_1_by_1","typeString":"int_const 1"},"value":"1"},"functionReturnParameters":43,"id":45,"nodeType":"Return","src":"140:8:2"}]},"functionSelector":"5c36b186","id":47,"implemented":true,"kind":"function","modifiers":[],"name":"ping","nameLocation":"91:4:2","nodeType":"FunctionDefinition","parameters":{"id":40,"nodeType":"ParameterList","parameters":[],"src":"95:2:2"},"returnParameters":{"id":43,"nodeType":"ParameterList","parameters":[{"constant":false,"id":42,"mutability":"mutable","name":"","nameLocation":"-1:-1:-1","nodeType":"VariableDeclaration","scope":47,"src":"121:7:2","stateVariable":false,"storageLocation":"default","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"},"typeName":{"id":41,"name":"uint256","nodeType":"ElementaryTypeName","src":"121:7:2","typeDescriptions":{"typeIdentifier":"t_uint256","typeString":"uint256"}},"visibility":"internal"}],"src":"120:9:2"},"scope":48,"src":"82:73:2","stateMutability":"pure","virtual":false,"visibility":"external"}],"scope":49,"src":"57:100:2","usedErrors":[],"usedEvents":[]}],"src":"32:126:2"},"id":2}},"contracts":{"contracts/Counter.sol":{"Counter":{"abi":[{"inputs":[],"name":"increment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],"evm":{"bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"6080604052348015600e575f80fd5b5060c580601a5f395ff3fe6080604052348015600e575f80fd5b50600436106030575f3560e01c80638381f58a146034578063d09de08a14604d575b5f80fd5b603b5f5481565b60405190815260200160405180910390f35b60536055565b005b60015f8082825460649190606b565b9091555050565b80820180821115608957634e487b7160e01b5f52601160045260245ffd5b9291505056fea2646970667358221220f4c4b4ac824aff6e0be3305ca4bbedb518f2472203674935d8cfa1550a31b88c64736f6c634300081a0033","opcodes":"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0xC5 DUP1 PUSH1 0x1A PUSH0 CODECOPY PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x30 JUMPI PUSH0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x8381F58A EQ PUSH1 0x34 JUMPI DUP1 PUSH4 0xD09DE08A EQ PUSH1 0x4D JUMPI JUMPDEST PUSH0 DUP1 REVERT JUMPDEST PUSH1 0x3B PUSH0 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x40 MLOAD SWAP1 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x53 PUSH1 0x55 JUMP JUMPDEST STOP JUMPDEST PUSH1 0x1 PUSH0 DUP1 DUP3 DUP3 SLOAD PUSH1 0x64 SWAP2 SWAP1 PUSH1 0x6B JUMP JUMPDEST SWAP1 SWAP2 SSTORE POP POP JUMP JUMPDEST DUP1 DUP3 ADD DUP1 DUP3 GT ISZERO PUSH1 0x89 JUMPI PUSH4 0x4E487B71 PUSH1 0xE0 SHL PUSH0 MSTORE PUSH1 0x11 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH0 REVERT JUMPDEST SWAP3 SWAP2 POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DELEGATECALL 0xC4 0xB4 0xAC DUP3 BLOBBASEFEE SELFDESTRUCT PUSH15 0xBE3305CA4BBEDB518F24722036749 CALLDATALOAD 0xD8 0xCF LOG1 SSTORE EXP BALANCE 0xB8 DUP13 PUSH5 0x736F6C6343 STOP ADDMOD BYTE STOP CALLER ","sourceMap":"57:111:0:-:0;;;;;;;;;;;;;;;;;;;"},"deployedBytecode":{"functionDebugData":{"@increment_11":{"entryPoint":85,"id":11,"parameterSlots":0,"returnSlots":0},"@number_3":{"entryPoint":null,"id":3,"parameterSlots":0,"returnSlots":0},"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed":{"entryPoint":null,"id":null,"parameterSlots":2,"returnSlots":1},"checked_add_t_uint256":{"entryPoint":107,"id":null,"parameterSlots":2,"returnSlots":1}},"generatedSources":[{"ast":{"nativeSrc":"0:420:3","nodeType":"YulBlock","src":"0:420:3","statements":[{"nativeSrc":"6:3:3","nodeType":"YulBlock","src":"6:3:3","statements":[]},{"body":{"nativeSrc":"115:76:3","nodeType":"YulBlock","src":"115:76:3","statements":[{"nativeSrc":"125:26:3","nodeType":"YulAssignment","src":"125:26:3","value":{"arguments":[{"name":"headStart","nativeSrc":"137:9:3","nodeType":"YulIdentifier","src":"137:9:3"},{"kind":"number","nativeSrc":"148:2:3","nodeType":"YulLiteral","src":"148:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"133:3:3","nodeType":"YulIdentifier","src":"133:3:3"},"nativeSrc":"133:18:3","nodeType":"YulFunctionCall","src":"133:18:3"},"variableNames":[{"name":"tail","nativeSrc":"125:4:3","nodeType":"YulIdentifier","src":"125:4:3"}]},{"expression":{"arguments":[{"name":"headStart","nativeSrc":"167:9:3","nodeType":"YulIdentifier","src":"167:9:3"},{"name":"value0","nativeSrc":"178:6:3","nodeType":"YulIdentifier","src":"178:6:3"}],"functionName":{"name":"mstore","nativeSrc":"160:6:3","nodeType":"YulIdentifier","src":"160:6:3"},"nativeSrc":"160:25:3","nodeType":"YulFunctionCall","src":"160:25:3"},"nativeSrc":"160:25:3","nodeType":"YulExpressionStatement","src":"160:25:3"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nativeSrc":"14:177:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nativeSrc":"84:9:3","nodeType":"YulTypedName","src":"84:9:3","type":""},{"name":"value0","nativeSrc":"95:6:3","nodeType":"YulTypedName","src":"95:6:3","type":""}],"returnVariables":[{"name":"tail","nativeSrc":"106:4:3","nodeType":"YulTypedName","src":"106:4:3","type":""}],"src":"14:177:3"},{"body":{"nativeSrc":"244:174:3","nodeType":"YulBlock","src":"244:174:3","statements":[{"nativeSrc":"254:16:3","nodeType":"YulAssignment","src":"254:16:3","value":{"arguments":[{"name":"x","nativeSrc":"265:1:3","nodeType":"YulIdentifier","src":"265:1:3"},{"name":"y","nativeSrc":"268:1:3","nodeType":"YulIdentifier","src":"268:1:3"}],"functionName":{"name":"add","nativeSrc":"261:3:3","nodeType":"YulIdentifier","src":"261:3:3"},"nativeSrc":"261:9:3","nodeType":"YulFunctionCall","src":"261:9:3"},"variableNames":[{"name":"sum","nativeSrc":"254:3:3","nodeType":"YulIdentifier","src":"254:3:3"}]},{"body":{"nativeSrc":"301:111:3","nodeType":"YulBlock","src":"301:111:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"322:1:3","nodeType":"YulLiteral","src":"322:1:3","type":"","value":"0"},{"arguments":[{"kind":"number","nativeSrc":"329:3:3","nodeType":"YulLiteral","src":"329:3:3","type":"","value":"224"},{"kind":"number","nativeSrc":"334:10:3","nodeType":"YulLiteral","src":"334:10:3","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nativeSrc":"325:3:3","nodeType":"YulIdentifier","src":"325:3:3"},"nativeSrc":"325:20:3","nodeType":"YulFunctionCall","src":"325:20:3"}],"functionName":{"name":"mstore","nativeSrc":"315:6:3","nodeType":"YulIdentifier","src":"315:6:3"},"nativeSrc":"315:31:3","nodeType":"YulFunctionCall","src":"315:31:3"},"nativeSrc":"315:31:3","nodeType":"YulExpressionStatement","src":"315:31:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"366:1:3","nodeType":"YulLiteral","src":"366:1:3","type":"","value":"4"},{"kind":"number","nativeSrc":"369:4:3","nodeType":"YulLiteral","src":"369:4:3","type":"","value":"0x11"}],"functionName":{"name":"mstore","nativeSrc":"359:6:3","nodeType":"YulIdentifier","src":"359:6:3"},"nativeSrc":"359:15:3","nodeType":"YulFunctionCall","src":"359:15:3"},"nativeSrc":"359:15:3","nodeType":"YulExpressionStatement","src":"359:15:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"394:1:3","nodeType":"YulLiteral","src":"394:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"397:4:3","nodeType":"YulLiteral","src":"397:4:3","type":"","value":"0x24"}],"functionName":{"name":"revert","nativeSrc":"387:6:3","nodeType":"YulIdentifier","src":"387:6:3"},"nativeSrc":"387:15:3","nodeType":"YulFunctionCall","src":"387:15:3"},"nativeSrc":"387:15:3","nodeType":"YulExpressionStatement","src":"387:15:3"}]},"condition":{"arguments":[{"name":"x","nativeSrc":"285:1:3","nodeType":"YulIdentifier","src":"285:1:3"},{"name":"sum","nativeSrc":"288:3:3","nodeType":"YulIdentifier","src":"288:3:3"}],"functionName":{"name":"gt","nativeSrc":"282:2:3","nodeType":"YulIdentifier","src":"282:2:3"},"nativeSrc":"282:10:3","nodeType":"YulFunctionCall","src":"282:10:3"},"nativeSrc":"279:133:3","nodeType":"YulIf","src":"279:133:3"}]},"name":"checked_add_t_uint256","nativeSrc":"196:222:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"x","nativeSrc":"227:1:3","nodeType":"YulTypedName","src":"227:1:3","type":""},{"name":"y","nativeSrc":"230:1:3","nodeType":"YulTypedName","src":"230:1:3","type":""}],"returnVariables":[{"name":"sum","nativeSrc":"236:3:3","nodeType":"YulTypedName","src":"236:3:3","type":""}],"src":"196:222:3"}]},"contents":"{\n { }\n function abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed(headStart, value0) -> tail\n {\n tail := add(headStart, 32)\n mstore(headStart, value0)\n }\n function checked_add_t_uint256(x, y) -> sum\n {\n sum := add(x, y)\n if gt(x, sum)\n {\n mstore(0, shl(224, 0x4e487b71))\n mstore(4, 0x11)\n revert(0, 0x24)\n }\n }\n}","id":3,"language":"Yul","name":"#utility.yul"}],"immutableReferences":{},"linkReferences":{},"object":"6080604052348015600e575f80fd5b50600436106030575f3560e01c80638381f58a146034578063d09de08a14604d575b5f80fd5b603b5f5481565b60405190815260200160405180910390f35b60536055565b005b60015f8082825460649190606b565b9091555050565b80820180821115608957634e487b7160e01b5f52601160045260245ffd5b9291505056fea2646970667358221220f4c4b4ac824aff6e0be3305ca4bbedb518f2472203674935d8cfa1550a31b88c64736f6c634300081a0033","opcodes":"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x30 JUMPI PUSH0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x8381F58A EQ PUSH1 0x34 JUMPI DUP1 PUSH4 0xD09DE08A EQ PUSH1 0x4D JUMPI JUMPDEST PUSH0 DUP1 REVERT JUMPDEST PUSH1 0x3B PUSH0 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x40 MLOAD SWAP1 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH1 0x53 PUSH1 0x55 JUMP JUMPDEST STOP JUMPDEST PUSH1 0x1 PUSH0 DUP1 DUP3 DUP3 SLOAD PUSH1 0x64 SWAP2 SWAP1 PUSH1 0x6B JUMP JUMPDEST SWAP1 SWAP2 SSTORE POP POP JUMP JUMPDEST DUP1 DUP3 ADD DUP1 DUP3 GT ISZERO PUSH1 0x89 JUMPI PUSH4 0x4E487B71 PUSH1 0xE0 SHL PUSH0 MSTORE PUSH1 0x11 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH0 REVERT JUMPDEST SWAP3 SWAP2 POP POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DELEGATECALL 0xC4 0xB4 0xAC DUP3 BLOBBASEFEE SELFDESTRUCT PUSH15 0xBE3305CA4BBEDB518F24722036749 CALLDATALOAD 0xD8 0xCF LOG1 SSTORE EXP BALANCE 0xB8 DUP13 PUSH5 0x736F6C6343 STOP ADDMOD BYTE STOP CALLER ","sourceMap":"57:111:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;80:21;;;;;;;;;160:25:3;;;148:2;133:18;80:21:0;;;;;;;108:58;;;:::i;:::-;;;158:1;148:6;;:11;;;;;;;:::i;:::-;;;;-1:-1:-1;;108:58:0:o;196:222:3:-;261:9;;;282:10;;;279:133;;;334:10;329:3;325:20;322:1;315:31;369:4;366:1;359:15;397:4;394:1;387:15;279:133;196:222;;;;:::o"},"methodIdentifiers":{"increment()":"d09de08a","number()":"8381f58a"}},"metadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"increment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"number\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/Counter.sol\":\"Counter\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/Counter.sol\":{\"keccak256\":\"0xc2db2c51f9be330b16e281a99dfe043e18dc2a60c40eea193802acb2aa612584\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://df36ff1caedb811d7f0b735d3a2739c75e7bba3361b2be8ae9b92320aac69395\",\"dweb:/ipfs/QmQz644QUD1bWRX8QcEa4CcQknRpLFrLZiJNXT2rZ9PcSu\"]}},\"version\":1}"}},"contracts/Greeter.sol":{"Greeter":{"abi":[{"inputs":[{"internalType":"uint256","name":"seed_","type":"uint256"},{"internalType":"string","name":"greeting_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"greeting","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"seed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],"evm":{"bytecode":{"functionDebugData":{"@_36":{"entryPoint":null,"id":36,"parameterSlots":2,"returnSlots":0},"abi_decode_tuple_t_uint256t_string_memory_ptr_fromMemory":{"entryPoint":90,"id":null,"parameterSlots":2,"returnSlots":2},"array_dataslot_string_storage":{"entryPoint":null,"id":null,"parameterSlots":1,"returnSlots":1},"clean_up_bytearray_end_slots_string_storage":{"entryPoint":333,"id":null,"parameterSlots":3,"returnSlots":0},"copy_byte_array_to_storage_from_t_string_memory_ptr_to_t_string_storage":{"entryPoint":409,"id":null,"parameterSlots":2,"returnSlots":0},"extract_byte_array_length":{"entryPoint":277,"id":null,"parameterSlots":1,"returnSlots":1},"extract_used_part_and_set_length_of_short_byte_array":{"entryPoint":null,"id":null,"parameterSlots":2,"returnSlots":1},"panic_error_0x41":{"entryPoint":70,"id":null,"parameterSlots":0,"returnSlots":0}},"generatedSources":[{"ast":{"nativeSrc":"0:3653:3","nodeType":"YulBlock","src":"0:3653:3","statements":[{"nativeSrc":"6:3:3","nodeType":"YulBlock","src":"6:3:3","statements":[]},{"body":{"nativeSrc":"46:95:3","nodeType":"YulBlock","src":"46:95:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"63:1:3","nodeType":"YulLiteral","src":"63:1:3","type":"","value":"0"},{"arguments":[{"kind":"number","nativeSrc":"70:3:3","nodeType":"YulLiteral","src":"70:3:3","type":"","value":"224"},{"kind":"number","nativeSrc":"75:10:3","nodeType":"YulLiteral","src":"75:10:3","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nativeSrc":"66:3:3","nodeType":"YulIdentifier","src":"66:3:3"},"nativeSrc":"66:20:3","nodeType":"YulFunctionCall","src":"66:20:3"}],"functionName":{"name":"mstore","nativeSrc":"56:6:3","nodeType":"YulIdentifier","src":"56:6:3"},"nativeSrc":"56:31:3","nodeType":"YulFunctionCall","src":"56:31:3"},"nativeSrc":"56:31:3","nodeType":"YulExpressionStatement","src":"56:31:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"103:1:3","nodeType":"YulLiteral","src":"103:1:3","type":"","value":"4"},{"kind":"number","nativeSrc":"106:4:3","nodeType":"YulLiteral","src":"106:4:3","type":"","value":"0x41"}],"functionName":{"name":"mstore","nativeSrc":"96:6:3","nodeType":"YulIdentifier","src":"96:6:3"},"nativeSrc":"96:15:3","nodeType":"YulFunctionCall","src":"96:15:3"},"nativeSrc":"96:15:3","nodeType":"YulExpressionStatement","src":"96:15:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"127:1:3","nodeType":"YulLiteral","src":"127:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"130:4:3","nodeType":"YulLiteral","src":"130:4:3","type":"","value":"0x24"}],"functionName":{"name":"revert","nativeSrc":"120:6:3","nodeType":"YulIdentifier","src":"120:6:3"},"nativeSrc":"120:15:3","nodeType":"YulFunctionCall","src":"120:15:3"},"nativeSrc":"120:15:3","nodeType":"YulExpressionStatement","src":"120:15:3"}]},"name":"panic_error_0x41","nativeSrc":"14:127:3","nodeType":"YulFunctionDefinition","src":"14:127:3"},{"body":{"nativeSrc":"254:888:3","nodeType":"YulBlock","src":"254:888:3","statements":[{"body":{"nativeSrc":"300:16:3","nodeType":"YulBlock","src":"300:16:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"309:1:3","nodeType":"YulLiteral","src":"309:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"312:1:3","nodeType":"YulLiteral","src":"312:1:3","type":"","value":"0"}],"functionName":{"name":"revert","nativeSrc":"302:6:3","nodeType":"YulIdentifier","src":"302:6:3"},"nativeSrc":"302:12:3","nodeType":"YulFunctionCall","src":"302:12:3"},"nativeSrc":"302:12:3","nodeType":"YulExpressionStatement","src":"302:12:3"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nativeSrc":"275:7:3","nodeType":"YulIdentifier","src":"275:7:3"},{"name":"headStart","nativeSrc":"284:9:3","nodeType":"YulIdentifier","src":"284:9:3"}],"functionName":{"name":"sub","nativeSrc":"271:3:3","nodeType":"YulIdentifier","src":"271:3:3"},"nativeSrc":"271:23:3","nodeType":"YulFunctionCall","src":"271:23:3"},{"kind":"number","nativeSrc":"296:2:3","nodeType":"YulLiteral","src":"296:2:3","type":"","value":"64"}],"functionName":{"name":"slt","nativeSrc":"267:3:3","nodeType":"YulIdentifier","src":"267:3:3"},"nativeSrc":"267:32:3","nodeType":"YulFunctionCall","src":"267:32:3"},"nativeSrc":"264:52:3","nodeType":"YulIf","src":"264:52:3"},{"nativeSrc":"325:26:3","nodeType":"YulAssignment","src":"325:26:3","value":{"arguments":[{"name":"headStart","nativeSrc":"341:9:3","nodeType":"YulIdentifier","src":"341:9:3"}],"functionName":{"name":"mload","nativeSrc":"335:5:3","nodeType":"YulIdentifier","src":"335:5:3"},"nativeSrc":"335:16:3","nodeType":"YulFunctionCall","src":"335:16:3"},"variableNames":[{"name":"value0","nativeSrc":"325:6:3","nodeType":"YulIdentifier","src":"325:6:3"}]},{"nativeSrc":"360:39:3","nodeType":"YulVariableDeclaration","src":"360:39:3","value":{"arguments":[{"arguments":[{"name":"headStart","nativeSrc":"384:9:3","nodeType":"YulIdentifier","src":"384:9:3"},{"kind":"number","nativeSrc":"395:2:3","nodeType":"YulLiteral","src":"395:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"380:3:3","nodeType":"YulIdentifier","src":"380:3:3"},"nativeSrc":"380:18:3","nodeType":"YulFunctionCall","src":"380:18:3"}],"functionName":{"name":"mload","nativeSrc":"374:5:3","nodeType":"YulIdentifier","src":"374:5:3"},"nativeSrc":"374:25:3","nodeType":"YulFunctionCall","src":"374:25:3"},"variables":[{"name":"offset","nativeSrc":"364:6:3","nodeType":"YulTypedName","src":"364:6:3","type":""}]},{"body":{"nativeSrc":"442:16:3","nodeType":"YulBlock","src":"442:16:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"451:1:3","nodeType":"YulLiteral","src":"451:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"454:1:3","nodeType":"YulLiteral","src":"454:1:3","type":"","value":"0"}],"functionName":{"name":"revert","nativeSrc":"444:6:3","nodeType":"YulIdentifier","src":"444:6:3"},"nativeSrc":"444:12:3","nodeType":"YulFunctionCall","src":"444:12:3"},"nativeSrc":"444:12:3","nodeType":"YulExpressionStatement","src":"444:12:3"}]},"condition":{"arguments":[{"name":"offset","nativeSrc":"414:6:3","nodeType":"YulIdentifier","src":"414:6:3"},{"arguments":[{"arguments":[{"kind":"number","nativeSrc":"430:2:3","nodeType":"YulLiteral","src":"430:2:3","type":"","value":"64"},{"kind":"number","nativeSrc":"434:1:3","nodeType":"YulLiteral","src":"434:1:3","type":"","value":"1"}],"functionName":{"name":"shl","nativeSrc":"426:3:3","nodeType":"YulIdentifier","src":"426:3:3"},"nativeSrc":"426:10:3","nodeType":"YulFunctionCall","src":"426:10:3"},{"kind":"number","nativeSrc":"438:1:3","nodeType":"YulLiteral","src":"438:1:3","type":"","value":"1"}],"functionName":{"name":"sub","nativeSrc":"422:3:3","nodeType":"YulIdentifier","src":"422:3:3"},"nativeSrc":"422:18:3","nodeType":"YulFunctionCall","src":"422:18:3"}],"functionName":{"name":"gt","nativeSrc":"411:2:3","nodeType":"YulIdentifier","src":"411:2:3"},"nativeSrc":"411:30:3","nodeType":"YulFunctionCall","src":"411:30:3"},"nativeSrc":"408:50:3","nodeType":"YulIf","src":"408:50:3"},{"nativeSrc":"467:32:3","nodeType":"YulVariableDeclaration","src":"467:32:3","value":{"arguments":[{"name":"headStart","nativeSrc":"481:9:3","nodeType":"YulIdentifier","src":"481:9:3"},{"name":"offset","nativeSrc":"492:6:3","nodeType":"YulIdentifier","src":"492:6:3"}],"functionName":{"name":"add","nativeSrc":"477:3:3","nodeType":"YulIdentifier","src":"477:3:3"},"nativeSrc":"477:22:3","nodeType":"YulFunctionCall","src":"477:22:3"},"variables":[{"name":"_1","nativeSrc":"471:2:3","nodeType":"YulTypedName","src":"471:2:3","type":""}]},{"body":{"nativeSrc":"547:16:3","nodeType":"YulBlock","src":"547:16:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"556:1:3","nodeType":"YulLiteral","src":"556:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"559:1:3","nodeType":"YulLiteral","src":"559:1:3","type":"","value":"0"}],"functionName":{"name":"revert","nativeSrc":"549:6:3","nodeType":"YulIdentifier","src":"549:6:3"},"nativeSrc":"549:12:3","nodeType":"YulFunctionCall","src":"549:12:3"},"nativeSrc":"549:12:3","nodeType":"YulExpressionStatement","src":"549:12:3"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_1","nativeSrc":"526:2:3","nodeType":"YulIdentifier","src":"526:2:3"},{"kind":"number","nativeSrc":"530:4:3","nodeType":"YulLiteral","src":"530:4:3","type":"","value":"0x1f"}],"functionName":{"name":"add","nativeSrc":"522:3:3","nodeType":"YulIdentifier","src":"522:3:3"},"nativeSrc":"522:13:3","nodeType":"YulFunctionCall","src":"522:13:3"},{"name":"dataEnd","nativeSrc":"537:7:3","nodeType":"YulIdentifier","src":"537:7:3"}],"functionName":{"name":"slt","nativeSrc":"518:3:3","nodeType":"YulIdentifier","src":"518:3:3"},"nativeSrc":"518:27:3","nodeType":"YulFunctionCall","src":"518:27:3"}],"functionName":{"name":"iszero","nativeSrc":"511:6:3","nodeType":"YulIdentifier","src":"511:6:3"},"nativeSrc":"511:35:3","nodeType":"YulFunctionCall","src":"511:35:3"},"nativeSrc":"508:55:3","nodeType":"YulIf","src":"508:55:3"},{"nativeSrc":"572:23:3","nodeType":"YulVariableDeclaration","src":"572:23:3","value":{"arguments":[{"name":"_1","nativeSrc":"592:2:3","nodeType":"YulIdentifier","src":"592:2:3"}],"functionName":{"name":"mload","nativeSrc":"586:5:3","nodeType":"YulIdentifier","src":"586:5:3"},"nativeSrc":"586:9:3","nodeType":"YulFunctionCall","src":"586:9:3"},"variables":[{"name":"length","nativeSrc":"576:6:3","nodeType":"YulTypedName","src":"576:6:3","type":""}]},{"body":{"nativeSrc":"638:22:3","nodeType":"YulBlock","src":"638:22:3","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nativeSrc":"640:16:3","nodeType":"YulIdentifier","src":"640:16:3"},"nativeSrc":"640:18:3","nodeType":"YulFunctionCall","src":"640:18:3"},"nativeSrc":"640:18:3","nodeType":"YulExpressionStatement","src":"640:18:3"}]},"condition":{"arguments":[{"name":"length","nativeSrc":"610:6:3","nodeType":"YulIdentifier","src":"610:6:3"},{"arguments":[{"arguments":[{"kind":"number","nativeSrc":"626:2:3","nodeType":"YulLiteral","src":"626:2:3","type":"","value":"64"},{"kind":"number","nativeSrc":"630:1:3","nodeType":"YulLiteral","src":"630:1:3","type":"","value":"1"}],"functionName":{"name":"shl","nativeSrc":"622:3:3","nodeType":"YulIdentifier","src":"622:3:3"},"nativeSrc":"622:10:3","nodeType":"YulFunctionCall","src":"622:10:3"},{"kind":"number","nativeSrc":"634:1:3","nodeType":"YulLiteral","src":"634:1:3","type":"","value":"1"}],"functionName":{"name":"sub","nativeSrc":"618:3:3","nodeType":"YulIdentifier","src":"618:3:3"},"nativeSrc":"618:18:3","nodeType":"YulFunctionCall","src":"618:18:3"}],"functionName":{"name":"gt","nativeSrc":"607:2:3","nodeType":"YulIdentifier","src":"607:2:3"},"nativeSrc":"607:30:3","nodeType":"YulFunctionCall","src":"607:30:3"},"nativeSrc":"604:56:3","nodeType":"YulIf","src":"604:56:3"},{"nativeSrc":"669:23:3","nodeType":"YulVariableDeclaration","src":"669:23:3","value":{"arguments":[{"kind":"number","nativeSrc":"689:2:3","nodeType":"YulLiteral","src":"689:2:3","type":"","value":"64"}],"functionName":{"name":"mload","nativeSrc":"683:5:3","nodeType":"YulIdentifier","src":"683:5:3"},"nativeSrc":"683:9:3","nodeType":"YulFunctionCall","src":"683:9:3"},"variables":[{"name":"memPtr","nativeSrc":"673:6:3","nodeType":"YulTypedName","src":"673:6:3","type":""}]},{"nativeSrc":"701:85:3","nodeType":"YulVariableDeclaration","src":"701:85:3","value":{"arguments":[{"name":"memPtr","nativeSrc":"723:6:3","nodeType":"YulIdentifier","src":"723:6:3"},{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"name":"length","nativeSrc":"747:6:3","nodeType":"YulIdentifier","src":"747:6:3"},{"kind":"number","nativeSrc":"755:4:3","nodeType":"YulLiteral","src":"755:4:3","type":"","value":"0x1f"}],"functionName":{"name":"add","nativeSrc":"743:3:3","nodeType":"YulIdentifier","src":"743:3:3"},"nativeSrc":"743:17:3","nodeType":"YulFunctionCall","src":"743:17:3"},{"arguments":[{"kind":"number","nativeSrc":"766:2:3","nodeType":"YulLiteral","src":"766:2:3","type":"","value":"31"}],"functionName":{"name":"not","nativeSrc":"762:3:3","nodeType":"YulIdentifier","src":"762:3:3"},"nativeSrc":"762:7:3","nodeType":"YulFunctionCall","src":"762:7:3"}],"functionName":{"name":"and","nativeSrc":"739:3:3","nodeType":"YulIdentifier","src":"739:3:3"},"nativeSrc":"739:31:3","nodeType":"YulFunctionCall","src":"739:31:3"},{"kind":"number","nativeSrc":"772:2:3","nodeType":"YulLiteral","src":"772:2:3","type":"","value":"63"}],"functionName":{"name":"add","nativeSrc":"735:3:3","nodeType":"YulIdentifier","src":"735:3:3"},"nativeSrc":"735:40:3","nodeType":"YulFunctionCall","src":"735:40:3"},{"arguments":[{"kind":"number","nativeSrc":"781:2:3","nodeType":"YulLiteral","src":"781:2:3","type":"","value":"31"}],"functionName":{"name":"not","nativeSrc":"777:3:3","nodeType":"YulIdentifier","src":"777:3:3"},"nativeSrc":"777:7:3","nodeType":"YulFunctionCall","src":"777:7:3"}],"functionName":{"name":"and","nativeSrc":"731:3:3","nodeType":"YulIdentifier","src":"731:3:3"},"nativeSrc":"731:54:3","nodeType":"YulFunctionCall","src":"731:54:3"}],"functionName":{"name":"add","nativeSrc":"719:3:3","nodeType":"YulIdentifier","src":"719:3:3"},"nativeSrc":"719:67:3","nodeType":"YulFunctionCall","src":"719:67:3"},"variables":[{"name":"newFreePtr","nativeSrc":"705:10:3","nodeType":"YulTypedName","src":"705:10:3","type":""}]},{"body":{"nativeSrc":"861:22:3","nodeType":"YulBlock","src":"861:22:3","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nativeSrc":"863:16:3","nodeType":"YulIdentifier","src":"863:16:3"},"nativeSrc":"863:18:3","nodeType":"YulFunctionCall","src":"863:18:3"},"nativeSrc":"863:18:3","nodeType":"YulExpressionStatement","src":"863:18:3"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nativeSrc":"804:10:3","nodeType":"YulIdentifier","src":"804:10:3"},{"arguments":[{"arguments":[{"kind":"number","nativeSrc":"824:2:3","nodeType":"YulLiteral","src":"824:2:3","type":"","value":"64"},{"kind":"number","nativeSrc":"828:1:3","nodeType":"YulLiteral","src":"828:1:3","type":"","value":"1"}],"functionName":{"name":"shl","nativeSrc":"820:3:3","nodeType":"YulIdentifier","src":"820:3:3"},"nativeSrc":"820:10:3","nodeType":"YulFunctionCall","src":"820:10:3"},{"kind":"number","nativeSrc":"832:1:3","nodeType":"YulLiteral","src":"832:1:3","type":"","value":"1"}],"functionName":{"name":"sub","nativeSrc":"816:3:3","nodeType":"YulIdentifier","src":"816:3:3"},"nativeSrc":"816:18:3","nodeType":"YulFunctionCall","src":"816:18:3"}],"functionName":{"name":"gt","nativeSrc":"801:2:3","nodeType":"YulIdentifier","src":"801:2:3"},"nativeSrc":"801:34:3","nodeType":"YulFunctionCall","src":"801:34:3"},{"arguments":[{"name":"newFreePtr","nativeSrc":"840:10:3","nodeType":"YulIdentifier","src":"840:10:3"},{"name":"memPtr","nativeSrc":"852:6:3","nodeType":"YulIdentifier","src":"852:6:3"}],"functionName":{"name":"lt","nativeSrc":"837:2:3","nodeType":"YulIdentifier","src":"837:2:3"},"nativeSrc":"837:22:3","nodeType":"YulFunctionCall","src":"837:22:3"}],"functionName":{"name":"or","nativeSrc":"798:2:3","nodeType":"YulIdentifier","src":"798:2:3"},"nativeSrc":"798:62:3","nodeType":"YulFunctionCall","src":"798:62:3"},"nativeSrc":"795:88:3","nodeType":"YulIf","src":"795:88:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"899:2:3","nodeType":"YulLiteral","src":"899:2:3","type":"","value":"64"},{"name":"newFreePtr","nativeSrc":"903:10:3","nodeType":"YulIdentifier","src":"903:10:3"}],"functionName":{"name":"mstore","nativeSrc":"892:6:3","nodeType":"YulIdentifier","src":"892:6:3"},"nativeSrc":"892:22:3","nodeType":"YulFunctionCall","src":"892:22:3"},"nativeSrc":"892:22:3","nodeType":"YulExpressionStatement","src":"892:22:3"},{"expression":{"arguments":[{"name":"memPtr","nativeSrc":"930:6:3","nodeType":"YulIdentifier","src":"930:6:3"},{"name":"length","nativeSrc":"938:6:3","nodeType":"YulIdentifier","src":"938:6:3"}],"functionName":{"name":"mstore","nativeSrc":"923:6:3","nodeType":"YulIdentifier","src":"923:6:3"},"nativeSrc":"923:22:3","nodeType":"YulFunctionCall","src":"923:22:3"},"nativeSrc":"923:22:3","nodeType":"YulExpressionStatement","src":"923:22:3"},{"body":{"nativeSrc":"995:16:3","nodeType":"YulBlock","src":"995:16:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"1004:1:3","nodeType":"YulLiteral","src":"1004:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"1007:1:3","nodeType":"YulLiteral","src":"1007:1:3","type":"","value":"0"}],"functionName":{"name":"revert","nativeSrc":"997:6:3","nodeType":"YulIdentifier","src":"997:6:3"},"nativeSrc":"997:12:3","nodeType":"YulFunctionCall","src":"997:12:3"},"nativeSrc":"997:12:3","nodeType":"YulExpressionStatement","src":"997:12:3"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_1","nativeSrc":"968:2:3","nodeType":"YulIdentifier","src":"968:2:3"},{"name":"length","nativeSrc":"972:6:3","nodeType":"YulIdentifier","src":"972:6:3"}],"functionName":{"name":"add","nativeSrc":"964:3:3","nodeType":"YulIdentifier","src":"964:3:3"},"nativeSrc":"964:15:3","nodeType":"YulFunctionCall","src":"964:15:3"},{"kind":"number","nativeSrc":"981:2:3","nodeType":"YulLiteral","src":"981:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"960:3:3","nodeType":"YulIdentifier","src":"960:3:3"},"nativeSrc":"960:24:3","nodeType":"YulFunctionCall","src":"960:24:3"},{"name":"dataEnd","nativeSrc":"986:7:3","nodeType":"YulIdentifier","src":"986:7:3"}],"functionName":{"name":"gt","nativeSrc":"957:2:3","nodeType":"YulIdentifier","src":"957:2:3"},"nativeSrc":"957:37:3","nodeType":"YulFunctionCall","src":"957:37:3"},"nativeSrc":"954:57:3","nodeType":"YulIf","src":"954:57:3"},{"expression":{"arguments":[{"arguments":[{"name":"memPtr","nativeSrc":"1030:6:3","nodeType":"YulIdentifier","src":"1030:6:3"},{"kind":"number","nativeSrc":"1038:2:3","nodeType":"YulLiteral","src":"1038:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"1026:3:3","nodeType":"YulIdentifier","src":"1026:3:3"},"nativeSrc":"1026:15:3","nodeType":"YulFunctionCall","src":"1026:15:3"},{"arguments":[{"name":"_1","nativeSrc":"1047:2:3","nodeType":"YulIdentifier","src":"1047:2:3"},{"kind":"number","nativeSrc":"1051:2:3","nodeType":"YulLiteral","src":"1051:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"1043:3:3","nodeType":"YulIdentifier","src":"1043:3:3"},"nativeSrc":"1043:11:3","nodeType":"YulFunctionCall","src":"1043:11:3"},{"name":"length","nativeSrc":"1056:6:3","nodeType":"YulIdentifier","src":"1056:6:3"}],"functionName":{"name":"mcopy","nativeSrc":"1020:5:3","nodeType":"YulIdentifier","src":"1020:5:3"},"nativeSrc":"1020:43:3","nodeType":"YulFunctionCall","src":"1020:43:3"},"nativeSrc":"1020:43:3","nodeType":"YulExpressionStatement","src":"1020:43:3"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"name":"memPtr","nativeSrc":"1087:6:3","nodeType":"YulIdentifier","src":"1087:6:3"},{"name":"length","nativeSrc":"1095:6:3","nodeType":"YulIdentifier","src":"1095:6:3"}],"functionName":{"name":"add","nativeSrc":"1083:3:3","nodeType":"YulIdentifier","src":"1083:3:3"},"nativeSrc":"1083:19:3","nodeType":"YulFunctionCall","src":"1083:19:3"},{"kind":"number","nativeSrc":"1104:2:3","nodeType":"YulLiteral","src":"1104:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"1079:3:3","nodeType":"YulIdentifier","src":"1079:3:3"},"nativeSrc":"1079:28:3","nodeType":"YulFunctionCall","src":"1079:28:3"},{"kind":"number","nativeSrc":"1109:1:3","nodeType":"YulLiteral","src":"1109:1:3","type":"","value":"0"}],"functionName":{"name":"mstore","nativeSrc":"1072:6:3","nodeType":"YulIdentifier","src":"1072:6:3"},"nativeSrc":"1072:39:3","nodeType":"YulFunctionCall","src":"1072:39:3"},"nativeSrc":"1072:39:3","nodeType":"YulExpressionStatement","src":"1072:39:3"},{"nativeSrc":"1120:16:3","nodeType":"YulAssignment","src":"1120:16:3","value":{"name":"memPtr","nativeSrc":"1130:6:3","nodeType":"YulIdentifier","src":"1130:6:3"},"variableNames":[{"name":"value1","nativeSrc":"1120:6:3","nodeType":"YulIdentifier","src":"1120:6:3"}]}]},"name":"abi_decode_tuple_t_uint256t_string_memory_ptr_fromMemory","nativeSrc":"146:996:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nativeSrc":"212:9:3","nodeType":"YulTypedName","src":"212:9:3","type":""},{"name":"dataEnd","nativeSrc":"223:7:3","nodeType":"YulTypedName","src":"223:7:3","type":""}],"returnVariables":[{"name":"value0","nativeSrc":"235:6:3","nodeType":"YulTypedName","src":"235:6:3","type":""},{"name":"value1","nativeSrc":"243:6:3","nodeType":"YulTypedName","src":"243:6:3","type":""}],"src":"146:996:3"},{"body":{"nativeSrc":"1202:325:3","nodeType":"YulBlock","src":"1202:325:3","statements":[{"nativeSrc":"1212:22:3","nodeType":"YulAssignment","src":"1212:22:3","value":{"arguments":[{"kind":"number","nativeSrc":"1226:1:3","nodeType":"YulLiteral","src":"1226:1:3","type":"","value":"1"},{"name":"data","nativeSrc":"1229:4:3","nodeType":"YulIdentifier","src":"1229:4:3"}],"functionName":{"name":"shr","nativeSrc":"1222:3:3","nodeType":"YulIdentifier","src":"1222:3:3"},"nativeSrc":"1222:12:3","nodeType":"YulFunctionCall","src":"1222:12:3"},"variableNames":[{"name":"length","nativeSrc":"1212:6:3","nodeType":"YulIdentifier","src":"1212:6:3"}]},{"nativeSrc":"1243:38:3","nodeType":"YulVariableDeclaration","src":"1243:38:3","value":{"arguments":[{"name":"data","nativeSrc":"1273:4:3","nodeType":"YulIdentifier","src":"1273:4:3"},{"kind":"number","nativeSrc":"1279:1:3","nodeType":"YulLiteral","src":"1279:1:3","type":"","value":"1"}],"functionName":{"name":"and","nativeSrc":"1269:3:3","nodeType":"YulIdentifier","src":"1269:3:3"},"nativeSrc":"1269:12:3","nodeType":"YulFunctionCall","src":"1269:12:3"},"variables":[{"name":"outOfPlaceEncoding","nativeSrc":"1247:18:3","nodeType":"YulTypedName","src":"1247:18:3","type":""}]},{"body":{"nativeSrc":"1320:31:3","nodeType":"YulBlock","src":"1320:31:3","statements":[{"nativeSrc":"1322:27:3","nodeType":"YulAssignment","src":"1322:27:3","value":{"arguments":[{"name":"length","nativeSrc":"1336:6:3","nodeType":"YulIdentifier","src":"1336:6:3"},{"kind":"number","nativeSrc":"1344:4:3","nodeType":"YulLiteral","src":"1344:4:3","type":"","value":"0x7f"}],"functionName":{"name":"and","nativeSrc":"1332:3:3","nodeType":"YulIdentifier","src":"1332:3:3"},"nativeSrc":"1332:17:3","nodeType":"YulFunctionCall","src":"1332:17:3"},"variableNames":[{"name":"length","nativeSrc":"1322:6:3","nodeType":"YulIdentifier","src":"1322:6:3"}]}]},"condition":{"arguments":[{"name":"outOfPlaceEncoding","nativeSrc":"1300:18:3","nodeType":"YulIdentifier","src":"1300:18:3"}],"functionName":{"name":"iszero","nativeSrc":"1293:6:3","nodeType":"YulIdentifier","src":"1293:6:3"},"nativeSrc":"1293:26:3","nodeType":"YulFunctionCall","src":"1293:26:3"},"nativeSrc":"1290:61:3","nodeType":"YulIf","src":"1290:61:3"},{"body":{"nativeSrc":"1410:111:3","nodeType":"YulBlock","src":"1410:111:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"1431:1:3","nodeType":"YulLiteral","src":"1431:1:3","type":"","value":"0"},{"arguments":[{"kind":"number","nativeSrc":"1438:3:3","nodeType":"YulLiteral","src":"1438:3:3","type":"","value":"224"},{"kind":"number","nativeSrc":"1443:10:3","nodeType":"YulLiteral","src":"1443:10:3","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nativeSrc":"1434:3:3","nodeType":"YulIdentifier","src":"1434:3:3"},"nativeSrc":"1434:20:3","nodeType":"YulFunctionCall","src":"1434:20:3"}],"functionName":{"name":"mstore","nativeSrc":"1424:6:3","nodeType":"YulIdentifier","src":"1424:6:3"},"nativeSrc":"1424:31:3","nodeType":"YulFunctionCall","src":"1424:31:3"},"nativeSrc":"1424:31:3","nodeType":"YulExpressionStatement","src":"1424:31:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"1475:1:3","nodeType":"YulLiteral","src":"1475:1:3","type":"","value":"4"},{"kind":"number","nativeSrc":"1478:4:3","nodeType":"YulLiteral","src":"1478:4:3","type":"","value":"0x22"}],"functionName":{"name":"mstore","nativeSrc":"1468:6:3","nodeType":"YulIdentifier","src":"1468:6:3"},"nativeSrc":"1468:15:3","nodeType":"YulFunctionCall","src":"1468:15:3"},"nativeSrc":"1468:15:3","nodeType":"YulExpressionStatement","src":"1468:15:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"1503:1:3","nodeType":"YulLiteral","src":"1503:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"1506:4:3","nodeType":"YulLiteral","src":"1506:4:3","type":"","value":"0x24"}],"functionName":{"name":"revert","nativeSrc":"1496:6:3","nodeType":"YulIdentifier","src":"1496:6:3"},"nativeSrc":"1496:15:3","nodeType":"YulFunctionCall","src":"1496:15:3"},"nativeSrc":"1496:15:3","nodeType":"YulExpressionStatement","src":"1496:15:3"}]},"condition":{"arguments":[{"name":"outOfPlaceEncoding","nativeSrc":"1366:18:3","nodeType":"YulIdentifier","src":"1366:18:3"},{"arguments":[{"name":"length","nativeSrc":"1389:6:3","nodeType":"YulIdentifier","src":"1389:6:3"},{"kind":"number","nativeSrc":"1397:2:3","nodeType":"YulLiteral","src":"1397:2:3","type":"","value":"32"}],"functionName":{"name":"lt","nativeSrc":"1386:2:3","nodeType":"YulIdentifier","src":"1386:2:3"},"nativeSrc":"1386:14:3","nodeType":"YulFunctionCall","src":"1386:14:3"}],"functionName":{"name":"eq","nativeSrc":"1363:2:3","nodeType":"YulIdentifier","src":"1363:2:3"},"nativeSrc":"1363:38:3","nodeType":"YulFunctionCall","src":"1363:38:3"},"nativeSrc":"1360:161:3","nodeType":"YulIf","src":"1360:161:3"}]},"name":"extract_byte_array_length","nativeSrc":"1147:380:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"data","nativeSrc":"1182:4:3","nodeType":"YulTypedName","src":"1182:4:3","type":""}],"returnVariables":[{"name":"length","nativeSrc":"1191:6:3","nodeType":"YulTypedName","src":"1191:6:3","type":""}],"src":"1147:380:3"},{"body":{"nativeSrc":"1588:65:3","nodeType":"YulBlock","src":"1588:65:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"1605:1:3","nodeType":"YulLiteral","src":"1605:1:3","type":"","value":"0"},{"name":"ptr","nativeSrc":"1608:3:3","nodeType":"YulIdentifier","src":"1608:3:3"}],"functionName":{"name":"mstore","nativeSrc":"1598:6:3","nodeType":"YulIdentifier","src":"1598:6:3"},"nativeSrc":"1598:14:3","nodeType":"YulFunctionCall","src":"1598:14:3"},"nativeSrc":"1598:14:3","nodeType":"YulExpressionStatement","src":"1598:14:3"},{"nativeSrc":"1621:26:3","nodeType":"YulAssignment","src":"1621:26:3","value":{"arguments":[{"kind":"number","nativeSrc":"1639:1:3","nodeType":"YulLiteral","src":"1639:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"1642:4:3","nodeType":"YulLiteral","src":"1642:4:3","type":"","value":"0x20"}],"functionName":{"name":"keccak256","nativeSrc":"1629:9:3","nodeType":"YulIdentifier","src":"1629:9:3"},"nativeSrc":"1629:18:3","nodeType":"YulFunctionCall","src":"1629:18:3"},"variableNames":[{"name":"data","nativeSrc":"1621:4:3","nodeType":"YulIdentifier","src":"1621:4:3"}]}]},"name":"array_dataslot_string_storage","nativeSrc":"1532:121:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"ptr","nativeSrc":"1571:3:3","nodeType":"YulTypedName","src":"1571:3:3","type":""}],"returnVariables":[{"name":"data","nativeSrc":"1579:4:3","nodeType":"YulTypedName","src":"1579:4:3","type":""}],"src":"1532:121:3"},{"body":{"nativeSrc":"1739:437:3","nodeType":"YulBlock","src":"1739:437:3","statements":[{"body":{"nativeSrc":"1772:398:3","nodeType":"YulBlock","src":"1772:398:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"1793:1:3","nodeType":"YulLiteral","src":"1793:1:3","type":"","value":"0"},{"name":"array","nativeSrc":"1796:5:3","nodeType":"YulIdentifier","src":"1796:5:3"}],"functionName":{"name":"mstore","nativeSrc":"1786:6:3","nodeType":"YulIdentifier","src":"1786:6:3"},"nativeSrc":"1786:16:3","nodeType":"YulFunctionCall","src":"1786:16:3"},"nativeSrc":"1786:16:3","nodeType":"YulExpressionStatement","src":"1786:16:3"},{"nativeSrc":"1815:30:3","nodeType":"YulVariableDeclaration","src":"1815:30:3","value":{"arguments":[{"kind":"number","nativeSrc":"1837:1:3","nodeType":"YulLiteral","src":"1837:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"1840:4:3","nodeType":"YulLiteral","src":"1840:4:3","type":"","value":"0x20"}],"functionName":{"name":"keccak256","nativeSrc":"1827:9:3","nodeType":"YulIdentifier","src":"1827:9:3"},"nativeSrc":"1827:18:3","nodeType":"YulFunctionCall","src":"1827:18:3"},"variables":[{"name":"data","nativeSrc":"1819:4:3","nodeType":"YulTypedName","src":"1819:4:3","type":""}]},{"nativeSrc":"1858:57:3","nodeType":"YulVariableDeclaration","src":"1858:57:3","value":{"arguments":[{"name":"data","nativeSrc":"1881:4:3","nodeType":"YulIdentifier","src":"1881:4:3"},{"arguments":[{"kind":"number","nativeSrc":"1891:1:3","nodeType":"YulLiteral","src":"1891:1:3","type":"","value":"5"},{"arguments":[{"name":"startIndex","nativeSrc":"1898:10:3","nodeType":"YulIdentifier","src":"1898:10:3"},{"kind":"number","nativeSrc":"1910:2:3","nodeType":"YulLiteral","src":"1910:2:3","type":"","value":"31"}],"functionName":{"name":"add","nativeSrc":"1894:3:3","nodeType":"YulIdentifier","src":"1894:3:3"},"nativeSrc":"1894:19:3","nodeType":"YulFunctionCall","src":"1894:19:3"}],"functionName":{"name":"shr","nativeSrc":"1887:3:3","nodeType":"YulIdentifier","src":"1887:3:3"},"nativeSrc":"1887:27:3","nodeType":"YulFunctionCall","src":"1887:27:3"}],"functionName":{"name":"add","nativeSrc":"1877:3:3","nodeType":"YulIdentifier","src":"1877:3:3"},"nativeSrc":"1877:38:3","nodeType":"YulFunctionCall","src":"1877:38:3"},"variables":[{"name":"deleteStart","nativeSrc":"1862:11:3","nodeType":"YulTypedName","src":"1862:11:3","type":""}]},{"body":{"nativeSrc":"1952:23:3","nodeType":"YulBlock","src":"1952:23:3","statements":[{"nativeSrc":"1954:19:3","nodeType":"YulAssignment","src":"1954:19:3","value":{"name":"data","nativeSrc":"1969:4:3","nodeType":"YulIdentifier","src":"1969:4:3"},"variableNames":[{"name":"deleteStart","nativeSrc":"1954:11:3","nodeType":"YulIdentifier","src":"1954:11:3"}]}]},"condition":{"arguments":[{"name":"startIndex","nativeSrc":"1934:10:3","nodeType":"YulIdentifier","src":"1934:10:3"},{"kind":"number","nativeSrc":"1946:4:3","nodeType":"YulLiteral","src":"1946:4:3","type":"","value":"0x20"}],"functionName":{"name":"lt","nativeSrc":"1931:2:3","nodeType":"YulIdentifier","src":"1931:2:3"},"nativeSrc":"1931:20:3","nodeType":"YulFunctionCall","src":"1931:20:3"},"nativeSrc":"1928:47:3","nodeType":"YulIf","src":"1928:47:3"},{"nativeSrc":"1988:41:3","nodeType":"YulVariableDeclaration","src":"1988:41:3","value":{"arguments":[{"name":"data","nativeSrc":"2002:4:3","nodeType":"YulIdentifier","src":"2002:4:3"},{"arguments":[{"kind":"number","nativeSrc":"2012:1:3","nodeType":"YulLiteral","src":"2012:1:3","type":"","value":"5"},{"arguments":[{"name":"len","nativeSrc":"2019:3:3","nodeType":"YulIdentifier","src":"2019:3:3"},{"kind":"number","nativeSrc":"2024:2:3","nodeType":"YulLiteral","src":"2024:2:3","type":"","value":"31"}],"functionName":{"name":"add","nativeSrc":"2015:3:3","nodeType":"YulIdentifier","src":"2015:3:3"},"nativeSrc":"2015:12:3","nodeType":"YulFunctionCall","src":"2015:12:3"}],"functionName":{"name":"shr","nativeSrc":"2008:3:3","nodeType":"YulIdentifier","src":"2008:3:3"},"nativeSrc":"2008:20:3","nodeType":"YulFunctionCall","src":"2008:20:3"}],"functionName":{"name":"add","nativeSrc":"1998:3:3","nodeType":"YulIdentifier","src":"1998:3:3"},"nativeSrc":"1998:31:3","nodeType":"YulFunctionCall","src":"1998:31:3"},"variables":[{"name":"_1","nativeSrc":"1992:2:3","nodeType":"YulTypedName","src":"1992:2:3","type":""}]},{"nativeSrc":"2042:24:3","nodeType":"YulVariableDeclaration","src":"2042:24:3","value":{"name":"deleteStart","nativeSrc":"2055:11:3","nodeType":"YulIdentifier","src":"2055:11:3"},"variables":[{"name":"start","nativeSrc":"2046:5:3","nodeType":"YulTypedName","src":"2046:5:3","type":""}]},{"body":{"nativeSrc":"2140:20:3","nodeType":"YulBlock","src":"2140:20:3","statements":[{"expression":{"arguments":[{"name":"start","nativeSrc":"2149:5:3","nodeType":"YulIdentifier","src":"2149:5:3"},{"kind":"number","nativeSrc":"2156:1:3","nodeType":"YulLiteral","src":"2156:1:3","type":"","value":"0"}],"functionName":{"name":"sstore","nativeSrc":"2142:6:3","nodeType":"YulIdentifier","src":"2142:6:3"},"nativeSrc":"2142:16:3","nodeType":"YulFunctionCall","src":"2142:16:3"},"nativeSrc":"2142:16:3","nodeType":"YulExpressionStatement","src":"2142:16:3"}]},"condition":{"arguments":[{"name":"start","nativeSrc":"2090:5:3","nodeType":"YulIdentifier","src":"2090:5:3"},{"name":"_1","nativeSrc":"2097:2:3","nodeType":"YulIdentifier","src":"2097:2:3"}],"functionName":{"name":"lt","nativeSrc":"2087:2:3","nodeType":"YulIdentifier","src":"2087:2:3"},"nativeSrc":"2087:13:3","nodeType":"YulFunctionCall","src":"2087:13:3"},"nativeSrc":"2079:81:3","nodeType":"YulForLoop","post":{"nativeSrc":"2101:26:3","nodeType":"YulBlock","src":"2101:26:3","statements":[{"nativeSrc":"2103:22:3","nodeType":"YulAssignment","src":"2103:22:3","value":{"arguments":[{"name":"start","nativeSrc":"2116:5:3","nodeType":"YulIdentifier","src":"2116:5:3"},{"kind":"number","nativeSrc":"2123:1:3","nodeType":"YulLiteral","src":"2123:1:3","type":"","value":"1"}],"functionName":{"name":"add","nativeSrc":"2112:3:3","nodeType":"YulIdentifier","src":"2112:3:3"},"nativeSrc":"2112:13:3","nodeType":"YulFunctionCall","src":"2112:13:3"},"variableNames":[{"name":"start","nativeSrc":"2103:5:3","nodeType":"YulIdentifier","src":"2103:5:3"}]}]},"pre":{"nativeSrc":"2083:3:3","nodeType":"YulBlock","src":"2083:3:3","statements":[]},"src":"2079:81:3"}]},"condition":{"arguments":[{"name":"len","nativeSrc":"1755:3:3","nodeType":"YulIdentifier","src":"1755:3:3"},{"kind":"number","nativeSrc":"1760:2:3","nodeType":"YulLiteral","src":"1760:2:3","type":"","value":"31"}],"functionName":{"name":"gt","nativeSrc":"1752:2:3","nodeType":"YulIdentifier","src":"1752:2:3"},"nativeSrc":"1752:11:3","nodeType":"YulFunctionCall","src":"1752:11:3"},"nativeSrc":"1749:421:3","nodeType":"YulIf","src":"1749:421:3"}]},"name":"clean_up_bytearray_end_slots_string_storage","nativeSrc":"1658:518:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"array","nativeSrc":"1711:5:3","nodeType":"YulTypedName","src":"1711:5:3","type":""},{"name":"len","nativeSrc":"1718:3:3","nodeType":"YulTypedName","src":"1718:3:3","type":""},{"name":"startIndex","nativeSrc":"1723:10:3","nodeType":"YulTypedName","src":"1723:10:3","type":""}],"src":"1658:518:3"},{"body":{"nativeSrc":"2266:81:3","nodeType":"YulBlock","src":"2266:81:3","statements":[{"nativeSrc":"2276:65:3","nodeType":"YulAssignment","src":"2276:65:3","value":{"arguments":[{"arguments":[{"name":"data","nativeSrc":"2291:4:3","nodeType":"YulIdentifier","src":"2291:4:3"},{"arguments":[{"arguments":[{"arguments":[{"kind":"number","nativeSrc":"2309:1:3","nodeType":"YulLiteral","src":"2309:1:3","type":"","value":"3"},{"name":"len","nativeSrc":"2312:3:3","nodeType":"YulIdentifier","src":"2312:3:3"}],"functionName":{"name":"shl","nativeSrc":"2305:3:3","nodeType":"YulIdentifier","src":"2305:3:3"},"nativeSrc":"2305:11:3","nodeType":"YulFunctionCall","src":"2305:11:3"},{"arguments":[{"kind":"number","nativeSrc":"2322:1:3","nodeType":"YulLiteral","src":"2322:1:3","type":"","value":"0"}],"functionName":{"name":"not","nativeSrc":"2318:3:3","nodeType":"YulIdentifier","src":"2318:3:3"},"nativeSrc":"2318:6:3","nodeType":"YulFunctionCall","src":"2318:6:3"}],"functionName":{"name":"shr","nativeSrc":"2301:3:3","nodeType":"YulIdentifier","src":"2301:3:3"},"nativeSrc":"2301:24:3","nodeType":"YulFunctionCall","src":"2301:24:3"}],"functionName":{"name":"not","nativeSrc":"2297:3:3","nodeType":"YulIdentifier","src":"2297:3:3"},"nativeSrc":"2297:29:3","nodeType":"YulFunctionCall","src":"2297:29:3"}],"functionName":{"name":"and","nativeSrc":"2287:3:3","nodeType":"YulIdentifier","src":"2287:3:3"},"nativeSrc":"2287:40:3","nodeType":"YulFunctionCall","src":"2287:40:3"},{"arguments":[{"kind":"number","nativeSrc":"2333:1:3","nodeType":"YulLiteral","src":"2333:1:3","type":"","value":"1"},{"name":"len","nativeSrc":"2336:3:3","nodeType":"YulIdentifier","src":"2336:3:3"}],"functionName":{"name":"shl","nativeSrc":"2329:3:3","nodeType":"YulIdentifier","src":"2329:3:3"},"nativeSrc":"2329:11:3","nodeType":"YulFunctionCall","src":"2329:11:3"}],"functionName":{"name":"or","nativeSrc":"2284:2:3","nodeType":"YulIdentifier","src":"2284:2:3"},"nativeSrc":"2284:57:3","nodeType":"YulFunctionCall","src":"2284:57:3"},"variableNames":[{"name":"used","nativeSrc":"2276:4:3","nodeType":"YulIdentifier","src":"2276:4:3"}]}]},"name":"extract_used_part_and_set_length_of_short_byte_array","nativeSrc":"2181:166:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"data","nativeSrc":"2243:4:3","nodeType":"YulTypedName","src":"2243:4:3","type":""},{"name":"len","nativeSrc":"2249:3:3","nodeType":"YulTypedName","src":"2249:3:3","type":""}],"returnVariables":[{"name":"used","nativeSrc":"2257:4:3","nodeType":"YulTypedName","src":"2257:4:3","type":""}],"src":"2181:166:3"},{"body":{"nativeSrc":"2448:1203:3","nodeType":"YulBlock","src":"2448:1203:3","statements":[{"nativeSrc":"2458:24:3","nodeType":"YulVariableDeclaration","src":"2458:24:3","value":{"arguments":[{"name":"src","nativeSrc":"2478:3:3","nodeType":"YulIdentifier","src":"2478:3:3"}],"functionName":{"name":"mload","nativeSrc":"2472:5:3","nodeType":"YulIdentifier","src":"2472:5:3"},"nativeSrc":"2472:10:3","nodeType":"YulFunctionCall","src":"2472:10:3"},"variables":[{"name":"newLen","nativeSrc":"2462:6:3","nodeType":"YulTypedName","src":"2462:6:3","type":""}]},{"body":{"nativeSrc":"2525:22:3","nodeType":"YulBlock","src":"2525:22:3","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nativeSrc":"2527:16:3","nodeType":"YulIdentifier","src":"2527:16:3"},"nativeSrc":"2527:18:3","nodeType":"YulFunctionCall","src":"2527:18:3"},"nativeSrc":"2527:18:3","nodeType":"YulExpressionStatement","src":"2527:18:3"}]},"condition":{"arguments":[{"name":"newLen","nativeSrc":"2497:6:3","nodeType":"YulIdentifier","src":"2497:6:3"},{"arguments":[{"arguments":[{"kind":"number","nativeSrc":"2513:2:3","nodeType":"YulLiteral","src":"2513:2:3","type":"","value":"64"},{"kind":"number","nativeSrc":"2517:1:3","nodeType":"YulLiteral","src":"2517:1:3","type":"","value":"1"}],"functionName":{"name":"shl","nativeSrc":"2509:3:3","nodeType":"YulIdentifier","src":"2509:3:3"},"nativeSrc":"2509:10:3","nodeType":"YulFunctionCall","src":"2509:10:3"},{"kind":"number","nativeSrc":"2521:1:3","nodeType":"YulLiteral","src":"2521:1:3","type":"","value":"1"}],"functionName":{"name":"sub","nativeSrc":"2505:3:3","nodeType":"YulIdentifier","src":"2505:3:3"},"nativeSrc":"2505:18:3","nodeType":"YulFunctionCall","src":"2505:18:3"}],"functionName":{"name":"gt","nativeSrc":"2494:2:3","nodeType":"YulIdentifier","src":"2494:2:3"},"nativeSrc":"2494:30:3","nodeType":"YulFunctionCall","src":"2494:30:3"},"nativeSrc":"2491:56:3","nodeType":"YulIf","src":"2491:56:3"},{"expression":{"arguments":[{"name":"slot","nativeSrc":"2600:4:3","nodeType":"YulIdentifier","src":"2600:4:3"},{"arguments":[{"arguments":[{"name":"slot","nativeSrc":"2638:4:3","nodeType":"YulIdentifier","src":"2638:4:3"}],"functionName":{"name":"sload","nativeSrc":"2632:5:3","nodeType":"YulIdentifier","src":"2632:5:3"},"nativeSrc":"2632:11:3","nodeType":"YulFunctionCall","src":"2632:11:3"}],"functionName":{"name":"extract_byte_array_length","nativeSrc":"2606:25:3","nodeType":"YulIdentifier","src":"2606:25:3"},"nativeSrc":"2606:38:3","nodeType":"YulFunctionCall","src":"2606:38:3"},{"name":"newLen","nativeSrc":"2646:6:3","nodeType":"YulIdentifier","src":"2646:6:3"}],"functionName":{"name":"clean_up_bytearray_end_slots_string_storage","nativeSrc":"2556:43:3","nodeType":"YulIdentifier","src":"2556:43:3"},"nativeSrc":"2556:97:3","nodeType":"YulFunctionCall","src":"2556:97:3"},"nativeSrc":"2556:97:3","nodeType":"YulExpressionStatement","src":"2556:97:3"},{"nativeSrc":"2662:18:3","nodeType":"YulVariableDeclaration","src":"2662:18:3","value":{"kind":"number","nativeSrc":"2679:1:3","nodeType":"YulLiteral","src":"2679:1:3","type":"","value":"0"},"variables":[{"name":"srcOffset","nativeSrc":"2666:9:3","nodeType":"YulTypedName","src":"2666:9:3","type":""}]},{"nativeSrc":"2689:17:3","nodeType":"YulAssignment","src":"2689:17:3","value":{"kind":"number","nativeSrc":"2702:4:3","nodeType":"YulLiteral","src":"2702:4:3","type":"","value":"0x20"},"variableNames":[{"name":"srcOffset","nativeSrc":"2689:9:3","nodeType":"YulIdentifier","src":"2689:9:3"}]},{"cases":[{"body":{"nativeSrc":"2752:642:3","nodeType":"YulBlock","src":"2752:642:3","statements":[{"nativeSrc":"2766:35:3","nodeType":"YulVariableDeclaration","src":"2766:35:3","value":{"arguments":[{"name":"newLen","nativeSrc":"2785:6:3","nodeType":"YulIdentifier","src":"2785:6:3"},{"arguments":[{"kind":"number","nativeSrc":"2797:2:3","nodeType":"YulLiteral","src":"2797:2:3","type":"","value":"31"}],"functionName":{"name":"not","nativeSrc":"2793:3:3","nodeType":"YulIdentifier","src":"2793:3:3"},"nativeSrc":"2793:7:3","nodeType":"YulFunctionCall","src":"2793:7:3"}],"functionName":{"name":"and","nativeSrc":"2781:3:3","nodeType":"YulIdentifier","src":"2781:3:3"},"nativeSrc":"2781:20:3","nodeType":"YulFunctionCall","src":"2781:20:3"},"variables":[{"name":"loopEnd","nativeSrc":"2770:7:3","nodeType":"YulTypedName","src":"2770:7:3","type":""}]},{"nativeSrc":"2814:49:3","nodeType":"YulVariableDeclaration","src":"2814:49:3","value":{"arguments":[{"name":"slot","nativeSrc":"2858:4:3","nodeType":"YulIdentifier","src":"2858:4:3"}],"functionName":{"name":"array_dataslot_string_storage","nativeSrc":"2828:29:3","nodeType":"YulIdentifier","src":"2828:29:3"},"nativeSrc":"2828:35:3","nodeType":"YulFunctionCall","src":"2828:35:3"},"variables":[{"name":"dstPtr","nativeSrc":"2818:6:3","nodeType":"YulTypedName","src":"2818:6:3","type":""}]},{"nativeSrc":"2876:10:3","nodeType":"YulVariableDeclaration","src":"2876:10:3","value":{"kind":"number","nativeSrc":"2885:1:3","nodeType":"YulLiteral","src":"2885:1:3","type":"","value":"0"},"variables":[{"name":"i","nativeSrc":"2880:1:3","nodeType":"YulTypedName","src":"2880:1:3","type":""}]},{"body":{"nativeSrc":"2956:165:3","nodeType":"YulBlock","src":"2956:165:3","statements":[{"expression":{"arguments":[{"name":"dstPtr","nativeSrc":"2981:6:3","nodeType":"YulIdentifier","src":"2981:6:3"},{"arguments":[{"arguments":[{"name":"src","nativeSrc":"2999:3:3","nodeType":"YulIdentifier","src":"2999:3:3"},{"name":"srcOffset","nativeSrc":"3004:9:3","nodeType":"YulIdentifier","src":"3004:9:3"}],"functionName":{"name":"add","nativeSrc":"2995:3:3","nodeType":"YulIdentifier","src":"2995:3:3"},"nativeSrc":"2995:19:3","nodeType":"YulFunctionCall","src":"2995:19:3"}],"functionName":{"name":"mload","nativeSrc":"2989:5:3","nodeType":"YulIdentifier","src":"2989:5:3"},"nativeSrc":"2989:26:3","nodeType":"YulFunctionCall","src":"2989:26:3"}],"functionName":{"name":"sstore","nativeSrc":"2974:6:3","nodeType":"YulIdentifier","src":"2974:6:3"},"nativeSrc":"2974:42:3","nodeType":"YulFunctionCall","src":"2974:42:3"},"nativeSrc":"2974:42:3","nodeType":"YulExpressionStatement","src":"2974:42:3"},{"nativeSrc":"3033:24:3","nodeType":"YulAssignment","src":"3033:24:3","value":{"arguments":[{"name":"dstPtr","nativeSrc":"3047:6:3","nodeType":"YulIdentifier","src":"3047:6:3"},{"kind":"number","nativeSrc":"3055:1:3","nodeType":"YulLiteral","src":"3055:1:3","type":"","value":"1"}],"functionName":{"name":"add","nativeSrc":"3043:3:3","nodeType":"YulIdentifier","src":"3043:3:3"},"nativeSrc":"3043:14:3","nodeType":"YulFunctionCall","src":"3043:14:3"},"variableNames":[{"name":"dstPtr","nativeSrc":"3033:6:3","nodeType":"YulIdentifier","src":"3033:6:3"}]},{"nativeSrc":"3074:33:3","nodeType":"YulAssignment","src":"3074:33:3","value":{"arguments":[{"name":"srcOffset","nativeSrc":"3091:9:3","nodeType":"YulIdentifier","src":"3091:9:3"},{"kind":"number","nativeSrc":"3102:4:3","nodeType":"YulLiteral","src":"3102:4:3","type":"","value":"0x20"}],"functionName":{"name":"add","nativeSrc":"3087:3:3","nodeType":"YulIdentifier","src":"3087:3:3"},"nativeSrc":"3087:20:3","nodeType":"YulFunctionCall","src":"3087:20:3"},"variableNames":[{"name":"srcOffset","nativeSrc":"3074:9:3","nodeType":"YulIdentifier","src":"3074:9:3"}]}]},"condition":{"arguments":[{"name":"i","nativeSrc":"2910:1:3","nodeType":"YulIdentifier","src":"2910:1:3"},{"name":"loopEnd","nativeSrc":"2913:7:3","nodeType":"YulIdentifier","src":"2913:7:3"}],"functionName":{"name":"lt","nativeSrc":"2907:2:3","nodeType":"YulIdentifier","src":"2907:2:3"},"nativeSrc":"2907:14:3","nodeType":"YulFunctionCall","src":"2907:14:3"},"nativeSrc":"2899:222:3","nodeType":"YulForLoop","post":{"nativeSrc":"2922:21:3","nodeType":"YulBlock","src":"2922:21:3","statements":[{"nativeSrc":"2924:17:3","nodeType":"YulAssignment","src":"2924:17:3","value":{"arguments":[{"name":"i","nativeSrc":"2933:1:3","nodeType":"YulIdentifier","src":"2933:1:3"},{"kind":"number","nativeSrc":"2936:4:3","nodeType":"YulLiteral","src":"2936:4:3","type":"","value":"0x20"}],"functionName":{"name":"add","nativeSrc":"2929:3:3","nodeType":"YulIdentifier","src":"2929:3:3"},"nativeSrc":"2929:12:3","nodeType":"YulFunctionCall","src":"2929:12:3"},"variableNames":[{"name":"i","nativeSrc":"2924:1:3","nodeType":"YulIdentifier","src":"2924:1:3"}]}]},"pre":{"nativeSrc":"2903:3:3","nodeType":"YulBlock","src":"2903:3:3","statements":[]},"src":"2899:222:3"},{"body":{"nativeSrc":"3169:166:3","nodeType":"YulBlock","src":"3169:166:3","statements":[{"nativeSrc":"3187:43:3","nodeType":"YulVariableDeclaration","src":"3187:43:3","value":{"arguments":[{"arguments":[{"name":"src","nativeSrc":"3214:3:3","nodeType":"YulIdentifier","src":"3214:3:3"},{"name":"srcOffset","nativeSrc":"3219:9:3","nodeType":"YulIdentifier","src":"3219:9:3"}],"functionName":{"name":"add","nativeSrc":"3210:3:3","nodeType":"YulIdentifier","src":"3210:3:3"},"nativeSrc":"3210:19:3","nodeType":"YulFunctionCall","src":"3210:19:3"}],"functionName":{"name":"mload","nativeSrc":"3204:5:3","nodeType":"YulIdentifier","src":"3204:5:3"},"nativeSrc":"3204:26:3","nodeType":"YulFunctionCall","src":"3204:26:3"},"variables":[{"name":"lastValue","nativeSrc":"3191:9:3","nodeType":"YulTypedName","src":"3191:9:3","type":""}]},{"expression":{"arguments":[{"name":"dstPtr","nativeSrc":"3254:6:3","nodeType":"YulIdentifier","src":"3254:6:3"},{"arguments":[{"name":"lastValue","nativeSrc":"3266:9:3","nodeType":"YulIdentifier","src":"3266:9:3"},{"arguments":[{"arguments":[{"arguments":[{"arguments":[{"kind":"number","nativeSrc":"3293:1:3","nodeType":"YulLiteral","src":"3293:1:3","type":"","value":"3"},{"name":"newLen","nativeSrc":"3296:6:3","nodeType":"YulIdentifier","src":"3296:6:3"}],"functionName":{"name":"shl","nativeSrc":"3289:3:3","nodeType":"YulIdentifier","src":"3289:3:3"},"nativeSrc":"3289:14:3","nodeType":"YulFunctionCall","src":"3289:14:3"},{"kind":"number","nativeSrc":"3305:3:3","nodeType":"YulLiteral","src":"3305:3:3","type":"","value":"248"}],"functionName":{"name":"and","nativeSrc":"3285:3:3","nodeType":"YulIdentifier","src":"3285:3:3"},"nativeSrc":"3285:24:3","nodeType":"YulFunctionCall","src":"3285:24:3"},{"arguments":[{"kind":"number","nativeSrc":"3315:1:3","nodeType":"YulLiteral","src":"3315:1:3","type":"","value":"0"}],"functionName":{"name":"not","nativeSrc":"3311:3:3","nodeType":"YulIdentifier","src":"3311:3:3"},"nativeSrc":"3311:6:3","nodeType":"YulFunctionCall","src":"3311:6:3"}],"functionName":{"name":"shr","nativeSrc":"3281:3:3","nodeType":"YulIdentifier","src":"3281:3:3"},"nativeSrc":"3281:37:3","nodeType":"YulFunctionCall","src":"3281:37:3"}],"functionName":{"name":"not","nativeSrc":"3277:3:3","nodeType":"YulIdentifier","src":"3277:3:3"},"nativeSrc":"3277:42:3","nodeType":"YulFunctionCall","src":"3277:42:3"}],"functionName":{"name":"and","nativeSrc":"3262:3:3","nodeType":"YulIdentifier","src":"3262:3:3"},"nativeSrc":"3262:58:3","nodeType":"YulFunctionCall","src":"3262:58:3"}],"functionName":{"name":"sstore","nativeSrc":"3247:6:3","nodeType":"YulIdentifier","src":"3247:6:3"},"nativeSrc":"3247:74:3","nodeType":"YulFunctionCall","src":"3247:74:3"},"nativeSrc":"3247:74:3","nodeType":"YulExpressionStatement","src":"3247:74:3"}]},"condition":{"arguments":[{"name":"loopEnd","nativeSrc":"3140:7:3","nodeType":"YulIdentifier","src":"3140:7:3"},{"name":"newLen","nativeSrc":"3149:6:3","nodeType":"YulIdentifier","src":"3149:6:3"}],"functionName":{"name":"lt","nativeSrc":"3137:2:3","nodeType":"YulIdentifier","src":"3137:2:3"},"nativeSrc":"3137:19:3","nodeType":"YulFunctionCall","src":"3137:19:3"},"nativeSrc":"3134:201:3","nodeType":"YulIf","src":"3134:201:3"},{"expression":{"arguments":[{"name":"slot","nativeSrc":"3355:4:3","nodeType":"YulIdentifier","src":"3355:4:3"},{"arguments":[{"arguments":[{"kind":"number","nativeSrc":"3369:1:3","nodeType":"YulLiteral","src":"3369:1:3","type":"","value":"1"},{"name":"newLen","nativeSrc":"3372:6:3","nodeType":"YulIdentifier","src":"3372:6:3"}],"functionName":{"name":"shl","nativeSrc":"3365:3:3","nodeType":"YulIdentifier","src":"3365:3:3"},"nativeSrc":"3365:14:3","nodeType":"YulFunctionCall","src":"3365:14:3"},{"kind":"number","nativeSrc":"3381:1:3","nodeType":"YulLiteral","src":"3381:1:3","type":"","value":"1"}],"functionName":{"name":"add","nativeSrc":"3361:3:3","nodeType":"YulIdentifier","src":"3361:3:3"},"nativeSrc":"3361:22:3","nodeType":"YulFunctionCall","src":"3361:22:3"}],"functionName":{"name":"sstore","nativeSrc":"3348:6:3","nodeType":"YulIdentifier","src":"3348:6:3"},"nativeSrc":"3348:36:3","nodeType":"YulFunctionCall","src":"3348:36:3"},"nativeSrc":"3348:36:3","nodeType":"YulExpressionStatement","src":"3348:36:3"}]},"nativeSrc":"2745:649:3","nodeType":"YulCase","src":"2745:649:3","value":{"kind":"number","nativeSrc":"2750:1:3","nodeType":"YulLiteral","src":"2750:1:3","type":"","value":"1"}},{"body":{"nativeSrc":"3411:234:3","nodeType":"YulBlock","src":"3411:234:3","statements":[{"nativeSrc":"3425:14:3","nodeType":"YulVariableDeclaration","src":"3425:14:3","value":{"kind":"number","nativeSrc":"3438:1:3","nodeType":"YulLiteral","src":"3438:1:3","type":"","value":"0"},"variables":[{"name":"value","nativeSrc":"3429:5:3","nodeType":"YulTypedName","src":"3429:5:3","type":""}]},{"body":{"nativeSrc":"3474:67:3","nodeType":"YulBlock","src":"3474:67:3","statements":[{"nativeSrc":"3492:35:3","nodeType":"YulAssignment","src":"3492:35:3","value":{"arguments":[{"arguments":[{"name":"src","nativeSrc":"3511:3:3","nodeType":"YulIdentifier","src":"3511:3:3"},{"name":"srcOffset","nativeSrc":"3516:9:3","nodeType":"YulIdentifier","src":"3516:9:3"}],"functionName":{"name":"add","nativeSrc":"3507:3:3","nodeType":"YulIdentifier","src":"3507:3:3"},"nativeSrc":"3507:19:3","nodeType":"YulFunctionCall","src":"3507:19:3"}],"functionName":{"name":"mload","nativeSrc":"3501:5:3","nodeType":"YulIdentifier","src":"3501:5:3"},"nativeSrc":"3501:26:3","nodeType":"YulFunctionCall","src":"3501:26:3"},"variableNames":[{"name":"value","nativeSrc":"3492:5:3","nodeType":"YulIdentifier","src":"3492:5:3"}]}]},"condition":{"name":"newLen","nativeSrc":"3455:6:3","nodeType":"YulIdentifier","src":"3455:6:3"},"nativeSrc":"3452:89:3","nodeType":"YulIf","src":"3452:89:3"},{"expression":{"arguments":[{"name":"slot","nativeSrc":"3561:4:3","nodeType":"YulIdentifier","src":"3561:4:3"},{"arguments":[{"name":"value","nativeSrc":"3620:5:3","nodeType":"YulIdentifier","src":"3620:5:3"},{"name":"newLen","nativeSrc":"3627:6:3","nodeType":"YulIdentifier","src":"3627:6:3"}],"functionName":{"name":"extract_used_part_and_set_length_of_short_byte_array","nativeSrc":"3567:52:3","nodeType":"YulIdentifier","src":"3567:52:3"},"nativeSrc":"3567:67:3","nodeType":"YulFunctionCall","src":"3567:67:3"}],"functionName":{"name":"sstore","nativeSrc":"3554:6:3","nodeType":"YulIdentifier","src":"3554:6:3"},"nativeSrc":"3554:81:3","nodeType":"YulFunctionCall","src":"3554:81:3"},"nativeSrc":"3554:81:3","nodeType":"YulExpressionStatement","src":"3554:81:3"}]},"nativeSrc":"3403:242:3","nodeType":"YulCase","src":"3403:242:3","value":"default"}],"expression":{"arguments":[{"name":"newLen","nativeSrc":"2725:6:3","nodeType":"YulIdentifier","src":"2725:6:3"},{"kind":"number","nativeSrc":"2733:2:3","nodeType":"YulLiteral","src":"2733:2:3","type":"","value":"31"}],"functionName":{"name":"gt","nativeSrc":"2722:2:3","nodeType":"YulIdentifier","src":"2722:2:3"},"nativeSrc":"2722:14:3","nodeType":"YulFunctionCall","src":"2722:14:3"},"nativeSrc":"2715:930:3","nodeType":"YulSwitch","src":"2715:930:3"}]},"name":"copy_byte_array_to_storage_from_t_string_memory_ptr_to_t_string_storage","nativeSrc":"2352:1299:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"slot","nativeSrc":"2433:4:3","nodeType":"YulTypedName","src":"2433:4:3","type":""},{"name":"src","nativeSrc":"2439:3:3","nodeType":"YulTypedName","src":"2439:3:3","type":""}],"src":"2352:1299:3"}]},"contents":"{\n { }\n function panic_error_0x41()\n {\n mstore(0, shl(224, 0x4e487b71))\n mstore(4, 0x41)\n revert(0, 0x24)\n }\n function abi_decode_tuple_t_uint256t_string_memory_ptr_fromMemory(headStart, dataEnd) -> value0, value1\n {\n if slt(sub(dataEnd, headStart), 64) { revert(0, 0) }\n value0 := mload(headStart)\n let offset := mload(add(headStart, 32))\n if gt(offset, sub(shl(64, 1), 1)) { revert(0, 0) }\n let _1 := add(headStart, offset)\n if iszero(slt(add(_1, 0x1f), dataEnd)) { revert(0, 0) }\n let length := mload(_1)\n if gt(length, sub(shl(64, 1), 1)) { panic_error_0x41() }\n let memPtr := mload(64)\n let newFreePtr := add(memPtr, and(add(and(add(length, 0x1f), not(31)), 63), not(31)))\n if or(gt(newFreePtr, sub(shl(64, 1), 1)), lt(newFreePtr, memPtr)) { panic_error_0x41() }\n mstore(64, newFreePtr)\n mstore(memPtr, length)\n if gt(add(add(_1, length), 32), dataEnd) { revert(0, 0) }\n mcopy(add(memPtr, 32), add(_1, 32), length)\n mstore(add(add(memPtr, length), 32), 0)\n value1 := memPtr\n }\n function extract_byte_array_length(data) -> length\n {\n length := shr(1, data)\n let outOfPlaceEncoding := and(data, 1)\n if iszero(outOfPlaceEncoding) { length := and(length, 0x7f) }\n if eq(outOfPlaceEncoding, lt(length, 32))\n {\n mstore(0, shl(224, 0x4e487b71))\n mstore(4, 0x22)\n revert(0, 0x24)\n }\n }\n function array_dataslot_string_storage(ptr) -> data\n {\n mstore(0, ptr)\n data := keccak256(0, 0x20)\n }\n function clean_up_bytearray_end_slots_string_storage(array, len, startIndex)\n {\n if gt(len, 31)\n {\n mstore(0, array)\n let data := keccak256(0, 0x20)\n let deleteStart := add(data, shr(5, add(startIndex, 31)))\n if lt(startIndex, 0x20) { deleteStart := data }\n let _1 := add(data, shr(5, add(len, 31)))\n let start := deleteStart\n for { } lt(start, _1) { start := add(start, 1) }\n { sstore(start, 0) }\n }\n }\n function extract_used_part_and_set_length_of_short_byte_array(data, len) -> used\n {\n used := or(and(data, not(shr(shl(3, len), not(0)))), shl(1, len))\n }\n function copy_byte_array_to_storage_from_t_string_memory_ptr_to_t_string_storage(slot, src)\n {\n let newLen := mload(src)\n if gt(newLen, sub(shl(64, 1), 1)) { panic_error_0x41() }\n clean_up_bytearray_end_slots_string_storage(slot, extract_byte_array_length(sload(slot)), newLen)\n let srcOffset := 0\n srcOffset := 0x20\n switch gt(newLen, 31)\n case 1 {\n let loopEnd := and(newLen, not(31))\n let dstPtr := array_dataslot_string_storage(slot)\n let i := 0\n for { } lt(i, loopEnd) { i := add(i, 0x20) }\n {\n sstore(dstPtr, mload(add(src, srcOffset)))\n dstPtr := add(dstPtr, 1)\n srcOffset := add(srcOffset, 0x20)\n }\n if lt(loopEnd, newLen)\n {\n let lastValue := mload(add(src, srcOffset))\n sstore(dstPtr, and(lastValue, not(shr(and(shl(3, newLen), 248), not(0)))))\n }\n sstore(slot, add(shl(1, newLen), 1))\n }\n default {\n let value := 0\n if newLen\n {\n value := mload(add(src, srcOffset))\n }\n sstore(slot, extract_used_part_and_set_length_of_short_byte_array(value, newLen))\n }\n }\n}","id":3,"language":"Yul","name":"#utility.yul"}],"linkReferences":{},"object":"60a060405234801561000f575f80fd5b5060405161041f38038061041f83398101604081905261002e9161005a565b60808290525f61003e8282610199565b505050610253565b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561006b575f80fd5b825160208401519092506001600160401b03811115610088575f80fd5b8301601f81018513610098575f80fd5b80516001600160401b038111156100b1576100b1610046565b604051601f8201601f19908116603f011681016001600160401b03811182821017156100df576100df610046565b6040528181528282016020018710156100f6575f80fd5b8160208401602083015e5f602083830101528093505050509250929050565b600181811c9082168061012957607f821691505b60208210810361014757634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561019457805f5260205f20601f840160051c810160208510156101725750805b601f840160051c820191505b81811015610191575f815560010161017e565b50505b505050565b81516001600160401b038111156101b2576101b2610046565b6101c6816101c08454610115565b8461014d565b6020601f8211600181146101f8575f83156101e15750848201515b5f19600385901b1c1916600184901b178455610191565b5f84815260208120601f198516915b828110156102275787850151825560209485019460019092019101610207565b508482101561024457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6080516101b561026a5f395f603d01526101b55ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80637d94792a14610038578063ef690cc014610072575b5f80fd5b61005f7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b61007a610087565b6040516100699190610112565b5f805461009390610147565b80601f01602080910402602001604051908101604052809291908181526020018280546100bf90610147565b801561010a5780601f106100e15761010080835404028352916020019161010a565b820191905f5260205f20905b8154815290600101906020018083116100ed57829003601f168201915b505050505081565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b600181811c9082168061015b57607f821691505b60208210810361017957634e487b7160e01b5f52602260045260245ffd5b5091905056fea26469706673582212207b2a6c1870d9c4c876c7d54da986f101f3e8b48127c03cf1800927f27489f58564736f6c634300081a0033","opcodes":"PUSH1 0xA0 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0xF JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH2 0x41F CODESIZE SUB DUP1 PUSH2 0x41F DUP4 CODECOPY DUP2 ADD PUSH1 0x40 DUP2 SWAP1 MSTORE PUSH2 0x2E SWAP2 PUSH2 0x5A JUMP JUMPDEST PUSH1 0x80 DUP3 SWAP1 MSTORE PUSH0 PUSH2 0x3E DUP3 DUP3 PUSH2 0x199 JUMP JUMPDEST POP POP POP PUSH2 0x253 JUMP JUMPDEST PUSH4 0x4E487B71 PUSH1 0xE0 SHL PUSH0 MSTORE PUSH1 0x41 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH0 REVERT JUMPDEST PUSH0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x6B JUMPI PUSH0 DUP1 REVERT JUMPDEST DUP3 MLOAD PUSH1 0x20 DUP5 ADD MLOAD SWAP1 SWAP3 POP PUSH1 0x1 PUSH1 0x1 PUSH1 0x40 SHL SUB DUP2 GT ISZERO PUSH2 0x88 JUMPI PUSH0 DUP1 REVERT JUMPDEST DUP4 ADD PUSH1 0x1F DUP2 ADD DUP6 SGT PUSH2 0x98 JUMPI PUSH0 DUP1 REVERT JUMPDEST DUP1 MLOAD PUSH1 0x1 PUSH1 0x1 PUSH1 0x40 SHL SUB DUP2 GT ISZERO PUSH2 0xB1 JUMPI PUSH2 0xB1 PUSH2 0x46 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x1F DUP3 ADD PUSH1 0x1F NOT SWAP1 DUP2 AND PUSH1 0x3F ADD AND DUP2 ADD PUSH1 0x1 PUSH1 0x1 PUSH1 0x40 SHL SUB DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0xDF JUMPI PUSH2 0xDF PUSH2 0x46 JUMP JUMPDEST PUSH1 0x40 MSTORE DUP2 DUP2 MSTORE DUP3 DUP3 ADD PUSH1 0x20 ADD DUP8 LT ISZERO PUSH2 0xF6 JUMPI PUSH0 DUP1 REVERT JUMPDEST DUP2 PUSH1 0x20 DUP5 ADD PUSH1 0x20 DUP4 ADD MCOPY PUSH0 PUSH1 0x20 DUP4 DUP4 ADD ADD MSTORE DUP1 SWAP4 POP POP POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x1 DUP2 DUP2 SHR SWAP1 DUP3 AND DUP1 PUSH2 0x129 JUMPI PUSH1 0x7F DUP3 AND SWAP2 POP JUMPDEST PUSH1 0x20 DUP3 LT DUP2 SUB PUSH2 0x147 JUMPI PUSH4 0x4E487B71 PUSH1 0xE0 SHL PUSH0 MSTORE PUSH1 0x22 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH0 REVERT JUMPDEST POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x1F DUP3 GT ISZERO PUSH2 0x194 JUMPI DUP1 PUSH0 MSTORE PUSH1 0x20 PUSH0 KECCAK256 PUSH1 0x1F DUP5 ADD PUSH1 0x5 SHR DUP2 ADD PUSH1 0x20 DUP6 LT ISZERO PUSH2 0x172 JUMPI POP DUP1 JUMPDEST PUSH1 0x1F DUP5 ADD PUSH1 0x5 SHR DUP3 ADD SWAP2 POP JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0x191 JUMPI PUSH0 DUP2 SSTORE PUSH1 0x1 ADD PUSH2 0x17E JUMP JUMPDEST POP POP JUMPDEST POP POP POP JUMP JUMPDEST DUP2 MLOAD PUSH1 0x1 PUSH1 0x1 PUSH1 0x40 SHL SUB DUP2 GT ISZERO PUSH2 0x1B2 JUMPI PUSH2 0x1B2 PUSH2 0x46 JUMP JUMPDEST PUSH2 0x1C6 DUP2 PUSH2 0x1C0 DUP5 SLOAD PUSH2 0x115 JUMP JUMPDEST DUP5 PUSH2 0x14D JUMP JUMPDEST PUSH1 0x20 PUSH1 0x1F DUP3 GT PUSH1 0x1 DUP2 EQ PUSH2 0x1F8 JUMPI PUSH0 DUP4 ISZERO PUSH2 0x1E1 JUMPI POP DUP5 DUP3 ADD MLOAD JUMPDEST PUSH0 NOT PUSH1 0x3 DUP6 SWAP1 SHL SHR NOT AND PUSH1 0x1 DUP5 SWAP1 SHL OR DUP5 SSTORE PUSH2 0x191 JUMP JUMPDEST PUSH0 DUP5 DUP2 MSTORE PUSH1 0x20 DUP2 KECCAK256 PUSH1 0x1F NOT DUP6 AND SWAP2 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x227 JUMPI DUP8 DUP6 ADD MLOAD DUP3 SSTORE PUSH1 0x20 SWAP5 DUP6 ADD SWAP5 PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 ADD PUSH2 0x207 JUMP JUMPDEST POP DUP5 DUP3 LT ISZERO PUSH2 0x244 JUMPI DUP7 DUP5 ADD MLOAD PUSH0 NOT PUSH1 0x3 DUP8 SWAP1 SHL PUSH1 0xF8 AND SHR NOT AND DUP2 SSTORE JUMPDEST POP POP POP POP PUSH1 0x1 SWAP1 DUP2 SHL ADD SWAP1 SSTORE POP JUMP JUMPDEST PUSH1 0x80 MLOAD PUSH2 0x1B5 PUSH2 0x26A PUSH0 CODECOPY PUSH0 PUSH1 0x3D ADD MSTORE PUSH2 0x1B5 PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0xF JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x34 JUMPI PUSH0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x7D94792A EQ PUSH2 0x38 JUMPI DUP1 PUSH4 0xEF690CC0 EQ PUSH2 0x72 JUMPI JUMPDEST PUSH0 DUP1 REVERT JUMPDEST PUSH2 0x5F PUSH32 0x0 DUP2 JUMP JUMPDEST PUSH1 0x40 MLOAD SWAP1 DUP2 MSTORE PUSH1 0x20 ADD JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x7A PUSH2 0x87 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x69 SWAP2 SWAP1 PUSH2 0x112 JUMP JUMPDEST PUSH0 DUP1 SLOAD PUSH2 0x93 SWAP1 PUSH2 0x147 JUMP JUMPDEST DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP3 DUP1 SLOAD PUSH2 0xBF SWAP1 PUSH2 0x147 JUMP JUMPDEST DUP1 ISZERO PUSH2 0x10A JUMPI DUP1 PUSH1 0x1F LT PUSH2 0xE1 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x10A JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH0 MSTORE PUSH1 0x20 PUSH0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0xED JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP DUP2 JUMP JUMPDEST PUSH1 0x20 DUP2 MSTORE PUSH0 DUP3 MLOAD DUP1 PUSH1 0x20 DUP5 ADD MSTORE DUP1 PUSH1 0x20 DUP6 ADD PUSH1 0x40 DUP6 ADD MCOPY PUSH0 PUSH1 0x40 DUP3 DUP6 ADD ADD MSTORE PUSH1 0x40 PUSH1 0x1F NOT PUSH1 0x1F DUP4 ADD AND DUP5 ADD ADD SWAP2 POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x1 DUP2 DUP2 SHR SWAP1 DUP3 AND DUP1 PUSH2 0x15B JUMPI PUSH1 0x7F DUP3 AND SWAP2 POP JUMPDEST PUSH1 0x20 DUP3 LT DUP2 SUB PUSH2 0x179 JUMPI PUSH4 0x4E487B71 PUSH1 0xE0 SHL PUSH0 MSTORE PUSH1 0x22 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH0 REVERT JUMPDEST POP SWAP2 SWAP1 POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH28 0x2A6C1870D9C4C876C7D54DA986F101F3E8B48127C03CF1800927F274 DUP10 CREATE2 DUP6 PUSH5 0x736F6C6343 STOP ADDMOD BYTE STOP CALLER ","sourceMap":"97:200:1:-:0;;;184:111;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;246:12;;;;268:8;:20;279:9;268:8;:20;:::i;:::-;;184:111;;97:200;;14:127:3;75:10;70:3;66:20;63:1;56:31;106:4;103:1;96:15;130:4;127:1;120:15;146:996;235:6;243;296:2;284:9;275:7;271:23;267:32;264:52;;;312:1;309;302:12;264:52;335:16;;395:2;380:18;;374:25;335:16;;-1:-1:-1;;;;;;411:30:3;;408:50;;;454:1;451;444:12;408:50;477:22;;530:4;522:13;;518:27;-1:-1:-1;508:55:3;;559:1;556;549:12;508:55;586:9;;-1:-1:-1;;;;;607:30:3;;604:56;;;640:18;;:::i;:::-;689:2;683:9;781:2;743:17;;-1:-1:-1;;739:31:3;;;772:2;735:40;731:54;719:67;;-1:-1:-1;;;;;801:34:3;;837:22;;;798:62;795:88;;;863:18;;:::i;:::-;899:2;892:22;923;;;964:15;;;981:2;960:24;957:37;-1:-1:-1;954:57:3;;;1007:1;1004;997:12;954:57;1056:6;1051:2;1047;1043:11;1038:2;1030:6;1026:15;1020:43;1109:1;1104:2;1095:6;1087;1083:19;1079:28;1072:39;1130:6;1120:16;;;;;146:996;;;;;:::o;1147:380::-;1226:1;1222:12;;;;1269;;;1290:61;;1344:4;1336:6;1332:17;1322:27;;1290:61;1397:2;1389:6;1386:14;1366:18;1363:38;1360:161;;1443:10;1438:3;1434:20;1431:1;1424:31;1478:4;1475:1;1468:15;1506:4;1503:1;1496:15;1360:161;;1147:380;;;:::o;1658:518::-;1760:2;1755:3;1752:11;1749:421;;;1796:5;1793:1;1786:16;1840:4;1837:1;1827:18;1910:2;1898:10;1894:19;1891:1;1887:27;1881:4;1877:38;1946:4;1934:10;1931:20;1928:47;;;-1:-1:-1;1969:4:3;1928:47;2024:2;2019:3;2015:12;2012:1;2008:20;2002:4;1998:31;1988:41;;2079:81;2097:2;2090:5;2087:13;2079:81;;;2156:1;2142:16;;2123:1;2112:13;2079:81;;;2083:3;;1749:421;1658:518;;;:::o;2352:1299::-;2472:10;;-1:-1:-1;;;;;2494:30:3;;2491:56;;;2527:18;;:::i;:::-;2556:97;2646:6;2606:38;2638:4;2632:11;2606:38;:::i;:::-;2600:4;2556:97;:::i;:::-;2702:4;2733:2;2722:14;;2750:1;2745:649;;;;3438:1;3455:6;3452:89;;;-1:-1:-1;3507:19:3;;;3501:26;3452:89;-1:-1:-1;;2309:1:3;2305:11;;;2301:24;2297:29;2287:40;2333:1;2329:11;;;2284:57;3554:81;;2715:930;;2745:649;1605:1;1598:14;;;1642:4;1629:18;;-1:-1:-1;;2781:20:3;;;2899:222;2913:7;2910:1;2907:14;2899:222;;;2995:19;;;2989:26;2974:42;;3102:4;3087:20;;;;3055:1;3043:14;;;;2929:12;2899:222;;;2903:3;3149:6;3140:7;3137:19;3134:201;;;3210:19;;;3204:26;-1:-1:-1;;3293:1:3;3289:14;;;3305:3;3285:24;3281:37;3277:42;3262:58;3247:74;;3134:201;-1:-1:-1;;;;3381:1:3;3365:14;;;3361:22;3348:36;;-1:-1:-1;2352:1299:3:o;:::-;97:200:1;;;;;;;;;;;;"},"deployedBytecode":{"functionDebugData":{"@greeting_18":{"entryPoint":135,"id":18,"parameterSlots":0,"returnSlots":0},"@seed_20":{"entryPoint":null,"id":20,"parameterSlots":0,"returnSlots":0},"abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack_reversed":{"entryPoint":274,"id":null,"parameterSlots":2,"returnSlots":1},"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed":{"entryPoint":null,"id":null,"parameterSlots":2,"returnSlots":1},"extract_byte_array_length":{"entryPoint":327,"id":null,"parameterSlots":1,"returnSlots":1}},"generatedSources":[{"ast":{"nativeSrc":"0:1001:3","nodeType":"YulBlock","src":"0:1001:3","statements":[{"nativeSrc":"6:3:3","nodeType":"YulBlock","src":"6:3:3","statements":[]},{"body":{"nativeSrc":"115:76:3","nodeType":"YulBlock","src":"115:76:3","statements":[{"nativeSrc":"125:26:3","nodeType":"YulAssignment","src":"125:26:3","value":{"arguments":[{"name":"headStart","nativeSrc":"137:9:3","nodeType":"YulIdentifier","src":"137:9:3"},{"kind":"number","nativeSrc":"148:2:3","nodeType":"YulLiteral","src":"148:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"133:3:3","nodeType":"YulIdentifier","src":"133:3:3"},"nativeSrc":"133:18:3","nodeType":"YulFunctionCall","src":"133:18:3"},"variableNames":[{"name":"tail","nativeSrc":"125:4:3","nodeType":"YulIdentifier","src":"125:4:3"}]},{"expression":{"arguments":[{"name":"headStart","nativeSrc":"167:9:3","nodeType":"YulIdentifier","src":"167:9:3"},{"name":"value0","nativeSrc":"178:6:3","nodeType":"YulIdentifier","src":"178:6:3"}],"functionName":{"name":"mstore","nativeSrc":"160:6:3","nodeType":"YulIdentifier","src":"160:6:3"},"nativeSrc":"160:25:3","nodeType":"YulFunctionCall","src":"160:25:3"},"nativeSrc":"160:25:3","nodeType":"YulExpressionStatement","src":"160:25:3"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nativeSrc":"14:177:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nativeSrc":"84:9:3","nodeType":"YulTypedName","src":"84:9:3","type":""},{"name":"value0","nativeSrc":"95:6:3","nodeType":"YulTypedName","src":"95:6:3","type":""}],"returnVariables":[{"name":"tail","nativeSrc":"106:4:3","nodeType":"YulTypedName","src":"106:4:3","type":""}],"src":"14:177:3"},{"body":{"nativeSrc":"317:297:3","nodeType":"YulBlock","src":"317:297:3","statements":[{"expression":{"arguments":[{"name":"headStart","nativeSrc":"334:9:3","nodeType":"YulIdentifier","src":"334:9:3"},{"kind":"number","nativeSrc":"345:2:3","nodeType":"YulLiteral","src":"345:2:3","type":"","value":"32"}],"functionName":{"name":"mstore","nativeSrc":"327:6:3","nodeType":"YulIdentifier","src":"327:6:3"},"nativeSrc":"327:21:3","nodeType":"YulFunctionCall","src":"327:21:3"},"nativeSrc":"327:21:3","nodeType":"YulExpressionStatement","src":"327:21:3"},{"nativeSrc":"357:27:3","nodeType":"YulVariableDeclaration","src":"357:27:3","value":{"arguments":[{"name":"value0","nativeSrc":"377:6:3","nodeType":"YulIdentifier","src":"377:6:3"}],"functionName":{"name":"mload","nativeSrc":"371:5:3","nodeType":"YulIdentifier","src":"371:5:3"},"nativeSrc":"371:13:3","nodeType":"YulFunctionCall","src":"371:13:3"},"variables":[{"name":"length","nativeSrc":"361:6:3","nodeType":"YulTypedName","src":"361:6:3","type":""}]},{"expression":{"arguments":[{"arguments":[{"name":"headStart","nativeSrc":"404:9:3","nodeType":"YulIdentifier","src":"404:9:3"},{"kind":"number","nativeSrc":"415:2:3","nodeType":"YulLiteral","src":"415:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"400:3:3","nodeType":"YulIdentifier","src":"400:3:3"},"nativeSrc":"400:18:3","nodeType":"YulFunctionCall","src":"400:18:3"},{"name":"length","nativeSrc":"420:6:3","nodeType":"YulIdentifier","src":"420:6:3"}],"functionName":{"name":"mstore","nativeSrc":"393:6:3","nodeType":"YulIdentifier","src":"393:6:3"},"nativeSrc":"393:34:3","nodeType":"YulFunctionCall","src":"393:34:3"},"nativeSrc":"393:34:3","nodeType":"YulExpressionStatement","src":"393:34:3"},{"expression":{"arguments":[{"arguments":[{"name":"headStart","nativeSrc":"446:9:3","nodeType":"YulIdentifier","src":"446:9:3"},{"kind":"number","nativeSrc":"457:2:3","nodeType":"YulLiteral","src":"457:2:3","type":"","value":"64"}],"functionName":{"name":"add","nativeSrc":"442:3:3","nodeType":"YulIdentifier","src":"442:3:3"},"nativeSrc":"442:18:3","nodeType":"YulFunctionCall","src":"442:18:3"},{"arguments":[{"name":"value0","nativeSrc":"466:6:3","nodeType":"YulIdentifier","src":"466:6:3"},{"kind":"number","nativeSrc":"474:2:3","nodeType":"YulLiteral","src":"474:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"462:3:3","nodeType":"YulIdentifier","src":"462:3:3"},"nativeSrc":"462:15:3","nodeType":"YulFunctionCall","src":"462:15:3"},{"name":"length","nativeSrc":"479:6:3","nodeType":"YulIdentifier","src":"479:6:3"}],"functionName":{"name":"mcopy","nativeSrc":"436:5:3","nodeType":"YulIdentifier","src":"436:5:3"},"nativeSrc":"436:50:3","nodeType":"YulFunctionCall","src":"436:50:3"},"nativeSrc":"436:50:3","nodeType":"YulExpressionStatement","src":"436:50:3"},{"expression":{"arguments":[{"arguments":[{"arguments":[{"name":"headStart","nativeSrc":"510:9:3","nodeType":"YulIdentifier","src":"510:9:3"},{"name":"length","nativeSrc":"521:6:3","nodeType":"YulIdentifier","src":"521:6:3"}],"functionName":{"name":"add","nativeSrc":"506:3:3","nodeType":"YulIdentifier","src":"506:3:3"},"nativeSrc":"506:22:3","nodeType":"YulFunctionCall","src":"506:22:3"},{"kind":"number","nativeSrc":"530:2:3","nodeType":"YulLiteral","src":"530:2:3","type":"","value":"64"}],"functionName":{"name":"add","nativeSrc":"502:3:3","nodeType":"YulIdentifier","src":"502:3:3"},"nativeSrc":"502:31:3","nodeType":"YulFunctionCall","src":"502:31:3"},{"kind":"number","nativeSrc":"535:1:3","nodeType":"YulLiteral","src":"535:1:3","type":"","value":"0"}],"functionName":{"name":"mstore","nativeSrc":"495:6:3","nodeType":"YulIdentifier","src":"495:6:3"},"nativeSrc":"495:42:3","nodeType":"YulFunctionCall","src":"495:42:3"},"nativeSrc":"495:42:3","nodeType":"YulExpressionStatement","src":"495:42:3"},{"nativeSrc":"546:62:3","nodeType":"YulAssignment","src":"546:62:3","value":{"arguments":[{"arguments":[{"name":"headStart","nativeSrc":"562:9:3","nodeType":"YulIdentifier","src":"562:9:3"},{"arguments":[{"arguments":[{"name":"length","nativeSrc":"581:6:3","nodeType":"YulIdentifier","src":"581:6:3"},{"kind":"number","nativeSrc":"589:2:3","nodeType":"YulLiteral","src":"589:2:3","type":"","value":"31"}],"functionName":{"name":"add","nativeSrc":"577:3:3","nodeType":"YulIdentifier","src":"577:3:3"},"nativeSrc":"577:15:3","nodeType":"YulFunctionCall","src":"577:15:3"},{"arguments":[{"kind":"number","nativeSrc":"598:2:3","nodeType":"YulLiteral","src":"598:2:3","type":"","value":"31"}],"functionName":{"name":"not","nativeSrc":"594:3:3","nodeType":"YulIdentifier","src":"594:3:3"},"nativeSrc":"594:7:3","nodeType":"YulFunctionCall","src":"594:7:3"}],"functionName":{"name":"and","nativeSrc":"573:3:3","nodeType":"YulIdentifier","src":"573:3:3"},"nativeSrc":"573:29:3","nodeType":"YulFunctionCall","src":"573:29:3"}],"functionName":{"name":"add","nativeSrc":"558:3:3","nodeType":"YulIdentifier","src":"558:3:3"},"nativeSrc":"558:45:3","nodeType":"YulFunctionCall","src":"558:45:3"},{"kind":"number","nativeSrc":"605:2:3","nodeType":"YulLiteral","src":"605:2:3","type":"","value":"64"}],"functionName":{"name":"add","nativeSrc":"554:3:3","nodeType":"YulIdentifier","src":"554:3:3"},"nativeSrc":"554:54:3","nodeType":"YulFunctionCall","src":"554:54:3"},"variableNames":[{"name":"tail","nativeSrc":"546:4:3","nodeType":"YulIdentifier","src":"546:4:3"}]}]},"name":"abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack_reversed","nativeSrc":"196:418:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nativeSrc":"286:9:3","nodeType":"YulTypedName","src":"286:9:3","type":""},{"name":"value0","nativeSrc":"297:6:3","nodeType":"YulTypedName","src":"297:6:3","type":""}],"returnVariables":[{"name":"tail","nativeSrc":"308:4:3","nodeType":"YulTypedName","src":"308:4:3","type":""}],"src":"196:418:3"},{"body":{"nativeSrc":"674:325:3","nodeType":"YulBlock","src":"674:325:3","statements":[{"nativeSrc":"684:22:3","nodeType":"YulAssignment","src":"684:22:3","value":{"arguments":[{"kind":"number","nativeSrc":"698:1:3","nodeType":"YulLiteral","src":"698:1:3","type":"","value":"1"},{"name":"data","nativeSrc":"701:4:3","nodeType":"YulIdentifier","src":"701:4:3"}],"functionName":{"name":"shr","nativeSrc":"694:3:3","nodeType":"YulIdentifier","src":"694:3:3"},"nativeSrc":"694:12:3","nodeType":"YulFunctionCall","src":"694:12:3"},"variableNames":[{"name":"length","nativeSrc":"684:6:3","nodeType":"YulIdentifier","src":"684:6:3"}]},{"nativeSrc":"715:38:3","nodeType":"YulVariableDeclaration","src":"715:38:3","value":{"arguments":[{"name":"data","nativeSrc":"745:4:3","nodeType":"YulIdentifier","src":"745:4:3"},{"kind":"number","nativeSrc":"751:1:3","nodeType":"YulLiteral","src":"751:1:3","type":"","value":"1"}],"functionName":{"name":"and","nativeSrc":"741:3:3","nodeType":"YulIdentifier","src":"741:3:3"},"nativeSrc":"741:12:3","nodeType":"YulFunctionCall","src":"741:12:3"},"variables":[{"name":"outOfPlaceEncoding","nativeSrc":"719:18:3","nodeType":"YulTypedName","src":"719:18:3","type":""}]},{"body":{"nativeSrc":"792:31:3","nodeType":"YulBlock","src":"792:31:3","statements":[{"nativeSrc":"794:27:3","nodeType":"YulAssignment","src":"794:27:3","value":{"arguments":[{"name":"length","nativeSrc":"808:6:3","nodeType":"YulIdentifier","src":"808:6:3"},{"kind":"number","nativeSrc":"816:4:3","nodeType":"YulLiteral","src":"816:4:3","type":"","value":"0x7f"}],"functionName":{"name":"and","nativeSrc":"804:3:3","nodeType":"YulIdentifier","src":"804:3:3"},"nativeSrc":"804:17:3","nodeType":"YulFunctionCall","src":"804:17:3"},"variableNames":[{"name":"length","nativeSrc":"794:6:3","nodeType":"YulIdentifier","src":"794:6:3"}]}]},"condition":{"arguments":[{"name":"outOfPlaceEncoding","nativeSrc":"772:18:3","nodeType":"YulIdentifier","src":"772:18:3"}],"functionName":{"name":"iszero","nativeSrc":"765:6:3","nodeType":"YulIdentifier","src":"765:6:3"},"nativeSrc":"765:26:3","nodeType":"YulFunctionCall","src":"765:26:3"},"nativeSrc":"762:61:3","nodeType":"YulIf","src":"762:61:3"},{"body":{"nativeSrc":"882:111:3","nodeType":"YulBlock","src":"882:111:3","statements":[{"expression":{"arguments":[{"kind":"number","nativeSrc":"903:1:3","nodeType":"YulLiteral","src":"903:1:3","type":"","value":"0"},{"arguments":[{"kind":"number","nativeSrc":"910:3:3","nodeType":"YulLiteral","src":"910:3:3","type":"","value":"224"},{"kind":"number","nativeSrc":"915:10:3","nodeType":"YulLiteral","src":"915:10:3","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nativeSrc":"906:3:3","nodeType":"YulIdentifier","src":"906:3:3"},"nativeSrc":"906:20:3","nodeType":"YulFunctionCall","src":"906:20:3"}],"functionName":{"name":"mstore","nativeSrc":"896:6:3","nodeType":"YulIdentifier","src":"896:6:3"},"nativeSrc":"896:31:3","nodeType":"YulFunctionCall","src":"896:31:3"},"nativeSrc":"896:31:3","nodeType":"YulExpressionStatement","src":"896:31:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"947:1:3","nodeType":"YulLiteral","src":"947:1:3","type":"","value":"4"},{"kind":"number","nativeSrc":"950:4:3","nodeType":"YulLiteral","src":"950:4:3","type":"","value":"0x22"}],"functionName":{"name":"mstore","nativeSrc":"940:6:3","nodeType":"YulIdentifier","src":"940:6:3"},"nativeSrc":"940:15:3","nodeType":"YulFunctionCall","src":"940:15:3"},"nativeSrc":"940:15:3","nodeType":"YulExpressionStatement","src":"940:15:3"},{"expression":{"arguments":[{"kind":"number","nativeSrc":"975:1:3","nodeType":"YulLiteral","src":"975:1:3","type":"","value":"0"},{"kind":"number","nativeSrc":"978:4:3","nodeType":"YulLiteral","src":"978:4:3","type":"","value":"0x24"}],"functionName":{"name":"revert","nativeSrc":"968:6:3","nodeType":"YulIdentifier","src":"968:6:3"},"nativeSrc":"968:15:3","nodeType":"YulFunctionCall","src":"968:15:3"},"nativeSrc":"968:15:3","nodeType":"YulExpressionStatement","src":"968:15:3"}]},"condition":{"arguments":[{"name":"outOfPlaceEncoding","nativeSrc":"838:18:3","nodeType":"YulIdentifier","src":"838:18:3"},{"arguments":[{"name":"length","nativeSrc":"861:6:3","nodeType":"YulIdentifier","src":"861:6:3"},{"kind":"number","nativeSrc":"869:2:3","nodeType":"YulLiteral","src":"869:2:3","type":"","value":"32"}],"functionName":{"name":"lt","nativeSrc":"858:2:3","nodeType":"YulIdentifier","src":"858:2:3"},"nativeSrc":"858:14:3","nodeType":"YulFunctionCall","src":"858:14:3"}],"functionName":{"name":"eq","nativeSrc":"835:2:3","nodeType":"YulIdentifier","src":"835:2:3"},"nativeSrc":"835:38:3","nodeType":"YulFunctionCall","src":"835:38:3"},"nativeSrc":"832:161:3","nodeType":"YulIf","src":"832:161:3"}]},"name":"extract_byte_array_length","nativeSrc":"619:380:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"data","nativeSrc":"654:4:3","nodeType":"YulTypedName","src":"654:4:3","type":""}],"returnVariables":[{"name":"length","nativeSrc":"663:6:3","nodeType":"YulTypedName","src":"663:6:3","type":""}],"src":"619:380:3"}]},"contents":"{\n { }\n function abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed(headStart, value0) -> tail\n {\n tail := add(headStart, 32)\n mstore(headStart, value0)\n }\n function abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack_reversed(headStart, value0) -> tail\n {\n mstore(headStart, 32)\n let length := mload(value0)\n mstore(add(headStart, 32), length)\n mcopy(add(headStart, 64), add(value0, 32), length)\n mstore(add(add(headStart, length), 64), 0)\n tail := add(add(headStart, and(add(length, 31), not(31))), 64)\n }\n function extract_byte_array_length(data) -> length\n {\n length := shr(1, data)\n let outOfPlaceEncoding := and(data, 1)\n if iszero(outOfPlaceEncoding) { length := and(length, 0x7f) }\n if eq(outOfPlaceEncoding, lt(length, 32))\n {\n mstore(0, shl(224, 0x4e487b71))\n mstore(4, 0x22)\n revert(0, 0x24)\n }\n }\n}","id":3,"language":"Yul","name":"#utility.yul"}],"immutableReferences":{"20":[{"length":32,"start":61}]},"linkReferences":{},"object":"608060405234801561000f575f80fd5b5060043610610034575f3560e01c80637d94792a14610038578063ef690cc014610072575b5f80fd5b61005f7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b61007a610087565b6040516100699190610112565b5f805461009390610147565b80601f01602080910402602001604051908101604052809291908181526020018280546100bf90610147565b801561010a5780601f106100e15761010080835404028352916020019161010a565b820191905f5260205f20905b8154815290600101906020018083116100ed57829003601f168201915b505050505081565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b600181811c9082168061015b57607f821691505b60208210810361017957634e487b7160e01b5f52602260045260245ffd5b5091905056fea26469706673582212207b2a6c1870d9c4c876c7d54da986f101f3e8b48127c03cf1800927f27489f58564736f6c634300081a0033","opcodes":"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0xF JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH2 0x34 JUMPI PUSH0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x7D94792A EQ PUSH2 0x38 JUMPI DUP1 PUSH4 0xEF690CC0 EQ PUSH2 0x72 JUMPI JUMPDEST PUSH0 DUP1 REVERT JUMPDEST PUSH2 0x5F PUSH32 0x0 DUP2 JUMP JUMPDEST PUSH1 0x40 MLOAD SWAP1 DUP2 MSTORE PUSH1 0x20 ADD JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST PUSH2 0x7A PUSH2 0x87 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x69 SWAP2 SWAP1 PUSH2 0x112 JUMP JUMPDEST PUSH0 DUP1 SLOAD PUSH2 0x93 SWAP1 PUSH2 0x147 JUMP JUMPDEST DUP1 PUSH1 0x1F ADD PUSH1 0x20 DUP1 SWAP2 DIV MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP3 DUP1 SLOAD PUSH2 0xBF SWAP1 PUSH2 0x147 JUMP JUMPDEST DUP1 ISZERO PUSH2 0x10A JUMPI DUP1 PUSH1 0x1F LT PUSH2 0xE1 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x10A JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH0 MSTORE PUSH1 0x20 PUSH0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0xED JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP DUP2 JUMP JUMPDEST PUSH1 0x20 DUP2 MSTORE PUSH0 DUP3 MLOAD DUP1 PUSH1 0x20 DUP5 ADD MSTORE DUP1 PUSH1 0x20 DUP6 ADD PUSH1 0x40 DUP6 ADD MCOPY PUSH0 PUSH1 0x40 DUP3 DUP6 ADD ADD MSTORE PUSH1 0x40 PUSH1 0x1F NOT PUSH1 0x1F DUP4 ADD AND DUP5 ADD ADD SWAP2 POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x1 DUP2 DUP2 SHR SWAP1 DUP3 AND DUP1 PUSH2 0x15B JUMPI PUSH1 0x7F DUP3 AND SWAP2 POP JUMPDEST PUSH1 0x20 DUP3 LT DUP2 SUB PUSH2 0x179 JUMPI PUSH4 0x4E487B71 PUSH1 0xE0 SHL PUSH0 MSTORE PUSH1 0x22 PUSH1 0x4 MSTORE PUSH1 0x24 PUSH0 REVERT JUMPDEST POP SWAP2 SWAP1 POP JUMP INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 PUSH28 0x2A6C1870D9C4C876C7D54DA986F101F3E8B48127C03CF1800927F274 DUP10 CREATE2 DUP6 PUSH5 0x736F6C6343 STOP ADDMOD BYTE STOP CALLER ","sourceMap":"97:200:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;148:29;;;;;;;;160:25:3;;;148:2;133:18;148:29:1;;;;;;;;120:22;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;196:418:3:-;345:2;334:9;327:21;308:4;377:6;371:13;420:6;415:2;404:9;400:18;393:34;479:6;474:2;466:6;462:15;457:2;446:9;442:18;436:50;535:1;530:2;521:6;510:9;506:22;502:31;495:42;605:2;598;594:7;589:2;581:6;577:15;573:29;562:9;558:45;554:54;546:62;;;196:418;;;;:::o;619:380::-;698:1;694:12;;;;741;;;762:61;;816:4;808:6;804:17;794:27;;762:61;869:2;861:6;858:14;838:18;835:38;832:161;;915:10;910:3;906:20;903:1;896:31;950:4;947:1;940:15;978:4;975:1;968:15;832:161;;619:380;;;:::o"},"methodIdentifiers":{"greeting()":"ef690cc0","seed()":"7d94792a"}},"metadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"seed_\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"greeting_\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"greeting\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"seed\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/Greeter.sol\":\"Greeter\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/Counter.sol\":{\"keccak256\":\"0xc2db2c51f9be330b16e281a99dfe043e18dc2a60c40eea193802acb2aa612584\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://df36ff1caedb811d7f0b735d3a2739c75e7bba3361b2be8ae9b92320aac69395\",\"dweb:/ipfs/QmQz644QUD1bWRX8QcEa4CcQknRpLFrLZiJNXT2rZ9PcSu\"]},\"contracts/Greeter.sol\":{\"keccak256\":\"0x1758099b79a52734a12d37366662fc7fdacf74767ca04a0b741976e2bcc754f0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://f5b995a3c5ae7000f3025326c412a1a08bfa89aea899d84a0751b8a84b16aefd\",\"dweb:/ipfs/QmbfaqxQmtcZVFXKEv7RDjnxcGhJ1cLHLhjemCKrVEjPZk\"]}},\"version\":1}"}},"contracts/Unrelated.sol":{"Unrelated":{"abi":[{"inputs":[],"name":"ping","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}],"evm":{"bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"6080604052348015600e575f80fd5b50607480601a5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c80635c36b18614602a575b5f80fd5b600160405190815260200160405180910390f3fea26469706673582212208e5dc637be622071539790de0be9e65e48e71992aefeb08ab22facdca752801664736f6c634300081a0033","opcodes":"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x74 DUP1 PUSH1 0x1A PUSH0 CODECOPY PUSH0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x26 JUMPI PUSH0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x5C36B186 EQ PUSH1 0x2A JUMPI JUMPDEST PUSH0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0x40 MLOAD SWAP1 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DUP15 TSTORE 0xC6 CALLDATACOPY 0xBE PUSH3 0x207153 SWAP8 SWAP1 0xDE SIGNEXTEND 0xE9 0xE6 MCOPY BASEFEE 0xE7 NOT SWAP3 0xAE INVALID 0xB0 DUP11 0xB2 0x2F 0xAC 0xDC 0xA7 MSTORE DUP1 AND PUSH5 0x736F6C6343 STOP ADDMOD BYTE STOP CALLER ","sourceMap":"57:100:2:-:0;;;;;;;;;;;;;;;;;;;"},"deployedBytecode":{"functionDebugData":{"@ping_47":{"entryPoint":null,"id":47,"parameterSlots":0,"returnSlots":1},"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed":{"entryPoint":null,"id":null,"parameterSlots":2,"returnSlots":1}},"generatedSources":[{"ast":{"nativeSrc":"0:193:3","nodeType":"YulBlock","src":"0:193:3","statements":[{"nativeSrc":"6:3:3","nodeType":"YulBlock","src":"6:3:3","statements":[]},{"body":{"nativeSrc":"115:76:3","nodeType":"YulBlock","src":"115:76:3","statements":[{"nativeSrc":"125:26:3","nodeType":"YulAssignment","src":"125:26:3","value":{"arguments":[{"name":"headStart","nativeSrc":"137:9:3","nodeType":"YulIdentifier","src":"137:9:3"},{"kind":"number","nativeSrc":"148:2:3","nodeType":"YulLiteral","src":"148:2:3","type":"","value":"32"}],"functionName":{"name":"add","nativeSrc":"133:3:3","nodeType":"YulIdentifier","src":"133:3:3"},"nativeSrc":"133:18:3","nodeType":"YulFunctionCall","src":"133:18:3"},"variableNames":[{"name":"tail","nativeSrc":"125:4:3","nodeType":"YulIdentifier","src":"125:4:3"}]},{"expression":{"arguments":[{"name":"headStart","nativeSrc":"167:9:3","nodeType":"YulIdentifier","src":"167:9:3"},{"name":"value0","nativeSrc":"178:6:3","nodeType":"YulIdentifier","src":"178:6:3"}],"functionName":{"name":"mstore","nativeSrc":"160:6:3","nodeType":"YulIdentifier","src":"160:6:3"},"nativeSrc":"160:25:3","nodeType":"YulFunctionCall","src":"160:25:3"},"nativeSrc":"160:25:3","nodeType":"YulExpressionStatement","src":"160:25:3"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nativeSrc":"14:177:3","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nativeSrc":"84:9:3","nodeType":"YulTypedName","src":"84:9:3","type":""},{"name":"value0","nativeSrc":"95:6:3","nodeType":"YulTypedName","src":"95:6:3","type":""}],"returnVariables":[{"name":"tail","nativeSrc":"106:4:3","nodeType":"YulTypedName","src":"106:4:3","type":""}],"src":"14:177:3"}]},"contents":"{\n { }\n function abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed(headStart, value0) -> tail\n {\n tail := add(headStart, 32)\n mstore(headStart, value0)\n }\n}","id":3,"language":"Yul","name":"#utility.yul"}],"immutableReferences":{},"linkReferences":{},"object":"6080604052348015600e575f80fd5b50600436106026575f3560e01c80635c36b18614602a575b5f80fd5b600160405190815260200160405180910390f3fea26469706673582212208e5dc637be622071539790de0be9e65e48e71992aefeb08ab22facdca752801664736f6c634300081a0033","opcodes":"PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xE JUMPI PUSH0 DUP1 REVERT JUMPDEST POP PUSH1 0x4 CALLDATASIZE LT PUSH1 0x26 JUMPI PUSH0 CALLDATALOAD PUSH1 0xE0 SHR DUP1 PUSH4 0x5C36B186 EQ PUSH1 0x2A JUMPI JUMPDEST PUSH0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0x40 MLOAD SWAP1 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN INVALID LOG2 PUSH5 0x6970667358 0x22 SLT KECCAK256 DUP15 TSTORE 0xC6 CALLDATACOPY 0xBE PUSH3 0x207153 SWAP8 SWAP1 0xDE SIGNEXTEND 0xE9 0xE6 MCOPY BASEFEE 0xE7 NOT SWAP3 0xAE INVALID 0xB0 DUP11 0xB2 0x2F 0xAC 0xDC 0xA7 MSTORE DUP1 AND PUSH5 0x736F6C6343 STOP ADDMOD BYTE STOP CALLER ","sourceMap":"57:100:2:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;82:73;147:1;82:73;;160:25:3;;;148:2;133:18;82:73:2;;;;;;"},"methodIdentifiers":{"ping()":"5c36b186"}},"metadata":"{\"compiler\":{\"version\":\"0.8.26+commit.8a97fa7a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"ping\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/Unrelated.sol\":\"Unrelated\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/Unrelated.sol\":{\"keccak256\":\"0x5cc667db0b908cd4179177879592dc9346bea9887b091554a9d9d82fe9a759b6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://04e1c4dce1fb8a75bce1b5b0e1ef9457e864dbe2ed7bb23df7603e151483c549\",\"dweb:/ipfs/QmRwLY8VjfogJj19xdFRdmZYLC5rqdotUnSjxfnPQ6MC8Y\"]}},\"version\":1}"}}}}} \ No newline at end of file diff --git a/tests/fixtures/verify/hardhat/artifacts/contracts/Greeter.sol/Greeter.dbg.json b/tests/fixtures/verify/hardhat/artifacts/contracts/Greeter.sol/Greeter.dbg.json new file mode 100644 index 0000000..65028ca --- /dev/null +++ b/tests/fixtures/verify/hardhat/artifacts/contracts/Greeter.sol/Greeter.dbg.json @@ -0,0 +1,4 @@ +{ + "_format": "hh-sol-dbg-1", + "buildInfo": "../../build-info/dd99b6a8e0696988f72849c906423ccc.json" +} diff --git a/tests/fixtures/verify/hardhat/artifacts/contracts/Greeter.sol/Greeter.json b/tests/fixtures/verify/hardhat/artifacts/contracts/Greeter.sol/Greeter.json new file mode 100644 index 0000000..4f9968c --- /dev/null +++ b/tests/fixtures/verify/hardhat/artifacts/contracts/Greeter.sol/Greeter.json @@ -0,0 +1,53 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "Greeter", + "sourceName": "contracts/Greeter.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "seed_", + "type": "uint256" + }, + { + "internalType": "string", + "name": "greeting_", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "greeting", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "seed", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60a060405234801561000f575f80fd5b5060405161041f38038061041f83398101604081905261002e9161005a565b60808290525f61003e8282610199565b505050610253565b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561006b575f80fd5b825160208401519092506001600160401b03811115610088575f80fd5b8301601f81018513610098575f80fd5b80516001600160401b038111156100b1576100b1610046565b604051601f8201601f19908116603f011681016001600160401b03811182821017156100df576100df610046565b6040528181528282016020018710156100f6575f80fd5b8160208401602083015e5f602083830101528093505050509250929050565b600181811c9082168061012957607f821691505b60208210810361014757634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561019457805f5260205f20601f840160051c810160208510156101725750805b601f840160051c820191505b81811015610191575f815560010161017e565b50505b505050565b81516001600160401b038111156101b2576101b2610046565b6101c6816101c08454610115565b8461014d565b6020601f8211600181146101f8575f83156101e15750848201515b5f19600385901b1c1916600184901b178455610191565b5f84815260208120601f198516915b828110156102275787850151825560209485019460019092019101610207565b508482101561024457868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b6080516101b561026a5f395f603d01526101b55ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80637d94792a14610038578063ef690cc014610072575b5f80fd5b61005f7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b61007a610087565b6040516100699190610112565b5f805461009390610147565b80601f01602080910402602001604051908101604052809291908181526020018280546100bf90610147565b801561010a5780601f106100e15761010080835404028352916020019161010a565b820191905f5260205f20905b8154815290600101906020018083116100ed57829003601f168201915b505050505081565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b600181811c9082168061015b57607f821691505b60208210810361017957634e487b7160e01b5f52602260045260245ffd5b5091905056fea26469706673582212207b2a6c1870d9c4c876c7d54da986f101f3e8b48127c03cf1800927f27489f58564736f6c634300081a0033", + "deployedBytecode": "0x608060405234801561000f575f80fd5b5060043610610034575f3560e01c80637d94792a14610038578063ef690cc014610072575b5f80fd5b61005f7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b61007a610087565b6040516100699190610112565b5f805461009390610147565b80601f01602080910402602001604051908101604052809291908181526020018280546100bf90610147565b801561010a5780601f106100e15761010080835404028352916020019161010a565b820191905f5260205f20905b8154815290600101906020018083116100ed57829003601f168201915b505050505081565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b600181811c9082168061015b57607f821691505b60208210810361017957634e487b7160e01b5f52602260045260245ffd5b5091905056fea26469706673582212207b2a6c1870d9c4c876c7d54da986f101f3e8b48127c03cf1800927f27489f58564736f6c634300081a0033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/tests/fixtures/verify/hardhat/hardhat-verify-2.1.3-requests.jsonl b/tests/fixtures/verify/hardhat/hardhat-verify-2.1.3-requests.jsonl new file mode 100644 index 0000000..a765fdc --- /dev/null +++ b/tests/fixtures/verify/hardhat/hardhat-verify-2.1.3-requests.jsonl @@ -0,0 +1,3 @@ +{"method": "GET", "path": "/api?apikey=citrate&module=contract&action=getsourcecode&address=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", "ctype": null, "body": ""} +{"method": "POST", "path": "/api", "ctype": "application/x-www-form-urlencoded", "body": "apikey=citrate&module=contract&action=verifysourcecode&contractaddress=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512&sourceCode=%7B%22language%22%3A%22Solidity%22%2C%22sources%22%3A%7B%22contracts%2FCounter.sol%22%3A%7B%22content%22%3A%22%2F%2F+SPDX-License-Identifier%3A+MIT%5Cnpragma+solidity+0.8.26%3B%5Cn%5Cncontract+Counter+%7B%5Cn++++uint256+public+number%3B%5Cn%5Cn++++function+increment%28%29+external+%7B%5Cn++++++++number+%2B%3D+1%3B%5Cn++++%7D%5Cn%7D%5Cn%22%7D%2C%22contracts%2FGreeter.sol%22%3A%7B%22content%22%3A%22%2F%2F+SPDX-License-Identifier%3A+MIT%5Cnpragma+solidity+0.8.26%3B%5Cn%5Cnimport+%7BCounter%7D+from+%5C%22.%2FCounter.sol%5C%22%3B%5Cn%5Cncontract+Greeter+%7B%5Cn++++string+public+greeting%3B%5Cn++++uint256+public+immutable+seed%3B%5Cn%5Cn++++constructor%28uint256+seed_%2C+string+memory+greeting_%29+%7B%5Cn++++++++seed+%3D+seed_%3B%5Cn++++++++greeting+%3D+greeting_%3B%5Cn++++%7D%5Cn%7D%5Cn%22%7D%7D%2C%22settings%22%3A%7B%22optimizer%22%3A%7B%22enabled%22%3Atrue%2C%22runs%22%3A200%7D%2C%22evmVersion%22%3A%22cancun%22%2C%22outputSelection%22%3A%7B%22*%22%3A%7B%22*%22%3A%5B%22abi%22%2C%22evm.bytecode%22%2C%22evm.deployedBytecode%22%2C%22evm.methodIdentifiers%22%2C%22metadata%22%5D%2C%22%22%3A%5B%22ast%22%5D%7D%7D%2C%22libraries%22%3A%7B%7D%7D%7D&codeformat=solidity-standard-json-input&contractname=contracts%2FGreeter.sol%3AGreeter&compilerversion=v0.8.26%2Bcommit.8a97fa7a&constructorArguements=00000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000"} +{"method": "GET", "path": "/api?apikey=citrate&module=contract&action=checkverifystatus&guid=abc-guid", "ctype": null, "body": ""} diff --git a/tests/test_verify.py b/tests/test_verify.py new file mode 100644 index 0000000..5485dd9 --- /dev/null +++ b/tests/test_verify.py @@ -0,0 +1,674 @@ +"""verify_contract / deploy_and_verify against the explorer's Etherscan-compatible API. + +The HTTP server here is a test-only recorder (Rule 1 allows test fakes). The request shapes it +checks come from real tools: ``tests/fixtures/verify/*/...-requests.jsonl`` are the requests +forge 1.5.1 and hardhat-verify 2.1.3 sent to a recording server, and the build-info files were +written by those tools' compilers (see the README there). +""" +from __future__ import annotations + +import json +import socket +import threading +import time +import traceback +from collections.abc import Iterator +from dataclasses import dataclass +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from typing import Any +from urllib.parse import parse_qsl, urlsplit + +import pytest +from eth_abi import encode + +import citrate_sdk +from citrate_sdk import CitrateClient +from citrate_sdk.errors import CitrateError +from citrate_sdk.verify import ( + DEFAULT_EXPLORER_URL, + BuildInfoError, + ExplorerResponseError, + VerificationFailedError, + VerificationRateLimitedError, + VerificationRejectedError, + VerificationTimeoutError, + build_verify_source_code_form, + check_verify_status, + deploy_and_verify, + load_build_info, + parse_build_info, + prepare_standard_json, + verify_contract, + wait_for_verification, +) + +FX = Path(__file__).parent / "fixtures" / "verify" +FORGE_BI = FX / "forge" / "build-info" / "970692f6b0ae96cc.json" +HH_BI = FX / "hardhat" / "artifacts" / "build-info" / "dd99b6a8e0696988f72849c906423ccc.json" +HH_ARTIFACT = FX / "hardhat" / "artifacts" / "contracts" / "Greeter.sol" / "Greeter.json" +FORGE_ADDR = "0x5fbdb2315678afecb367f032d99f2f9fc9a2f74b" +HH_ADDR = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" +GUID = "5d1d6cbe-5f4d-4bd4-a1c9-2c1a1c5b1e01" + +OK_GUID = json.dumps({"status": "1", "message": "OK", "result": GUID}) +PENDING = json.dumps({"status": "0", "message": "NOTOK", "result": "Pending in queue"}) +PASS = json.dumps({"status": "1", "message": "OK", "result": "Pass - Verified"}) +FAIL = json.dumps({"status": "0", "message": "NOTOK", "result": "Fail - Unable to verify"}) +LIMITED = json.dumps({"status": "0", "message": "NOTOK", "result": "Max rate limit reached"}) +FAST: dict[str, Any] = {"poll_interval": 0.005, "timeout": 5.0, "rate_limit_backoff": 0.005, "rate_limit_retries": 2} + + +def _json(p: Path) -> Any: + return json.loads(p.read_text(encoding="utf-8")) + + +def forge_bi() -> dict[str, Any]: + bi: dict[str, Any] = _json(FORGE_BI) + return bi + + +def recorded(name: str) -> list[tuple[str, str, list[tuple[str, str]]]]: + out = [] + for line in (FX / name).read_text(encoding="utf-8").splitlines(): + v = json.loads(line) + out.append((v["method"], v["path"], parse_qsl(v["body"], keep_blank_values=True))) + return out + + +def normalized(fields: list[tuple[str, str]]) -> dict[str, Any]: + return {k: (json.loads(v) if k == "sourceCode" else v) for k, v in fields} + + +def hardhat_post() -> list[tuple[str, str]]: + return next(f for m, _, f in recorded("hardhat/hardhat-verify-2.1.3-requests.jsonl") if m == "POST") + + +# --------------------------------------------------------------------------- +# test-only HTTP recorder +# --------------------------------------------------------------------------- + + +#: Script status RESET: drop the connection without answering. +RESET = 0 + + +@dataclass +class Seen: + method: str + path: str + content_type: str | None + body: str + #: time.monotonic() when the request arrived. + at: float = 0.0 + + def query(self) -> dict[str, str]: + return dict(parse_qsl(urlsplit(self.path).query, keep_blank_values=True)) + + def form(self) -> dict[str, str]: + return dict(parse_qsl(self.body, keep_blank_values=True)) + + +class Recorder: + def __init__(self, script: list[tuple[int, str]]): + self.seen: list[Seen] = [] + self._script = list(script) + rec = self + + class Handler(BaseHTTPRequestHandler): + def _answer(self) -> None: + n = int(self.headers.get("content-length") or 0) + body = self.rfile.read(n).decode("utf-8") + rec.seen.append(Seen(self.command, self.path, self.headers.get("content-type"), body, time.monotonic())) + status, answer = rec._script.pop(0) if len(rec._script) > 1 else rec._script[0] + if status == RESET: + self.close_connection = True + return + data = answer.encode("utf-8") + self.send_response(status) + self.send_header("content-type", "application/json") + self.send_header("content-length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + do_GET = _answer + do_POST = _answer + + def log_message(self, *args: Any) -> None: + pass + + self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self.url = f"http://127.0.0.1:{self.server.server_address[1]}/api" + self._thread = threading.Thread(target=self.server.serve_forever, daemon=True) + self._thread.start() + + def close(self) -> None: + self.server.shutdown() + self.server.server_close() + + +@pytest.fixture +def recorder() -> Iterator[Any]: + made: list[Recorder] = [] + + def make(script: list[tuple[int, str]]) -> Recorder: + r = Recorder(script) + made.append(r) + return r + + yield make + for r in made: + r.close() + + +# --------------------------------------------------------------------------- +# build-info parsing +# --------------------------------------------------------------------------- + + +def test_forge_build_info_gives_forges_own_standard_json() -> None: + s = prepare_standard_json(forge_bi(), "src/Greeter.sol:Greeter") + assert s.contract_name == "src/Greeter.sol:Greeter" + # forge writes solcLongVersion "0.8.26"; the commit comes from the contract metadata. + assert s.compiler_version == "v0.8.26+commit.8a97fa7a" + assert s.standard_json_input == _json(FX / "forge" / "standard-json-input.json") + assert "src/Unrelated.sol" not in s.standard_json_input["sources"] + for extra in ("version", "allowPaths", "basePath", "includePaths"): + assert extra not in s.standard_json_input + + +def test_bare_name_resolves_and_unknown_is_refused() -> None: + assert prepare_standard_json(forge_bi(), "Greeter").contract_name == "src/Greeter.sol:Greeter" + with pytest.raises(BuildInfoError, match="not in this build-info"): + prepare_standard_json(forge_bi(), "Nope") + + +def test_plain_forge_build_info_is_refused_with_the_fix() -> None: + with pytest.raises(BuildInfoError, match="forge build --build-info"): + parse_build_info(_json(FX / "forge" / "minimal-build-info.json")) + + +def test_hardhat_artifact_gives_what_hardhat_verify_submits() -> None: + bi, contract = load_build_info(HH_ARTIFACT) + assert contract == "contracts/Greeter.sol:Greeter" + s = prepare_standard_json(bi, contract) + assert s.compiler_version == "v0.8.26+commit.8a97fa7a" + assert normalized(hardhat_post())["sourceCode"] == s.standard_json_input + + +def test_hardhat_build_info_with_bare_name() -> None: + bi, contract = load_build_info(HH_BI) + assert contract is None + assert prepare_standard_json(bi, "Greeter").contract_name == "contracts/Greeter.sol:Greeter" + + +def test_hardhat_artifact_as_json_points_to_load_build_info() -> None: + with pytest.raises(BuildInfoError, match="dbg.json"): + parse_build_info(_json(HH_ARTIFACT)) + + +def test_bare_standard_json_needs_qualified_name_and_long_version() -> None: + std = _json(FX / "forge" / "standard-json-input.json") + bi = parse_build_info(std) + with pytest.raises(BuildInfoError, match="path:Name"): + prepare_standard_json(bi, "Greeter", "v0.8.26+commit.8a97fa7a") + with pytest.raises(BuildInfoError, match="compiler version"): + prepare_standard_json(bi, "src/Greeter.sol:Greeter") + with pytest.raises(BuildInfoError, match="commit"): + prepare_standard_json(bi, "src/Greeter.sol:Greeter", "0.8.26") + s = prepare_standard_json(bi, "src/Greeter.sol:Greeter", "0.8.26+commit.8a97fa7a") + assert s.standard_json_input == std + + +def test_url_only_sources_are_refused() -> None: + std = _json(FX / "forge" / "standard-json-input.json") + std["sources"]["src/Counter.sol"] = {"urls": ["ipfs://x"]} + with pytest.raises(BuildInfoError, match="inline content"): + prepare_standard_json(parse_build_info(std), "src/Greeter.sol:Greeter", "v0.8.26+commit.8a97fa7a") + + +# --------------------------------------------------------------------------- +# request shape +# --------------------------------------------------------------------------- + + +def test_request_matches_what_forge_sends() -> None: + sub = prepare_standard_json(forge_bi(), "src/Greeter.sol:Greeter") + ours = build_verify_source_code_form( + address=FORGE_ADDR, + submission=sub, + constructor_args="0x0000000000000000000000000000000000000000000000000000000000000007", + ) + method, path, fields = recorded("forge/forge-1.5.1-custom-verifier-requests.jsonl")[0] + assert (method, path) == ("POST", "/api") + assert normalized(ours) == normalized(fields) + + +def test_constructor_arguements_spelling_without_0x() -> None: + sub = prepare_standard_json(forge_bi(), "Greeter") + f = dict(build_verify_source_code_form(address=FORGE_ADDR, submission=sub, constructor_args="0xABCD")) + assert f["constructorArguements"] == "abcd" + assert f["constructorArguments"] == "abcd" + assert dict(build_verify_source_code_form(address=FORGE_ADDR, submission=sub, constructor_args=b"\x01"))[ + "constructorArguements" + ] == "01" + assert "constructorArguements" not in dict(build_verify_source_code_form(address=FORGE_ADDR, submission=sub)) + with pytest.raises(BuildInfoError): + build_verify_source_code_form(address=FORGE_ADDR, submission=sub, constructor_args="0xabc") + with pytest.raises(BuildInfoError): + build_verify_source_code_form(address="0x1234", submission=sub) + + +# --------------------------------------------------------------------------- +# verify_contract against a recorder +# --------------------------------------------------------------------------- + + +def test_submit_poll_pending_to_pass(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (200, PENDING), (200, PENDING), (200, PASS)]) + r = verify_contract( + chain_id=40204, + address=FORGE_ADDR, + build_info=forge_bi(), + contract="src/Greeter.sol:Greeter", + constructor_args="0x07", + explorer_url=srv.url, + api_key="citrate", + **FAST, + ) + assert (r.status, r.guid, r.message) == ("verified", GUID, "Pass - Verified") + assert (r.contract_name, r.compiler_version) == ("src/Greeter.sol:Greeter", "v0.8.26+commit.8a97fa7a") + assert len(srv.seen) == 4 + post = srv.seen[0] + assert (post.method, post.path, post.content_type) == ("POST", "/api", "application/x-www-form-urlencoded") + body = post.form() + assert {k: body[k] for k in ("apikey", "chainid", "module", "action", "contractaddress", "codeformat", + "contractname", "compilerversion", "constructorArguements")} == { + "apikey": "citrate", + "chainid": "40204", + "module": "contract", + "action": "verifysourcecode", + "contractaddress": FORGE_ADDR, + "codeformat": "solidity-standard-json-input", + "contractname": "src/Greeter.sol:Greeter", + "compilerversion": "v0.8.26+commit.8a97fa7a", + "constructorArguements": "07", + } + for s in srv.seen[1:]: + assert s.method == "GET" + assert s.query() == { + "apikey": "citrate", + "chainid": "40204", + "module": "contract", + "action": "checkverifystatus", + "guid": GUID, + } + + +def test_without_chain_id_no_chainid_is_sent_anywhere(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (200, PENDING), (200, PASS)]) + r = verify_contract(address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", explorer_url=srv.url, + api_key="citrate", **FAST) + assert r.status == "verified" + assert len(srv.seen) == 3 + assert "chainid" not in srv.seen[0].form() + for s in srv.seen[1:]: + assert s.query() == {"apikey": "citrate", "module": "contract", "action": "checkverifystatus", "guid": GUID} + + +def test_a_given_chain_id_is_sent_and_a_bad_one_refused(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (200, PASS)]) + verify_contract(chain_id=1337, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url=srv.url, **FAST) + assert srv.seen[0].form()["chainid"] == "1337" + assert srv.seen[1].query()["chainid"] == "1337" + bad_ids: list[Any] = [0, -1, True, 1.5, "40204"] + for bad in bad_ids: + with pytest.raises(BuildInfoError): + verify_contract(chain_id=bad, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter") + + +def test_sends_what_hardhat_verify_sends(recorder: Any) -> None: + srv = recorder([(200, OK_GUID)]) + bi, contract = load_build_info(HH_ARTIFACT) + assert contract is not None + hh = dict(hardhat_post()) + verify_contract( + chain_id=40204, + address=HH_ADDR, + build_info=bi, + contract=contract, + constructor_args=hh["constructorArguements"], + explorer_url=srv.url, + api_key="citrate", + wait=False, + ) + ours = normalized(parse_qsl(srv.seen[0].body, keep_blank_values=True)) + assert ours.pop("chainid") == "40204" + assert ours.pop("constructorArguments") == hh["constructorArguements"] + assert ours == normalized(hardhat_post()) + + +def test_wait_false_returns_guid_without_polling(recorder: Any) -> None: + srv = recorder([(200, OK_GUID)]) + r = verify_contract(chain_id=40204, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url=srv.url, wait=False) + assert (r.status, r.guid) == ("pending", GUID) + assert len(srv.seen) == 1 + assert "apikey" not in srv.seen[0].form() + + +def test_accepts_standard_json_input(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (200, PASS)]) + r = verify_contract( + chain_id=40204, + address=FORGE_ADDR, + standard_json_input=_json(FX / "forge" / "standard-json-input.json"), + contract="src/Greeter.sol:Greeter", + compiler_version="v0.8.26+commit.8a97fa7a", + explorer_url=srv.url, + **FAST, + ) + assert r.status == "verified" + + +def test_fail_verdict_raises_typed(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (200, PENDING), (200, FAIL)]) + with pytest.raises(VerificationFailedError) as e: + verify_contract(chain_id=40204, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url=srv.url, **FAST) + assert (e.value.guid, e.value.result, e.value.error_code) == (GUID, "Fail - Unable to verify", "VERIFY_FAILED") + + +def test_poll_times_out_with_last_status(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (200, PENDING)]) + with pytest.raises(VerificationTimeoutError) as e: + verify_contract(chain_id=40204, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url=srv.url, poll_interval=0.005, timeout=0.06) + assert (e.value.guid, e.value.last_status) == (GUID, "Pending in queue") + + +def test_reset_5xx_and_429_while_polling_do_not_end_the_wait(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (RESET, ""), (503, "Service Unavailable"), (429, "Too Many Requests"), (200, PASS)]) + r = verify_contract(address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", explorer_url=srv.url, **FAST) + assert (r.status, r.guid, r.message) == ("verified", GUID, "Pass - Verified") + assert len(srv.seen) == 5 + + +def test_a_4xx_other_than_429_while_polling_ends_the_wait(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (404, "not found"), (200, PASS)]) + with pytest.raises(ExplorerResponseError) as e: + verify_contract(address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", explorer_url=srv.url, **FAST) + assert (e.value.http_status, e.value.transient) == (404, False) + assert len(srv.seen) == 2 + + +def test_transient_errors_until_the_deadline_time_out(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (503, "Service Unavailable")]) + with pytest.raises(VerificationTimeoutError) as e: + verify_contract(address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", explorer_url=srv.url, + poll_interval=0.005, timeout=0.15) + assert e.value.guid == GUID + assert "HTTP 503" in e.value.last_status + assert len(srv.seen) > 2 + + +def test_backoff_between_transient_errors_is_bounded(recorder: Any) -> None: + srv = recorder([(503, "x"), (503, "x"), (503, "x"), (503, "x"), (200, PASS)]) + wait_for_verification(GUID, explorer_url=srv.url, poll_interval=0.02, transient_backoff_max=0.05, timeout=5.0) + gaps = [b.at - a.at for a, b in zip(srv.seen, srv.seen[1:], strict=False)] + assert len(gaps) == 4 + for got, want in zip(gaps, [0.02, 0.04, 0.05, 0.05], strict=True): + assert want - 0.003 <= got < want + 0.2 + + +def test_transport_failures_5xx_and_429_are_transient(recorder: Any) -> None: + cases: list[tuple[tuple[int, str], int | None, bool]] = [ + ((503, "down"), 503, True), + ((429, "slow down"), 429, True), + ((RESET, ""), None, True), + ((200, '{"verified": true}'), 200, False), + ] + for answer, status, transient in cases: + srv = recorder([answer]) + with pytest.raises(ExplorerResponseError) as e: + check_verify_status("g", explorer_url=srv.url) + assert (e.value.http_status, e.value.transient) == (status, transient) + + +#: A recognisable probe value standing in for an explorer API key. +KEY_PROBE = "-".join(["probe", "7f3a9c", "do", "not", "print"]) + + +def closed_port_url() -> str: + """A loopback URL nothing listens on (connection refused).""" + with socket.socket() as sock: + sock.bind(("127.0.0.1", 0)) + port = sock.getsockname()[1] + return f"http://127.0.0.1:{port}/api" + + +def assert_no_key(e: BaseException) -> None: + text = "".join(traceback.format_exception(type(e), e, e.__traceback__)) + for shown in (str(e), repr(e), repr(getattr(e, "details", None)), text): + assert KEY_PROBE not in shown + assert "apikey=" not in shown + + +def test_connection_error_messages_never_carry_the_api_key() -> None: + url = closed_port_url() + with pytest.raises(ExplorerResponseError) as status_err: + check_verify_status(GUID, explorer_url=url, api_key=KEY_PROBE, chain_id=40204) + assert status_err.value.transient + assert "could not reach the explorer" in str(status_err.value) + assert_no_key(status_err.value) + + with pytest.raises(ExplorerResponseError) as submit_err: + verify_contract(address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", explorer_url=url, + api_key=KEY_PROBE, wait=False) + assert_no_key(submit_err.value) + + with pytest.raises(VerificationTimeoutError) as wait_err: + wait_for_verification(GUID, explorer_url=url, api_key=KEY_PROBE, poll_interval=0.005, timeout=0.05) + assert_no_key(wait_err.value) + assert KEY_PROBE not in wait_err.value.last_status + + +def test_already_verified_is_a_result(recorder: Any) -> None: + srv = recorder([(200, json.dumps({"status": "0", "message": "NOTOK", + "result": "Contract source code already verified"}))]) + r = verify_contract(chain_id=40204, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url=srv.url) + assert r.status == "already_verified" + + +def test_other_refusals_raise_with_explorer_string(recorder: Any) -> None: + msg = f"Unable to locate ContractCode at {FORGE_ADDR}" + srv = recorder([(200, json.dumps({"status": "0", "message": "NOTOK", "result": msg}))]) + with pytest.raises(VerificationRejectedError) as e: + verify_contract(chain_id=40204, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url=srv.url) + assert e.value.result == msg + + +def test_rate_limit_is_retried_then_typed(recorder: Any) -> None: + ok = recorder([(429, LIMITED), (200, OK_GUID)]) + r = verify_contract(chain_id=40204, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url=ok.url, wait=False, **FAST) + assert r.status == "pending" + assert len(ok.seen) == 2 + never = recorder([(429, LIMITED)]) + with pytest.raises(VerificationRateLimitedError): + verify_contract(chain_id=40204, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url=never.url, **FAST) + assert len(never.seen) == 3 + + +def test_non_envelope_answers_raise(recorder: Any) -> None: + bad = recorder([(502, "bad gateway")]) + with pytest.raises(ExplorerResponseError) as e: + check_verify_status("g", explorer_url=bad.url) + assert e.value.http_status == 502 + odd = recorder([(200, '{"verified": true}')]) + with pytest.raises(ExplorerResponseError): + check_verify_status("g", explorer_url=odd.url) + + +def test_refuses_plaintext_remote_and_bad_params_before_sending() -> None: + with pytest.raises(ValueError, match="(?i)http"): + verify_contract(chain_id=40204, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter", + explorer_url="http://explorer.example.com/api") + with pytest.raises(BuildInfoError): + verify_contract(chain_id=0, address=FORGE_ADDR, build_info=forge_bi(), contract="Greeter") + with pytest.raises(BuildInfoError): + verify_contract(chain_id=40204, address=FORGE_ADDR, contract="Greeter") + + +def test_default_url_and_package_exports() -> None: + assert DEFAULT_EXPLORER_URL == "https://explorer.citrate.ai/api" + assert citrate_sdk.verify_contract is verify_contract + assert citrate_sdk.deploy_and_verify is deploy_and_verify + for name in ("verify_contract", "deploy_and_verify", "VerificationError", "BuildInfoError"): + assert name in citrate_sdk.__all__ + + +# --------------------------------------------------------------------------- +# deploy_and_verify +# --------------------------------------------------------------------------- + +DEPLOYED = "0x00000000000000000000000000000000000000d1" +TX = "0x" + "ab" * 32 + + +class RecordingDeployer: + """Test-only deployer that records what it was asked to send.""" + + def __init__(self) -> None: + self.sent: list[tuple[str, int, int | None]] = [] + + def deploy_contract(self, data: str, value: int = 0, gas_limit: int | None = None) -> dict[str, Any]: + self.sent.append((data, value, gas_limit)) + return {"address": DEPLOYED, "tx_hash": TX, "block_number": 1} + + +def test_deploy_then_verify_with_creation_tx(recorder: Any) -> None: + srv = recorder([(200, OK_GUID), (200, PASS)]) + bi = forge_bi() + d = RecordingDeployer() + r = deploy_and_verify(client=d, chain_id=40204, build_info=bi, contract="Greeter", args=[7, "hello"], + explorer_url=srv.url, **FAST) + greeter = bi["output"]["contracts"]["src/Greeter.sol"]["Greeter"] + encoded = encode(["uint256", "string"], [7, "hello"]).hex() + assert d.sent == [("0x" + greeter["evm"]["bytecode"]["object"] + encoded, 0, None)] + assert (r.address, r.tx_hash, r.verification.status, r.verification.guid) == (DEPLOYED, TX, "verified", GUID) + body = srv.seen[0].form() + assert body["contractaddress"] == DEPLOYED + assert body["creationtxhash"] == TX + assert body["constructorArguements"] == encoded + assert body["contractname"] == "src/Greeter.sol:Greeter" + + +def test_deploy_chain_id_is_optional(recorder: Any) -> None: + without = recorder([(200, OK_GUID), (200, PENDING), (200, PASS)]) + deploy_and_verify(client=RecordingDeployer(), build_info=forge_bi(), contract="Greeter", args=[1, "x"], + explorer_url=without.url, **FAST) + assert len(without.seen) == 3 + assert "chainid" not in without.seen[0].form() + assert all("chainid" not in s.query() for s in without.seen[1:]) + + with_id = recorder([(200, OK_GUID), (200, PENDING), (200, PASS)]) + deploy_and_verify(client=RecordingDeployer(), chain_id=40204, build_info=forge_bi(), contract="Greeter", + args=[1, "x"], explorer_url=with_id.url, **FAST) + assert len(with_id.seen) == 3 + assert with_id.seen[0].form()["chainid"] == "40204" + assert all(s.query()["chainid"] == "40204" for s in with_id.seen[1:]) + + +def test_deploy_retries_until_indexed(recorder: Any) -> None: + not_yet = json.dumps({"status": "0", "message": "NOTOK", "result": f"Unable to locate ContractCode at {DEPLOYED}"}) + srv = recorder([(200, not_yet), (200, not_yet), (200, OK_GUID), (200, PASS)]) + r = deploy_and_verify(client=RecordingDeployer(), chain_id=40204, build_info=forge_bi(), contract="Greeter", + args=[1, "x"], explorer_url=srv.url, **FAST) + assert r.verification.status == "verified" + assert len([s for s in srv.seen if s.method == "POST"]) == 3 + + +def test_not_indexed_resubmissions_wait_the_rate_limit_backoff(recorder: Any) -> None: + not_yet = json.dumps({"status": "0", "message": "NOTOK", "result": f"Unable to locate ContractCode at {DEPLOYED}"}) + srv = recorder([(200, not_yet), (200, not_yet), (200, OK_GUID), (200, PASS)]) + deploy_and_verify(client=RecordingDeployer(), build_info=forge_bi(), contract="Greeter", args=[1, "x"], + explorer_url=srv.url, poll_interval=0.005, rate_limit_backoff=0.15, timeout=5.0) + posts = [s for s in srv.seen if s.method == "POST"] + assert len(posts) == 3 + for a, b in zip(posts, posts[1:], strict=False): + assert b.at - a.at >= 0.147 + + +def test_deploy_refuses_bad_inputs_before_sending() -> None: + d = RecordingDeployer() + with pytest.raises(BuildInfoError): + deploy_and_verify(client=d, chain_id=40204, build_info=forge_bi(), contract="Greeter", + args=[1, "x"], constructor_args="0x00") + with pytest.raises(BuildInfoError, match="argument"): + deploy_and_verify(client=d, chain_id=40204, build_info=forge_bi(), contract="Greeter", args=[1]) + linked = forge_bi() + linked["output"]["contracts"]["src/Greeter.sol"]["Greeter"]["evm"]["bytecode"]["object"] = ( + "6080__$0123456789abcdef0123456789abcdef01$__" + ) + with pytest.raises(BuildInfoError, match="unlinked"): + deploy_and_verify(client=d, chain_id=40204, build_info=linked, contract="Greeter") + assert d.sent == [] + + +def test_client_deploy_contract_needs_a_key_and_hex() -> None: + c = CitrateClient(rpc_url="http://127.0.0.1:1", expected_genesis_hash=None) + with pytest.raises(CitrateError, match="Private key"): + c.deploy_contract("0x6080") + with pytest.raises(CitrateError, match="hex"): + c.deploy_contract("6080") + + +class FakeRpc: + """Test-only JSON-RPC answers for CitrateClient._rpc_call (no node).""" + + def __init__(self, receipt: dict[str, Any] | None): + self.receipt = receipt + self.calls: list[tuple[str, list[Any]]] = [] + + def __call__(self, method: str, params: list[Any] | None = None) -> Any: + self.calls.append((method, list(params or []))) + answers: dict[str, Any] = { + "eth_chainId": hex(40204), + "eth_getTransactionCount": "0x0", + "eth_estimateGas": "0x5208", + "eth_sendRawTransaction": TX, + "eth_getTransactionReceipt": self.receipt, + } + return answers[method] + + +def keyed_client(rpc: FakeRpc, monkeypatch: pytest.MonkeyPatch) -> CitrateClient: + c = CitrateClient(rpc_url="http://127.0.0.1:1", private_key="0x" + "11" * 32, expected_genesis_hash=None) + monkeypatch.setattr(c, "_rpc_call", rpc) + return c + + +def test_client_deploy_contract_status_0_is_deploy_failed(monkeypatch: pytest.MonkeyPatch) -> None: + reverted = FakeRpc({"status": "0x0", "contractAddress": DEPLOYED, "blockNumber": "0x7"}) + with pytest.raises(CitrateError) as e: + keyed_client(reverted, monkeypatch).deploy_contract("0x6080") + assert (e.value.error_code, e.value.details) == ("DEPLOY_FAILED", {"tx_hash": TX}) + sent = [m for m, _ in reverted.calls] + assert "eth_sendRawTransaction" in sent and sent[-1] == "eth_getTransactionReceipt" + + no_address = FakeRpc({"status": "0x1", "contractAddress": None, "blockNumber": "0x7"}) + with pytest.raises(CitrateError, match="no contractAddress") as e: + keyed_client(no_address, monkeypatch).deploy_contract("0x6080") + assert e.value.error_code == "DEPLOY_FAILED" + + ok = FakeRpc({"status": "0x1", "contractAddress": DEPLOYED, "blockNumber": "0x7"}) + assert keyed_client(ok, monkeypatch).deploy_contract("0x6080") == { + "address": DEPLOYED, + "tx_hash": TX, + "block_number": 7, + } diff --git a/tests/test_verify_deploy_anvil.py b/tests/test_verify_deploy_anvil.py new file mode 100644 index 0000000..fe770ce --- /dev/null +++ b/tests/test_verify_deploy_anvil.py @@ -0,0 +1,92 @@ +"""deploy_and_verify through CitrateClient's real signer path on a local anvil. + +Skipped unless ``CITRATE_VERIFY_ANVIL_RPC`` and ``CITRATE_VERIFY_ANVIL_KEY`` are set (start +``anvil --chain-id 40204`` and pass one of its funded dev keys; no key is stored here). The chain +is real; the explorer is a test-only recorder, since this checks what the SDK sends: the creation +transaction on chain must be exactly the build-info's creation code plus the encoded constructor +arguments, and the submission must name that transaction and those arguments. +""" +from __future__ import annotations + +import json +import os +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from typing import Any +from urllib.parse import parse_qsl + +import pytest +import requests +from eth_abi import encode + +from citrate_sdk import CitrateClient +from citrate_sdk.verify import deploy_and_verify + +RPC = os.environ.get("CITRATE_VERIFY_ANVIL_RPC") +KEY = os.environ.get("CITRATE_VERIFY_ANVIL_KEY") + +pytestmark = [ + pytest.mark.integration, + pytest.mark.skipif(not (RPC and KEY), reason="CITRATE_VERIFY_ANVIL_RPC/KEY not set"), +] + +FORGE_BI = Path(__file__).parent / "fixtures" / "verify" / "forge" / "build-info" / "970692f6b0ae96cc.json" + + +def _rpc(method: str, params: list[Any]) -> Any: + r = requests.post(str(RPC), json={"jsonrpc": "2.0", "id": 1, "method": method, "params": params}, timeout=10) + return r.json()["result"] + + +def test_deploy_from_forge_build_info_on_anvil() -> None: + bodies: list[str] = [] + answers = [ + json.dumps({"status": "1", "message": "OK", "result": "guid-anvil"}), + json.dumps({"status": "1", "message": "OK", "result": "Pass - Verified"}), + ] + + class Handler(BaseHTTPRequestHandler): + def _answer(self) -> None: + n = int(self.headers.get("content-length") or 0) + bodies.append(self.rfile.read(n).decode("utf-8")) + data = (answers.pop(0) if len(answers) > 1 else answers[0]).encode() + self.send_response(200) + self.send_header("content-type", "application/json") + self.send_header("content-length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + do_GET = _answer + do_POST = _answer + + def log_message(self, *args: Any) -> None: + pass + + server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + threading.Thread(target=server.serve_forever, daemon=True).start() + try: + bi = json.loads(FORGE_BI.read_text(encoding="utf-8")) + client = CitrateClient(rpc_url=str(RPC), private_key=KEY, expected_genesis_hash=None) + r = deploy_and_verify( + client=client, + chain_id=40204, + build_info=bi, + contract="src/Greeter.sol:Greeter", + args=[7, "hello"], + explorer_url=f"http://127.0.0.1:{server.server_address[1]}/api", + poll_interval=0.01, + ) + greeter = bi["output"]["contracts"]["src/Greeter.sol"]["Greeter"] + encoded = encode(["uint256", "string"], [7, "hello"]).hex() + tx = _rpc("eth_getTransactionByHash", [r.tx_hash]) + assert tx["input"] == "0x" + greeter["evm"]["bytecode"]["object"] + encoded + assert len(_rpc("eth_getCode", [r.address, "latest"])) > 2 + submitted = dict(parse_qsl(bodies[0])) + assert submitted["contractaddress"].lower() == r.address.lower() + assert submitted["creationtxhash"] == r.tx_hash + assert submitted["constructorArguements"] == encoded + assert r.verification.status == "verified" + finally: + server.shutdown() + server.server_close()