approve 이후 스왑입니다.
EIP-5792에 정의된 wallet_sendCalls RPC 메서드를 사용하여 배치 트랜잭션을 제출할 수 있습니다.
동영상 콘텐츠를 선호하시나요?이 페이지의 마지막 섹션에 구현을 자세히 다루는 동영상 가이드가 있습니다.
설치
Base Account SDK를 설치합니다:npm install @base-org/account
설정
SDK 초기화
Base Account SDK 인스턴스를 가져오고 생성합니다:batchTransactions.tsx
import { createBaseAccountSDK } from "@base-org/account";
const sdk = createBaseAccountSDK({
appName: "Base Account SDK 데모",
appLogoUrl: "https://base.org/logo.png",
});
const provider = sdk.getProvider();
기본 배치 트랜잭션
간단한 다중 전송
단일 트랜잭션에서 여러 ETH 전송을 보냅니다:batchTransactions.tsx
import { createBaseAccountSDK, getCryptoKeyAccount } from "@base-org/account";
import { numberToHex, parseEther } from "viem";
const sdk = createBaseAccountSDK({
appName: "배치 트랜잭션 데모",
appLogoUrl: "https://base.org/logo.png",
});
const provider = sdk.getProvider();
async function sendBatchTransfers() {
try {
// 크립토 계정 가져오기
const cryptoAccount = await getCryptoKeyAccount();
const fromAddress = cryptoAccount?.account?.address;
// 배치 콜 준비
const calls = [
{
to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
value: numberToHex(parseEther("0.001")), // 0.001 ETH
data: "0x", // 간단한 전송을 위한 빈 데이터
},
{
to: "0x742d35Cc6634C0532925a3b844Bc9e7595f6E456",
value: numberToHex(parseEther("0.001")), // 0.001 ETH
data: "0x", // 간단한 전송을 위한 빈 데이터
},
];
// 배치 트랜잭션 전송
const result = await provider.request({
method: "wallet_sendCalls",
params: [
{
version: "2.0.0",
from: fromAddress,
chainId: numberToHex(base.constants.CHAIN_IDS.base),
atomicRequired: true, // 모든 콜이 성공하거나 모두 실패
calls: calls,
},
],
});
console.log("Batch transaction sent:", result);
return result;
} catch (error) {
console.error("Batch transaction failed:", error);
throw error;
}
}
컨트랙트 상호작용
ERC-20 승인 및 NFT(ERC-721) 민팅
일반적인 패턴은 NFT 컨트랙트가 ERC-20을 이동하도록 승인한 다음 NFT(ERC-721)를 민팅하는 것입니다:batchTransactions.tsx
import {
createBaseAccountSDK,
getCryptoKeyAccount,
base,
} from "@base-org/account";
import { numberToHex, parseUnits, encodeFunctionData } from "viem";
// approve를 위한 ERC-20 ABI
const erc20Abi = [
{
inputs: [
{ name: "spender", type: "address" },
{ name: "amount", type: "uint256" },
],
name: "approve",
outputs: [{ name: "", type: "bool" }],
stateMutability: "nonpayable",
type: "function",
},
] as const;
// mint 함수를 위한 ERC721 ABI
const erc721Abi = [
{
inputs: [
{ name: "to", type: "address" },
{ name: "tokenId", type: "uint256" },
],
name: "mint",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as const;
// Base Sepolia의 USDC 컨트랙트 주소
const USDC_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
// Base Sepolia의 NFT 컨트랙트 주소
const NFT_CONTRACT_ADDRESS = "0x82039e7C37D7aAac98D0F4d0A762F4E0d8c8DC273";
async function approveAndTransfer() {
const sdk = createBaseAccountSDK({
appName: "ERC-20 배치 데모",
appLogoUrl: "https://base.org/logo.png",
});
const provider = sdk.getProvider();
const cryptoAccount = await getCryptoKeyAccount();
const fromAddress = cryptoAccount?.account?.address;
// 첫 번째 approve 콜 인코딩 - NFT 컨트랙트에 USDC 승인
const call1Data = encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [
NFT_CONTRACT_ADDRESS,
parseUnits("1000", 6), // USDC는 소수점 6자리
],
});
// 두 번째 콜 인코딩 - 사용자 주소로 NFT 민팅
const call2Data = encodeFunctionData({
abi: erc721Abi,
functionName: "mint",
args: [fromAddress as `0x${string}`, BigInt("1")],
});
const result = await provider.request({
method: "wallet_sendCalls",
params: [
{
version: "2.0.0",
from: fromAddress,
chainId: numberToHex(base.constants.CHAIN_IDS.baseSepolia),
atomicRequired: true,
calls: [
{
to: USDC_ADDRESS,
data: call1Data,
},
{
to: NFT_CONTRACT_ADDRESS,
data: call2Data,
},
],
},
],
});
return result;
}
고급 기능
지갑 기능 확인
배치 트랜잭션을 보내기 전에 지갑이 원자적 배치를 지원하는지 확인할 수 있습니다:batchTransactions.tsx
async function checkCapabilities() {
const provider = sdk.getProvider();
try {
const cryptoAccount = await getCryptoKeyAccount();
const address = cryptoAccount?.account?.address;
const capabilities = await provider.request({
method: "wallet_getCapabilities",
params: [address],
});
const baseCapabilities = capabilities[base.constants.CHAIN_IDS.base];
if (baseCapabilities?.atomicBatch?.supported) {
console.log("원자적 배치가 지원됩니다");
return true;
} else {
console.log("원자적 배치가 지원되지 않습니다");
return false;
}
} catch (error) {
console.error("Failed to check capabilities:", error);
return false;
}
}
비원자적 배치
일부 실패해도 콜을 순차적으로 실행하고 싶을 때:batchTransactions.tsx
const result = await provider.request({
method: "wallet_sendCalls",
params: [
{
version: "2.0.0",
from: fromAddress,
chainId: numberToHex(base.constants.CHAIN_IDS.base),
atomicRequired: false, // 부분 실행 허용
calls: calls,
},
],
});
배치 트랜잭션 결과 가져오기
wallet_getCallsStatus는 이전에 wallet_sendCalls로 제출한 배치의 실행 상태를 반환합니다. wallet_sendCalls가 반환한 callsId를 캡처한 다음 배치가 확인되거나 실패할 때까지 상태를 폴링합니다.
batchTransactions.tsx
async function trackBatchTransaction(
calls: Array<{
to: `0x${string}`;
data: `0x${string}`;
value?: `0x${string}`;
}>
) {
const cryptoAccount = await getCryptoKeyAccount();
const fromAddress = cryptoAccount?.account?.address;
const callsId = await provider.request({
method: "wallet_sendCalls",
params: [
{
version: "2.0.0",
from: fromAddress,
chainId: numberToHex(base.constants.CHAIN_IDS.base),
atomicRequired: true,
calls,
},
],
});
try {
const status = await provider.request({
method: "wallet_getCallsStatus",
params: [callsId],
});
if (status.status === 200) {
console.log("배치 완료", status.receipts);
} else if (status.status === 100) {
console.log("배치 대기 중", status.id);
} else {
console.error("배치 실패", status.status);
}
return status;
} catch (error: any) {
if (error.code === 4200) {
throw new Error("제공된 callsId에 대한 배치를 찾을 수 없습니다.");
}
if (error.code === 4100) {
throw new Error(
"연결된 지갑이 wallet_getCallsStatus를 지원하지 않습니다."
);
}
if (error.code === -32602) {
throw new Error("callsId 파라미터가 유효하지 않습니다.");
}
throw error;
}
}
wallet_getCallsStatus는 제출한 배치 호출의 상태를 조회할 때 사용합니다.
가스에 대한 더 많은 제어가 필요하신가요?
gasLimitOverride 기능을 사용하여 배치의 개별 콜에 대한 가스 한도를 재정의할 수 있습니다. 이는 스왑과 같이 비결정적 가스 소비가 있는 콜에 유용합니다. 지원되는 기능은 사용 중인 지갑 및 프로바이더 설정과 함께 확인하세요.