EIP-7702를 사용한 세션 키
세션 키는 계정에 서명할 수 있는 두 번째 키로, 소유자가 설정한 권한 및 만료 기간으로 제한됩니다. 소유자 키는 한 번 서명하여 권한을 부여합니다. 그 후 앱, 백엔드 또는 에이전트가 세션 키로 트랜잭션을 보내고 사용자는 각 트랜잭션을 승인할 필요가 없습니다.
이 가이드는 Stable 테스트넷에서 Alchemy Wallet API를 사용합니다. EOA는 감사된 스마트 계정인 Modular Account v2에 위임하므로 위임 계약을 작성하거나 배포할 필요가 없습니다.
작동 방식
- EOA 위임. 소유자는 EOA를 Modular Account v2로 지정하는 EIP-7702 권한을 서명합니다. EOA는 주소, 키 및 잔액을 유지합니다.
- 세션 부여. 소유자는 세션 부여에 서명합니다. 세션 키의 주소, 권한 및 만료 기간입니다. 아직 온체인에 기록된 내용은 없습니다.
- 세션 키로 전송. 유일한 서명자가 세션 키인 클라이언트가 소유자 계정으로 호출을 보냅니다. 첫 번째 호출은 온체인에 세션을 설치합니다.
전제 조건
- Node 22 이상.
- Stable 테스트넷이 활성화된 Alchemy 앱 및 API 키.
- 선택 사항: Alchemy Gas Manager 정책 ID. 정책을 사용하면 Alchemy가 가스를 지원합니다. 정책이 없으면 EOA가 USDT0으로 가스를 지불하고 테스트넷 잔액이 필요합니다.
npm install @alchemy/wallet-apis viemadded 2 packages1단계: 클라이언트 구성
소유자가 서명한 클라이언트 하나를 생성합니다. Wallet API는 기본적으로 EIP-7702 모드에서 실행되므로 소유자의 EOA 주소가 스마트 계정 주소입니다.
// config.ts
import { alchemyWalletTransport, createSmartWalletClient } from "@alchemy/wallet-apis";
import { createPublicClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { stableTestnet } from "viem/chains";
export const chain = stableTestnet;
export const transport = alchemyWalletTransport({ apiKey: process.env.ALCHEMY_API_KEY! });
export const paymaster = process.env.ALCHEMY_GAS_POLICY_ID
? { policyId: process.env.ALCHEMY_GAS_POLICY_ID }
: undefined;
export const USDT0_ADDRESS = "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9";
export const owner = privateKeyToAccount(process.env.OWNER_PRIVATE_KEY as Hex);
export const account = owner.address;
export const ownerClient = createSmartWalletClient({ transport, chain, signer: owner, paymaster });
export const publicClient = createPublicClient({ chain, transport: http() });2단계: EOA 위임
세션은 이미 위임된 계정에만 설치할 수 있습니다. 빈 호출 세트를 보냅니다. Wallet API는 누락된 위임을 감지하고 동일한 트랜잭션에서 EIP-7702 권한을 제출합니다.
// delegate.ts
import { account, ownerClient, publicClient } from "./config";
// 위임된 EOA는 코드 0xef0100 || 위임 주소를 가집니다.
const code = await publicClient.getCode({ address: account });
if (code?.startsWith("0xef0100")) {
console.log("already delegated to", `0x${code.slice(8)}`);
} else {
const { id } = await ownerClient.sendCalls({ calls: [] });
const status = await ownerClient.waitForCallsStatus({ id });
if (status.status !== "success") throw new Error("delegation failed");
console.log("delegated in tx", status.receipts?.[0]?.transactionHash);
}delegated in tx 0x...3단계: 범위가 지정된 세션 키 부여
세션 키를 생성하고 사용 사례에 맞는 가장 좁은 권한을 부여합니다. 이 예제에서는 키가 1시간 동안 최대 10 USDT0를 전송할 수 있도록 합니다.
// grant.ts
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { parseUnits } from "viem";
import { account, ownerClient, USDT0_ADDRESS } from "./config";
// 이 키는 이를 사용하는 앱 또는 에이전트가 실행되는 곳에 저장합니다.
export const sessionKey = privateKeyToAccount(generatePrivateKey());
const expirySec = Math.floor(Date.now() / 1000) + 60 * 60;
export const permissions = await ownerClient.grantPermissions({
account,
expirySec,
key: { publicKey: sessionKey.address, type: "secp256k1" },
permissions: [
{
type: "erc20-token-transfer",
data: { address: USDT0_ADDRESS, allowance: parseUnits("10", 6) },
},
],
});
console.log("session key", sessionKey.address);
console.log("expires ", new Date(expirySec * 1000).toISOString());session key 0x...
expires 2026-10-12T13:00:00.000Zpermissions.context는 부여 증명입니다. 세션 키가 보내는 모든 호출과 함께 이를 전달하십시오.
가스 정책이 가스를 후원하지 않는 경우, 세션 키의 트랜잭션은 계정의 USDT0에서 가스를 지불합니다. 지출 상한을 설정하려면 gas-limit 권한을 추가하십시오.
권한 유형
| 유형 | data | 부여 내용 |
|---|---|---|
erc20-token-transfer | address, allowance | 하나의 ERC-20 토큰에 대한 transfer 및 approve, 총 allowance까지 |
native-token-transfer | allowance | 총 allowance까지의 네이티브 값 전송 |
gas-limit | limit | 세션 키가 계정에서 사용할 수 있는 가스 |
contract-access | address | 하나의 계약에 대한 모든 함수 |
functions-on-contract | address, functions | 하나의 계약에 대한 나열된 함수 선택자 |
functions-on-all-contracts | functions | 모든 계약에 대한 나열된 함수 선택자 |
account-functions | functions | 계정 자체에 대한 나열된 함수 선택자 |
root | 없음 | 만료 전까지 소유자가 할 수 있는 모든 것 |
4단계: 세션 키로 트랜잭션 보내기
소유자의 계정 역할을 하는 세션 키가 유일한 서명자인 두 번째 클라이언트를 생성합니다. 소유자 키는 여기부터 사용되지 않습니다.
// send.ts
import { createSmartWalletClient } from "@alchemy/wallet-apis";
import { encodeFunctionData, erc20Abi, parseUnits } from "viem";
import { account, chain, paymaster, transport, USDT0_ADDRESS } from "./config";
import { permissions, sessionKey } from "./grant";
const sessionClient = createSmartWalletClient({ transport, chain, signer: sessionKey, account, paymaster });
const { id } = await sessionClient.sendCalls({
calls: [
{
to: USDT0_ADDRESS,
data: encodeFunctionData({
abi: erc20Abi,
functionName: "transfer",
args: ["0xRecipient...", parseUnits("1", 6)],
}),
},
],
capabilities: { permissions },
});
const status = await sessionClient.waitForCallsStatus({ id });
if (status.status !== "success") throw new Error("session-key call failed");
console.log("sent in tx", status.receipts?.[0]?.transactionHash);sent in tx 0x...부여된 권한 외부의 호출은 실패하고 자금을 이동하지 않습니다. 예: 남은 허용량을 초과하는 전송 또는 다른 토큰의 전송.
세션 종료
세션은 expirySec에 작동을 중지합니다. 만료 기간을 짧게 유지하고 이전 세션이 끝나면 새 세션을 부여하십시오.
위임 해제
스마트 계정을 완전히 제거하려면 소유자는 EOA를 영주소에 다시 위임합니다. EOA는 이 트랜잭션을 직접 보내고 USDT0으로 가스를 지불합니다.
// undelegate.ts
import { createWalletClient, http, zeroAddress } from "viem";
import { chain, owner } from "./config";
const walletClient = createWalletClient({ account: owner, chain, transport: http() });
const authorization = await walletClient.signAuthorization({
contractAddress: zeroAddress,
executor: "self", // the EOA sends the transaction, so the authorization nonce is its nonce + 1
});
const hash = await walletClient.sendTransaction({
to: owner.address,
authorizationList: [authorization],
maxPriorityFeePerGas: 0n,
});
console.log("delegation cleared in tx", hash);delegation cleared in tx 0x...Alchemy 플랜에 위임 해제를 위한 후원 정책이 포함되어 있는 경우, ownerClient.undelegateAccount()는 가스 후원과 함께 동일한 재설정을 보냅니다.
고려 사항
- 세션 키는 자격 증명입니다. 이를 소유한 사람은 만료될 때까지 권한 내에서 행동할 수 있습니다. 다른 개인 키처럼 저장하십시오.
- 위임은 지속됩니다. EOA는 소유자가 위임을 해제하거나 교체할 때까지 Modular Account v2를 실행합니다. 업그레이드할 의향이 있는 키로 테스트하십시오.
- EOA당 하나의 위임. 구독 관리자와 같은 다른 계약에 위임하면 Modular Account v2가 대체되고 세션이 비활성화됩니다.
다음 추천
- EIP-7702 개념: 배포하기 전에 위임 모델과 보안 절충을 이해하십시오.
- EIP-7702를 사용한 계정 추상화: 위임된 EOA에서 여러 호출을 하나의 트랜잭션으로 일괄 처리합니다.
- EIP-7702 참조: 유형 4 트랜잭션 형식 및 권한 필드를 찾아보십시오.

