Pectra 이후의 EIP-7702: 이더리움 앱 개발자를 위한 실용 가이드북
2025년 5월 7일, 이더리움의 Pectra 업그레이드(Prague + Electra)가 메인넷에 적용되었습니다. 개발자에게 가장 눈에 띄는 변화 중 하나는 EIP-7702로, 이는 외부 소유 계정(EOA)이 자금을 마이그레이션하거나 주소를 변경하지 않고도 스마트 컨트랙트 로직을 "마운트"할 수 있게 합니다. 지갑, dapp, 또는 릴레이어를 구축한다면, 이것은 스마트 계정 UX로의 더 간단한 경로를 제공합니다.
아래는 간결한 구현 중심 가이드입니다: 실제로 배포된 것, 7702가 어떻게 작동하는지, 순수 ERC-4337보다 언제 선택해야 하는지, 그리고 오늘 적용할 수 있는 복사-붙여넣기 가능한 스캐폴드.
실제로 배포된 것
- EIP-7702는 Pectra의 최종 범위에 포함되어 있습니다. Pectra 하드포크의 메타 EIP는 공식적으로 포함된 변경사항 중 7702를 나열합니다.
- 활성화 세부사항: Pectra는 2025년 5월 7일 에포크 364032에서 메인넷에 활성화되었으며, 모든 주요 테스트넷에서의 성공적인 활성화를 따랐습니다.
- 툴체인 주의사항: Solidity v0.8.30은 Pectra 호환성을 위해 기본 EVM 타겟을 prague로 업데이트했습니다. 특히 특 정 버전을 고정하는 경우 컴파일러와 CI 파이프라인을 업그레이드해야 합니다.
EIP-7702—어떻게 작동하는가 (세부사항)
EIP-7702는 새로운 트랜잭션 유형과 EOA가 실행 로직을 스마트 컨트랙트에 위임할 수 있는 메커니즘을 도입합니다.
- 새로운 트랜잭션 유형 (0x04): 타입-4 트랜잭션은
authorization_list
라는 새로운 필드를 포함합니다. 이 리스트는 하나 이상의 인증 튜플—(chain_id, address, nonce, y_parity, r, s)
—을 포함하며, 각각은 EOA의 개인키로 서명됩니다. 이 트랜잭션이 처리되면, 프로토콜은 EOA의 코드 필드에 위임 표시자를 씁니다:0xef0100 || address
. 그 순간부터, EOA에 대한 모든 호출은 지정된address
(구현)로 프록시되지만, EOA의 저장소 및 잔고 컨텍스트 내에서 실행됩니다. 이 위임은 명시적으로 변경될 때까지 활성 상태를 유지합니다. - 체인 범위: 인증은
chain_id
를 제공하여 체인별이 될 수도 있고,chain_id
가0
으로 설정된 경우 모든 체인에 적용될 수도 있습니다. 이를 통해 사용자가 각각에 대해 새 인증에 서명할 필요 없이 여러 네트워크에 동일한 구현 컨트랙트를 배포할 수 있습니다. - 취소: EOA를 원래의 프로그래밍 불가능한 동작으로 되돌리려면, 구현
address
가 제로 주소로 설정된 다른 7702 트랜잭션을 보내기만 하면 됩니다. 이렇게 하면 위임 표시자가 지워집니다. - 셀프 스폰서 vs. 릴레이: EOA는 타입-4 트랜잭션을 스스로 제출할 수도 있고, 제3자 릴레이어가 EOA를 대신하여 제출할 수도 있습니다. 후자는 가스없는 사용자 경험을 만드는 데 일반적입니다. 논스 처리는 방법에 따라 약간 다르므로, 이 구별을 올바르게 관리하는 라이브러리를 사용하는 것이 중요합니다.
보안 모델 변경: 원래 EOA 개인키가 여전히 존재하기 때문에, 새로운 7702 트랜잭션을 제출하여 위임을 변경함으로써 항상 스마트 컨트랙트 규칙(소셜 복구나 지출 한도 등)을 재정의할 수 있습니다. 이것은 근본적인 변화입니다.
tx.origin
에 의존하여 호출이 EOA에서 왔는지 확인하는 컨트랙트는 7702가 이러한 가정을 깨뜨릴 수 있으므로 재감사가 필요합니다. 이에 따라 플로우를 감사하세요.
7702 또는 ERC-4337? (그리고 언제 결합할 것인가)
EIP-7702와 ERC-4337 모두 계정 추상화를 가능하게 하지만, 서로 다른 요구를 충족합니다.
- EIP-7702를 선택해야 할 때…
- 사용자에게 자금을 마이그레이션하거나 주소를 변경하도록 강요하지 않고 기존 EOA에 즉시 스마트 계정 UX를 제공하고 싶을 때.
- 새로운 기능으로 점진적으로 업그레이드할 수 있는 체인 간 일관된 주소가 필요할 때.
- 계정 추상화로의 전환을 단계별로 진행하고 싶을 때, 간단한 기능부터 시작하여 시간이 지나면서 복잡성을 추가하는.
- 순수 ERC-4337을 선택해야 할 때…
- 제품이 첫날부터 완전한 프로그래밍 가능성과 복잡한 정책 엔진(멀티시그, 고급 복구 등)을 필요로 할 때.
- 기존 EOA가 없는 새로운 사용자를 위해 구축하는 경우, 새로운 스마트 계정 주소와 관련 설정이 허용되는.
- 둘을 결합: 가장 강력한 패턴은 둘 다 사용하는 것입니다. EOA는 7702 트랜잭션을 사용하여 ERC-4337 지갑 구현을 로직으로 지정할 수 있습니다. 이렇게 하면 EOA가 4337 계정처럼 동작하게 되어, 기존 4337 인프라에 의해 번들링되고, 페이마스터에 의해 후원되며, 처리될 수 있습니다—사용자가 새 주소를 필요로 하지 않으면서 말이죠. 이는 EIP 저자들이 명시적으로 권장하는 전진 호환 경로입니다.
적용 가능한 최소 7702 스캐폴드
구현 컨트랙트와 이를 활성화하는 클라이언트 측 코드의 실용적인 예제는 다음과 같습니다.
1. 작고 감사 가능한 구현 컨트랙트
이 컨트랙트 코드는 지정되면 EOA의 컨텍스트 내에서 실행됩니다. 작고 감사 가능하게 유지하고, 업그레이드 메커니즘 추가를 고려하세요.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @notice EIP-7702를 통해 지정될 때 EOA 컨텍스트에서 호출을 실행합니다.
contract DelegatedAccount {
// 다른 컨트랙트와의 충돌을 피하기 위한 고유한 저장소 슬롯.
bytes32 private constant INIT_SLOT =
0x3fb93b3d3dcd1d1f4b4a1a8db6f4c5d55a1b7f9ac01dfe8e53b1b0f35f0c1a01;
event Initialized(address indexed account);
event Executed(address indexed to, uint256 value, bytes data, bytes result);
modifier onlyEOA() {
// 선택사항: 특정 함수를 호출할 수 있는 사람을 제한하는 검사 추가.
_;
}
function initialize() external payable onlyEOA {
// EOA의 저장소에 간단한 일회성 초기화 플래그 설정.
bytes32 slot = INIT_SLOT;
assembly {
if iszero(iszero(sload(slot))) { revert(0, 0) } // 이미 초기화된 경우 revert
sstore(slot, 1)
}
emit Initialized(address(this));
}
function execute(address to, uint256 value, bytes calldata data)
external
payable
onlyEOA
returns (bytes memory result)
{
(bool ok, bytes memory ret) = to.call{value: value}(data);
require(ok, "CALL_FAILED");
emit Executed(to, value, data, ret);
return ret;
}
function executeBatch(address[] calldata to, uint256[] calldata value, bytes[] calldata data)
external
payable
onlyEOA
{
uint256 n = to.length;
require(n == value.length && n == data.length, "LENGTH_MISMATCH");
for (uint256 i = 0; i < n; i++) {
(bool ok, ) = to[i].call{value: value[i]}(data[i]);
require(ok, "CALL_FAILED");
}
}
}
2. viem으로 EOA에 컨트랙트 지정 (타입-4 tx)
viem
과 같은 현대적 클라이언트는 인증에 서명하고 타입-4 트랜잭션을 보내는 내장 헬퍼를 가지고 있습니다. 이 예제에서 relayer
계정은 eoa
를 업그레이드하기 위한 가스를 지불합니다.
import { createWalletClient, http, encodeFunctionData } from "viem";
import { sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
import { abi, implementationAddress } from "./DelegatedAccountABI";
// 1. 릴레이어(가스 후원)와 업그레이드될 EOA 정의
const relayer = privateKeyToAccount(process.env.RELAYER_PK as `0x${string}`);
const eoa = privateKeyToAccount(process.env.EOA_PK as `0x${string}`);
const client = createWalletClient({
account: relayer,
chain: sepolia,
transport: http(),
});
// 2. EOA가 구현 컨트랙트를 가리키는 인증에 서명
const authorization = await client.signAuthorization({
account: eoa,
contractAddress: implementationAddress,
// EOA 자체가 이를 보내는 경우 추가할 것: executor: 'self'
});
// 3. 릴레이어가 EOA의 코드를 설정하고 initialize() 호출하기 위해 타입-4 트랜잭션을 보냄
const hash = await client.sendTransaction({
to: eoa.address, // 목적지는 EOA 자체
authorizationList: [authorization], // 새로운 EIP-7702 필드
data: encodeFunctionData({ abi, functionName: "initialize" }),
});
// 4. 이제 EOA는 추가 인증 없이 새로운 로직을 통해 제어할 수 있습니다
// 예를 들어, 트랜잭션을 실행하려면:
// await client.sendTransaction({
// to: eoa.address,
// data: encodeFunctionData({ abi, functionName: 'execute', args: [...] })
// });
3. 위임 취소 (일반 EOA로 되돌리기)
업그레이드를 취소하려면, EOA가 제로 주소를 구현으로 지정하는 인증에 서명하고 다른 타입-4 트랜잭션을 보내도록 합니다. 이후 eth_getCode(eoa.address)
호출은 빈 바이트를 반환해야 합니다.
프로덕션에서 작동하는 통합 패턴
- 기존 사용자를 위한 제자리 업그레이드: dapp에서 사용자가 Pectra 호환 네트워크에 있는지 감지합니다. 그렇다면 일회성 인증 서명을 트리거하는 선택적 "계정 업그레이드" 버튼을 표시합니다. 오래된 지갑을 가진 사용자를 위한 대체 경로(클래식
approve
+swap
등)를 유지합니 다. - 가스리스 온보딩: 초기 타입-4 트랜잭션을 후원하기 위해 릴레이어(백엔드 또는 서비스)를 사용합니다. 지속적인 가스리스 트랜잭션의 경우, 기존 페이마스터와 공개 멤풀을 활용하기 위해 ERC-4337 번들러를 통해 사용자 작업을 라우팅합니다.
- 크로스체인 롤아웃: 모든 체인에서 동일한 구현 컨트랙트를 지정하기 위해
chain_id = 0
인증을 사용합니다. 그런 다음 애플리케이션 로직 내에서 체인별로 기능을 활성화하거나 비활성화할 수 있습니다. - 관찰가능성: 백엔드는 타입-4 트랜잭션을 인덱싱하고 어떤 EOA가 업그레이드되었는지 추적하기 위해
authorization_list
를 파싱해야 합니다. 트랜잭션 후에는eth_getCode
를 호출하고 EOA의 코드가 이제 위임 표시자(0xef0100 || implementationAddress
)와 일치하는지 확인하여 변경을 검증합니다.
위협 모델 및 주의사항 (이것을 건너뛰지 마세요)
- 위임은 지속적입니다: EOA의 구현 컨트랙트 변경을 표준 스마트 컨트랙트 업그레이드와 같은 심각성으로 다루세요. 이는 감사, 명확한 사용자 커뮤니케이션, 이상적으로는 옵트인 플로우를 필요로 합니다. 사용자에게 조용히 새로운 로직을 푸시하지 마세요.
tx.origin
지뢰:msg.sender == tx.origin
을 사용하여 호출이 EOA에서 직접 왔는지 확인하는 로직은 이제 잠재적으로 취약합니다. 이 패턴은 EIP-712 서명이나 명시적 허용 목록과 같은 더 견고한 검사로 대체되어야 합니다.- 논스 수학: EOA가 자체 7702 트랜잭션을 후원하는 경우(
executor: 'self'
), 인증 논스와 트랜잭션 논스가 특정한 방식으로 상호작용합니다. 재생 문제를 피하기 위해 이를 올바르게 처리하는 라이브러리를 항상 사용하세요. - 지갑 UX 책임: EIP-7702 사양은 dapp이 사용자에게 임의의 지정에 서명하도록 요청해서는 안 된다고 경고합니다. 제안된 구현을 검증하고 안전한지 확인하는 것은 지갑의 책임입니다. 지갑 매개 보안의 이 원칙에 맞춰 UX를 설계하세요.
7702가 명확한 승리인 경우
- DEX 플로우: 멀티스텝
approve
와swap
을executeBatch
함수를 사용하여 단일 클릭으로 결합할 수 있습니다. - 게임 및 세션: 사용자가 새로운 지갑을 생성하고 자금을 조달할 필요 없이 제한된 시간이나 범위에서 세션 키와 같은 권한을 부여합니다.
- 기업 및 핀테크: 후원된 트랜잭션을 활성화하고 회계 및 신원을 위해 모든 체인에서 동일한 기업 주소를 유지하면서 맞춤 지출 정책을 적용합니다.
- L2 브리지 및 인텐트: 다른 네트워크에서 일관된 EOA 신원을 가진 더 부드러운 메타 트랜잭션 플로우를 생성합니다.
이러한 사용 사례는 ERC-4337이 약속한 동일한 핵심 이익을 나타내지만, 이제 단일 인증으로 모든 기존 EOA에서 사용할 수 있습니다.
배송 체크리스트
프로토콜
- 노드, SDK, 인프라 제공업체가 타입-4 트랜잭션과 Pectra의 "prague" EVM을 지원하는지 확인.
- 새로운 트랜잭션에서
authorization_list
필드를 파싱하도록 인덱서와 분석 도구 업데이트.
컨트랙트
- 필수 기능(배칭, 취소 등)을 가진 최소한의 감사된 구현 컨트랙트 개발.
- 메인넷에 배포하기 전에 테스트넷에서 취소 및 재지정 플로우를 철저히 테스트.