컴파일러 사용하기

컴파일러 사용하기 (Using the Compiler)

Solidity 컴파일러 solc를 명령줄에서 사용하는 방법, 최적화 옵션, 기본 경로와 임포트 리매핑, 라이브러리 링킹, 대상 EVM 버전 설정, 그리고 JSON 입력·출력 인터페이스까지 다루는 문서예요. 복잡하거나 자동화된 설정에서는 JSON 입력·출력 인터페이스가 권장돼요.

출처: 문서

본문

명령줄 컴파일러 사용하기 (Using the Commandline Compiler)

참고 (Note)

이 절은 solcjs에는 적용되지 않아요, 명령줄 모드로 사용해도 마찬가지예요.

기본 사용법 (Basic Usage)

Solidity 저장소의 빌드 대상 중 하나는 Solidity 명령줄 컴파일러인 solc예요. solc --help를 사용하면 모든 옵션에 대한 설명을 얻을 수 있어요. 컴파일러는 단순한 바이너리와 어셈블리, 추상 구문 트리(파스 트리), 가스 사용량 추정에 이르는 다양한 출력을 만들어요.

단일 파일만 컴파일하려면 solc --bin sourceFile.sol로 실행하면 바이너리를 출력해요. solc의 더 고급 출력 변형을 원한다면, solc -o outputDirectory --bin --ast-compact-json --asm sourceFile.sol처럼 모든 것을 별도 파일로 출력하도록 지시하는 것이 더 낫습니다.

최적화 옵션 (Optimizer Options)

컨트랙트를 배포하기 전에 solc --optimize --bin sourceFile.sol을 사용해 컴파일할 때 최적화 프로그램을 활성화해요. 기본적으로 최적화 프로그램은 컨트랙트가 수명 동안 200번 호출된다고 가정해 최적화해요(더 구체적으로, 각 opcode가 약 200번 실행된다고 가정). 초기 컨트랙트 배포를 더 저렴하게 하고 나중의 함수 실행을 더 비싸게 하고 싶다면 --optimize-runs=1로 설정해요. 많은 트랜잭션이 예상되고 더 높은 배포 비용과 출력 크기를 신경 쓰지 않는다면 --optimize-runs를 높은 숫자로 설정해요.

이 매개변수는 다음에 영향을 줘요(이것은 미래에 바뀔 수 있어요):

  • 함수 디스패치 루틴의 바이너리 탐색 크기
  • 큰 숫자나 문자열 같은 상수가 저장되는 방식

기본 경로와 임포트 리매핑 (Base Path and Import Remapping)

명령줄 컴파일러는 파일시스템에서 임포트한 파일을 자동으로 읽지만, prefix=path를 사용해 경로 리다이렉트를 제공하는 것도 가능해요:

solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ file.sol

이것은 본질적으로 컴파일러에게 github.com/ethereum/dapp-bin/으로 시작하는 어떤 것이든 /usr/local/lib/dapp-bin 아래에서 찾으라고 지시해요.

임포트를 찾기 위해 파일시스템에 접근할 때, ./이나 ../로 시작하지 않는 경로는 --base-path와 --include-path 옵션으로 지정된 디렉토리(또는 기본 경로가 지정되지 않으면 현재 작업 디렉토리)에 상대적인 것으로 취급돼요. 게다가 이 옵션들로 추가된 경로의 부분은 컨트랙트 메타데이터에 나타나지 않아요.

보안 이유로 컴파일러는 접근할 수 있는 디렉토리에 제한이 있어요. 명령줄에 지정된 소스 파일의 디렉토리와 리매핑의 대상 경로는 파일 리더가 접근하도록 자동으로 허용되지만, 그 외의 모든 것은 기본적으로 거부돼요. --allow-paths /sample/path,/another/sample/path 스위치로 추가 경로(와 그 하위 디렉토리)를 허용할 수 있어요. --base-path로 지정된 경로 안의 모든 것은 항상 허용돼요.

위 내용은 컴파일러가 임포트 경로를 처리하는 방법의 단순화일 뿐이에요. 예시와 엣지 케이스 논의를 통한 자세한 설명은 경로 해석 절을 참고해요.

라이브러리 링킹 (Library Linking)

컨트랙트가 라이브러리를 사용하면, 바이트코드에 __$53aea86b7d70b31448b230b20ae141a537$__ 형태의 부분 문자열(형식은 <v0.5.0에서 달랐어요)이 포함되어 있음을 알게 될 거예요. 이는 실제 라이브러리 주소에 대한 플레이스홀더예요. 플레이스홀더는 완전 정규화된 라이브러리 이름의 keccak256 해시의 hex 인코딩에서 34자리 접두사예요. 바이트코드 파일은 또한 식별을 돕기 위해 끝에 // <placeholder> -> <fq library name> 형태의 줄을 포함해요. 완전 정규화된 라이브러리 이름은 그 소스 파일의 경로와 라이브러리 이름을 :로 구분한 것이라는 점에 주의해요.

solc를 링커로 사용할 수 있는데, 즉 그 지점에서 라이브러리 주소를 대신 삽입해 준다는 뜻이에요. 각 라이브러리에 주소를 제공하려면 명령에 --libraries "file.sol:Math=0x1234567890123456789012345678901234567890 file.sol:Heap=0xabCD567890123456789012345678901234567890"을 추가하거나(구분자로 쉼표 또는 공백 사용), 문자열을 파일에 저장하고(한 줄에 라이브러리 하나) --libraries fileName으로 solc를 실행해요.

참고 (Note)

Solidity 0.8.1부터 라이브러리와 주소 사이의 구분자로 =을 받고, :은 구분자로 비권장됐어요. 미래에 제거될 거예요. 현재는 --libraries "file.sol:Math:0x123456... file.sol:Heap:0xabCD..."도 작동해요.

solc가 --standard-json 옵션으로 호출되면, 표준 입력에서 JSON 입력을(아래에서 설명하듯) 기대하고 표준 출력에서 JSON 출력을 반환해요. 이것은 더 복잡하고 특히 자동화된 사용을 위한 권장 인터페이스예요. 프로세스는 항상 "성공" 상태로 종료되고 어떤 에러든 JSON 출력을 통해 보고해요. --base-path 옵션도 표준-json 모드에서 처리돼요.

solc가 --link 옵션으로 호출되면, 모든 입력 파일이 위에서 주어진 __$53aea86b7d70b31448b230b20ae141a537$__-형식의 링크되지 않은 바이너리(hex-인코딩)로 해석되고, 제자리에서 링크돼요(입력이 stdin에서 읽히면 stdout에 쓰여짐). 이 경우 --libraries를 제외한 모든 옵션(-o 포함)은 무시돼요.

경고 (Warning)

생성된 바이트코드에서 라이브러리를 수동으로 링크하는 것은 컨트랙트 메타데이터를 갱신하지 않으므로 권장되지 않아요. 메타데이터는 컴파일 시점에 지정된 라이브러리 목록을 포함하고 바이트코드는 메타데이터 해시를 포함하므로, 링크가 언제 수행되는지에 따라 다른 바이너리를 얻게 될 거예요. 컨트랙트가 컴파일되는 시점에 컴파일러가 라이브러리를 링크하도록, solc의 --libraries 옵션 또는 표준-JSON 인터페이스를 사용한다면 libraries 키를 사용해 요청해야 해요.

참고 (Note)

라이브러리 플레이스홀더는 예전에는 그 해시가 아니라 라이브러리 자체의 완전 정규화된 이름이었어요. 이 형식은 solc --link이 여전히 지원하지만 컴파일러는 더 이상 출력하지 않아요. 완전 정규화된 라이브러리 이름의 처음 36자만 사용될 수 있으므로, 이 변경은 라이브러리 사이의 충돌 가능성을 줄이기 위해 이루어졌어요.

대상 EVM 버전 설정 (Setting the EVM Version to Target)

컨트랙트 코드를 컴파일할 때 특정 기능이나 동작을 피하기 위해 컴파일할 이더리움 가상 머신 버전을 지정할 수 있어요.

경고 (Warning)

잘못된 EVM 버전으로 컴파일하면 잘못되고, 이상하고, 실패하는 동작을 만들 수 있어요. 특히 프라이빗 체인을 실행한다면 일치하는 EVM 버전을 사용하는지 확인하세요.

명령줄에서 다음과 같이 EVM 버전을 선택할 수 있어요:

solc --evm-version <VERSION> contract.sol

표준 JSON 인터페이스에서는 settings 필드에 "evmVersion" 키를 사용해요:

{
  "sources": {/* ... */},
  "settings": {
    "optimizer": {/* ... */},
    "evmVersion": "<VERSION>"
  }
}

대상 옵션 (Target Options)

아래는 대상 EVM 버전 목록과 각 버전에서 도입된 컴파일러 관련 변경 목록이에요. 각 버전 사이에 하위 호환성은 보장되지 않아요.

  • homestead(지원 비권장)(가장 오래된 버전)
  • tangerineWhistle(지원 비권장): 다른 계정에 대한 접근의 가스 비용이 증가해, 가스 추정과 최적화 프로그램에 관련돼요. 외부 호출에 대한 모든 가스가 기본으로 보내지며, 이전에는 일정 양을 유지해야 했어요.
  • spuriousDragon(지원 비권장): exp opcode의 가스 비용이 증가해, 가스 추정과 최적화 프로그램에 관련돼요.
  • byzantium(지원 비권장): opcode returndatacopy, returndatasize, staticcall을 어셈블리에서 사용할 수 있어요. staticcall opcode는 non-library view 또는 pure 함수를 호출할 때 사용돼, EVM 수준에서 함수가 상태를 수정하는 것을 방지해요(즉 잘못된 타입 변환을 사용해도 적용). 함수 호출에서 반환된 동적 데이터에 접근하는 것이 가능해요. revert opcode가 도입돼 revert()가 가스를 낭비하지 않게 돼요.
  • constantinople(지원 비권장): opcode create2, extcodehash, shl, shr, sar을 어셈블리에서 사용할 수 있어요. 시프트 연산자가 시프트 opcode를 사용하므로 가스가 덜 필요해요.
  • petersburg(지원 비권장): 컴파일러가 constantinople과 같은 방식으로 동작해요.
  • istanbul(지원 비권장): opcode chainid와 selfbalance를 어셈블리에서 사용할 수 있어요.
  • berlin(지원 비권장): SLOAD, *CALL, BALANCE, EXT*, SELFDESTRUCT의 가스 비용이 증가했어요. 컴파일러는 그런 연산에 대해 cold gas 비용을 가정해요. 이는 가스 추정과 최적화 프로그램에 관련돼요.
  • london: 블록의 기본 수수료(EIP-3198과 EIP-1559)를 전역 block.basefee나 인라인 어셈블리의 basefee()로 접근할 수 있어요.
  • paris: prevrandao()와 block.prevrandao을 도입하고, 이제 비권장된 block.difficulty의 의미를 바꾸며 인라인 어셈블리에서 difficulty()를 허용하지 않아요(EIP-4399 참고).
  • shanghai: push0의 도입으로 코드 크기와 가스가 절약돼요(EIP-3855 참고).
  • cancun: 블록의 blob 기본 수수료(EIP-7516과 EIP-4844)를 전역 block.blobbasefee나 인라인 어셈블리의 blobbasefee()로 접근할 수 있어요. 인라인 어셈블리와 대응되는 전역 함수에 blobhash()를 도입해 트랜잭션과 연관된 blob의 버전 해시를 검색할 수 있어요(EIP-4844 참고). opcode mcopy를 어셈블리에서 사용할 수 있어요(EIP-5656 참고). opcode tstore와 tload를 어셈블리에서 사용할 수 있어요(EIP-1153 참고).
  • prague
  • osaka(기본): clz 내장 함수를 인라인 어셈블리에서 사용할 수 있어요.(EIP-7939)
  • amsterdam(실험적): 비콘 체인 슬롯 번호(EIP-7843)를 전역 block.slotnum이나 인라인 어셈블리의 slotnum()으로 접근할 수 있어요.

컴파일러 입력과 출력 JSON 설명 (Compiler Input and Output JSON Description)

특히 더 복잡하고 자동화된 설정에서 Solidity 컴파일러와 인터페이스하는 권장 방법은 소위 JSON-입력-출력 인터페이스예요. 같은 인터페이스가 컴파일러의 모든 배포판에서 제공돼요. 필드는 일반적으로 변경될 수 있고, 일부는 선택적이며(주석에서), 우리는 하위 호환 변경만 하려고 노력해요.

컴파일러 API는 JSON 형식 입력을 기대하고 JSON 형식 출력으로 컴파일 결과를 출력해요. 표준 에러 출력은 사용되지 않고, 에러가 있어도 프로세스는 항상 "성공" 상태로 종료돼요. 에러는 항상 JSON 출력의 일부로 보고돼요. 다음 하위 절들이 예시를 통해 형식을 설명해요. 주석은 당연히 허용되지 않고 여기서는 설명 목적으로만 사용돼요.

입력 설명 (Input Description)

{
  // Required: Source code language. Currently supported are "Solidity", "Yul", "SolidityAST" (experimental), "EVMAssembly" (experimental).
  "language": "Solidity",
  // Required
  "sources":
  {
    // The keys here are the "global" names of the source files,
    // imports can use other files via remappings (see below).
    "myFile.sol":
    {
      // Optional: keccak256 hash of the source file
      // It is used to verify the retrieved content if imported via URLs.
      "keccak256": "0x123...",
      // Required (unless "content" is used, see below): URL(s) to the source file.
      // URL(s) should be imported in this order and the result checked against the
      // keccak256 hash (if available). If the hash doesn't match or none of the
      // URL(s) result in success, an error should be raised.
      // Using the commandline interface only filesystem paths are supported.
      // With the JavaScript interface the URL will be passed to the user-supplied
      // read callback, so any URL supported by the callback can be used.
      "urls":
      [
        "bzzr://56ab...",
        "ipfs://Qma...",
        "/tmp/path/to/file.sol"
        // If files are used, their directories should be added to the command-line via
        // `--allow-paths <path>`.
      ]
    },
    "settable":
    {
      // Optional: keccak256 hash of the source file
      "keccak256": "0x234...",
      // Required (unless "urls" is used): literal contents of the source file
      "content": "contract settable is owned { uint256 private x = 0; function set(uint256 _x) public { if (msg.sender == owner) x = _x; } }"
    },
    "myFile.sol_json.ast":
    {
      // If language is set to "SolidityAST", an AST needs to be supplied under the "ast" key
      // and there can be only one source file present.
      // The format is the same as used by the `ast` output.
      // Note that importing ASTs is experimental and in particular that:
      // - importing invalid ASTs can produce undefined results and
      // - no proper error reporting is available on invalid ASTs.
      // Furthermore, note that the AST import only consumes the fields of the AST as
      // produced by the compiler in "stopAfter": "parsing" mode and then re-performs
      // analysis, so any analysis-based annotations of the AST are ignored upon import.
      "ast": { ... }
    },
    "myFile_evm.json":
    {
      // If language is set to "EVMAssembly", an EVM Assembly JSON object needs to be supplied
      // under the "assemblyJson" key and there can be only one source file present.
      // The format is the same as used by the `evm.legacyAssembly` output or `--asm-json`
      // output on the command line.
      // Note that importing EVM assembly is experimental.
      "assemblyJson":
      {
        ".code": [ ... ],
        ".data": { ... }, // optional
        "sourceList": [ ... ] // optional (if no `source` node was defined in any `.code` object)
      }
    }
  },
  // Optional
  "settings":
  {
    // Optional: Stop compilation after the given stage. Currently only "parsing" is valid here
    "stopAfter": "parsing",
    // Optional: List of remappings
    "remappings": [ ":g=/dir" ],
    // Optional: Experimental mode toggle (Default: false)
    // Makes it possible to use experimental features (but does not enable any such feature by itself).
    // The use of this mode is recorded in contract metadata.
    "experimental": true,
    // Optional: Optimizer settings
    "optimizer": {
      // Turn on the optimizer. Optional. Default: false.
      // NOTE: The state of the optimizer is fully determined by the 'details' dict and this setting
      // only affects its defaults - when enabled, all components default to being enabled.
      // The opposite is not true - there are several components that always default to being
      // enabled an can only be explicitly disabled via 'details'.
      // WARNING: Before version 0.8.6 omitting this setting was not equivalent to setting
      // it to false and would result in all components being disabled instead.
      // WARNING: Enabling optimizations for EVMAssembly input is allowed but not necessary under normal
      // circumstances. It forces the opcode-based optimizer to run again and can produce bytecode that
      // is not reproducible from metadata.
      "enabled": true,
      // Optimize for how many times you intend to run the code. Optional. Default: 200.
      // Lower values will optimize more for initial deployment cost, higher
      // values will optimize more for high-frequency usage.
      "runs": 200,
      // State of all optimizer components. Optional.
      // Default values are determined by whether the optimizer is enabled or not.
      // Note that the 'enabled' setting only affects the defaults here and has no effect when
      // all values are provided explicitly.
      "details": {
        // Peephole optimizer (opcode-based). Optional. Default: true.
        // Default for EVMAssembly input: false when optimization is not enabled.
        // NOTE: Always runs (even with optimization disabled) except for EVMAssembly input or when explicitly turned off here.
        "peephole": true,
        // Inliner (opcode-based). Optional. Default: true when optimization is enabled.
        "inliner": false,
        // Unused JUMPDEST remover (opcode-based). Optional. Default: true.
        // Default for EVMAssembly input: false when optimization is not enabled.
        // NOTE: Always runs (even with optimization disabled) except for EVMAssembly input or when explicitly turned off here.
        "jumpdestRemover": true,
        // Literal reordering (codegen-based). Optional. Default: true when optimization is enabled.
        // Moves literals to the right of commutative binary operators during code generation, helping exploit associativity.
        "orderLiterals": false,
        // Block deduplicator (opcode-based). Optional. Default: true when optimization is enabled.
        // Unifies assembly code blocks that share content.
        "deduplicate": false,
        // Common subexpression elimination (opcode-based). Optional. Default: true when optimization is enabled.
        // This is the most complicated step but can also provide the largest gain.
        "cse": false,
        // Constant optimizer (opcode-based). Optional. Default: true when optimization is enabled.
        // Tries to find better representations of literal numbers and strings, that satisfy the
        // size/cost trade-off determined by the 'runs' setting.
        "constantOptimizer": false,
        // Unchecked loop increment (codegen-based). Optional. Default: true.
        // Use unchecked arithmetic when incrementing the counter of 'for' loops under certain circumstances.
        // NOTE: Always runs (even with optimization disabled) unless explicitly turned off here.
        "simpleCounterForLoopUncheckedIncrement": true,
        // Yul optimizer. Optional. Default: true when optimization is enabled.
        // Used to optimize the IR produced by the Yul IR-based pipeline as well as inline assembly
        // and utility Yul code generated by the compiler.
        // NOTE: Before Solidity 0.6.0 the default was false.
        "yul": false,
        // Tuning options for the Yul optimizer. Optional.
        "yulDetails": {
          // Improve allocation of stack slots for variables, can free up stack slots early.
          // Optional. Default: true if Yul optimizer is enabled.
          "stackAllocation": true,
          // Optimization step sequence.
          // The general form of the value is "<main sequence>:<cleanup sequence>".
          // The setting is optional and when omitted, default values are used for both sequences.
          // If the value does not contain the ':' delimiter, it is interpreted as the main
          // sequence and the default is used for the cleanup sequence.
          // To make one of the sequences empty, the delimiter must be present at the first or last position.
          // In particular if the whole value consists only of the delimiter, both sequences are empty.
          // Note that there are several hard-coded steps that always run, even when both sequences are empty.
          // For more information see "The Optimizer > Selecting Optimizations".
          "optimizerSteps": "dfDvulfnTUtnIf..."
        }
      }
    },
    // Version of the EVM to compile for (optional).
    // Affects type checking and code generation. Can be homestead,
    // tangerineWhistle, spuriousDragon, byzantium, constantinople,
    // petersburg, istanbul, berlin, london, paris, shanghai, cancun,
    // prague, osaka (default), amsterdam (experimental), or @future (experimental).
    "evmVersion": "osaka",
    // Optional: Change compilation pipeline to go through the Yul intermediate representation.
    // This is false by default.
    "viaIR": true,
    // Optional: Turn on SSA CFG-based code generation via the IR (experimental).
    // Implies viaIR: true. This is false by default.
    "viaSSACFG": false,
    // Optional: Debugging settings
    "debug": {
      // How to treat revert (and require) reason strings. Settings are
      // "default", "strip", "debug" and "verboseDebug".
      // "default" does not inject compiler-generated revert strings and keeps user-supplied ones.
      // "strip" removes all revert strings (if possible, i.e. if literals are used) keeping side-effects.
      // NOTE: "strip" does not remove custom errors.
      // "debug" injects strings for compiler-generated internal reverts, implemented for ABI encoders V1 and V2 for now.
      // "verboseDebug" even appends further information to user-supplied revert strings (not yet implemented)
      "revertStrings": "default",
      // Optional: How much extra debug information to include in comments in the produced EVM
      // assembly and Yul code. Available components are:
      // - `location`: Annotations of the form `@src <index>:<start>:<end>` indicating the
      //    location of the corresponding element in the original Solidity file, where:
      //     - `<index>` is the file index matching the `@use-src` annotation,
      //     - `<start>` is the index of the first byte at that location,
      //     - `<end>` is the index of the first byte after that location.
      // - `snippet`: A single-line code snippet from the location indicated by `@src`.
      //     The snippet is quoted and follows the corresponding `@src` annotation.
      //     Depends on `location`; selecting `snippet` without it is an error.
      // - `ast-id`: Annotations of the form `@ast-id <id>` over elements that can be mapped back to a definition in the original Solidity file.
      //   `<id>` is a node ID in the Solidity AST ('ast' output).
      // - `ethdebug`: Ethdebug annotations (experimental). Depends on `ast-id`; selecting
      //   `ethdebug` without `ast-id` is an error. Requesting an ethdebug output does not
      //   change this selection; without `ethdebug` in it the `evm.bytecode.ethdebug` and
      //   `evm.deployedBytecode.ethdebug` outputs carry none of the semantic debug info
      //   this component adds.
      // - `*`: Wildcard value that can be used to request all non-experimental components.
      "debugInfo": ["location", "snippet", "ast-id", "ethdebug"]
    },
    // Metadata settings (optional)
    "metadata": {
      // The CBOR metadata is appended at the end of the bytecode by default.
      // Setting this to false omits the metadata from the runtime and deploy time code.
      "appendCBOR": true,
      // Use only literal content and not URLs (false by default)
      "useLiteralContent": true,
      // Use the given hash method for the metadata hash that is appended to the bytecode.
      // The metadata hash can be removed from the bytecode via option "none".
      // The other options are "ipfs" and "bzzr1".
      // If the option is omitted, "ipfs" is used by default.
      "bytecodeHash": "ipfs"
    },
    // Addresses of the libraries. If not all libraries are given here,
    // it can result in unlinked objects whose output data is different.
    "libraries": {
      // The top level key is the name of the source file where the library is used.
      // If remappings are used, this source file should match the global path
      // after remappings were applied.
      // If this key is an empty string, that refers to a global level.
      "myFile.sol": {
        "MyLib": "0x123123..."
      }
    },
    // The following can be used to select desired outputs based
    // on file and contract names.
    // If this field is omitted, then the compiler loads and does type checking,
    // but will not generate any outputs apart from errors.
    // The first level key is the file name and the second level key is the contract name.
    // An empty contract name is used for outputs that are not tied to a contract
    // but to the whole source file like the AST.
    // A star as contract name refers to all contracts in the file.
    // Similarly, a star as a file name matches all files.
    // To select all outputs the compiler can possibly generate, with the exclusion of
    // Yul intermediate representation outputs, use
    // "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }"
    // but note that this might slow down the compilation process needlessly.
    //
    // The available output types are as follows:
    //
    // File level (needs empty string as contract name):
    //   ast - AST of all source files
    //
    // Contract level (needs the contract name or "*"):
    //   abi - ABI
    //   devdoc - Developer documentation (natspec)
    //   userdoc - User documentation (natspec)
    //   metadata - Metadata
    //   ir - Yul intermediate representation of the code before optimization
    //   irAst - AST of Yul intermediate representation of the code before optimization (experimental)
    //   irOptimized - Intermediate representation after optimization
    //   irOptimizedAst - AST of intermediate representation after optimization (experimental)
    //   storageLayout - Slots, offsets and types of the contract's state variables in storage
    //   transientStorageLayout - Slots, offsets and types of the contract's state variables in transient storage
    //   evm.assembly - New assembly format
    //   evm.legacyAssembly - Old-style assembly format in JSON
    //   evm.bytecode.ethdebug - Debug information in ethdebug format (ethdebug/format/program schema for creation bytecode). Can only be requested when compiling via IR. Carries semantic debug info only when the `ethdebug` component is present in `settings.debug.debugInfo`. (experimental)
    //   evm.deployedBytecode.ethdebug - Debug information in ethdebug format (ethdebug/format/program schema for deployed bytecode). Can only be requested when compiling via IR. Carries semantic debug info only when the `ethdebug` component is present in `settings.debug.debugInfo`. (experimental)
    //   evm.bytecode.functionDebugData - Debugging information at function level
    //   evm.bytecode.object - Bytecode object
    //   evm.bytecode.opcodes - Opcodes list
    //   evm.bytecode.sourceMap - Source mapping (useful for debugging)
    //   evm.bytecode.linkReferences - Link references (if unlinked object)
    //   evm.bytecode.generatedSources - Sources generated by the compiler
    //   evm.deployedBytecode* - Deployed bytecode (has all the options that evm.bytecode has)
    //   evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables
    //   evm.methodIdentifiers - The list of function hashes
    //   evm.gasEstimates - Function gas estimates
    //   yulCFGJson - Control Flow Graph (CFG) of the Single Static Assignment (SSA) form of the contract (experimental)
    //
    // Global level (needs "*" as file name and "*" as contract name):
    //   ethdebug.resources - Global ethdebug output (ethdebug/format/info/resources schema) containing source list and compiler info (experimental)
    //   ethdebug.compilation - Global ethdebug compilation output (the 'compilation' key from ethdebug/format/info/resources schema) (experimental)
    //
    // Note that using `evm`, `evm.bytecode`, etc. will select every
    // target part of that output. Additionally, `*` can be used as a wildcard to request everything.
    //
    "outputSelection": {
      "*": {
        "*": [
          "metadata", "evm.bytecode" // Enable the metadata and bytecode outputs of every single contract.
          , "evm.bytecode.sourceMap" // Enable the source map output of every single contract.
        ],
        "": [
          "ast" // Enable the AST output of every single file.
        ]
      },
      // Enable the abi and opcodes output of MyContract defined in file def.
      "def": {
        "MyContract": [ "abi", "evm.bytecode.opcodes" ]
      }
    },
    // The modelChecker object is experimental and subject to changes.
    "modelChecker":
    {
      // Chose which contracts should be analyzed as the deployed one.
      "contracts":
      {
        "source1.sol": ["contract1"],
        "source2.sol": ["contract2", "contract3"]
      },
      // Choose how division and modulo operations should be encoded.
      // When using `false` they are replaced by multiplication with slack
      // variables. This is the default.
      // Using `true` here is recommended if you are using the CHC engine
      // and not using Spacer as the Horn solver (using Eldarica, for example).
      // See the Formal Verification section for a more detailed explanation of this option.
      "divModNoSlacks": false,
      // Choose which model checker engine to use: all (default), bmc, chc, none.
      "engine": "chc",
      // Choose whether external calls should be considered trusted in case the
      // code of the called function is available at compile-time.
      // For details see the SMTChecker section.
      "extCalls": "trusted",
      // Choose which types of invariants should be reported to the user: contract, reentrancy.
      "invariants": ["contract", "reentrancy"],
      // Choose whether to output all proved targets. The default is `false`.
      "showProvedSafe": true,
      // Choose whether to output all unproved targets. The default is `false`.
      "showUnproved": true,
      // Choose whether to output all unsupported language features. The default is `false`.
      "showUnsupported": true,
      // Choose which solvers should be used, if available.
      // See the Formal Verification section for the solvers description.
      "solvers": ["cvc5", "smtlib2", "z3"],
      // Choose which targets should be checked: constantCondition,
      // underflow, overflow, divByZero, balance, assert, popEmptyArray, outOfBounds.
      // If the option is not given all targets are checked by default,
      // except underflow/overflow for Solidity >=0.8.7.
      // See the Formal Verification section for the targets description.
      "targets": ["underflow", "overflow", "assert"],
      // Timeout for each SMT query in milliseconds.
      // If this option is not given, the SMTChecker will use a deterministic
      // resource limit by default.
      // A given timeout of 0 means no resource/time restrictions for any query.
      "timeout": 20000
    }
  }
}

출력 설명 (Output Description)

{
  // Optional: not present if no errors/warnings/infos were encountered
  "errors": [
    {
      // Optional: Location within the source file.
      "sourceLocation": {
        "file": "sourceFile.sol",
        "start": 0,
        "end": 100
      },
      // Optional: Further locations (e.g. places of conflicting declarations)
      "secondarySourceLocations": [
        {
          "file": "sourceFile.sol",
          "start": 64,
          "end": 92,
          "message": "Other declaration is here:"
        }
      ],
      // Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc.
      // See below for complete list of types.
      "type": "TypeError",
      // Mandatory: Component where the error originated, such as "general" etc.
      "component": "general",
      // Mandatory ("error", "warning" or "info", but please note that this may be extended in the future)
      "severity": "error",
      // Optional: unique code for the cause of the error
      "errorCode": "3141",
      // Mandatory
      "message": "Invalid keyword",
      // Optional: the message formatted with source location
      "formattedMessage": "sourceFile.sol:100: Invalid keyword"
    }
  ],
  // This contains the file-level outputs.
  // It can be limited/filtered by the outputSelection settings.
  "sources": {
    "sourceFile.sol": {
      // Identifier of the source (used in source maps)
      "id": 1,
      // The AST object
      "ast": {}
    }
  },
  // This contains the contract-level outputs.
  // It can be limited/filtered by the outputSelection settings.
  "contracts": {
    "sourceFile.sol": {
      // If the language used has no contract names, this field should equal to an empty string.
      "ContractName": {
        // The Ethereum Contract ABI. If empty, it is represented as an empty array.
        // See https://docs.soliditylang.org/en/develop/abi-spec.html
        "abi": [],
        // See the Metadata Output documentation (serialised JSON string)
        "metadata": "{/* ... */}",
        // User documentation (natspec)
        "userdoc": {},
        // Developer documentation (natspec)
        "devdoc": {},
        // Intermediate representation before optimization (string)
        "ir": "",
        // AST of intermediate representation before optimization
        "irAst":  {/* ... */},
        // Intermediate representation after optimization (string)
        "irOptimized": "",
        // AST of intermediate representation after optimization
        "irOptimizedAst": {/* ... */},
        // See the Storage Layout documentation.
        "storageLayout": {"storage": [/* ... */], "types": {/* ... */} },
        // See the Storage Layout documentation.
        "transientStorageLayout": {"storage": [/* ... */], "types": {/* ... */} },
        // EVM-related outputs
        "evm": {
          // Assembly (string)
          "assembly": "",
          // Old-style assembly (object)
          "legacyAssembly": {},
          // Bytecode and related details.
          "bytecode": {
            // Ethdebug output (experimental)
            "ethdebug": {/* ... */},
            // Debugging data at the level of functions.
            "functionDebugData": {
              // Now follows a set of functions including compiler-internal and
              // user-defined function. The set does not have to be complete.
              "@mint_13": { // Internal name of the function
                "entryPoint": 128, // Byte offset into the bytecode where the function starts (optional)
                "id": 13, // AST ID of the function definition or null for compiler-internal functions (optional)
                "parameterSlots": 2, // Number of EVM stack slots for the function parameters (optional)
                "returnSlots": 1 // Number of EVM stack slots for the return values (optional)
              }
            },
            // The bytecode as a hex string.
            "object": "00fe",
            // Opcodes list (string)
            "opcodes": "",
            // The source mapping as a string. See the source mapping definition.
            "sourceMap": "",
            // Array of sources generated by the compiler. Currently only
            // contains a single Yul file.
            "generatedSources": [{
              // Yul AST
              "ast": {/* ... */},
              // Source file in its text form (may contain comments)
              "contents":"{ function abi_decode(start, end) -> data { data := calldataload(start) } }",
              // Source file ID, used for source references, same "namespace" as the Solidity source files
              "id": 2,
              "language": "Yul",
              "name": "#utility.yul"
            }],
            // If given, this is an unlinked object.
            "linkReferences": {
              "libraryFile.sol": {
                // Byte offsets into the bytecode.
                // Linking replaces the 20 bytes located there.
                "Library1": [
                  { "start": 0, "length": 20 },
                  { "start": 200, "length": 20 }
                ]
              }
            }
          },
          "deployedBytecode": {
            // Ethdebug output (experimental)
            "ethdebug": {/* ... */},
            /* ..., */ // The same layout as above.
            "immutableReferences": {
              // There are two references to the immutable with AST ID 3, both 32 bytes long. One is
              // at bytecode offset 42, the other at bytecode offset 80.
              "3": [{ "start": 42, "length": 32 }, { "start": 80, "length": 32 }]
            }
          },
          // The list of function hashes
          "methodIdentifiers": {
            "delegate(address)": "5c19a95c"
          },
          // Function gas estimates
          "gasEstimates": {
            "creation": {
              "codeDepositCost": "420000",
              "executionCost": "infinite",
              "totalCost": "infinite"
            },
            "external": {
              "delegate(address)": "25000"
            },
            "internal": {
              "heavyLifting()": "infinite"
            }
          },
          // Yul CFG representation of the SSA form (experimental)
          "yulCFGJson": {/* ... */}
        }
      }
    }
  },
  // Global Ethdebug output (experimental)
  "ethdebug": {
    // Requested via ethdebug.resources output selection
    "resources": {/* ... */},
    // Requested via ethdebug.compilation output selection
    "compilation": {/* ... */}
  }
}

에러 타입 (Error Types)

  • JSONError: JSON 입력이 요구된 형식을 따르지 않음, 예: 입력이 JSON 객체가 아님, 언어가 지원되지 않음 등.
  • IOError: IO 및 임포트 처리 에러, 예: 해결 불가능한 URL 또는 제공된 소스의 해시 불일치.
  • ParserError: 소스 코드가 언어 규칙을 따르지 않음.
  • DocstringParsingError: 주석 블록의 NatSpec 태그를 파싱할 수 없음.
  • SyntaxError: 구문 에러, 예: continue가 for 루프 밖에서 사용됨.
  • DeclarationError: 유효하지 않거나, 해결 불가능하거나, 충돌하는 식별자 이름. 예: 식별자를 찾을 수 없음.
  • TypeError: 타입 시스템 안의 에러, 예: 잘못된 타입 변환, 잘못된 할당 등.
  • UnimplementedFeatureError: 컴파일러가 지원하지 않지만 미래 버전에서 지원될 것으로 기대되는 기능.
  • InternalCompilerError: 컴파일러에서 트리거된 내부 버그 — 이슈로 보고해야 해요.
  • Exception: 컴파일 중 알 수 없는 실패 — 이슈로 보고해야 해요.
  • CompilerError: 컴파일러 스택의 잘못된 사용 — 이슈로 보고해야 해요.
  • FatalError: 제대로 처리되지 않은 치명적 에러 — 이슈로 보고해야 해요.
  • YulException: Yul 코드 생성 중 에러 — 이슈로 보고해야 해요.
  • Warning: 컴파일을 멈추지 않았지만 가능하면 다뤄야 하는 경고.
  • Info: 컴파일러가 사용자에게 유용할 것이라고 생각하는 정보. 위험하지 않고 반드시 다뤄야 할 필요는 없음.

실험적 모드 (Experimental Mode)

스테이블 릴리스에 포함된 일부 언어·컴파일러 기능은 그 자체로 스테이블로 간주되지 않아요. 그것들은 문서화가 부실하고(전혀 없기도), 종종 충분히 테스트되지 않았으며, 따라서 아직 프로덕션 사용을 위한 것이 아니에요. 많은 경우 큰 기능을 각 반복이 이미 스테이블인 상태로 점진적으로 개발하는 것이 가능해요. 그러나 때로는 프로토타입으로 시작해 사용자의 피드백을 받으며 여러 릴리스에 걸쳐 안정화하는 것이 더 선호돼요.

우발적 사용을 막기 위해, 그런 기능은 실험적 모드를 활성화해서만 접근할 수 있어요. 실험적 기능에 대한 하위 호환 보장은 없어요. 그것들은 컴파일러의 비-브레이킹 릴리스에서 브레이킹 방식으로 변경될 수 있어요. 그것들에 영향을 주는 주요 변경만 changelog에 기록돼요.

실험적 모드를 활성화하려면 명령줄에서 --experimental 플래그를, Standard JSON 입력에서는 동등한 settings.experimental 불리언 설정을 사용해요. 이 모드의 사용은 메타데이터에 기록된다는 점을 주의해요:

  • CBOR 메타데이터의 experimental 플래그가 true로 설정,
  • JSON 메타데이터의 settings.experimental이 true로 설정.

참고 (Note)

버전 0.8.35 이전에는 대부분의 실험적 기능이 추가 보호 장치 없이 사용 가능했어요. 일부는 pragma experimental 뒤에 있었지만 일관되게 그런 것은 아니었어요. 그것들에 대한 정보도 CBOR 메타데이터에만 기록됐고 항상 그런 것도 아니었어요. 실험적 모드의 주요 목표는 이것을 체계화하고, 미완성이거나 프로덕션 준비가 되지 않은 기능에 의존할 때 사용자가 완전히 인지하도록 하는 것이에요.

아래 표는 현재 사용 가능한 모든 실험적 기능을 자세히 설명해요.

기능 ID 바이트코드에 영향 플래그/pragma
AST import ast-import 예 --import-ast
EVM Assembly import evmasm-import 예 --import-asm-json
IR AST ir-ast 아니오 --ir-ast-json, --ir-optimized-ast-json
Non-mainnet EVMs evm 예 --evm-version <version name>
Ethdebug ethdebug 아니오 --ethdebug-resources, --ethdebug-compilation, --ethdebug-program, --ethdebug-program-runtime, --debug-info ethdebug
SSA CFG ssa-cfg 아니오 --yul-cfg-json
예 --via-ssa-cfg

더 알아보기 (Learn more)