이전까지는 여러 IP를 등록하거나 민팅, 라이선스 조건 첨부, 파생물 등록과 같은 다른 작업을 수행하려면 각 작업마다 별도의 트랜잭션이 필요했습니다. 이는 비효율적이고 비용이 많이 들 수 있습니다. 이 과정을 간소화하기 위해 여러 트랜잭션을 하나로 일괄 처리할 수 있습니다. 이를 위한 두 가지 솔루션이 이제 사용 가능합니다:
- SPG 함수 호출 일괄 처리: SPG의 내장 SPG의 내장
multicall
함수 사용.
- SPG 이외의 함수 호출 일괄 처리: Multicall3 계약 사용Multicall3 계약.
1. 내장 multicall
함수를 통한 SPG 함수 호출 일괄 처리
SPG는 multicall
함수를 포함하고 있어 여러 읽기 또는 쓰기 작업을 단일 트랜잭션으로 결합할 수 있습니다.
함수 정의
이 multicall
함수는 인코딩된 호출 데이터 배열을 받아 각 함수 호출에 해당하는 인코딩된 결과 배열을 반환합니다:
/// @dev Executes a batch of function calls on this contract.
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results);
사용 예시
여러 NFT를 민팅하고, 이를 IP로 등록하고, 일부 부모 IP의 파생물로 연결하고 싶다고 가정해 봅시다.
이를 수행하기 위해 SPG의 multicall
함수를 사용하여 mintAndRegisterIpAndMakeDerivative
함수 호출을 일괄 처리할 수 있습니다.
다음과 같이 할 수 있습니다:
// an SPG workflow contract: https://github.com/storyprotocol/protocol-periphery-v1/blob/main/contracts/workflows/DerivativeWorkflows.sol
contract DerivativeWorkflows {
...
function mintAndRegisterIpAndMakeDerivative(
address nftContract,
MakeDerivative calldata derivData,
IPMetadata calldata ipMetadata,
address recipient
) external returns (address ipId, uint256 tokenId) {
....
}
...
}
를 사용하여 mintAndRegisterIpAndMakeDerivative
를 일괄 호출하려면 multicall
function:
// batch mint, register, and make derivatives for multiple IPs
await DerivativeWorkflows.multicall([
DerivativeWorkflows.contract.methods.mintAndRegisterIpAndMakeDerivative(
nftContract1,
derivData1,
recipient1,
ipMetadata1,
).encodeABI(),
DerivativeWorkflows.contract.methods.mintAndRegisterIpAndMakeDerivative(
nftContract2,
derivData2,
recipient2,
ipMetadata2,
).encodeABI(),
DerivativeWorkflows.contract.methods.mintAndRegisterIpAndMakeDerivative(
nftContract3,
derivData3,
recipient3,
ipMetadata3,
).encodeABI(),
...
// Add more calls as needed
]);
2. Multicall3 계약을 통한 함수 호출 일괄 처리
Multicall3 계약을 사용하면 단일 트랜잭션 내에서 여러 호출을 실행하고 결과를 집계할 수 있습니다. viem
라이브러리는 Multicall3에 대한 기본 지원을 제공합니다.
Story Aeneid 테스트넷 Multicall3 배포 정보
(모든 EVM 체인에서 동일한 주소)
{
"contractName": "Multicall3",
"chainId": 1516,
"contractAddress": "0xcA11bde05977b3631167028862bE2a173976CA11",
"url": "https://aeneid.storyscan.io/address/0xcA11bde05977b3631167028862bE2a173976CA11"
}
주요 함수
여러 함수 호출을 일괄 처리하기 위해 다음 함수들을 사용할 수 있습니다:
aggregate3
: Call3
구조체를 사용하여 호출을 일괄 처리합니다.
aggregate3Value
: aggregate3
와 유사하지만 각 호출에 값을 첨부할 수 있습니다.
/// @notice Aggregate calls, ensuring each returns success if required.
/// @param calls An array of Call3 structs.
/// @return returnData An array of Result structs.
function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData);
/// @notice Aggregate calls with an attached msg value.
/// @param calls An array of Call3Value structs.
/// @return returnData An array of Result structs.
function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData);
구조체 정의
- Call3:
aggregate3
에서 사용됩니다.
- Call3Value:
aggregate3Value
에서 사용됩니다.
struct Call3 {
address target; // Target contract to call.
bool allowFailure; // If false, the multicall will revert if this call fails.
bytes callData; // Data to call on the target contract.
}
struct Call3Value {
address target;
bool allowFailure;
uint256 value; // Value (in wei) to send with the call.
bytes callData; // Data to call on the target contract.
}
반환 타입
- Result:
aggregate3
와 aggregate3Value
모두에서 반환되는 구조체입니다.
struct Result {
bool success; // Whether the function call succeeded.
bytes returnData; // Data returned from the function call.
}
제한 사항
Multicall3 사용 시 제한 사항 목록은 Multicall3 README를 참조하세요.
추가 자료
전체 Multicall3 인터페이스
interface IMulticall3 {
struct Call {
address target;
bytes callData;
}
struct Call3 {
address target;
bool allowFailure;
bytes callData;
}
struct Call3Value {
address target;
bool allowFailure;
uint256 value;
bytes callData;
}
struct Result {
bool success;
bytes returnData;
}
function aggregate(Call[] calldata calls) external payable returns (uint256 blockNumber, bytes[] memory returnData);
function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData);
function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData);
function blockAndAggregate(Call[] calldata calls) external payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData);
function getBasefee() external view returns (uint256 basefee);
function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash);
function getBlockNumber() external view returns (uint256 blockNumber);
function getChainId() external view returns (uint256 chainId);
function getCurrentBlockCoinbase() external view returns (address coinbase);
function getCurrentBlockDifficulty() external view returns (uint256 difficulty);
function getCurrentBlockGasLimit() external view returns (uint256 gaslimit);
function getCurrentBlockTimestamp() external view returns (uint256 timestamp);
function getEthBalance(address addr) external view returns (uint256 balance);
function getLastBlockHash() external view returns (bytes32 blockHash);
function tryAggregate(bool requireSuccess, Call[] calldata calls) external payable returns (Result[] memory returnData);
function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls) external payable returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData);
}