궁금한 주제를 찾아보세요

비트코인, 스테이블코인, 온체인 데이터처럼 주제로 검색하세요.

다시 읽을 이야기

저장한 글은 이 브라우저에만 보관됩니다.

해설기술·생태계

EVM CALL·DELEGATECALL·STATICCALL의 상태와 실행 문맥 비교

세 호출 opcode가 실행할 코드, storage 주소, msg.sender·msg.value와 상태 변경 권한을 어떻게 다르게 정하는지 프록시 사례로 설명합니다.

난이도 중급주제 카테고리와 개념 밀도 기준검토 정보AI 보조 초안 · 출처 목록 제공 · 주장별 대조 진행 중
호출 대상의 코드와 상태 금고, 위임된 코드, 쓰기가 제한된 유리 덮개를 나눈 EVM 호출 방식 비교
주제의 이해를 돕기 위해 imagegen으로 제작한 AI 생성 개념 일러스트
먼저 읽는 핵심

CALL은 대상 상태를, DELEGATECALL은 호출자 상태를 변경합니다.
STATICCALL은 하위 호출까지 상태 변경 금지를 전파합니다.
프록시는 storage layout과 반환 데이터 처리를 함께 검증해야 합니다.

CALL은 대상 주소의 코드와 storage·balance 문맥에서 실행하며 대상이 보는 msg.sender는 호출 계약입니다. DELEGATECALL은 대상의 코드만 빌리고 호출자의 주소·storage·balance에서 실행하며 원래 msg.sender와 msg.value를 유지합니다. STATICCALL은 CALL과 비슷하지만 value 인수가 없고 현재 호출과 하위 호출에 static flag를 전달해 SSTORE, LOG, CREATE와 value 전송 같은 상태 변경을 예외로 막습니다. 세 opcode 모두 성공 여부와 returndata를 반환하므로 low-level call 결과를 확인하지 않으면 실패를 성공처럼 처리할 수 있습니다.

코드 주소와 상태 주소를 먼저 나눕니다

Contract A가 B를 CALL하면 EVM은 B 주소의 bytecode를 B 계정 문맥에서 실행합니다. B의 SLOAD·SSTORE는 B storage를 읽고 쓰며 ADDRESS는 B, BALANCE 관련 문맥도 B를 가리킵니다. B가 보는 msg.sender는 A이고 CALL에 value가 있으면 msg.value로 전달됩니다. 외부 사용자가 A를 호출했다는 사실은 B에서 msg.sender만 보고 직접 알 수 없습니다.

DELEGATECALL은 B의 bytecode를 가져오지만 실행 주소는 A로 유지합니다. 따라서 B 코드의 slot 0 접근은 B가 아니라 A slot 0을 건드립니다. ADDRESS는 A이고 원래 외부 호출의 msg.sender와 msg.value가 보존됩니다. 이 특성이 프록시 구현을 가능하게 하지만 구현 계약의 변수 순서가 프록시 상태와 다르면 멀쩡한 코드가 관리자나 잔액을 덮어쓸 수 있습니다.

DELEGATECALL은 다른 계약의 상태로 이동하는 호출이 아니라, 다른 주소의 코드를 현재 계약의 상태 위에서 실행하는 방식입니다.

JOBCOIN 해설

VISUAL GUIDE프로토콜 실행의 기본 구조
입력이 실행 규칙을 통과할 때 상태가 바뀌는 프로토콜 개념도
그림과 함께 짚어볼 본문 내용

그림은 이 주제의 공통 개념을 단순화한 설명입니다. 아래 항목에서 이 글의 구체적인 조건과 예외를 함께 읽어보세요.

  1. 코드 주소와 상태 주소를 먼저 나눕니다

    Contract A가 B를 CALL하면 EVM은 B 주소의 bytecode를 B 계정 문맥에서 실행합니다.

  2. 세 opcode의 관찰 문맥을 표로 비교합니다

    STATICCALL은 대상 코드를 별도 대상 문맥에서 읽되 static flag를 true로 둡니다.

  3. 프록시 한 번의 호출을 단계별로 추적합니다

    사용자 U가 Proxy P 의 transfer 함수를 부르면 P fallback은 calldata를 Implementation I에 DELEGATECALL합니다.

AI로 제작한 개념도 · 실제 가격·거래 내역·통계가 아닙니다.

세 opcode의 관찰 문맥을 표로 비교합니다

STATICCALL은 대상 코드를 별도 대상 문맥에서 읽되 static flag를 true로 둡니다. EIP-214는 SSTORE, CREATE·CREATE2, LOG0부터 LOG4, SELFDESTRUCT와 0이 아닌 value의 CALL을 상태 변경으로 보고 예외를 내도록 정합니다. 호출된 코드가 다시 다른 계약을 호출해도 static flag가 복사되므로 우회해서 저장할 수 없습니다.

view 함수라는 Solidity 표시는 compiler가 STATICCALL을 선택하는 데 도움을 주지만, 임의 bytecode의 의미를 인간이 신뢰하라는 보증이 아닙니다. Static 문맥에서는 로그조차 상태 변경으로 금지되며, 호출 실패는 바깥 transaction 전체를 자동으로 revert시키지 않고 low-level 성공 값으로 돌아올 수 있습니다.

호출 opcode 문맥 비교
항목 CALL DELEGATECALL STATICCALL
실행 코드 대상 대상 대상
storage 주소 대상 호출자 대상
msg.sender 호출자 계약 원래 sender 유지 호출자 계약
msg.value 지정 value 원래 value 유지 0
상태 변경 가능 호출자 상태에 가능 현재·하위 frame 금지

프록시 한 번의 호출을 단계별로 추적합니다

사용자 U가 Proxy P의 transfer 함수를 부르면 P fallback은 calldata를 Implementation I에 DELEGATECALL합니다. I 코드가 balances[msg.sender]를 읽을 때 msg.sender는 U이고 storage는 P입니다. I 주소에 직접 저장된 balances를 읽지 않습니다. I 코드가 address(this)를 검사하면 I가 아니라 P가 나옵니다.

반대로 P가 I를 CALL했다면 I의 storage를 읽고 msg.sender는 P가 됩니다. 권한 검사 onlyOwner가 사용자 U를 기대했다면 실패하거나 P 자체가 권한자로 취급될 수 있습니다. 호출 종류 한 단어가 인증과 자산 위치를 동시에 바꾸므로 trace에는 opcode, code address, context address, sender, value를 함께 남깁니다.

  • Proxy와 implementation의 storage slot 표를 비교합니다.
  • Trace에서 CALL 계열 opcode와 depth를 확인합니다.
  • 각 frame의 msg.sender·msg.value·address(this)를 기록합니다.
  • Success bit와 returndata를 ABI 기준으로 디코딩합니다.
  • 업그레이드 전 delegatecall 회귀 테스트를 실행합니다.

실패와 returndata를 분리해 처리합니다

CALL 계열 opcode는 callee가 revert하거나 out of gas가 나면 success 0과 returndata를 돌려줍니다. Solidity high-level external call은 보통 예외를 전파하지만 address.call 같은 low-level API는 boolean을 확인해야 합니다. 반환값을 버리면 토큰 전송이나 관리자 호출 실패 뒤에도 로컬 로직이 계속될 수 있습니다.

Returndata가 Error(string), Panic(uint256), custom error 형식인지 또는 빈 bytes인지 구분합니다. 공격 대상은 임의 bytes를 정상 ABI 오류처럼 만들 수 있으므로 selector만 보고 신뢰하지 않습니다. Gas 전달도 EIP-150의 63/64 규칙과 callee 작업량에 영향을 받으며 gas가 많다는 이유로 호출 안전성이 생기지 않습니다.

STATICCALL도 읽기 결과의 최신성을 보장하지 않습니다

Static 실행은 해당 frame 동안 state write를 막을 뿐 조회한 block state가 최종 확정됐다는 의미가 아닙니다. eth_call의 block tag가 latest이면 reorg로 결과가 달라질 수 있고, 함수는 timestamp·block number·다른 계약 상태에 따라 매번 다른 값을 반환할 수 있습니다.

진단에서는 opcode 수준의 쓰기 금지와 RPC 조회 기준을 분리합니다. 안전한 프록시는 implementation code hash, upgrade authority, storage layout을 검증하고, 읽기 서비스는 safe 또는 finalized block 기준과 실패 반환을 기록해야 합니다.

자주 묻는 질문

DELEGATECALL 대상이 msg.sender를 프록시로 보나요?

아닙니다. DELEGATECALL은 호출 전 frame의 msg.sender와 msg.value를 보존하므로 일반 프록시에서는 원래 사용자를 봅니다.

STATICCALL 안에서 event를 emit할 수 있나요?

없습니다. LOG opcode는 EIP-214의 상태 변경 금지 목록에 있어 static 문맥에서 예외가 납니다.

Low-level call의 false는 transaction 전체 revert인가요?

호출 frame 실패를 뜻합니다. 호출자가 false를 확인해 직접 revert하지 않으면 바깥 실행은 계속될 수 있습니다.

더 깊이 읽기

본문에서 다룬 개념과 확인 절차를 다음 글에서 이어서 살펴보세요.

참고한 원문 자료

자료 확인 기준일 2026.09.27
  1. Solidity Documentation — Delegatecall and Librariesdocs.soliditylang.org
  2. EIP-214: New opcode STATICCALLeips.ethereum.org
자료 대조 기록과 확인 범위
출처 수집
원문 링크 2개 제공
핵심 주장 대조
완료 근거가 아직 기록되지 않았습니다.
분야 전문가 검수
별도 완료 기록이 없습니다.

원문 링크와 자료 확인일은 글 전체의 주장 대조나 전문가 검수 완료를 뜻하지 않습니다. 별도 확인이 필요한 절차는 원문의 적용 대상과 최신 안내를 함께 확인해 주세요.

AI 활용 안내

이 글은 초안 구성과 자료 정리에 AI를 활용했습니다. 글에 표시된 출처와 기준일을 함께 확인해 주세요. 별도 검토 정보가 없다면 전문가 검수를 뜻하지 않습니다.

이해를 위한 정보 콘텐츠

이 글은 특정 자산의 매수·매도 또는 수익을 권유하지 않습니다. 자료의 발표 시점과 이후 변경 사항을 함께 확인해 주세요.

편집 원칙 보기 →이 기사 정정 제보 →