Skip to content
This repository has been archived by the owner on Nov 27, 2024. It is now read-only.

feat: llama message broadcaster #96

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions script/DeployLlamaTokenVotingFactory.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.23;
import {Script} from "forge-std/Script.sol";

import {DeployUtils} from "script/DeployUtils.sol";
import {LlamaMessageBroadcaster} from "src/message-broadcaster/LlamaMessageBroadcaster.sol";
import {LlamaTokenGovernor} from "src/token-voting/LlamaTokenGovernor.sol";
import {LlamaTokenVotingFactory} from "src/token-voting/LlamaTokenVotingFactory.sol";
import {LlamaTokenAdapterVotesTimestamp} from "src/token-voting/token-adapters/LlamaTokenAdapterVotesTimestamp.sol";
Expand All @@ -16,6 +17,9 @@ contract DeployLlamaTokenVotingFactory is Script {
// Factory contracts.
LlamaTokenVotingFactory tokenVotingFactory;

// Llama Message Broadcaster.
LlamaMessageBroadcaster llamaMessageBroadcaster;

function run() public {
DeployUtils.print(
string.concat("Deploying Llama token voting factory and logic contracts to chain:", vm.toString(block.chainid))
Expand All @@ -34,5 +38,9 @@ contract DeployLlamaTokenVotingFactory is Script {
DeployUtils.print(
string.concat(" LlamaTokenAdapterVotesTimestamp: ", vm.toString(address(llamaTokenAdapterTimestampLogic)))
);

vm.broadcast();
llamaMessageBroadcaster = new LlamaMessageBroadcaster();
DeployUtils.print(string.concat(" LlamaMessageBroadcaster: ", vm.toString(address(llamaMessageBroadcaster))));
}
}
22 changes: 22 additions & 0 deletions src/message-broadcaster/LlamaMessageBroadcaster.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {ILlamaCore} from "src/interfaces/ILlamaCore.sol";
import {ILlamaExecutor} from "src/interfaces/ILlamaExecutor.sol";

/// @title LlamaMessageBroadcaster
/// @author Llama (devsdosomething@llama.xyz)
/// @notice This contract enables Llama instances to broadcast an offchain message.
contract LlamaMessageBroadcaster {
/// @dev Emitted when a message is broadcast by a Llama instance.
event MessageBroadcasted(ILlamaExecutor indexed llamaExecutor, string message);

/// @notice Broadcasts a message from a Llama instance.
/// @param message Message to be broadcasted.
function broadcastMessage(string calldata message) external {
ILlamaExecutor llamaExecutor = ILlamaExecutor(msg.sender);
// Duck testing to check if the caller is a Llama instance.
ILlamaCore(llamaExecutor.LLAMA_CORE()).actionsCount();
Comment on lines +18 to +19
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the duck test here to reduce possible spam since it's an open function.

emit MessageBroadcasted(llamaExecutor, message);
}
}
75 changes: 75 additions & 0 deletions test/message-broadcaster/LlamaMessageBroadcaster.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Test, console2} from "forge-std/Test.sol";
import {Vm} from "forge-std/Vm.sol";

import {LlamaPeripheryTestSetup} from "test/LlamaPeripheryTestSetup.sol";

import {DeployLlamaTokenVotingFactory} from "script/DeployLlamaTokenVotingFactory.s.sol";

import {ILlamaExecutor} from "src/interfaces/ILlamaExecutor.sol";
import {ILlamaPolicy} from "src/interfaces/ILlamaPolicy.sol";
import {ActionInfo} from "src/lib/Structs.sol";
import {LlamaMessageBroadcaster} from "src/message-broadcaster/LlamaMessageBroadcaster.sol";

contract LlamaMessageBroadcasterTest is LlamaPeripheryTestSetup, DeployLlamaTokenVotingFactory {
event MessageBroadcasted(ILlamaExecutor indexed llamaExecutor, string message);

function setUp() public virtual override {
LlamaPeripheryTestSetup.setUp();

// Deploy the peripheral contracts
DeployLlamaTokenVotingFactory.run();
}
}

contract BroadcastMessage is LlamaMessageBroadcasterTest {
function test_BroadcastMessage() public {
string memory message = "Hello World!";
vm.expectEmit();
emit MessageBroadcasted(EXECUTOR, message);
vm.prank(address(EXECUTOR));
llamaMessageBroadcaster.broadcastMessage(message);
}

function test_BroadcastMessageFullActionLifecycle() public {
string memory message = "Hello World!";

// Giving Action Creator permission to call `LlamaMessageBroadcaster.broadcastMessage`.
vm.prank(address(EXECUTOR));
POLICY.setRolePermission(
CORE_TEAM_ROLE,
ILlamaPolicy.PermissionData(
address(llamaMessageBroadcaster), LlamaMessageBroadcaster.broadcastMessage.selector, address(STRATEGY)
),
true
);

// Create Action to broadcast message.
bytes memory data = abi.encodeCall(LlamaMessageBroadcaster.broadcastMessage, (message));
vm.prank(coreTeam1);
uint256 actionId = CORE.createAction(CORE_TEAM_ROLE, STRATEGY, address(llamaMessageBroadcaster), 0, data, "");
ActionInfo memory actionInfo =
ActionInfo(actionId, coreTeam1, CORE_TEAM_ROLE, STRATEGY, address(llamaMessageBroadcaster), 0, data);

// Approval and auto-queue process.
vm.prank(coreTeam2);
CORE.castApproval(CORE_TEAM_ROLE, actionInfo, "");
vm.prank(coreTeam3);
CORE.castApproval(CORE_TEAM_ROLE, actionInfo, "");
vm.prank(coreTeam4);
CORE.castApproval(CORE_TEAM_ROLE, actionInfo, "");

// Execute Action.
vm.expectEmit();
emit MessageBroadcasted(EXECUTOR, message);
CORE.executeAction(actionInfo);
}

function test_RevertIf_CallerIsNotExecutor() public {
string memory message = "Hello World!";
vm.expectRevert();
llamaMessageBroadcaster.broadcastMessage(message);
}
}
Loading