> ## Documentation Index
> Fetch the complete documentation index at: https://daehan-base.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 스펜드 퍼미션

> 추가 서명 없이 신뢰할 수 있는 지출자가 자산을 이동하도록 허용하기

export const GithubRepoCard = ({title, githubUrl}) => {
  return <a href={githubUrl} target="_blank" rel="noopener noreferrer" className="mb-4 flex items-center rounded-lg bg-zinc-900 p-4 text-white transition-all hover:bg-zinc-800">
      <div className="flex w-full items-center gap-3">
        <svg height="24" width="24" className="flex-shrink-0 dark:fill-white" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
          <path fill="currentColor" fillRule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
        </svg>

        <div className="flex min-w-0 flex-grow flex-col">
          <span className="truncate text-base font-medium">{title}</span>
          <span className="truncate text-xs text-zinc-400">{githubUrl}</span>
        </div>

        <svg className="h-5 w-5 flex-shrink-0 text-zinc-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
        </svg>
      </div>
    </a>;
};

신뢰할 수 있는 지출자가 추가 사용자 서명 없이 Base Account에서 자산을 이동할 수 있도록 스펜드 퍼미션을 생성하고 관리하는 방법을 알아보세요.

## 개요

스펜드 퍼미션을 통해 사용자는 신뢰할 수 있는 지출자에게 일정 기간 한도를 부여하여, 정해진 한도 내에서 사용자를 대신해 자산을 이동할 수 있도록 허용합니다. 이를 통해 정기 결제, 구독, 자동화된 트랜잭션에 대한 원활한 사용자 경험을 만들 수 있습니다.

### 달성할 내용

이 가이드를 마치면 다음을 할 수 있습니다:

* 특정 한도와 기간으로 스펜드 퍼미션 생성
* 기존 스펜드 퍼미션 조회 및 관리
* 스펜드 퍼미션을 사용하여 트랜잭션 실행
* React 애플리케이션에 스펜드 퍼미션 기능 구현

이 가이드의 코드 스니펫은 다음 예제 프로젝트를 기반으로 합니다:

<GithubRepoCard title="Base Account Privy 템플릿" githubUrl="https://github.com/base/base-account-privy" />

## 설정

[설정](/base-account/framework-integrations/privy/setup) 가이드를 따라 Base Account와 함께 Privy를 설정하세요.

## 구현

### 컴포넌트 설정

<CodeGroup>
  ```tsx 스펜드 퍼미션 컴포넌트 (components/sections/spend-permissions.tsx) expandable theme={null}
  "use client";

  import { useState, useEffect, useCallback, useMemo } from "react";
  import { useBaseAccountSdk, useWallets } from "@privy-io/react-auth";
  import {
    requestSpendPermission,
    prepareSpendCallData,
    fetchPermissions,
    getPermissionStatus,
    type SpendPermission,
  } from "@base-org/account/spend-permission/browser";
  import { base } from "@privy-io/chains";

  export const SpendPermissions = () => {
    const { baseAccountSdk } = useBaseAccountSdk();
    const { wallets } = useWallets();
    const [permissions, setPermissions] = useState<SpendPermission[]>([]);
    const [selectedPermission, setSelectedPermission] = useState<SpendPermission | null>(null);
    const [loading, setLoading] = useState(false);
    
    // 설정
    const spenderAddress = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
    const tokenAddress = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; // Base의 USDC

    // Base Account 지갑 찾기
    const baseAccount = useMemo(() => {
      return wallets.find((wallet) => wallet.walletClientType === 'base_account');
    }, [wallets]);

    const provider = baseAccountSdk?.getProvider();
    const account = baseAccount?.address;

    const loadPermissions = useCallback(async () => {
      if (!account || !provider || !spenderAddress) return;
      
      try {
        setLoading(true);
        const fetchedPermissions = await fetchPermissions({
          account,
          chainId: base.id,
          spender: spenderAddress,
          provider,
        });
        setPermissions(fetchedPermissions);
      } catch (error) {
        console.error("Failed to load permissions:", error);
      } finally {
        setLoading(false);
      }
    }, [account, provider, spenderAddress]);

    const handleRequestSpendPermission = async () => {
      if (!account || !provider || !spenderAddress || !tokenAddress) return;

      try {
        setLoading(true);
        
        const permission = await requestSpendPermission({
          account,
          spender: spenderAddress,
          token: tokenAddress,
          chainId: base.id,
          allowance: BigInt(1) * BigInt(10 ** 6), // 1 USDC (소수점 6자리)
          periodInDays: 1, // 1일
          provider,
        });

        setPermissions([...permissions, permission]);
      } catch (error) {
        console.error("Failed to create Spend Permission:", error);
      } finally {
        setLoading(false);
      }
    };

    const handleUseSpendPermission = async () => {
      if (!selectedPermission || !provider || !spenderAddress) return;

      try {
        setLoading(true);
        
        // 퍼미션 상태 확인
        const { isActive, remainingSpend } = await getPermissionStatus(selectedPermission);
        
        if (!isActive) {
          console.error("Selected permission is not active");
          return;
        }

        const spendAmount = BigInt(100) * BigInt(10 ** 6); // 100 USDC
        
        if (remainingSpend < spendAmount) {
          console.error("Insufficient remaining allowance");
          return;
        }

        // 스펜드 콜 준비
        const spendCalls = await prepareSpendCallData(selectedPermission, spendAmount);
        console.log("Spend calls prepared:", spendCalls);
        
      } catch (error) {
        console.error("Failed to use Spend Permission:", error);
      } finally {
        setLoading(false);
      }
    };

    return (
      <div className="space-y-4">
        <div className="flex gap-4">
          <button 
            onClick={handleRequestSpendPermission}
            disabled={loading || !account}
            className="px-4 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
          >
            스펜드 퍼미션 생성
          </button>
          <button 
            onClick={loadPermissions}
            disabled={loading || !account}
            className="px-4 py-2 bg-green-600 text-white rounded disabled:opacity-50"
          >
            퍼미션 불러오기
          </button>
          <button 
            onClick={handleUseSpendPermission}
            disabled={loading || !selectedPermission}
            className="px-4 py-2 bg-purple-600 text-white rounded disabled:opacity-50"
          >
            퍼미션 사용
          </button>
        </div>

        {/* 설정 표시 */}
        <div className="p-4 rounded-lg">
          <h4 className="font-semibold mb-3">설정</h4>
          <div className="text-sm space-y-1">
            <div><strong>지출자:</strong> {spenderAddress}</div>
            <div><strong>토큰:</strong> {tokenAddress} (USDC)</div>
            <div><strong>한도:</strong> 하루 $1 USDC</div>
          </div>
        </div>

        {/* 퍼미션 목록 */}
        {permissions.length > 0 && (
          <div className="p-4 rounded-lg">
            <h4 className="font-semibold mb-3">기존 퍼미션</h4>
            <div className="space-y-2">
              {permissions.map((permission, index) => (
                <div
                  key={index}
                  className={`p-3 border rounded-md cursor-pointer ${
                    selectedPermission === permission
                      ? "border-blue-500"
                      : "border-gray-300"
                  }`}
                  onClick={() => setSelectedPermission(permission)}
                >
                  <div className="text-sm">
                    <div><strong>지출자:</strong> {permission.permission.spender?.slice(0, 10)}...</div>
                    <div><strong>토큰:</strong> {permission.permission.token?.slice(0, 10)}...</div>
                    <div><strong>한도:</strong> {permission.permission.allowance?.toString()}</div>
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}
      </div>
    );
  };
  ```
</CodeGroup>

### 주요 메서드

#### 스펜드 퍼미션 생성

`requestSpendPermission`을 사용하여 새 스펜드 퍼미션을 생성합니다:

```tsx theme={null}
const permission = await requestSpendPermission({
  account,
  spender: spenderAddress,
  token: tokenAddress,
  chainId: base.id,
  allowance: BigInt(1) * BigInt(10 ** 6), // 1 USDC
  periodInDays: 1, // 1일
  provider,
});
```

#### 퍼미션 조회

`fetchPermissions`를 사용하여 기존 퍼미션을 조회합니다:

```tsx theme={null}
const permissions = await fetchPermissions({
  account,
  chainId: base.id,
  spender: spenderAddress,
  provider,
});
```

#### 퍼미션 사용

퍼미션 상태를 확인하고 스펜드 콜을 준비합니다:

```tsx theme={null}
// 퍼미션이 활성 상태인지 확인
const { isActive, remainingSpend } = await getPermissionStatus(permission);

// 스펜드 트랜잭션 데이터 준비
const spendCalls = await prepareSpendCallData(permission, spendAmount);
```

### 설정 옵션

#### 한도 및 기간

지출 한도와 기간을 설정합니다:

```tsx theme={null}
{
  allowance: BigInt(100) * BigInt(10 ** 6), // 100 USDC (소수점 6자리)
  periodInDays: 7, // 7일
}
```

#### 토큰 주소

Base의 주요 토큰 주소:

* **USDC**: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`
* **ETH**: 네이티브 토큰 `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`
* **DAI**: `0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb`

### 활용 사례

스펜드 퍼미션이 적합한 경우:

* **구독**: 사용자 상호작용 없는 정기 결제
* **DeFi 프로토콜**: 자동화된 거래 및 수익 농업
* **게임**: 게임 내 구매 및 보상
* **커머스**: 간소화된 결제 경험

<GithubRepoCard title="Base Account Privy 템플릿" githubUrl="https://github.com/base/base-account-privy" />
