diff --git a/src/Container.sol b/src/Container.sol index aab1d2d9..a8e55bc5 100644 --- a/src/Container.sol +++ b/src/Container.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.26; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; @@ -88,8 +89,9 @@ contract Container is IContainer, ModuleManager { /// @inheritdoc IContainer function withdrawERC721(IERC721 collection, uint256 tokenId) public onlyOwner { - // Interactions: withdraw by transferring the token to the sender - // We're using `safeTransferFrom` as the owner can be an ERC-4337 smart account + // Checks, Effects, Interactions: withdraw by transferring the token to the container owner + // Notes: + // - we're using `safeTransferFrom` as the owner can be an ERC-4337 smart account // therefore the `onERC721Received` hook must be implemented collection.safeTransferFrom(address(this), msg.sender, tokenId); @@ -97,6 +99,23 @@ contract Container is IContainer, ModuleManager { emit ERC721Withdrawn({ to: msg.sender, collection: address(collection), tokenId: tokenId }); } + /// @inheritdoc IContainer + function withdrawERC1155(IERC1155 collection, uint256[] memory ids, uint256[] memory amounts) public onlyOwner { + // Checks, Effects, Interactions: withdraw by transferring the tokens to the container owner + // Notes: + // - we're using `safeTransferFrom` as the owner can be an ERC-4337 smart account + // therefore the `onERC1155Received` hook must be implemented + // - depending on the length of the `ids` array, we're using `safeBatchTransferFrom` or `safeTransferFrom` + if (ids.length > 1) { + collection.safeBatchTransferFrom({ from: address(this), to: msg.sender, ids: ids, values: amounts, data: "" }); + } else { + collection.safeTransferFrom({ from: address(this), to: msg.sender, id: ids[0], value: amounts[0], data: "" }); + } + + // Log the successful ERC-1155 token withdrawal + emit ERC1155Withdrawn(msg.sender, address(collection), ids, amounts); + } + /// @inheritdoc IContainer function withdrawNative(uint256 amount) public onlyOwner { // Checks: the native balance of the container minus the amount locked for operations is greater than the requested amount diff --git a/src/interfaces/IContainer.sol b/src/interfaces/IContainer.sol index b88cc67e..88883b92 100644 --- a/src/interfaces/IContainer.sol +++ b/src/interfaces/IContainer.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.26; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import { IERC1155 } from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; @@ -43,11 +44,12 @@ interface IContainer is IERC165, IERC721Receiver, IERC1155Receiver { /// @param tokenId The ID of the token event ERC721Withdrawn(address indexed to, address indexed collection, uint256 tokenId); - /// @notice Emitted when an ERC-1155 token is withdrawn from the container + /// @notice Emitted when a `value` amount of ERC-1155 `id` tokens are withdrawn from the container /// @param to The address to which the tokens were transferred - /// @param id The ID of the token - /// @param value The amount of the tokens withdrawn - event ERC1155Withdrawn(address indexed to, address indexed collection, uint256 id, uint256 value); + /// @param collection The address of the ERC-1155 collection + /// @param ids The IDs of the tokens + /// @param values The amounts of the token types withdrawn + event ERC1155Withdrawn(address indexed to, address indexed collection, uint256[] ids, uint256[] values); /// @notice Emitted when a module execution is successful /// @param module The address of the module @@ -83,6 +85,16 @@ interface IContainer is IERC165, IERC721Receiver, IERC1155Receiver { /// @param tokenId The ID of the token to withdraw function withdrawERC721(IERC721 collection, uint256 tokenId) external; + /// @notice Withdraws an `amount` amount of ERC-1155 `id` token from the container + /// + /// Requirements: + /// - `msg.sender` must be the owner of the container + /// + /// @param collection The address of the ERC-1155 collection + /// @param ids The IDs of tokens to withdraw + /// @param amounts The amounts of tokens to withdraw + function withdrawERC1155(IERC1155 collection, uint256[] memory ids, uint256[] memory amounts) external; + /// @notice Withdraws an `amount` amount of native token (ETH) from the container /// /// Requirements: