Skip to main content
Wagmi 훅과 Base Account 프로바이더 메서드를 사용하여 서브 어카운트를 생성하고 관리하는 방법을 알아보세요.

사전 요구 사항

다음이 완료되어 있는지 확인하세요:

개요

서브 어카운트를 통해 스펜드 퍼미션을 사용하여 상위 계정의 잔액을 사용할 수 있는 자식 계정을 생성할 수 있습니다. 이를 통해 빈번한 사용자 서명의 필요성을 줄이고 사용자 경험을 향상시킵니다.

서브 어카운트 생성

Base Account 프로바이더를 사용하여 서브 어카운트를 생성합니다:
import { useAccount, useConnector } from 'wagmi'
import { useState } from 'react'

export function CreateSubAccount() {
  const { address, isConnected } = useAccount()
  const connector = useConnector()
  const [subAccount, setSubAccount] = useState<string | null>(null)
  const [isCreating, setIsCreating] = useState(false)

  const createSubAccount = async () => {
    if (!connector || !isConnected) return

    setIsCreating(true)
    try {
      const provider = connector.provider
      
      // 새 서브 어카운트 생성
      const result = await provider?.request({
        method: 'wallet_addSubAccount',
        params: [{
          version: '1.0',
          chainId: `0x${Number(8453).toString(16)}`, // Base 메인넷
          from: address,
        }]
      })

      if (result?.subAccount) {
        setSubAccount(result.subAccount)
        console.log('Sub Account created:', result.subAccount)
      }
    } catch (error) {
      console.error('Failed to create Sub Account:', error)
    } finally {
      setIsCreating(false)
    }
  }

  return (
    <div className="space-y-4">
      <h3 className="text-lg font-semibold">서브 어카운트 관리</h3>
      
      {!subAccount ? (
        <button
          onClick={createSubAccount}
          disabled={!isConnected || isCreating}
          className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
        >
          {isCreating ? '서브 어카운트 생성 중...' : '서브 어카운트 생성'}
        </button>
      ) : (
        <div className="p-4 bg-green-50 border border-green-200 rounded">
          <p className="text-sm text-green-700">
            서브 어카운트 생성됨: <span className="font-mono">{subAccount}</span>
          </p>
        </div>
      )}
    </div>
  )
}

서브 어카운트 목록 조회

연결된 지갑의 기존 서브 어카운트를 조회합니다:
import { useAccount, useConnector } from 'wagmi'
import { useState, useEffect } from 'react'

export function SubAccountsList() {
  const { address, isConnected } = useAccount()
  const connector = useConnector()
  const [subAccounts, setSubAccounts] = useState<string[]>([])
  const [isLoading, setIsLoading] = useState(false)

  const fetchSubAccounts = async () => {
    if (!connector || !isConnected) return

    setIsLoading(true)
    try {
      const provider = connector.provider
      
      const result = await provider?.request({
        method: 'wallet_getSubAccounts',
        params: [{
          version: '1.0',
          chainId: `0x${Number(8453).toString(16)}`,
          from: address,
        }]
      })

      if (result?.subAccounts) {
        setSubAccounts(result.subAccounts)
      }
    } catch (error) {
      console.error('Failed to fetch Sub Accounts:', error)
    } finally {
      setIsLoading(false)
    }
  }

  useEffect(() => {
    if (isConnected) {
      fetchSubAccounts()
    }
  }, [isConnected, address])

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between">
        <h3 className="text-lg font-semibold">내 서브 어카운트</h3>
        <button
          onClick={fetchSubAccounts}
          disabled={isLoading}
          className="px-3 py-1 bg-gray-500 text-white rounded text-sm"
        >
          {isLoading ? '로딩 중...' : '새로고침'}
        </button>
      </div>

      {subAccounts.length === 0 ? (
        <p className="text-gray-500">서브 어카운트가 없습니다</p>
      ) : (
        <div className="space-y-2">
          {subAccounts.map((subAccount, index) => (
            <div
              key={subAccount}
              className="p-3 bg-blue-50 border border-blue-200 rounded"
            >
              <div className="flex items-center justify-between">
                <span className="font-mono text-sm">{subAccount}</span>
                <span className="text-xs text-blue-600">서브 어카운트 #{index + 1}</span>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

트랜잭션에 서브 어카운트 사용

서브 어카운트가 있으면 트랜잭션 수행에 사용할 수 있습니다:
import { useAccount, useConnector, useWriteContract } from 'wagmi'
import { useState } from 'react'

export function SubAccountTransactions() {
  const { address } = useAccount()
  const connector = useConnector()
  const [selectedSubAccount, setSelectedSubAccount] = useState('')
  const [isLoading, setIsLoading] = useState(false)

  const sendTransactionFromSubAccount = async () => {
    if (!connector || !selectedSubAccount) return

    setIsLoading(true)
    try {
      const provider = connector.provider
      
      // 서브 어카운트를 사용하여 트랜잭션 전송
      const result = await provider?.request({
        method: 'wallet_sendCalls',
        params: [{
          version: '1.0',
          chainId: `0x${Number(8453).toString(16)}`,
          from: selectedSubAccount, // 서브 어카운트를 발신자로 사용
          calls: [{
            to: '0x...' as `0x${string}`,
            value: '0x0',
            data: '0x' as `0x${string}`
          }]
        }]
      })

      console.log('Transaction sent from Sub Account:', result)
    } catch (error) {
      console.error('Sub Account transaction failed:', error)
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <div className="space-y-4">
      <h3 className="text-lg font-semibold">서브 어카운트 트랜잭션</h3>
      
      <div>
        <label className="block mb-2">서브 어카운트 선택:</label>
        <input
          type="text"
          value={selectedSubAccount}
          onChange={(e) => setSelectedSubAccount(e.target.value)}
          className="w-full p-2 border rounded"
          placeholder="0x... (서브 어카운트 주소)"
        />
      </div>

      <button
        onClick={sendTransactionFromSubAccount}
        disabled={!selectedSubAccount || isLoading}
        className="px-4 py-2 bg-green-500 text-white rounded disabled:opacity-50"
      >
        {isLoading ? '전송 중...' : '서브 어카운트에서 전송'}
      </button>
    </div>
  )
}

완전한 예제

모든 서브 어카운트 기능을 결합한 완전한 컴포넌트:
import { useAccount, useConnector } from 'wagmi'
import { useState, useEffect } from 'react'

export function SubAccountManager() {
  const { address, isConnected } = useAccount()
  const connector = useConnector()
  const [subAccounts, setSubAccounts] = useState<string[]>([])
  const [isCreating, setIsCreating] = useState(false)
  const [isLoading, setIsLoading] = useState(false)

  const fetchSubAccounts = async () => {
    if (!connector || !isConnected) return

    setIsLoading(true)
    try {
      const provider = connector.provider
      const result = await provider?.request({
        method: 'wallet_getSubAccounts',
        params: [{
          version: '1.0',
          chainId: `0x${Number(8453).toString(16)}`,
          from: address,
        }]
      })

      if (result?.subAccounts) {
        setSubAccounts(result.subAccounts)
      }
    } catch (error) {
      console.error('Failed to fetch Sub Accounts:', error)
    } finally {
      setIsLoading(false)
    }
  }

  const createSubAccount = async () => {
    if (!connector || !isConnected) return

    setIsCreating(true)
    try {
      const provider = connector.provider
      const result = await provider?.request({
        method: 'wallet_addSubAccount',
        params: [{
          version: '1.0',
          chainId: `0x${Number(8453).toString(16)}`,
          from: address,
        }]
      })

      if (result?.subAccount) {
        // 목록 새로고침
        await fetchSubAccounts()
      }
    } catch (error) {
      console.error('Failed to create Sub Account:', error)
    } finally {
      setIsCreating(false)
    }
  }

  useEffect(() => {
    if (isConnected) {
      fetchSubAccounts()
    }
  }, [isConnected])

  if (!isConnected) {
    return <p>먼저 지갑을 연결해 주세요</p>
  }

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between">
        <h2 className="text-xl font-bold">서브 어카운트 관리자</h2>
        <button
          onClick={createSubAccount}
          disabled={isCreating}
          className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
        >
          {isCreating ? '생성 중...' : '서브 어카운트 생성'}
        </button>
      </div>

      <div className="space-y-4">
        <div className="flex items-center justify-between">
          <h3 className="text-lg font-semibold">내 서브 어카운트 ({subAccounts.length})</h3>
          <button
            onClick={fetchSubAccounts}
            disabled={isLoading}
            className="px-3 py-1 bg-gray-500 text-white rounded text-sm"
          >
            {isLoading ? '로딩 중...' : '새로고침'}
          </button>
        </div>

        {subAccounts.length === 0 ? (
          <p className="text-gray-500">서브 어카운트가 없습니다. 하나를 생성해서 시작하세요!</p>
        ) : (
          <div className="grid gap-3">
            {subAccounts.map((subAccount, index) => (
              <div
                key={subAccount}
                className="p-4 bg-blue-50 border border-blue-200 rounded-lg"
              >
                <div className="flex items-center justify-between">
                  <div>
                    <p className="font-mono text-sm">{subAccount}</p>
                    <p className="text-xs text-blue-600">서브 어카운트 #{index + 1}</p>
                  </div>
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  )
}

더 알아보기