-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BOOST-4122] feat(evm): Event Action (#12)
- Loading branch information
Showing
13 changed files
with
715 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.24; | ||
|
||
import {ERC721} from "@solady/tokens/ERC721.sol"; | ||
|
||
import {BoostError} from "contracts/shared/BoostError.sol"; | ||
import {Cloneable} from "contracts/shared/Cloneable.sol"; | ||
|
||
import {Action} from "contracts/actions/Action.sol"; | ||
|
||
/// @title Event Action | ||
/// @notice A primitive action to mint and/or validate that an ERC721 token has been minted | ||
/// @dev The action is expected to be prepared with the data payload for the minting of the token | ||
/// @dev This a minimal generic implementation that should be extended if additional functionality or customizations are required | ||
/// @dev It is expected that the target contract has an externally accessible mint function whose selector | ||
abstract contract AEventAction is Action { | ||
ActionEvent[] internal actionEvents; | ||
|
||
// Define Enums | ||
enum FilterType { | ||
EQUAL, | ||
NOT_EQUAL, | ||
GREATER_THAN, | ||
LESS_THAN, | ||
CONTAINS | ||
} | ||
|
||
enum PrimitiveType { | ||
UINT, | ||
ADDRESS, | ||
BYTES, | ||
STRING | ||
} | ||
|
||
// Define Structs | ||
struct Criteria { | ||
FilterType filterType; | ||
PrimitiveType fieldType; | ||
uint8 fieldIndex; // Where in the logs arg array the field is located | ||
bytes filterData; // data fiels in case we need more complex filtering in the future - initially unused | ||
} | ||
|
||
struct ActionEvent { | ||
bytes4 eventSignature; | ||
uint8 actionType; | ||
address targetContract; | ||
Criteria actionParameter; | ||
} | ||
|
||
/// @inheritdoc Cloneable | ||
function initialize(bytes calldata data_) public virtual override(Cloneable) { | ||
revert NotInitializing(); | ||
} | ||
|
||
/// @notice Prepare the action for execution and return the expected payload | ||
/// @param data_ The ABI-encoded payload for the target contract call | ||
/// @return bytes_ The encoded payload to be sent to the target contract | ||
/// @dev Note that the mint value is NOT included in the prepared payload but must be sent with the call | ||
function prepare(bytes calldata data_) public view virtual override returns (bytes memory bytes_) { | ||
// Since this action is marshalled off-chain we don't need to prepare the payload | ||
revert BoostError.NotImplemented(); | ||
//return data_; | ||
} | ||
|
||
function execute(bytes calldata data_) external payable virtual override returns (bool, bytes memory) { | ||
// Since this action is marshalled off-chain we don't need to execute the payload | ||
revert BoostError.NotImplemented(); | ||
//return (true, data_); | ||
} | ||
|
||
/// @inheritdoc Action | ||
function getComponentInterface() public pure virtual override(Action) returns (bytes4) { | ||
return type(AEventAction).interfaceId; | ||
} | ||
|
||
function getActionEventsCount() public view virtual returns (uint256) { | ||
return actionEvents.length; | ||
} | ||
|
||
function getActionEvent(uint256 index) public view virtual returns (ActionEvent memory) { | ||
return actionEvents[index]; | ||
} | ||
|
||
function getActionEvents() public view virtual returns (ActionEvent[] memory) { | ||
return actionEvents; | ||
} | ||
|
||
/// @inheritdoc Action | ||
function supportsInterface(bytes4 interfaceId) public view virtual override(Action) returns (bool) { | ||
return interfaceId == type(AEventAction).interfaceId || super.supportsInterface(interfaceId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.24; | ||
|
||
import {ERC721} from "@solady/tokens/ERC721.sol"; | ||
|
||
import {Cloneable} from "contracts/shared/Cloneable.sol"; | ||
|
||
import {AEventAction} from "contracts/actions/AEventAction.sol"; | ||
|
||
contract EventAction is AEventAction { | ||
/// @notice The payload for initializing a ContractAction | ||
/// @param target The target contract address | ||
/// @param selector The selector for the function to be called | ||
/// @param value The native token value to send with the function call | ||
struct InitPayload { | ||
ActionEvent actionEventOne; | ||
ActionEvent actionEventTwo; | ||
ActionEvent actionEventThree; | ||
ActionEvent actionEventFour; | ||
} | ||
|
||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
/// @inheritdoc Cloneable | ||
/// @notice Initialize the contract with the owner and the required data | ||
function initialize(bytes calldata data_) public virtual override initializer { | ||
_initialize(abi.decode(data_, (InitPayload))); | ||
} | ||
|
||
function _initialize(InitPayload memory init_) internal virtual onlyInitializing { | ||
actionEvents.push(init_.actionEventOne); | ||
actionEvents.push(init_.actionEventTwo); | ||
actionEvents.push(init_.actionEventThree); | ||
actionEvents.push(init_.actionEventFour); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.24; | ||
|
||
import {Test, console} from "lib/forge-std/src/Test.sol"; | ||
import {LibClone} from "@solady/utils/LibClone.sol"; | ||
|
||
import {MockERC721} from "contracts/shared/Mocks.sol"; | ||
import {EventAction} from "contracts/actions/EventAction.sol"; | ||
import {AEventAction} from "contracts/actions/AEventAction.sol"; | ||
import {Cloneable} from "contracts/shared/Cloneable.sol"; | ||
|
||
contract EventActionTest is Test { | ||
MockERC721 public mockAsset = new MockERC721(); | ||
EventAction public baseAction = new EventAction(); | ||
EventAction public action; | ||
|
||
function setUp() public { | ||
action = _newActionClone(); | ||
|
||
// Define the InitPayload with an ActionEvent | ||
AEventAction.Criteria memory criteria; | ||
AEventAction.ActionEvent memory actionEventOne; | ||
|
||
criteria = AEventAction.Criteria({ | ||
filterType: AEventAction.FilterType.EQUAL, | ||
fieldType: AEventAction.PrimitiveType.ADDRESS, | ||
fieldIndex: 0, // Assume the first field in the log is the 'from' address | ||
filterData: abi.encode(address(this)) // The filter checks if 'from' address equals this contract's address | ||
}); | ||
|
||
actionEventOne = AEventAction.ActionEvent({ | ||
eventSignature: bytes4(keccak256("Transfer(address,address,uint256)")), | ||
actionType: 0, | ||
targetContract: address(mockAsset), | ||
actionParameter: criteria | ||
}); | ||
|
||
EventAction.InitPayload memory payload = EventAction.InitPayload({ | ||
actionEventOne: actionEventOne, | ||
actionEventTwo: actionEventOne, | ||
actionEventThree: actionEventOne, | ||
actionEventFour: actionEventOne | ||
}); | ||
|
||
// Initialize the EventAction contract | ||
action.initialize(abi.encode(payload)); | ||
} | ||
|
||
/////////////////////////// | ||
// EventAction.initialize // | ||
/////////////////////////// | ||
|
||
function testInitialize() public { | ||
// Ensure the action was initialized correctly | ||
assertEq(action.getActionEventsCount(), 4); | ||
assertEq(action.getActionEvent(0).eventSignature, bytes4(keccak256("Transfer(address,address,uint256)"))); | ||
} | ||
|
||
//////////////////////////// | ||
// EventAction.getActionEvents // | ||
//////////////////////////// | ||
|
||
function testGetActionEvents() public { | ||
// Ensure the action events are retrieved correctly | ||
AEventAction.ActionEvent[] memory retrievedEvents = action.getActionEvents(); | ||
|
||
assertEq(retrievedEvents.length, 4); | ||
assertEq(retrievedEvents[0].eventSignature, bytes4(keccak256("Transfer(address,address,uint256)"))); | ||
} | ||
|
||
///////////////////////////////// | ||
// EventAction.getActionEvent // | ||
///////////////////////////////// | ||
|
||
function testGetActionEvent() public { | ||
// Ensure the action event is retrieved correctly | ||
AEventAction.ActionEvent memory retrievedEvent = action.getActionEvent(0); | ||
|
||
assertEq(retrievedEvent.eventSignature, bytes4(keccak256("Transfer(address,address,uint256)"))); | ||
} | ||
|
||
//////////////////////////////////// | ||
// EventAction.getComponentInterface // | ||
//////////////////////////////////// | ||
|
||
function testGetComponentInterface() public { | ||
// Retrieve the component interface | ||
console.logBytes4(action.getComponentInterface()); | ||
} | ||
//////////////////////////////////// | ||
// EventAction.supportsInterface // | ||
//////////////////////////////////// | ||
|
||
function testSupportsInterface() public { | ||
// Check the interface support | ||
assertTrue(action.supportsInterface(type(AEventAction).interfaceId)); | ||
assertTrue(action.supportsInterface(type(Cloneable).interfaceId)); | ||
} | ||
|
||
/////////////////////////// | ||
// Test Helper Functions // | ||
/////////////////////////// | ||
|
||
function _newActionClone() internal returns (EventAction) { | ||
return EventAction(LibClone.clone(address(baseAction))); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,9 @@ | |
"license": "GPL-3.0-or-later", | ||
"private": true, | ||
"type": "module", | ||
"files": ["dist"], | ||
"files": [ | ||
"dist" | ||
], | ||
"repository": "https://github.com/rabbitholegg/boost-protocol", | ||
"author": "Boost Team<[email protected]>", | ||
"access": "public", | ||
|
@@ -93,6 +95,12 @@ | |
"node": "./dist/Actions/ERC721MintAction.js", | ||
"types": "./dist/Actions/ERC721MintAction.d.ts" | ||
}, | ||
"./Actions/EventAction": { | ||
"require": "./dist/Actions/EventAction.cjs", | ||
"import": "./dist/Actions/EventAction.js", | ||
"node": "./dist/Actions/EventAction.js", | ||
"types": "./dist/Actions/EventAction.d.ts" | ||
}, | ||
"./AllowLists/AllowList": { | ||
"require": "./dist/AllowLists/AllowList.cjs", | ||
"import": "./dist/AllowLists/AllowList.js", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.