Skip to content

Commit

Permalink
Merge pull request #24 from metadock/feat/container-on-erc721-received
Browse files Browse the repository at this point in the history
Make `Container` a valid ERC721 and ERC1155 receiver
  • Loading branch information
gabrielstoica authored Sep 10, 2024
2 parents 282b9bc + fcca133 commit 28ec7de
Show file tree
Hide file tree
Showing 18 changed files with 566 additions and 95 deletions.
124 changes: 66 additions & 58 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
AddToAllowlist_Unit_Concrete_Test:test_AddToAllowlist() (gas: 241256)
AddToAllowlist_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 13752)
AddToAllowlist_Unit_Concrete_Test:test_RevertWhen_InvalidZeroCodeModule() (gas: 13925)
AddToAllowlist_Unit_Concrete_Test:test_AddToAllowlist() (gas: 178178)
AddToAllowlist_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 12954)
AddToAllowlist_Unit_Concrete_Test:test_RevertWhen_InvalidZeroCodeModule() (gas: 13122)
CancelInvoice_Integration_Concret_Test:test_CancelInvoice_PaymentMethodLinearStream_StatusOngoing() (gas: 446811)
CancelInvoice_Integration_Concret_Test:test_CancelInvoice_PaymentMethodLinearStream_StatusPending() (gas: 30578)
CancelInvoice_Integration_Concret_Test:test_CancelInvoice_PaymentMethodTranchedStream_StatusOngoing() (gas: 446790)
Expand All @@ -13,73 +13,81 @@ CancelInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodLinearStream
CancelInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTranchedStream_StatusOngoing_SenderNoInitialtStreamSender() (gas: 404628)
CancelInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTranchedStream_StatusPending_SenderNotInvoiceRecipient() (gas: 20611)
CancelInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTransfer_SenderNotInvoiceRecipient() (gas: 20553)
ComputeNumberOfPayments_Helpers_Test:test_ComputeNumberOfPayments_Monthly() (gas: 4156)
ComputeNumberOfPayments_Helpers_Test:test_ComputeNumberOfPayments_Weekly() (gas: 4082)
ComputeNumberOfPayments_Helpers_Test:test_ComputeNumberOfPayments_Yearly() (gas: 4266)
Constructor_DockRegistry_Test:test_Constructor() (gas: 3353665)
Constructor_ModuleKeeper_Test:test_Constructor() (gas: 416041)
ComputeNumberOfPayments_Helpers_Test:test_ComputeNumberOfPayments_Monthly() (gas: 3530)
ComputeNumberOfPayments_Helpers_Test:test_ComputeNumberOfPayments_Weekly() (gas: 3483)
ComputeNumberOfPayments_Helpers_Test:test_ComputeNumberOfPayments_Yearly() (gas: 3613)
Constructor_DockRegistry_Test:test_Constructor() (gas: 2577196)
Constructor_ModuleKeeper_Test:test_Constructor() (gas: 241794)
Constructor_StreamManager_Integration_Concret_Test:test_Constructor() (gas: 1615852)
CreateContainer_Unit_Concrete_Test:test_CreateContainer_DockIdNonZero() (gas: 3030474)
CreateContainer_Unit_Concrete_Test:test_CreateContainer_DockIdZero() (gas: 1548403)
CreateContainer_Unit_Concrete_Test:test_RevertWhen_CallerNotDockOwner() (gas: 1561938)
CreateInvoice_Integration_Concret_Test:test_CreateInvoice_LinearStream() (gas: 258963)
CreateInvoice_Integration_Concret_Test:test_CreateInvoice_PaymentMethodOneOffTransfer() (gas: 259167)
CreateInvoice_Integration_Concret_Test:test_CreateInvoice_RecurringTransfer() (gas: 260318)
CreateInvoice_Integration_Concret_Test:test_CreateInvoice_Tranched() (gas: 260591)
CreateContainer_Unit_Concrete_Test:test_CreateContainer_DockIdNonZero() (gas: 3153626)
CreateContainer_Unit_Concrete_Test:test_CreateContainer_DockIdZero() (gas: 1607335)
CreateContainer_Unit_Concrete_Test:test_RevertWhen_CallerNotDockOwner() (gas: 1624571)
CreateInvoice_Integration_Concret_Test:test_CreateInvoice_LinearStream() (gas: 259080)
CreateInvoice_Integration_Concret_Test:test_CreateInvoice_PaymentMethodOneOffTransfer() (gas: 259284)
CreateInvoice_Integration_Concret_Test:test_CreateInvoice_RecurringTransfer() (gas: 260435)
CreateInvoice_Integration_Concret_Test:test_CreateInvoice_Tranched() (gas: 260708)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_CallerNotContract() (gas: 89299)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_EndTimeInThePast() (gas: 107235)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_EndTimeInThePast() (gas: 107352)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_NonCompliantContainer() (gas: 92554)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodLinearStream_PaymentAssetNativeToken() (gas: 107353)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodRecurringTransfer_PaymentIntervalTooShortForSelectedRecurrence() (gas: 108043)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTranchedStream_PaymentAssetNativeToken() (gas: 108523)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTranchedStream_PaymentIntervalTooShortForSelectedRecurrence() (gas: 108087)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTranchedStream_RecurrenceSetToOneOff() (gas: 106731)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_StartTimeGreaterThanEndTime() (gas: 106559)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_ZeroPaymentAmount() (gas: 85918)
CreateInvoice_Integration_Fuzz_Test:testFuzz_CreateInvoice(uint8,uint8,address,uint40,uint40,uint128) (runs: 10001, μ: 202392, ~: 259073)
DisableModule_Unit_Concrete_Test:test_DisableModule() (gas: 249454)
DisableModule_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 22806)
EnableModule_Unit_Concrete_Test:test_EnableModule() (gas: 42771)
EnableModule_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 22785)
EnableModule_Unit_Concrete_Test:test_RevertWhen_ModuleNotAllowlisted() (gas: 32658)
Execute_Unit_Concrete_Test:test_Execute() (gas: 96131)
Execute_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 26420)
Execute_Unit_Concrete_Test:test_RevertWhen_ModuleNotEnabled() (gas: 26485)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodLinearStream_PaymentAssetNativeToken() (gas: 107470)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodRecurringTransfer_PaymentIntervalTooShortForSelectedRecurrence() (gas: 108160)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTranchedStream_PaymentAssetNativeToken() (gas: 108640)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTranchedStream_PaymentIntervalTooShortForSelectedRecurrence() (gas: 108204)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTranchedStream_RecurrenceSetToOneOff() (gas: 106848)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_StartTimeGreaterThanEndTime() (gas: 106676)
CreateInvoice_Integration_Concret_Test:test_RevertWhen_ZeroPaymentAmount() (gas: 86035)
CreateInvoice_Integration_Fuzz_Test:testFuzz_CreateInvoice(uint8,uint8,address,uint40,uint40,uint128) (runs: 10001, μ: 202482, ~: 259190)
DisableModule_Unit_Concrete_Test:test_DisableModule() (gas: 183277)
DisableModule_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 21350)
EnableModule_Unit_Concrete_Test:test_EnableModule() (gas: 39197)
EnableModule_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 21302)
EnableModule_Unit_Concrete_Test:test_RevertWhen_ModuleNotAllowlisted() (gas: 29881)
Execute_Unit_Concrete_Test:test_Execute() (gas: 89623)
Execute_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 23959)
Execute_Unit_Concrete_Test:test_RevertWhen_ModuleNotEnabled() (gas: 24032)
Fallback_Unit_Concrete_Test:test_Fallback() (gas: 20887)
PayInvoice_Integration_Concret_Test:test_PayInvoice_PaymentMethodLinearStream() (gas: 309509)
PayInvoice_Integration_Concret_Test:test_PayInvoice_PaymentMethodTranchedStream() (gas: 436650)
PayInvoice_Integration_Concret_Test:test_PayInvoice_PaymentMethodTransfer_ERC20Token_Recurring() (gas: 87178)
PayInvoice_Integration_Concret_Test:test_PayInvoice_PaymentMethodTransfer_NativeToken_OneOff() (gas: 63525)
PayInvoice_Integration_Concret_Test:test_RevertWhen_InvoiceAlreadyPaid() (gas: 62436)
PayInvoice_Integration_Concret_Test:test_RevertWhen_InvoiceCanceled() (gas: 29609)
PayInvoice_Integration_Concret_Test:test_RevertWhen_InvoiceNull() (gas: 17829)
PayInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTransfer_NativeTokenTransferFails() (gas: 170827)
PayInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTransfer_NativeTokenTransferFails() (gas: 170944)
PayInvoice_Integration_Concret_Test:test_RevertWhen_PaymentMethodTransfer_PaymentAmountLessThanInvoiceValue() (gas: 31912)
PayInvoice_Integration_Fuzz_Test:testFuzz_PayInvoice(uint8,uint8,uint40,uint40,uint128) (runs: 10002, μ: 369458, ~: 342362)
Receive_Unit_Concrete_Test:test_Receive() (gas: 21868)
RemoveFromAllowlist_Unit_Concrete_Test:test_AddToAllowlist() (gas: 23859)
RemoveFromAllowlist_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 13752)
TransferContainerOwnership_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 1561103)
TransferContainerOwnership_Unit_Concrete_Test:test_RevertWhen_InvalidOwnerZeroAddress() (gas: 1559040)
TransferContainerOwnership_Unit_Concrete_Test:test_TransferContainerOwnership() (gas: 1569104)
TransferDockOwnership_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 1560963)
TransferDockOwnership_Unit_Concrete_Test:test_TransferDockOwnership() (gas: 1568645)
TransferOwnership_Unit_Concrete_Test:test_RevertWhen_CallerNotCurrentOwner() (gas: 15849)
TransferOwnership_Unit_Concrete_Test:test_RevertWhen_NewOwnerZeroAddress() (gas: 13765)
TransferOwnership_Unit_Concrete_Test:test_TransferOwnership() (gas: 24347)
TransferOwnership_Unit_Fuzz_Test:testFuzz_RevertWhen_CallerNotCurrentOwner(address) (runs: 10002, μ: 14624, ~: 14624)
TransferOwnership_Unit_Fuzz_Test:testFuzz_TransferOwnership(address) (runs: 10002, μ: 22748, ~: 22748)
UpdateModuleKeeper_Unit_Concrete_Test:test_RevertWhen_CallerNotRegistryOwner() (gas: 20219)
UpdateModuleKeeper_Unit_Concrete_Test:test_UpdateModuleKeeper() (gas: 29457)
PayInvoice_Integration_Fuzz_Test:testFuzz_PayInvoice(uint8,uint8,uint40,uint40,uint128) (runs: 10002, μ: 370886, ~: 342456)
Receive_Unit_Concrete_Test:test_Receive() (gas: 20733)
RemoveFromAllowlist_Unit_Concrete_Test:test_AddToAllowlist() (gas: 22211)
RemoveFromAllowlist_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 12981)
TransferContainerOwnership_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 1624113)
TransferContainerOwnership_Unit_Concrete_Test:test_RevertWhen_InvalidOwnerZeroAddress() (gas: 1622072)
TransferContainerOwnership_Unit_Concrete_Test:test_TransferContainerOwnership() (gas: 1630377)
TransferDockOwnership_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 1623891)
TransferDockOwnership_Unit_Concrete_Test:test_TransferDockOwnership() (gas: 1629839)
TransferOwnership_Unit_Concrete_Test:test_RevertWhen_CallerNotCurrentOwner() (gas: 15031)
TransferOwnership_Unit_Concrete_Test:test_RevertWhen_NewOwnerZeroAddress() (gas: 12972)
TransferOwnership_Unit_Concrete_Test:test_TransferOwnership() (gas: 22589)
TransferOwnership_Unit_Fuzz_Test:testFuzz_RevertWhen_CallerNotCurrentOwner(address) (runs: 10002, μ: 13560, ~: 13560)
TransferOwnership_Unit_Fuzz_Test:testFuzz_TransferOwnership(address) (runs: 10002, μ: 20802, ~: 20803)
UpdateModuleKeeper_Unit_Concrete_Test:test_RevertWhen_CallerNotRegistryOwner() (gas: 18570)
UpdateModuleKeeper_Unit_Concrete_Test:test_UpdateModuleKeeper() (gas: 26803)
UpdateStreamBrokerFee_Integration_Concret_Test:test_RevertWhen_CallerNotOwner() (gas: 12864)
UpdateStreamBrokerFee_Integration_Concret_Test:test_UpdateStreamBrokerFee() (gas: 38936)
UpgradeToAndCall_DockRegistry_Test:test_RevertWhen_CallerNotRegistryOwner() (gas: 23388)
UpgradeToAndCall_DockRegistry_Test:test_UpgradeToAndCall() (gas: 35430)
WithdrawERC20_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 23342)
WithdrawERC20_Unit_Concrete_Test:test_RevertWhen_InsufficientERC20ToWithdraw() (gas: 31627)
WithdrawERC20_Unit_Concrete_Test:test_WithdrawERC20() (gas: 104376)
UpgradeToAndCall_DockRegistry_Test:test_RevertWhen_CallerNotRegistryOwner() (gas: 21343)
UpgradeToAndCall_DockRegistry_Test:test_UpgradeToAndCall() (gas: 32559)
WithdrawERC1155_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 35766)
WithdrawERC1155_Unit_Concrete_Test:test_RevertWhen_InsufficientERC1155Balance() (gas: 47878)
WithdrawERC1155_Unit_Concrete_Test:test_WithdrawERC1155() (gas: 119637)
WithdrawERC1155_Unit_Concrete_Test:test_WithdrawERC1155_Batch() (gas: 138047)
WithdrawERC20_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 21352)
WithdrawERC20_Unit_Concrete_Test:test_RevertWhen_InsufficientERC20ToWithdraw() (gas: 28992)
WithdrawERC20_Unit_Concrete_Test:test_WithdrawERC20() (gas: 95442)
WithdrawERC721_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 21367)
WithdrawERC721_Unit_Concrete_Test:test_RevertWhen_NonexistentERC721Token() (gas: 34534)
WithdrawERC721_Unit_Concrete_Test:test_WithdrawERC721() (gas: 99513)
WithdrawLinearStream_Integration_Concret_Test:test_WithdrawStream_LinearStream() (gas: 295994)
WithdrawLinearStream_Integration_Concret_Test:test_WithdrawStream_TranchedStream() (gas: 424090)
WithdrawNative_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 22882)
WithdrawNative_Unit_Concrete_Test:test_RevertWhen_InsufficientNativeToWithdraw() (gas: 22871)
WithdrawNative_Unit_Concrete_Test:test_RevertWhen_NativeWithdrawFailed() (gas: 38894)
WithdrawNative_Unit_Concrete_Test:test_WithdrawNative() (gas: 45302)
WithdrawNative_Unit_Concrete_Test:test_RevertWhen_CallerNotOwner() (gas: 21281)
WithdrawNative_Unit_Concrete_Test:test_RevertWhen_InsufficientNativeToWithdraw() (gas: 21271)
WithdrawNative_Unit_Concrete_Test:test_RevertWhen_NativeWithdrawFailed() (gas: 36766)
WithdrawNative_Unit_Concrete_Test:test_WithdrawNative() (gas: 42337)
103 changes: 94 additions & 9 deletions src/Container.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
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";
import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import { ExcessivelySafeCall } from "@nomad-xyz/excessively-safe-call/src/ExcessivelySafeCall.sol";

import { IContainer } from "./interfaces/IContainer.sol";
Expand Down Expand Up @@ -31,6 +35,21 @@ contract Container is IContainer, ModuleManager {
dockRegistry = _dockRegistry;
}

/*//////////////////////////////////////////////////////////////////////////
RECEIVE & FALLBACK
//////////////////////////////////////////////////////////////////////////*/

/// @dev Allow container to receive native token (ETH)
receive() external payable {
// Log the successful native token deposit
emit NativeReceived({ from: msg.sender, amount: msg.value });
}

/// @dev Fallback function to handle incoming calls with data
fallback() external payable {
emit NativeReceived({ from: msg.sender, amount: msg.value });
}

/*//////////////////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -80,7 +99,36 @@ contract Container is IContainer, ModuleManager {
asset.safeTransfer({ to: msg.sender, value: amount });

// Log the successful ERC-20 token withdrawal
emit AssetWithdrawn({ sender: msg.sender, asset: address(asset), amount: amount });
emit AssetWithdrawn({ to: msg.sender, asset: address(asset), amount: amount });
}

/// @inheritdoc IContainer
function withdrawERC721(IERC721 collection, uint256 tokenId) public onlyOwner {
// 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);

// Log the successful ERC-721 token withdrawal
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
Expand All @@ -89,12 +137,12 @@ contract Container is IContainer, ModuleManager {
if (amount > address(this).balance) revert Errors.InsufficientNativeToWithdraw();

// Interactions: withdraw by transferring the amount to the sender
(bool success,) = payable(msg.sender).call{ value: amount }("");
(bool success,) = msg.sender.call{ value: amount }("");
// Revert if the call failed
if (!success) revert Errors.NativeWithdrawFailed();

// Log the successful native token withdrawal
emit AssetWithdrawn({ sender: msg.sender, asset: address(0), amount: amount });
emit AssetWithdrawn({ to: msg.sender, asset: address(0), amount: amount });
}

/// @inheritdoc IModuleManager
Expand All @@ -107,12 +155,6 @@ contract Container is IContainer, ModuleManager {
super.disableModule(module);
}

/// @dev Allow container to receive native token (ETH)
receive() external payable {
// Log the successful native token deposit
emit NativeDeposited({ sender: msg.sender, amount: msg.value });
}

/*//////////////////////////////////////////////////////////////////////////
CONSTANT FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
Expand All @@ -121,4 +163,47 @@ contract Container is IContainer, ModuleManager {
function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
return interfaceId == type(IContainer).interfaceId || interfaceId == type(IERC165).interfaceId;
}

/// @inheritdoc IERC721Receiver
function onERC721Received(
address,
address from,
uint256 tokenId,
bytes calldata
) external override returns (bytes4) {
// Log the successful ERC-721 token receipt
emit ERC721Received(from, tokenId);

return this.onERC721Received.selector;
}

/// @inheritdoc IERC1155Receiver
function onERC1155Received(
address,
address from,
uint256 id,
uint256 value,
bytes calldata
) external override returns (bytes4) {
// Log the successful ERC-1155 token receipt
emit ERC1155Received(from, id, value);

return this.onERC1155Received.selector;
}

/// @inheritdoc IERC1155Receiver
function onERC1155BatchReceived(
address,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata
) external override returns (bytes4) {
for (uint256 i; i < ids.length; ++i) {
// Log the successful ERC-1155 token receipt
emit ERC1155Received(from, ids[i], values[i]);
}

return this.onERC1155BatchReceived.selector;
}
}
Loading

0 comments on commit 28ec7de

Please sign in to comment.