Skip to content

Commit

Permalink
Allow popping of messages at specified indices (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
kongzii authored Jan 27, 2025
1 parent cf16bde commit 25e01e3
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Repository holding the contracts made by Gnosis Labs team.
| OmenThumbnailMapping | Manages IPFS hashes for market thumbnails on Omen 2.0 | [0xe0cf08311F03850497B0ed6A2cf067f1750C3eFc](https://gnosisscan.io/address/0xe0cf08311f03850497b0ed6a2cf067f1750c3efc#code) | [omen-thumbnailmapping](https://thegraph.com/studio/subgraph/omen-thumbnailmapping/) |
| OmenAgentResultMapping | Maps prediction results to markets on Omen 2.0 | [0xbe1F6944496923683ca849fc0cC93fD10523cB83](https://gnosisscan.io/address/0x260E1077dEA98e738324A6cEfB0EE9A272eD471a#code) | [omen-agentresultmapping](https://thegraph.com/studio/subgraph/omen-agentresultmapping/) |
| Agent NFT | Agent NFTs that control mechs for NFT game | [0x0D7C0Bd4169D090038c6F41CFd066958fe7619D0](https://gnosisscan.io/address/0x0D7C0Bd4169D090038c6F41CFd066958fe7619D0#code) | |
| Agent communication contract | Simple contract storing message queue for each agent | [0xc566Cb829Ed7aC097D17a38011A40Ad2DC25Dd82](https://gnosisscan.io/address/0xc566Cb829Ed7aC097D17a38011A40Ad2DC25Dd82#code) | |
| Agent communication contract | Simple contract storing message queue for each agent | [0xe9dd78FF297DbaAbe5D0E45aE554a4B561935DE9](https://gnosisscan.io/address/0xe9dd78FF297DbaAbe5D0E45aE554a4B561935DE9#code) | |
| Simple Treasury contract | Contract for storing the NFT agent game treasury | [0x624ad0db52e6b18afb4d36b8e79d0c2a74f3fc8a](https://gnosisscan.io/address/0x624ad0db52e6b18afb4d36b8e79d0c2a74f3fc8a#code) | |

## Set up contracts development
Expand Down
7 changes: 5 additions & 2 deletions src/NFT/AgentCommunication.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,14 @@ contract AgentCommunication is Ownable {
return DoubleEndedStructQueue.at(queues[agentAddress], idx);
}

function popNextMessage(address agentAddress) public returns (DoubleEndedStructQueue.MessageContainer memory) {
function popMessageAtIndex(address agentAddress, uint256 idx)
public
returns (DoubleEndedStructQueue.MessageContainer memory)
{
if (msg.sender != agentAddress) {
revert MessageNotSentByAgent();
}
DoubleEndedStructQueue.MessageContainer memory message = DoubleEndedStructQueue.popFront(queues[agentAddress]);
DoubleEndedStructQueue.MessageContainer memory message = DoubleEndedStructQueue.popAt(queues[agentAddress], idx);
emit LogMessage(message.sender, agentAddress, message.message, message.value);
return message;
}
Expand Down
22 changes: 22 additions & 0 deletions src/NFT/DoubleEndedStructQueue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,28 @@ library DoubleEndedStructQueue {
}
}

function popAt(Bytes32Deque storage deque, uint256 index) internal returns (MessageContainer memory value) {
if (index >= length(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
unchecked {
if (index == 0) {
return popFront(deque);
} else if (index == length(deque) - 1) {
return popBack(deque);
} else {
uint128 actualIndex = deque._begin + uint128(index);
value = deque._data[actualIndex];
delete deque._data[actualIndex];

// Shift elements to fill the gap
for (uint128 i = actualIndex; i < deque._end - 1; i++) {
deque._data[i] = deque._data[i + 1];
}
delete deque._data[deque._end - 1];
deque._end--;
}
}
}

function clear(Bytes32Deque storage deque) internal {
deque._begin = 0;
deque._end = 0;
Expand Down
73 changes: 50 additions & 23 deletions test/AgentCommunication.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ contract AgentCommunicationTest is Test {
address payable treasury = payable(address(0x789));
uint256 pctToTreasuryInBasisPoints = 7000;

function buildMessage() public view returns (DoubleEndedStructQueue.MessageContainer memory) {
function buildMessage(bytes memory customMessage)
public
view
returns (DoubleEndedStructQueue.MessageContainer memory)
{
return DoubleEndedStructQueue.MessageContainer({
sender: agent,
recipient: address(0x789),
message: "Hello, Agent!",
message: customMessage,
value: 1000000000000000000
});
}
Expand Down Expand Up @@ -56,7 +60,7 @@ contract AgentCommunicationTest is Test {
}

function testSendMessage() public {
DoubleEndedStructQueue.MessageContainer memory message = buildMessage();
DoubleEndedStructQueue.MessageContainer memory message = buildMessage("Hello!");
vm.deal(owner, 1 ether);

// Record initial balances
Expand All @@ -82,7 +86,7 @@ contract AgentCommunicationTest is Test {
}

function testSendMessageInsufficientValue() public {
DoubleEndedStructQueue.MessageContainer memory message = buildMessage();
DoubleEndedStructQueue.MessageContainer memory message = buildMessage("Hello!");
vm.deal(agent, 1 ether);
vm.startPrank(agent);
vm.expectRevert("Insufficient message value");
Expand All @@ -91,7 +95,7 @@ contract AgentCommunicationTest is Test {
}

function testNewMessageSentEvent() public {
DoubleEndedStructQueue.MessageContainer memory message = buildMessage();
DoubleEndedStructQueue.MessageContainer memory message = buildMessage("Hello!");
vm.deal(agent, 1 ether);
vm.startPrank(agent);

Expand All @@ -104,38 +108,61 @@ contract AgentCommunicationTest is Test {
vm.stopPrank();
}

function testPopNextMessage() public {
// Create a message container
DoubleEndedStructQueue.MessageContainer memory message = buildMessage();
function testPopMessage() public {
// Create a message containers
DoubleEndedStructQueue.MessageContainer memory message_1 = buildMessage("Hello 1!");
DoubleEndedStructQueue.MessageContainer memory message_2 = buildMessage("Hello 2!");
DoubleEndedStructQueue.MessageContainer memory message_3 = buildMessage("Hello 3!");
DoubleEndedStructQueue.MessageContainer memory message_4 = buildMessage("Hello 4!");
DoubleEndedStructQueue.MessageContainer memory message_5 = buildMessage("Hello 5!");

// Fund the agent and start the prank
vm.deal(agent, 1 ether);
vm.deal(agent, 5 ether);
vm.startPrank(agent);

// Send the message
agentComm.sendMessage{value: message.value}(message.recipient, message.message);
// Send the messages
agentComm.sendMessage{value: message_1.value}(message_1.recipient, message_1.message);
agentComm.sendMessage{value: message_2.value}(message_2.recipient, message_2.message);
agentComm.sendMessage{value: message_3.value}(message_3.recipient, message_3.message);
agentComm.sendMessage{value: message_4.value}(message_4.recipient, message_4.message);
agentComm.sendMessage{value: message_5.value}(message_5.recipient, message_5.message);
vm.stopPrank();

// Start the prank again for popping the message
vm.startPrank(message.recipient);
vm.startPrank(message_1.recipient);

// Expect the LogMessage event to be emitted when popping the message
vm.expectEmit(true, true, true, true);
emit AgentCommunication.LogMessage(message.sender, message.recipient, message.message, message.value);
emit AgentCommunication.LogMessage(message_1.sender, message_1.recipient, message_1.message, message_1.value);

// Pop the next message
DoubleEndedStructQueue.MessageContainer memory poppedMessage = agentComm.popNextMessage(message.recipient);
DoubleEndedStructQueue.MessageContainer memory poppedMessage_1 =
agentComm.popMessageAtIndex(message_1.recipient, 0);
vm.stopPrank();

// Assert that the popped message matches the original message
assertEq(poppedMessage_1.sender, message_1.sender);
assertEq(poppedMessage_1.recipient, message_1.recipient);
assertEq(poppedMessage_1.message, message_1.message);
assertEq(poppedMessage_1.value, message_1.value);

// Start the prank again for popping another message
vm.startPrank(message_1.recipient);

// Pop the message at specified index
DoubleEndedStructQueue.MessageContainer memory poppedMessage_2 =
agentComm.popMessageAtIndex(message_1.recipient, 2);
vm.stopPrank();

// Assert that the popped message matches the original message
assertEq(poppedMessage.sender, message.sender);
assertEq(poppedMessage.recipient, message.recipient);
assertEq(poppedMessage.message, message.message);
assertEq(poppedMessage.value, message.value);
assertEq(poppedMessage_2.sender, message_4.sender);
assertEq(poppedMessage_2.recipient, message_4.recipient);
assertEq(poppedMessage_2.message, message_4.message);
assertEq(poppedMessage_2.value, message_4.value);
}

function testPopNextMessageNotByAgent() public {
DoubleEndedStructQueue.MessageContainer memory message = buildMessage();
function testPopMessageNotByAgent() public {
DoubleEndedStructQueue.MessageContainer memory message = buildMessage("Hello!");
vm.deal(agent, 1 ether);
vm.startPrank(agent);
agentComm.sendMessage{value: 10000000000000}(agent, message.message);
Expand All @@ -144,15 +171,15 @@ contract AgentCommunicationTest is Test {
address notAgent = address(0x789);
vm.startPrank(notAgent);
vm.expectRevert(AgentCommunication.MessageNotSentByAgent.selector);
agentComm.popNextMessage(agent);
agentComm.popMessageAtIndex(agent, 0);
vm.stopPrank();
}

function testCountMessages() public {
// Initialize a message
DoubleEndedStructQueue.MessageContainer memory message1 = buildMessage();
DoubleEndedStructQueue.MessageContainer memory message1 = buildMessage("Hello!");

DoubleEndedStructQueue.MessageContainer memory message2 = buildMessage();
DoubleEndedStructQueue.MessageContainer memory message2 = buildMessage("Hello!");

// Fund the agent and start the prank
vm.deal(agent, 1 ether);
Expand Down

0 comments on commit 25e01e3

Please sign in to comment.