Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

EIP-7702를 사용한 계정 추상화

이 가이드는 EIP-7702를 EOA에 적용하고, 위임을 사용하여 여러 USDT0 전송을 하나의 원자적 트랜잭션으로 일괄 처리합니다. EOA는 그 과정에서 주소와 개인 키를 유지합니다. 세션 키 및 지출 한도에 대해서는 감사된 스마트 계정을 사용하는 EIP-7702를 사용한 세션 키를 참조하세요.

전제 조건

  • EOA와 스마트 계약 계정의 이해 (EOA는 기본적으로 코드가 없음).
  • EVM 트랜잭션 유형에 대한 이해 (EIP-2718).
  • 위임 계약 배포를 위한 Foundry.

개요

EIP-7702는 authorizationList를 포함하는 새로운 트랜잭션 유형(0x04)을 도입합니다. 각 권한 부여는 EOA가 코드를 실행할 스마트 계약을 지정합니다. 흐름은 다음과 같습니다:

  1. 위임 계약 선택 또는 배포: EOA가 사용하려는 로직을 구현하는 표준 Solidity 계약입니다. 가능한 경우 감사된 계약을 사용하세요.
  2. 권한 부여 서명: EOA 소유자가 위임 계약을 승인하는 메시지에 서명합니다.
  3. EIP-7702 트랜잭션 제출: 트랜잭션에 권한 부여가 포함되며, EOA는 실행 중에 위임자의 코드를 실행합니다.

사용 사례: 배치 트랜잭션

배치는 여러 호출(토큰 전송, 승인 또는 모든 조합)을 하나의 원자적 트랜잭션으로 실행합니다. 배치 결제는 한 가지 예시입니다. 급여 실행을 위해 10개의 개별 트랜잭션을 보내는 대신, EOA는 모든 트랜잭션을 한 번에 실행합니다.

1단계: 배치 실행기 배포

BatchExecutor는 호출 목록을 순서대로 실행하고, 어떤 호출이라도 실패하면 전체 배치를 되돌립니다. EOA 자체에서만 호출을 허용하므로, 소유자의 키만 배치를 트리거할 수 있습니다.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
 
/// @title BatchExecutor (example, unaudited)
/// @notice EIP-7702 delegate that executes a batch of calls from the EOA.
contract BatchExecutor {
    struct Call {
        address target;
        uint256 value;
        bytes data;
    }
 
    function execute(Call[] calldata calls) external payable {
        // SAFE: only the EOA itself, signing with its own key, can run a batch.
        require(msg.sender == address(this), "only self");
 
        for (uint256 i = 0; i < calls.length; i++) {
            (bool success,) = calls[i].target.call{value: calls[i].value}(calls[i].data);
            require(success, "call failed");
        }
    }
}

한 번 배포합니다. 여러 EOA가 동일한 배포에 위임할 수 있습니다.

forge create src/BatchExecutor.sol:BatchExecutor \
  --rpc-url https://rpc.testnet.stable.xyz \
  --private-key $PRIVATE_KEY \
  --broadcast
Deployer: 0xYourAddress...
Deployed to: 0xBatchExecutor...
Transaction hash: 0x...

2단계: 권한 부여 서명

EOA 소유자는 위임 계약을 지정하는 권한 부여에 서명합니다. 이 권한 부여는 EIP-7702 트랜잭션에 포함됩니다.

// config.ts
import { ethers } from "ethers";
 
export const STABLE_TESTNET_RPC = "https://rpc.testnet.stable.xyz";
export const STABLE_TESTNET_CHAIN_ID = 2201;
export const USDT0_ADDRESS = "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9";
export const DELEGATE_ADDRESS = "0xBatchExecutor..."; // from Step 1
 
export const provider = new ethers.JsonRpcProvider(STABLE_TESTNET_RPC);
export const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// signAuthorization.ts
import { DELEGATE_ADDRESS, STABLE_TESTNET_CHAIN_ID, provider, wallet } from "./config";
 
export async function signAuthorization() {
  const txNonce = await provider.getTransactionCount(wallet.address);
 
  return wallet.authorize({
    chainId: STABLE_TESTNET_CHAIN_ID,
    address: DELEGATE_ADDRESS,
    // The EOA sends this transaction itself. Its nonce increments before the
    // authorization is checked, so the authorization must use the next nonce.
    nonce: txNonce + 1,
  });
}

3단계: EIP-7702 트랜잭션 제출

권한 부여와 execute 호출을 결합합니다. 이 예시는 단일 트랜잭션에서 세 개의 USDT0 전송을 일괄 처리합니다.

import { ethers } from "ethers";
import { wallet, USDT0_ADDRESS } from "./config";
import { signAuthorization } from "./signAuthorization";
 
const usdt0Interface = new ethers.Interface([
  "function transfer(address to, uint256 amount)",
]);
 
const batchInterface = new ethers.Interface([
  "function execute((address target, uint256 value, bytes data)[] calls)",
]);
 
async function main() {
  const recipients = [
    { to: "0xAlice...", amount: ethers.parseUnits("100", 6) },
    { to: "0xBob...",   amount: ethers.parseUnits("200", 6) },
    { to: "0xCarol...", amount: ethers.parseUnits("150", 6) },
  ];
 
  const batchData = batchInterface.encodeFunctionData("execute", [
    recipients.map(({ to, amount }) => ({
      target: USDT0_ADDRESS,
      value: 0n,
      data: usdt0Interface.encodeFunctionData("transfer", [to, amount]),
    })),
  ]);
 
  const signedAuth = await signAuthorization();
 
  const tx = await wallet.sendTransaction({
    type: 4,                          // EIP-7702 transaction type
    to: wallet.address,               // call is directed at the EOA itself
    data: batchData,                  // execute() call to run
    authorizationList: [signedAuth],
    maxPriorityFeePerGas: 0n,
  });
 
  const receipt = await tx.wait(1);
  console.log("Batch transactions executed in tx:", receipt.hash);
}
 
main();
Batch transactions executed in tx: 0x...

EOA는 단일 원자적 트랜잭션에서 세 가지 전송을 모두 실행합니다. 위임은 지속되므로, 이후의 배치는 authorizationList 없이 execute calldata를 사용하여 wallet.address로 일반 트랜잭션만 필요합니다.

지출 한도 및 세션 키

지출 한도 및 세션 키는 소유자가 설정한 규칙 내에서 두 번째 키가 EOA를 대신하여 작동하도록 합니다. 사용자 지정 위임자 대신 감사된 스마트 계정을 사용하세요.

  • 접근 제어가 전체 기능입니다. 사용자 지정 위임자에서 호출자 확인을 놓치면 세션 키뿐만 아니라 누구에게나 EOA가 노출됩니다.
  • 저장은 위임자보다 오래 지속됩니다. 위임자 상태는 EOA의 저장소에 있습니다. 다른 저장소 레이아웃을 가진 계약에 다시 위임하면 잘못 읽거나 덮어쓸 수 있습니다.

EIP-7702를 사용한 세션 키는 EOA를 Modular Account v2에 위임하고, 키에 만료되는 USDT0 허용량을 부여합니다. 이 허용량이 지출 한도입니다.

중요한 고려 사항

  • 영구 위임: 위임은 EOA가 명시적으로 변경하거나 지울 때까지 지속됩니다. 단일 트랜잭션에 국한되지 않습니다.
  • 가스 비용: EIP-7702 트랜잭션은 권한 부여 처리로 인해 기본 가스가 약간 더 높지만, 위임자가 여러 호출을 일괄 처리할 때 상쇄됩니다.
  • 감사된 위임자 사용: 악의적이거나 보호되지 않은 위임 계약은 EOA의 자산을 고갈시킬 수 있습니다. 위의 BatchExecutor는 감사되지 않은 예시입니다. 프로덕션에서는 감사된 계약에만 위임하세요.

주요 요점

  • EIP-7702는 EOA가 새로운 계정 유형으로 마이그레이션하지 않고도 스마트 계약 로직을 실행할 수 있도록 합니다.
  • Stable에서 EIP-7702는 기존 EOA에서 배치 결제, 지출 한도 및 범위가 지정된 세션 키를 가능하게 합니다.
  • 위임자의 모든 함수는 누구나 EOA에서 호출할 수 있으므로, 위임자는 호출자를 확인해야 합니다.
  • 위임은 명시적으로 변경될 때까지 지속됩니다. 항상 감사된 위임 계약을 사용하세요.

다음 권장 사항

  • EIP-7702를 사용한 세션 키: 소유자 키 없이 트랜잭션을 보낼 수 있는 범위가 지정된 만료 키를 부여합니다.
  • 구독 및 수집: SubscriptionManager를 사용하여 반복 구독 결제에 EIP-7702를 적용합니다.
  • EIP-7702 개념: 배포하기 전에 위임 모델을 이해합니다.
  • EIP-7702 참조: 0x04 트랜잭션 형식 및 권한 부여 필드를 찾아봅니다.