Skip to content

Commit

Permalink
feat: XMTP messages contracts are upgradeable and pausable
Browse files Browse the repository at this point in the history
  • Loading branch information
fbac committed Dec 16, 2024
1 parent 10808fb commit b490c21
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 17 deletions.
2 changes: 2 additions & 0 deletions contracts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/11155111/
/broadcast/*/241320161/
/broadcast/*/31337/
/broadcast/**/dry-run/

Expand Down
6 changes: 6 additions & 0 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@ libs = ["dependencies"]
gas_reports = ["*"]
optimizer = true
optimizer_runs = 10_000
remappings = [
"@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.1.0/",
"@openzeppelin-contracts-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.1.0/",
"forge-std/=dependencies/forge-std-1.9.4/",
]

[soldeer]
recursive_deps = true

[dependencies]
forge-std = "1.9.4"
"@openzeppelin-contracts-upgradeable" = "5.1.0"
"@openzeppelin-contracts" = "5.1.0"
5 changes: 3 additions & 2 deletions contracts/remappings.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@openzeppelin-contracts-5.1.0/=dependencies/@openzeppelin-contracts-5.1.0/
forge-std-1.9.4/=dependencies/forge-std-1.9.4/
@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.1.0/
@openzeppelin-contracts-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.1.0/
forge-std/=dependencies/forge-std-1.9.4/
28 changes: 28 additions & 0 deletions contracts/script/DeployerGroupMessages.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import "forge-std/src/Script.sol";
import "src/GroupMessages.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract DeployProxiedGroupMessages is Script {
function run() external {
uint256 privateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(privateKey);

// Step 1: Deploy the implementation contract
GroupMessages groupMessagesImpl = new GroupMessages();

// Step 2: Deploy the proxy contract
ERC1967Proxy proxy = new ERC1967Proxy(
address(groupMessagesImpl),
abi.encodeWithSelector(GroupMessages.initialize.selector)
);

// Log the deployed contract addresses
console.log("Implementation Address:", address(groupMessagesImpl));
console.log("Proxy Address:", address(proxy));

vm.stopBroadcast();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.28;

import {Script, console} from "forge-std-1.9.4/src/Script.sol";
import {Script, console} from "forge-std/src/Script.sol";
import "../src/Nodes.sol";

contract Deployer is Script {
Expand Down
7 changes: 7 additions & 0 deletions contracts/soldeer.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_1_0_
checksum = "fd3d1ea561cb27897008aee18ada6e85f248eb161c86e4435272fc2b5777574f"
integrity = "cb6cf6e878f2943b2291d5636a9d72ac51d43d8135896ceb6cf88d36c386f212"

[[dependencies]]
name = "@openzeppelin-contracts-upgradeable"
version = "5.1.0"
url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts-upgradeable/5_1_0_19-10-2024_10:28:58_contracts-upgradeable.zip"
checksum = "87854223d14941d6fda3d78d900217e79e25755ea5bc48beca035766fa6a4e7e"
integrity = "826fb621339dcee4261f848b283ec86364743d3c289d61f621747d95e315215a"

[[dependencies]]
name = "forge-std"
version = "1.9.4"
Expand Down
45 changes: 41 additions & 4 deletions contracts/src/GroupMessages.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

contract GroupMessages {
import "@openzeppelin-contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

/// @title XMTP Group Messages Contract
contract GroupMessages is Initializable, AccessControlUpgradeable, UUPSUpgradeable, PausableUpgradeable {
event MessageSent(bytes32 groupId, bytes message, uint64 sequenceId);
event UpgradeAuthorized(address deployer, address newImplementation);

uint64 sequenceId;
uint64 private sequenceId;

function addMessage(bytes32 groupId, bytes memory message) public {
sequenceId++;
/// @notice Initializes the contract with the deployer as admin.
function initialize() public initializer {
__UUPSUpgradeable_init();
__AccessControl_init();
__Pausable_init();
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}

/// @notice Pauses the contract.
function pause() public onlyRole(DEFAULT_ADMIN_ROLE) {
_pause();
}

/// @notice Unpauses the contract.
function unpause() public onlyRole(DEFAULT_ADMIN_ROLE) {
_unpause();
}

/// @notice Adds a message to the group.
/// @param groupId The group ID.
/// @param message The message in bytes.
function addMessage(bytes32 groupId, bytes calldata message) public whenNotPaused {
/// @dev Incrementing the sequence ID is safe here due to the extremely large limit of uint64.
unchecked {
sequenceId++;
}

emit MessageSent(groupId, message, sequenceId);
}

/// @dev Authorizes the upgrade of the contract.
/// @param newImplementation The address of the new implementation.
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {
emit UpgradeAuthorized(msg.sender, newImplementation);
}
}
45 changes: 41 additions & 4 deletions contracts/src/IdentityUpdates.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

contract IdentityUpdates {
import "@openzeppelin-contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

/// @title XMTP Identity Updates Contract
contract IdentityUpdates is Initializable, AccessControlUpgradeable, UUPSUpgradeable, PausableUpgradeable {
event IdentityUpdateCreated(bytes32 inboxId, bytes update, uint64 sequenceId);
event UpgradeAuthorized(address deployer, address newImplementation);

uint64 sequenceId;
uint64 private sequenceId;

function addIdentityUpdate(bytes32 inboxId, bytes memory update) public {
sequenceId++;
/// @notice Initializes the contract with the deployer as admin.
function initialize() public initializer {
__UUPSUpgradeable_init();
__AccessControl_init();
__Pausable_init();
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}

/// @notice Pauses the contract.
function pause() public onlyRole(DEFAULT_ADMIN_ROLE) {
_pause();
}

/// @notice Unpauses the contract.
function unpause() public onlyRole(DEFAULT_ADMIN_ROLE) {
_unpause();
}

/// @notice Adds an identity update to an specific inbox ID.
/// @param inboxId The inbox ID.
/// @param update The identity update in bytes.
function addIdentityUpdate(bytes32 inboxId, bytes calldata update) public whenNotPaused {
/// @dev Incrementing the sequence ID is safe here due to the extremely large limit of uint64.
unchecked {
sequenceId++;
}

emit IdentityUpdateCreated(inboxId, update, sequenceId);
}

/// @dev Authorizes the upgrade of the contract.
/// @param newImplementation The address of the new implementation.
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {
emit UpgradeAuthorized(msg.sender, newImplementation);
}
}
4 changes: 2 additions & 2 deletions contracts/src/Nodes.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import "@openzeppelin-contracts-5.1.0/token/ERC721/ERC721.sol";
import "@openzeppelin-contracts-5.1.0/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
* A NFT contract for XMTP Node Operators.
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/GroupMessage.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.28;

import {Test, console} from "forge-std-1.9.4/src/Test.sol";
import {Test, console} from "forge-std/src/Test.sol";
import {GroupMessages} from "../src/GroupMessages.sol";

contract GroupMessagesTest is Test {
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/IdentityUpdates.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.28;

import {Test, console} from "forge-std-1.9.4/src/Test.sol";
import {Test, console} from "forge-std/src/Test.sol";
import {IdentityUpdates} from "../src/IdentityUpdates.sol";

contract IdentityUpdatesTest is Test {
Expand Down
4 changes: 2 additions & 2 deletions contracts/test/Nodes.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.28;

import {Test, console} from "forge-std-1.9.4/src/Test.sol";
import {Ownable} from "@openzeppelin-contracts-5.1.0/access/Ownable.sol";
import {Test, console} from "forge-std/src/Test.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Nodes} from "../src/Nodes.sol";

contract NodesTest is Test {
Expand Down

0 comments on commit b490c21

Please sign in to comment.