From cfbf07451880922318e2ec3f61835e78c314d8cb Mon Sep 17 00:00:00 2001 From: Akash Date: Wed, 11 Jun 2025 17:56:16 +0530 Subject: [PATCH 01/12] chore: package.json trace script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b61572d5..b9d1c8a9 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "compile": "forge build", "deploy": "bash setupInfraContracts.sh", "publish-core": "yarn build && yarn publish --patch --no-git-tag-version", - "trace":"bash trace.sh" + "trace":"source .env && bash trace.sh" }, "pre-commit": [], "author": "", From e38b8063fbf9b5b2938aea747c174a759e4efb21 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 16 Jun 2025 12:48:50 +0530 Subject: [PATCH 02/12] feat: added CCTP switchboard support --- contracts/evmx/interfaces/IAppGateway.sol | 4 + contracts/evmx/interfaces/IWatcher.sol | 1 - contracts/protocol/SocketBatcher.sol | 50 +++- .../protocol/interfaces/ICCTPSwitchboard.sol | 62 +++++ .../protocol/interfaces/IMessageHandler.sol | 21 ++ .../interfaces/IMessageTransmitter.sol | 19 ++ contracts/protocol/interfaces/ISocket.sol | 6 +- .../protocol/switchboard/CCTPSwitchboard.sol | 219 ++++++++++++++++++ contracts/utils/common/Constants.sol | 1 + contracts/utils/common/Structs.sol | 15 ++ package.json | 2 +- test/SetupTest.t.sol | 211 ++++++++++++++--- test/apps/CCTPSuperToken.t.sol | 179 ++++++++++++++ .../super-token/SuperTokenAppGateway.sol | 4 + test/mock/CCTPMessageTransmitter.sol | 78 +++++++ test/mock/MockSocket.sol | 6 - 16 files changed, 842 insertions(+), 36 deletions(-) create mode 100644 contracts/protocol/interfaces/ICCTPSwitchboard.sol create mode 100644 contracts/protocol/interfaces/IMessageHandler.sol create mode 100644 contracts/protocol/interfaces/IMessageTransmitter.sol create mode 100644 contracts/protocol/switchboard/CCTPSwitchboard.sol create mode 100644 test/apps/CCTPSuperToken.t.sol create mode 100644 test/mock/CCTPMessageTransmitter.sol diff --git a/contracts/evmx/interfaces/IAppGateway.sol b/contracts/evmx/interfaces/IAppGateway.sol index 132e8c96..3ec414b8 100644 --- a/contracts/evmx/interfaces/IAppGateway.sol +++ b/contracts/evmx/interfaces/IAppGateway.sol @@ -45,4 +45,8 @@ interface IAppGateway { bytes32 contractId_, uint32 chainSlug_ ) external view returns (address forwarderAddress); + + /// @notice get the switchboard type + /// @return sbType The switchboard type + function sbType() external view returns (bytes32); } diff --git a/contracts/evmx/interfaces/IWatcher.sol b/contracts/evmx/interfaces/IWatcher.sol index d601d8d7..7110b4eb 100644 --- a/contracts/evmx/interfaces/IWatcher.sol +++ b/contracts/evmx/interfaces/IWatcher.sol @@ -11,7 +11,6 @@ import "./IPromiseResolver.sol"; /// @notice Interface for the Watcher Precompile system that handles payload verification and execution /// @dev Defines core functionality for payload processing and promise resolution interface IWatcher { - function requestHandler__() external view returns (IRequestHandler); function configurations__() external view returns (IConfigurations); diff --git a/contracts/protocol/SocketBatcher.sol b/contracts/protocol/SocketBatcher.sol index 1013af2f..7e2384bd 100644 --- a/contracts/protocol/SocketBatcher.sol +++ b/contracts/protocol/SocketBatcher.sol @@ -5,8 +5,10 @@ import "solady/auth/Ownable.sol"; import "./interfaces/ISocket.sol"; import "./interfaces/ISocketBatcher.sol"; import "./interfaces/ISwitchboard.sol"; +import "./interfaces/ICCTPSwitchboard.sol"; import "../utils/RescueFundsLib.sol"; -import {ExecuteParams, TransmissionParams} from "../utils/common/Structs.sol"; +import {ExecuteParams, TransmissionParams, CCTPBatchParams, CCTPExecutionParams} from "../utils/common/Structs.sol"; +import {createPayloadId} from "../utils/common/IdUtils.sol"; /** * @title SocketBatcher @@ -55,6 +57,52 @@ contract SocketBatcher is ISocketBatcher, Ownable { ); } + function attestCCTPAndProveAndExecute( + CCTPExecutionParams calldata execParams_, + CCTPBatchParams calldata cctpParams_, + address switchboard_ + ) external payable returns (bool, bytes memory) { + bytes32 payloadId = _createPayloadId(execParams_.executeParams, switchboard_); + ICCTPSwitchboard(switchboard_).attest(payloadId, execParams_.digest, execParams_.proof); + + ICCTPSwitchboard(switchboard_).verifyAttestations( + cctpParams_.messages, + cctpParams_.attestations + ); + ICCTPSwitchboard(switchboard_).proveRemoteExecutions( + cctpParams_.previousPayloadIds, + payloadId, + execParams_.transmitterSignature, + execParams_.executeParams + ); + (bool success, bytes memory returnData) = socket__.execute{value: msg.value}( + execParams_.executeParams, + TransmissionParams({ + transmitterSignature: execParams_.transmitterSignature, + socketFees: 0, + extraData: execParams_.executeParams.extraData, + refundAddress: execParams_.refundAddress + }) + ); + + ICCTPSwitchboard(switchboard_).syncOut(payloadId, cctpParams_.nextBatchRemoteChainSlugs); + return (success, returnData); + } + + function _createPayloadId( + ExecuteParams memory executeParams_, + address switchboard_ + ) internal view returns (bytes32) { + return + createPayloadId( + executeParams_.requestCount, + executeParams_.batchCount, + executeParams_.payloadCount, + switchboard_, + socket__.chainSlug() + ); + } + /** * @notice Rescues funds from the contract * @param token_ The address of the token to rescue diff --git a/contracts/protocol/interfaces/ICCTPSwitchboard.sol b/contracts/protocol/interfaces/ICCTPSwitchboard.sol new file mode 100644 index 00000000..ac3bb40e --- /dev/null +++ b/contracts/protocol/interfaces/ICCTPSwitchboard.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import "./ISwitchboard.sol"; +import {ExecuteParams} from "../../utils/common/Structs.sol"; + +/** + * @title ISwitchboard + * @dev The interface for a switchboard contract that is responsible for verification of payloads if the correct + * digest is executed. + */ +interface ICCTPSwitchboard is ISwitchboard { + /** + * @notice Attests a payload + * @param digest_ The digest of the payload + * @param proof_ The proof of the payload + */ + function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) external; + + /** + * @notice Syncs out a payload to the remote chains + * @param payloadId_ The unique identifier for the payload + * @param remoteChainSlugs_ The remote chain slugs + */ + function syncOut(bytes32 payloadId_, uint32[] calldata remoteChainSlugs_) external; + + /** + * @notice Handles the receive message + * @param sourceDomain The source domain + * @param sender The sender + * @param messageBody The message body + */ + function handleReceiveMessage( + uint32 sourceDomain, + bytes32 sender, + bytes calldata messageBody + ) external returns (bool); + + /** + * @notice Proves the remote executions + * @param previousPayloadIds_ The previous payload ids + * @param currentPayloadId_ The current payload id + * @param transmitterSignature_ The transmitter signature + * @param executeParams_ The execute parameters + */ + function proveRemoteExecutions( + bytes32[] calldata previousPayloadIds_, + bytes32 currentPayloadId_, + bytes calldata transmitterSignature_, + ExecuteParams calldata executeParams_ + ) external; + + /** + * @notice Verifies the attestations + * @param messages_ The messages + * @param attestations_ The attestations + */ + function verifyAttestations( + bytes[] calldata messages_, + bytes[] calldata attestations_ + ) external; +} diff --git a/contracts/protocol/interfaces/IMessageHandler.sol b/contracts/protocol/interfaces/IMessageHandler.sol new file mode 100644 index 00000000..c6c29b10 --- /dev/null +++ b/contracts/protocol/interfaces/IMessageHandler.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; +/** + * @title IMessageHandler + * @notice Handles messages on destination domain forwarded from + * an IReceiver + */ +interface IMessageHandler { + /** + * @notice handles an incoming message from a Receiver + * @param sourceDomain the source domain of the message + * @param sender the sender of the message + * @param messageBody The message raw bytes + * @return success bool, true if successful + */ + function handleReceiveMessage( + uint32 sourceDomain, + bytes32 sender, + bytes calldata messageBody + ) external returns (bool); +} diff --git a/contracts/protocol/interfaces/IMessageTransmitter.sol b/contracts/protocol/interfaces/IMessageTransmitter.sol new file mode 100644 index 00000000..427f2813 --- /dev/null +++ b/contracts/protocol/interfaces/IMessageTransmitter.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +interface IMessageTransmitter { + function sendMessage( + uint32 destinationDomain, + bytes32 recipient, + bytes calldata messageBody + ) external returns (uint64 nonce); + + function receiveMessage( + bytes calldata message, + bytes calldata attestation + ) external returns (bool success); + + function localDomain() external view returns (uint32); + + function attestationManager() external view returns (address); +} diff --git a/contracts/protocol/interfaces/ISocket.sol b/contracts/protocol/interfaces/ISocket.sol index 80a8a25a..e27eaefd 100644 --- a/contracts/protocol/interfaces/ISocket.sol +++ b/contracts/protocol/interfaces/ISocket.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {ExecuteParams, TransmissionParams} from "../../utils/common/Structs.sol"; +import {ExecuteParams, TransmissionParams, ExecutionStatus} from "../../utils/common/Structs.sol"; /** * @title ISocket @@ -76,4 +76,8 @@ interface ISocket { function getPlugConfig( address plugAddress_ ) external view returns (bytes32 appGatewayId, address switchboard); + + function payloadExecuted(bytes32 payloadId_) external view returns (ExecutionStatus); + + function chainSlug() external view returns (uint32); } diff --git a/contracts/protocol/switchboard/CCTPSwitchboard.sol b/contracts/protocol/switchboard/CCTPSwitchboard.sol new file mode 100644 index 00000000..539ea06a --- /dev/null +++ b/contracts/protocol/switchboard/CCTPSwitchboard.sol @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import "./FastSwitchboard.sol"; +import {ISocket} from "../interfaces/ISocket.sol"; +import {ExecuteParams, ExecutionStatus} from "../../utils/common/Structs.sol"; +import {IMessageTransmitter} from "../interfaces/IMessageTransmitter.sol"; +import {IMessageHandler} from "../interfaces/IMessageHandler.sol"; + +contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { + struct RemoteEndpoint { + uint256 remoteChainId; + address remoteAddress; + uint32 remoteDomain; + } + + // remoteChainSlug => remoteEndpoint + mapping(uint32 => RemoteEndpoint) public remoteEndpoints; + // remoteDomain => remoteAddress + mapping(uint32 => address) public remoteAddresses; + + mapping(bytes32 => bool) public isSyncedOut; + mapping(bytes32 => bytes32) public payloadIdToDigest; + mapping(bytes32 => bytes32) public remoteExecutedDigests; + mapping(bytes32 => bool) public isRemoteExecuted; + + IMessageTransmitter public immutable messageTransmitter; + + error RemoteExecutionNotFound(); + error DigestMismatch(); + error PreviousDigestsHashMismatch(); + error NotAttested(); + error NotExecuted(); + error InvalidDomain(); + error InvalidSender(); + error OnlyMessageTransmitter(); + event Attested(bytes32 payloadId, bytes32 digest, address watcher); + + constructor( + uint32 chainSlug_, + ISocket socket_, + address owner_, + address messageTransmitter_ + ) FastSwitchboard(chainSlug_, socket_, owner_) { + messageTransmitter = IMessageTransmitter(messageTransmitter_); + } + + function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) external { + address watcher = _recoverSigner( + keccak256(abi.encode(address(this), chainSlug, digest_)), + proof_ + ); + + if (isAttested[digest_]) revert AlreadyAttested(); + if (!_hasRole(WATCHER_ROLE, watcher)) revert WatcherNotFound(); + + isAttested[digest_] = true; + payloadIdToDigest[payloadId_] = digest_; + emit Attested(payloadId_, digest_, watcher); + } + + function allowPacket(bytes32 digest_, bytes32 payloadId_) external view returns (bool) { + // digest has enough attestations and is remote executed + return + payloadIdToDigest[payloadId_] == digest_ && + isAttested[digest_] && + isRemoteExecuted[payloadId_]; + } + + function syncOut(bytes32 payloadId_, uint32[] calldata remoteChainSlugs_) external { + bytes32 digest = payloadIdToDigest[payloadId_]; + + // not attested + if (digest == bytes32(0) || !isAttested[digest]) revert NotAttested(); + + // already synced out + if (isSyncedOut[digest]) return; + isSyncedOut[digest] = true; + + // not executed + ExecutionStatus isExecuted = socket__.payloadExecuted(payloadId_); + if (isExecuted != ExecutionStatus.Executed) revert NotExecuted(); + + bytes memory message = abi.encode(payloadId_, digest); + for (uint256 i = 0; i < remoteChainSlugs_.length; i++) { + RemoteEndpoint memory endpoint = remoteEndpoints[remoteChainSlugs_[i]]; + if (endpoint.remoteDomain == 0) revert InvalidDomain(); + + messageTransmitter.sendMessage( + endpoint.remoteDomain, + addressToBytes32(endpoint.remoteAddress), + message + ); + } + } + + function handleReceiveMessage( + uint32 sourceDomain, + bytes32 sender, + bytes calldata messageBody + ) external returns (bool) { + if (msg.sender != address(messageTransmitter)) revert OnlyMessageTransmitter(); + + (bytes32 payloadId, bytes32 digest) = abi.decode(messageBody, (bytes32, bytes32)); + if (remoteAddresses[sourceDomain] != bytes32ToAddress(sender)) { + revert InvalidSender(); + } + + remoteExecutedDigests[payloadId] = digest; + return true; + } + + function verifyAttestations(bytes[] calldata messages, bytes[] calldata attestations) external { + for (uint256 i = 0; i < messages.length; i++) { + messageTransmitter.receiveMessage(messages[i], attestations[i]); + } + } + + function proveRemoteExecutions( + bytes32[] calldata previousPayloadIds_, + bytes32 payloadId_, + bytes calldata transmitterSignature_, + ExecuteParams calldata executeParams_ + ) external { + // Calculate previousDigestsHash from stored remoteExecutedDigests + bytes32 previousDigestsHash = bytes32(0); + for (uint256 i = 0; i < previousPayloadIds_.length; i++) { + if (remoteExecutedDigests[previousPayloadIds_[i]] == bytes32(0)) + revert RemoteExecutionNotFound(); + previousDigestsHash = keccak256( + abi.encodePacked(previousDigestsHash, remoteExecutedDigests[previousPayloadIds_[i]]) + ); + } + // Check if the calculated previousDigestsHash matches the one in executeParams_ + if (previousDigestsHash != executeParams_.prevBatchDigestHash) + revert PreviousDigestsHashMismatch(); + + address transmitter = _recoverSigner( + keccak256(abi.encode(address(socket__), payloadId_)), + transmitterSignature_ + ); + + // Construct current digest + (bytes32 appGatewayId, ) = socket__.getPlugConfig(executeParams_.target); + bytes32 constructedDigest = _createDigest( + transmitter, + payloadId_, + appGatewayId, + executeParams_ + ); + + bytes32 storedDigest = payloadIdToDigest[payloadId_]; + // Verify the constructed digest matches the stored one + if (storedDigest == bytes32(0) || !isAttested[storedDigest]) revert NotAttested(); + if (constructedDigest != storedDigest) revert DigestMismatch(); + + isRemoteExecuted[payloadId_] = true; + } + + /** + * @notice creates the digest for the payload + * @param transmitter_ The address of the transmitter + * @param payloadId_ The ID of the payload + * @param appGatewayId_ The id of the app gateway + * @param executeParams_ The parameters of the payload + * @return The packed payload as a bytes32 hash + */ + function _createDigest( + address transmitter_, + bytes32 payloadId_, + bytes32 appGatewayId_, + ExecuteParams calldata executeParams_ + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + address(socket__), + transmitter_, + payloadId_, + executeParams_.deadline, + executeParams_.callType, + executeParams_.gasLimit, + executeParams_.value, + executeParams_.payload, + executeParams_.target, + appGatewayId_, + executeParams_.prevBatchDigestHash, + executeParams_.extraData + ) + ); + } + + function addRemoteEndpoint( + uint32 remoteChainSlug_, + uint256 remoteChainId_, + address remoteAddress_, + uint32 remoteDomain_ + ) external onlyOwner { + remoteEndpoints[remoteChainSlug_] = RemoteEndpoint({ + remoteChainId: remoteChainId_, + remoteAddress: remoteAddress_, + remoteDomain: remoteDomain_ + }); + remoteAddresses[remoteDomain_] = remoteAddress_; + } + + function removeRemoteEndpoint(uint32 remoteChainSlug_) external onlyOwner { + uint32 remoteDomain = remoteEndpoints[remoteChainSlug_].remoteDomain; + delete remoteEndpoints[remoteChainSlug_]; + delete remoteAddresses[remoteDomain]; + } + + function addressToBytes32(address addr_) public pure returns (bytes32) { + return bytes32(uint256(uint160(addr_))); + } + function bytes32ToAddress(bytes32 addrBytes32_) public pure returns (address) { + return address(uint160(uint256(addrBytes32_))); + } +} diff --git a/contracts/utils/common/Constants.sol b/contracts/utils/common/Constants.sol index 2afa6479..e6588e43 100644 --- a/contracts/utils/common/Constants.sol +++ b/contracts/utils/common/Constants.sol @@ -13,6 +13,7 @@ bytes4 constant SCHEDULE = bytes4(keccak256("SCHEDULE")); bytes32 constant CALLBACK = keccak256("CALLBACK"); bytes32 constant FAST = keccak256("FAST"); +bytes32 constant CCTP = keccak256("CCTP"); uint256 constant PAYLOAD_SIZE_LIMIT = 24_500; uint16 constant MAX_COPY_BYTES = 2048; // 2KB diff --git a/contracts/utils/common/Structs.sol b/contracts/utils/common/Structs.sol index 80943216..efc936ef 100644 --- a/contracts/utils/common/Structs.sol +++ b/contracts/utils/common/Structs.sol @@ -199,3 +199,18 @@ struct RequestParams { uint256 writeCount; bytes onCompleteData; } + +struct CCTPExecutionParams { + ExecuteParams executeParams; + bytes32 digest; + bytes proof; + bytes transmitterSignature; + address refundAddress; +} + +struct CCTPBatchParams { + bytes32[] previousPayloadIds; + uint32[] nextBatchRemoteChainSlugs; + bytes[] messages; + bytes[] attestations; +} diff --git a/package.json b/package.json index b9d1c8a9..9d0b88f1 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "compile": "forge build", "deploy": "bash setupInfraContracts.sh", "publish-core": "yarn build && yarn publish --patch --no-git-tag-version", - "trace":"source .env && bash trace.sh" + "trace": "source .env && bash trace.sh" }, "pre-commit": [], "author": "", diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 377e2c4b..f1dad31c 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -12,6 +12,7 @@ import "../contracts/evmx/interfaces/IForwarder.sol"; import "../contracts/protocol/Socket.sol"; import "../contracts/protocol/switchboard/FastSwitchboard.sol"; +import "../contracts/protocol/switchboard/CCTPSwitchboard.sol"; import "../contracts/protocol/SocketBatcher.sol"; import "../contracts/protocol/SocketFeeManager.sol"; @@ -32,6 +33,7 @@ import "../contracts/evmx/fees/FeesPool.sol"; import "../contracts/evmx/plugs/FeesPlug.sol"; import "../contracts/evmx/AuctionManager.sol"; import "../contracts/evmx/mocks/TestUSDC.sol"; +import "./mock/CCTPMessageTransmitter.sol"; import "solady/utils/ERC1967Factory.sol"; @@ -68,11 +70,14 @@ contract SetupStore is Test { uint256 public payloadIdCounter; uint256 public triggerCounter; uint256 public asyncPromiseCounter; + struct SocketContracts { uint32 chainSlug; Socket socket; SocketFeeManager socketFeeManager; FastSwitchboard switchboard; + CCTPSwitchboard cctpSwitchboard; + CCTPMessageTransmitter cctpMessageTransmitter; SocketBatcher socketBatcher; ContractFactoryPlug contractFactoryPlug; FeesPlug feesPlug; @@ -121,6 +126,20 @@ contract DeploySetup is SetupStore { optConfig = _deploySocket(optChainSlug); _configureChain(optChainSlug); + vm.startPrank(socketOwner); + arbConfig.cctpSwitchboard.addRemoteEndpoint( + optChainSlug, + optChainSlug, + address(optConfig.cctpSwitchboard), + optChainSlug + ); + optConfig.cctpSwitchboard.addRemoteEndpoint( + arbChainSlug, + arbChainSlug, + address(arbConfig.cctpSwitchboard), + arbChainSlug + ); + vm.stopPrank(); // transfer eth to fees pool for native fee payouts vm.deal(address(feesPool), 100000 ether); @@ -209,13 +228,23 @@ contract DeploySetup is SetupStore { function _deploySocket(uint32 chainSlug_) internal returns (SocketContracts memory) { // socket Socket socket = new Socket(chainSlug_, socketOwner, "test"); - + CCTPMessageTransmitter cctpMessageTransmitter = new CCTPMessageTransmitter( + chainSlug_, + address(0) + ); return SocketContracts({ chainSlug: chainSlug_, socket: socket, socketFeeManager: new SocketFeeManager(socketOwner, socketFees), switchboard: new FastSwitchboard(chainSlug_, socket, socketOwner), + cctpSwitchboard: new CCTPSwitchboard( + chainSlug_, + socket, + socketOwner, + address(cctpMessageTransmitter) + ), + cctpMessageTransmitter: cctpMessageTransmitter, socketBatcher: new SocketBatcher(socketOwner, socket), contractFactoryPlug: new ContractFactoryPlug(address(socket), socketOwner), feesPlug: new FeesPlug(address(socket), socketOwner), @@ -227,6 +256,7 @@ contract DeploySetup is SetupStore { SocketContracts memory socketConfig = getSocketConfig(chainSlug_); Socket socket = socketConfig.socket; FastSwitchboard switchboard = socketConfig.switchboard; + CCTPSwitchboard cctpSwitchboard = socketConfig.cctpSwitchboard; FeesPlug feesPlug = socketConfig.feesPlug; ContractFactoryPlug contractFactoryPlug = socketConfig.contractFactoryPlug; @@ -241,6 +271,9 @@ contract DeploySetup is SetupStore { switchboard.grantRole(WATCHER_ROLE, watcherEOA); switchboard.grantRole(RESCUE_ROLE, address(socketOwner)); + cctpSwitchboard.registerSwitchboard(); + cctpSwitchboard.grantRole(WATCHER_ROLE, watcherEOA); + feesPlug.grantRole(RESCUE_ROLE, address(socketOwner)); feesPlug.whitelistToken(address(socketConfig.testUSDC)); feesPlug.connectSocket( @@ -261,6 +294,7 @@ contract DeploySetup is SetupStore { vm.startPrank(watcherEOA); configurations.setSocket(chainSlug_, address(socket)); configurations.setSwitchboard(chainSlug_, FAST, address(switchboard)); + configurations.setSwitchboard(chainSlug_, CCTP, address(cctpSwitchboard)); // plugs feesManager.setFeesPlug(chainSlug_, address(feesPlug)); @@ -888,33 +922,160 @@ contract WatcherSetup is AuctionSetup { transmitterPrivateKey ); bytes memory returnData; - (success, returnData) = getSocketConfig(chainSlug).socketBatcher.attestAndExecute( - ExecuteParams({ - callType: digestParams.callType, - deadline: digestParams.deadline, - gasLimit: digestParams.gasLimit, - value: digestParams.value, - payload: digestParams.payload, - target: digestParams.target, - requestCount: payloadParams.requestCount, - batchCount: payloadParams.batchCount, - payloadCount: payloadParams.payloadCount, - prevBatchDigestHash: digestParams.prevBatchDigestHash, - extraData: digestParams.extraData - }), - switchboard, - digest, - watcherProof, - transmitterSig, - transmitterEOA - ); - + ExecuteParams memory executeParams = ExecuteParams({ + callType: digestParams.callType, + deadline: digestParams.deadline, + gasLimit: digestParams.gasLimit, + value: digestParams.value, + payload: digestParams.payload, + target: digestParams.target, + requestCount: payloadParams.requestCount, + batchCount: payloadParams.batchCount, + payloadCount: payloadParams.payloadCount, + prevBatchDigestHash: digestParams.prevBatchDigestHash, + extraData: digestParams.extraData + }); + if (switchboard == address(getSocketConfig(chainSlug).switchboard)) { + (success, returnData) = getSocketConfig(chainSlug).socketBatcher.attestAndExecute( + executeParams, + switchboard, + digest, + watcherProof, + transmitterSig, + transmitterEOA + ); + } else if (switchboard == address(getSocketConfig(chainSlug).cctpSwitchboard)) { + (success, returnData) = _executeWithCCTPBatcher( + chainSlug, + executeParams, + digest, + watcherProof, + transmitterSig, + payloadParams + ); + } promiseReturnData = PromiseReturnData({ exceededMaxCopy: false, payloadId: payloadParams.payloadId, returnData: returnData }); } + function _executeWithCCTPBatcher( + uint32 chainSlug, + ExecuteParams memory executeParams, + bytes32 digest, + bytes memory watcherProof, + bytes memory transmitterSig, + PayloadParams memory payloadParams + ) internal returns (bool success, bytes memory returnData) { + CCTPBatchParams memory cctpBatchParams = _prepareCCTPBatchData(chainSlug, payloadParams); + + return + getSocketConfig(chainSlug).socketBatcher.attestCCTPAndProveAndExecute( + CCTPExecutionParams({ + executeParams: executeParams, + digest: digest, + proof: watcherProof, + transmitterSignature: transmitterSig, + refundAddress: transmitterEOA + }), + cctpBatchParams, + address(getSocketConfig(chainSlug).cctpSwitchboard) + ); + } + + function _prepareCCTPBatchData( + uint32 chainSlug, + PayloadParams memory payloadParams + ) internal view returns (CCTPBatchParams memory cctpBatchParams) { + uint40[] memory requestBatchIds = requestHandler.getRequestBatchIds( + payloadParams.requestCount + ); + uint40 currentBatchCount = payloadParams.batchCount; + + bytes32[] memory prevBatchPayloadIds = _getPrevBatchPayloadIds( + currentBatchCount, + requestBatchIds + ); + bytes32[] memory nextBatchPayloadIds = _getNextBatchPayloadIds( + currentBatchCount, + requestBatchIds + ); + + uint32[] memory prevBatchRemoteChainSlugs = _getRemoteChainSlugs(prevBatchPayloadIds); + uint32[] memory nextBatchRemoteChainSlugs = _getRemoteChainSlugs(nextBatchPayloadIds); + + bytes[] memory messages = _createCCTPMessages( + prevBatchPayloadIds, + prevBatchRemoteChainSlugs, + chainSlug + ); + + cctpBatchParams = CCTPBatchParams({ + previousPayloadIds: prevBatchPayloadIds, + nextBatchRemoteChainSlugs: nextBatchRemoteChainSlugs, + messages: messages, + attestations: new bytes[](prevBatchPayloadIds.length) // using mock attestations for now + }); + } + + function _getPrevBatchPayloadIds( + uint40 currentBatchCount, + uint40[] memory requestBatchIds + ) internal view returns (bytes32[] memory) { + if (currentBatchCount == requestBatchIds[0]) { + return new bytes32[](0); + } + return requestHandler.getBatchPayloadIds(currentBatchCount - 1); + } + + function _getNextBatchPayloadIds( + uint40 currentBatchCount, + uint40[] memory requestBatchIds + ) internal view returns (bytes32[] memory) { + if (currentBatchCount == requestBatchIds[requestBatchIds.length - 1]) { + return new bytes32[](0); + } + return requestHandler.getBatchPayloadIds(currentBatchCount + 1); + } + + function _getRemoteChainSlugs(bytes32[] memory payloadIds) internal view returns (uint32[] memory) { + uint32[] memory chainSlugs = new uint32[](payloadIds.length); + for (uint i = 0; i < payloadIds.length; i++) { + PayloadParams memory params = requestHandler.getPayload(payloadIds[i]); + (, Transaction memory transaction, , , , ) = abi.decode( + params.precompileData, + (address, Transaction, WriteFinality, uint256, uint256, address) + ); + chainSlugs[i] = transaction.chainSlug; + } + return chainSlugs; + } + + function _createCCTPMessages( + bytes32[] memory payloadIds, + uint32[] memory remoteChainSlugs, + uint32 chainSlug + ) internal view returns (bytes[] memory) { + bytes[] memory messages = new bytes[](payloadIds.length); + for (uint i = 0; i < payloadIds.length; i++) { + messages[i] = abi.encode( + remoteChainSlugs[i], + addressToBytes32(address(getSocketConfig(remoteChainSlugs[i]).cctpSwitchboard)), + chainSlug, + addressToBytes32(address(getSocketConfig(chainSlug).cctpSwitchboard)), + abi.encode(payloadIds[i], writePrecompile.digestHashes(payloadIds[i])) + ); + } + return messages; + } + + function addressToBytes32(address addr_) internal pure returns (bytes32) { + return bytes32(uint256(uint160(addr_))); + } + function bytes32ToAddress(bytes32 addrBytes32_) internal pure returns (address) { + return address(uint160(uint256(addrBytes32_))); + } function _resolvePromise(PromiseReturnData[] memory promiseReturnData) internal { watcherMultiCall( @@ -944,16 +1105,15 @@ contract WatcherSetup is AuctionSetup { ) internal { AppGatewayConfig[] memory configs = new AppGatewayConfig[](contractIds_.length); - SocketContracts memory socketConfig = getSocketConfig(chainSlug_); for (uint i = 0; i < contractIds_.length; i++) { address plug = appGateway_.getOnChainAddress(contractIds_[i], chainSlug_); - + address switchboard = configurations.switchboards(chainSlug_, appGateway_.sbType()); configs[i] = AppGatewayConfig({ plug: plug, chainSlug: chainSlug_, plugConfig: PlugConfig({ appGatewayId: encodeAppGatewayId(address(appGateway_)), - switchboard: address(socketConfig.switchboard) + switchboard: switchboard }) }); } @@ -963,7 +1123,6 @@ contract WatcherSetup is AuctionSetup { ); } } - contract AppGatewayBaseSetup is WatcherSetup { function getOnChainAndForwarderAddresses( uint32 chainSlug_, diff --git a/test/apps/CCTPSuperToken.t.sol b/test/apps/CCTPSuperToken.t.sol new file mode 100644 index 00000000..3c7df780 --- /dev/null +++ b/test/apps/CCTPSuperToken.t.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import {SuperTokenAppGateway} from "./app-gateways/super-token/SuperTokenAppGateway.sol"; +import {SuperToken} from "./app-gateways/super-token/SuperToken.sol"; +import "../SetupTest.t.sol"; + +/** + * @title SuperToken Test + * @notice Test contract for verifying the functionality of the SuperToken system, which enables + * multi-chain token bridging capabilities. + * @dev Inherits from AppGatewayBaseSetup to utilize multi-chain messaging infrastructure + * + * The test suite validates: + * - Contract deployment across different chains + * - Token transfers between chains + * - Proper balance updates + * - Integration with the delivery and auction system + */ +contract CCTPSuperTokenTest is AppGatewayBaseSetup { + /** + * @notice Groups the main contracts needed for SuperToken functionality + * @param superTokenApp The gateway contract that handles multi-chain token operations + * @param superTokenDeployer Contract responsible for deploying SuperToken instances + * @param superToken Identifier for the SuperToken contract + */ + struct AppContracts { + SuperTokenAppGateway superTokenApp; + bytes32 superToken; + } + address owner = address(uint160(c++)); + uint256 maxFees = 0.01 ether; + + /// @dev Main contracts used throughout the tests + AppContracts appContracts; + /// @dev Array storing contract IDs for deployment (currently only SuperToken) + bytes32[] contractIds = new bytes32[](1); + + /// @dev Test amount for token transfers (0.01 ETH) + uint256 srcAmount = 0.01 ether; + /// @dev Structure holding transfer order details + SuperTokenAppGateway.TransferOrder transferOrder; + + /** + * @notice Sets up the test environment + * @dev Initializes core infrastructure and deploys SuperToken-specific contracts + * Sequence: + * 1. Sets up delivery helper for multi-chain communication + * 2. Deploys SuperToken application + * 3. Initializes contract IDs array + */ + function setUp() public { + deploy(); + + SuperTokenAppGateway superTokenApp = new SuperTokenAppGateway( + address(addressResolver), + owner, + maxFees, + SuperTokenAppGateway.ConstructorParams({ + name_: "SUPER TOKEN", + symbol_: "SUPER", + decimals_: 18, + initialSupplyHolder_: owner, + initialSupply_: 1000000000 ether + }) + ); + vm.prank(owner); + superTokenApp.setSbType(CCTP); + // Enable app gateways to do all operations in the Watcher: Read, Write and Schedule on EVMx + // Watcher sets the limits for apps in this SOCKET protocol version + depositNativeAndCredits(arbChainSlug, 1 ether, 0, address(superTokenApp)); + + appContracts = AppContracts({ + superTokenApp: superTokenApp, + superToken: superTokenApp.superToken() + }); + + contractIds[0] = appContracts.superToken; + } + + /** + * @notice Deploys the SuperToken application and its components + * @dev Creates both the deployer and gateway contracts with initial configuration + * - Sets up SuperToken with "SUPER TOKEN" name and "SUPER" symbol + * - Configures initial supply of 1 billion tokens + * - Sets up fee structure and auction manager integration + */ + function deploySuperToken(uint32 chainSlug) internal { + appContracts.superTokenApp.deployContracts(chainSlug); + executeDeploy(IAppGateway(appContracts.superTokenApp), chainSlug, contractIds); + } + + /** + * @notice Tests the deployment of SuperToken contracts across chains + * @dev Verifies: + * - Correct deployment on target chain (Arbitrum in this case) + * - Proper initialization of token parameters + * - Correct setup of forwarder contracts for multi-chain communication + */ + function testCCTPContractDeployment() public { + deploySuperToken(arbChainSlug); + + (address onChain, address forwarder) = getOnChainAndForwarderAddresses( + arbChainSlug, + appContracts.superToken, + IAppGateway(appContracts.superTokenApp) + ); + + assertEq( + SuperToken(onChain).name(), + "SUPER TOKEN", + "OnChain SuperToken name should be SUPER TOKEN" + ); + + assertEq( + IForwarder(forwarder).getChainSlug(), + arbChainSlug, + "Forwarder SuperToken chainSlug should be arbChainSlug" + ); + + assertEq( + IForwarder(forwarder).getOnChainAddress(), + onChain, + "Forwarder SuperToken onChainAddress should be correct" + ); + assertEq(SuperToken(onChain).owner(), owner, "SuperToken owner should be correct"); + } + + /** + * @notice Tests multi-chain token transfers + * @dev Verifies: + * - Correct token burning on source chain (Arbitrum) + * - Correct token minting on destination chain (Optimism) + * - Accurate balance updates on both chains + * - Proper execution of multi-chain messaging + */ + function testCCTPTransfer() public { + deploySuperToken(arbChainSlug); + deploySuperToken(optChainSlug); + + (address onChainArb, address forwarderArb) = getOnChainAndForwarderAddresses( + arbChainSlug, + appContracts.superToken, + IAppGateway(appContracts.superTokenApp) + ); + + (address onChainOpt, address forwarderOpt) = getOnChainAndForwarderAddresses( + optChainSlug, + appContracts.superToken, + IAppGateway(appContracts.superTokenApp) + ); + + uint256 arbBalanceBefore = SuperToken(onChainArb).balanceOf(owner); + uint256 optBalanceBefore = SuperToken(onChainOpt).balanceOf(owner); + + transferOrder = SuperTokenAppGateway.TransferOrder({ + srcToken: forwarderArb, + dstToken: forwarderOpt, + user: owner, + srcAmount: srcAmount, + deadline: block.timestamp + 1000000 + }); + + bytes memory encodedOrder = abi.encode(transferOrder); + appContracts.superTokenApp.transfer(encodedOrder); + executeRequest(); + + assertEq( + SuperToken(onChainArb).balanceOf(owner), + arbBalanceBefore - srcAmount, + "Arb balance should be decreased by srcAmount" + ); + assertEq( + SuperToken(onChainOpt).balanceOf(owner), + optBalanceBefore + srcAmount, + "Opt balance should be increased by srcAmount" + ); + } +} diff --git a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol index 0e835b7e..00870143 100644 --- a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol +++ b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol @@ -68,4 +68,8 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { emit Transferred(_getCurrentRequestCount()); } + + function setSbType(bytes32 sbType_) external onlyOwner { + _setSbType(sbType_); + } } diff --git a/test/mock/CCTPMessageTransmitter.sol b/test/mock/CCTPMessageTransmitter.sol new file mode 100644 index 00000000..cb30570e --- /dev/null +++ b/test/mock/CCTPMessageTransmitter.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import "../../contracts/protocol/interfaces/IMessageTransmitter.sol"; +import "../../contracts/protocol/interfaces/IMessageHandler.sol"; + +contract CCTPMessageTransmitter is IMessageTransmitter { + uint32 public immutable override localDomain; + address public immutable override attestationManager; + + // Mapping to store sent messages for verification + mapping(uint64 => bytes) public sentMessages; + uint64 public nonce; + + event MessageSent( + uint32 destinationDomain, + bytes32 recipient, + bytes messageBody, + uint64 nonce, + bytes message + ); + + event MessageReceived(bytes message, bytes attestation, bool success); + + constructor(uint32 _localDomain, address _attestationManager) { + localDomain = _localDomain; + attestationManager = _attestationManager; + } + + function sendMessage( + uint32 destinationDomain, + bytes32 recipient, + bytes calldata messageBody + ) external override returns (uint64) { + uint64 currentNonce = nonce++; + sentMessages[currentNonce] = messageBody; + + bytes memory message = abi.encode( + localDomain, + msg.sender, + destinationDomain, + recipient, + messageBody + ); + emit MessageSent(destinationDomain, recipient, messageBody, currentNonce, message); + + return currentNonce; + } + + function receiveMessage( + bytes calldata message, + bytes calldata attestation + ) external override returns (bool) { + ( + uint32 sourceDomain, + bytes32 sender, + , // destinationDomain + bytes32 recipient, + bytes memory messageBody + ) = abi.decode(message, (uint32, bytes32, uint32, bytes32, bytes)); + IMessageHandler(bytes32ToAddress(recipient)).handleReceiveMessage( + sourceDomain, + sender, + messageBody + ); + // In mock implementation, we'll always return true + // In real implementation, this would verify the attestation + emit MessageReceived(message, attestation, true); + return true; + } + + function addressToBytes32(address addr_) public pure returns (bytes32) { + return bytes32(uint256(uint160(addr_))); + } + function bytes32ToAddress(bytes32 addrBytes32_) public pure returns (address) { + return address(uint160(uint256(addrBytes32_))); + } +} diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index fbe737a8..7ae53bfa 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -65,12 +65,6 @@ contract MockSocket is ISocket { uint64 public triggerCounter; uint32 public chainSlug; - enum ExecutionStatus { - NotExecuted, - Executed, - Reverted - } - /** * @dev keeps track of whether a payload has been executed or not using payload id */ From 8affab262ea3a4ce5bc6e9aeed04653cb4a23121 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 17 Jun 2025 16:26:12 +0530 Subject: [PATCH 03/12] feat: cctp support --- Errors.md | 13 +++ EventTopics.md | 16 ++++ .../protocol/switchboard/CCTPSwitchboard.sol | 3 +- foundry.toml | 8 +- hardhat-scripts/config/config.ts | 15 +++ hardhat-scripts/constants/constants.ts | 1 + hardhat-scripts/deploy/1.deploy.ts | 16 ++++ hardhat-scripts/deploy/2.roles.ts | 6 +- hardhat-scripts/deploy/3.configureChains.ts | 95 ++++++++++++++++++- .../misc-scripts/getAttestations.ts | 95 +++++++++++++++++++ package.json | 2 +- .../supertoken/DeployEVMxSuperTokenApp.s.sol | 38 ++++++++ script/supertoken/TransferSuperToken.s.sol | 36 +++++++ src/enums.ts | 1 + src/types.ts | 1 + test/SetupTest.t.sol | 4 +- test/mock/CCTPMessageTransmitter.sol | 4 +- trace.sh | 2 +- 18 files changed, 345 insertions(+), 11 deletions(-) create mode 100644 hardhat-scripts/misc-scripts/getAttestations.ts create mode 100644 script/supertoken/DeployEVMxSuperTokenApp.s.sol create mode 100644 script/supertoken/TransferSuperToken.s.sol diff --git a/Errors.md b/Errors.md index 77179268..f88ea278 100644 --- a/Errors.md +++ b/Errors.md @@ -68,6 +68,19 @@ | `OnlyOffChain()` | `0x9cbfe066` | | `SimulationFailed()` | `0x2fbab3ac` | +## protocol/switchboard/CCTPSwitchboard.sol + +| Error | Signature | +| ------------------------------- | ------------ | +| `RemoteExecutionNotFound()` | `0xbd506972` | +| `DigestMismatch()` | `0x582e0907` | +| `PreviousDigestsHashMismatch()` | `0x3e62c52c` | +| `NotAttested()` | `0x99efb890` | +| `NotExecuted()` | `0xec84b1da` | +| `InvalidDomain()` | `0xeb127982` | +| `InvalidSender()` | `0xddb5de5e` | +| `OnlyMessageTransmitter()` | `0x935ac89c` | + ## protocol/switchboard/FastSwitchboard.sol | Error | Signature | diff --git a/EventTopics.md b/EventTopics.md index c94cd569..cb090d72 100644 --- a/EventTopics.md +++ b/EventTopics.md @@ -222,6 +222,22 @@ | `TriggerFeesSet` | `(triggerFees: uint256)` | `0x7df3967b7c8727af5ac0ee9825d88aafeb899d769bc428b91f8967fa0b623084` | | `TriggerSucceeded` | `(triggerId: bytes32)` | `0x92d20fbcbf31370b8218e10ed00c5aad0e689022da30a08905ba5ced053219eb` | +## ICCTPSwitchboard + +| Event | Arguments | Topic | +| ----- | --------- | ----- | + +## CCTPSwitchboard + +| Event | Arguments | Topic | +| ---------------------------- | ----------------------------------------- | -------------------------------------------------------------------- | +| `Attested` | `(payloadId_: bytes32, watcher: address)` | `0x3d83c7bc55c269e0bc853ddc0d7b9fca30216ecc43779acb4e36b7e0ad1c71e4` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | +| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | + ## FastSwitchboard | Event | Arguments | Topic | diff --git a/contracts/protocol/switchboard/CCTPSwitchboard.sol b/contracts/protocol/switchboard/CCTPSwitchboard.sol index 539ea06a..3be56b01 100644 --- a/contracts/protocol/switchboard/CCTPSwitchboard.sol +++ b/contracts/protocol/switchboard/CCTPSwitchboard.sol @@ -34,7 +34,6 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { error InvalidDomain(); error InvalidSender(); error OnlyMessageTransmitter(); - event Attested(bytes32 payloadId, bytes32 digest, address watcher); constructor( uint32 chainSlug_, @@ -56,7 +55,7 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { isAttested[digest_] = true; payloadIdToDigest[payloadId_] = digest_; - emit Attested(payloadId_, digest_, watcher); + emit Attested(payloadId_, watcher); } function allowPacket(bytes32 digest_, bytes32 payloadId_) external view returns (bool) { diff --git a/foundry.toml b/foundry.toml index 6d1a2709..8dca882d 100644 --- a/foundry.toml +++ b/foundry.toml @@ -33,4 +33,10 @@ via_ir = false 0x7726e559A5129A9174f89F7E2029f7212B66dD13 = "WatcherImpl" 0xd8be408E271EEe9d3D0f28305bB9b6003589E1A9 = "WritePrecompile" 0xE24c4b0f67f566Fa558b3FE85f1780CD330f1F4D = "WritePrecompileImpl" -0x4Faa9C39f4E1C5be5f9c2e3F5AC8774da3b7B1C2 = "APP_GATEWAY" +0xf9A93a92c0754084f6320f3fC1D54584C2e0439d = "CCTPSwitchboard" +0xAA78A6c96DF690d30eF161490f6590fCAb8f4406 = "ContractFactoryPlug" +0x696d7d0Af367cFE3d3c56BD61ca16B3A0939618b = "FastSwitchboard" +0xC6Fb338F2009B5AD1e1bbA2dd2c9f52e9dE2C91C = "FeesPlug" +0x4B5718c1f739A83EF5c75f64776f2c9D4D460B1D = "Socket" +0xc9E18b73C1A575D8A5975754a01CD19BE8400037 = "SocketBatcher" +0x86081C993583916720E58788b68292b2BBcDA6f3 = "APP_GATEWAY" diff --git a/hardhat-scripts/config/config.ts b/hardhat-scripts/config/config.ts index 7ba92e79..f75bbb56 100644 --- a/hardhat-scripts/config/config.ts +++ b/hardhat-scripts/config/config.ts @@ -116,3 +116,18 @@ export const UPGRADE_VERSION = 1; // Transmitter constants export const TRANSMITTER_CREDIT_THRESHOLD = ethers.utils.parseEther("100"); // 100 ETH threshold export const TRANSMITTER_NATIVE_THRESHOLD = ethers.utils.parseEther("100"); // 100 ETH threshold + +// CCTP +export const MESSAGE_TRANSMITTER: { + [chainSlug: number]: string; +} = { + 421614: "0xaCF1ceeF35caAc005e15888dDb8A3515C41B4872", + 11155420: "0x7865fAfC2db2093669d92c0F33AeEF291086BEFD", +}; + +export const CCTP_DOMAINS: { + [chainSlug: number]: number; +} = { + 421614: 3, + 11155420: 2, +}; diff --git a/hardhat-scripts/constants/constants.ts b/hardhat-scripts/constants/constants.ts index 71aa09e1..168d2b47 100644 --- a/hardhat-scripts/constants/constants.ts +++ b/hardhat-scripts/constants/constants.ts @@ -7,6 +7,7 @@ export const IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; export const FAST_SWITCHBOARD_TYPE = id("FAST"); +export const CCTP_SWITCHBOARD_TYPE = id("CCTP"); export const ZERO_APP_GATEWAY_ID = ethers.utils.hexZeroPad( constants.AddressZero, diff --git a/hardhat-scripts/deploy/1.deploy.ts b/hardhat-scripts/deploy/1.deploy.ts index f5e19534..3f088d74 100644 --- a/hardhat-scripts/deploy/1.deploy.ts +++ b/hardhat-scripts/deploy/1.deploy.ts @@ -13,6 +13,7 @@ import { logConfig, MAX_RE_AUCTION_COUNT, MAX_SCHEDULE_DELAY_SECONDS, + MESSAGE_TRANSMITTER, mode, READ_FEES, SCHEDULE_CALLBACK_FEES, @@ -322,6 +323,21 @@ const deploySocketContracts = async () => { ); deployUtils.addresses[contractName] = sb.address; + contractName = Contracts.CCTPSwitchboard; + const cctpSwitchboard: Contract = await getOrDeploy( + contractName, + contractName, + `contracts/protocol/switchboard/${contractName}.sol`, + [ + chain as ChainSlug, + socket.address, + socketOwner, + MESSAGE_TRANSMITTER[chain as ChainSlug], + ], + deployUtils + ); + deployUtils.addresses[contractName] = cctpSwitchboard.address; + if (getFeesPlugChains().includes(chain as ChainSlug)) { contractName = Contracts.FeesPlug; const feesPlug: Contract = await getOrDeploy( diff --git a/hardhat-scripts/deploy/2.roles.ts b/hardhat-scripts/deploy/2.roles.ts index 79723cec..99db548c 100644 --- a/hardhat-scripts/deploy/2.roles.ts +++ b/hardhat-scripts/deploy/2.roles.ts @@ -16,6 +16,7 @@ export const REQUIRED_ROLES = { }, Chain: { FastSwitchboard: [ROLES.WATCHER_ROLE, ROLES.RESCUE_ROLE], + CCTPSwitchboard: [ROLES.WATCHER_ROLE, ROLES.RESCUE_ROLE], Socket: [ ROLES.GOVERNANCE_ROLE, ROLES.RESCUE_ROLE, @@ -80,8 +81,9 @@ async function setRolesOnChain(chain: number, addresses: DeploymentAddresses) { for (const roleName of roles) { const targetAddress = - contractName === Contracts.FastSwitchboard && - roleName === ROLES.WATCHER_ROLE + [Contracts.FastSwitchboard, Contracts.CCTPSwitchboard].includes( + contractName as Contracts + ) && roleName === ROLES.WATCHER_ROLE ? watcher : signer.address; diff --git a/hardhat-scripts/deploy/3.configureChains.ts b/hardhat-scripts/deploy/3.configureChains.ts index 54a3cb11..b5a568e5 100644 --- a/hardhat-scripts/deploy/3.configureChains.ts +++ b/hardhat-scripts/deploy/3.configureChains.ts @@ -3,8 +3,17 @@ dotenvConfig(); import { Contract, Signer, Wallet } from "ethers"; import { ChainAddressesObj, ChainSlug, Contracts } from "../../src"; -import { chains, EVMX_CHAIN_ID, MAX_MSG_VALUE_LIMIT, mode } from "../config"; import { + CCTP_DOMAINS, + chains, + EVMX_CHAIN_ID, + mainnetChains, + MAX_MSG_VALUE_LIMIT, + mode, + testnetChains, +} from "../config"; +import { + CCTP_SWITCHBOARD_TYPE, DeploymentAddresses, FAST_SWITCHBOARD_TYPE, getFeeTokens, @@ -48,6 +57,13 @@ export const configureChains = async (addresses: DeploymentAddresses) => { socketContract ); + await registerSb( + chain, + chainAddresses[Contracts.CCTPSwitchboard], + signer, + socketContract + ); + if (chainAddresses[Contracts.FeesPlug]) { await whitelistToken(chain, chainAddresses[Contracts.FeesPlug], signer); } @@ -55,6 +71,13 @@ export const configureChains = async (addresses: DeploymentAddresses) => { await setMaxMsgValueLimit(chain); await setOnchainContracts(chain, addresses); + + await addRemoteEndpointsToCCTPSwitchboard( + chain, + addresses, + signer, + socketContract + ); } }; @@ -91,6 +114,16 @@ async function setOnchainContracts( [chain, FAST_SWITCHBOARD_TYPE, chainAddresses[Contracts.FastSwitchboard]], signer ); + await updateContractSettings( + EVMX_CHAIN_ID, + Contracts.Configurations, + "switchboards", + [chain, CCTP_SWITCHBOARD_TYPE], + chainAddresses[Contracts.CCTPSwitchboard], + "setSwitchboard", + [chain, CCTP_SWITCHBOARD_TYPE, chainAddresses[Contracts.CCTPSwitchboard]], + signer + ); await updateContractSettings( EVMX_CHAIN_ID, Contracts.Configurations, @@ -126,6 +159,66 @@ async function setOnchainContracts( ); } +const addRemoteEndpointsToCCTPSwitchboard = async ( + chain: number, + addresses: DeploymentAddresses, + signer: Wallet, + socket: Contract +) => { + try { + console.log("Adding remote endpoints to CCTP switchboard"); + const chainAddresses = addresses[chain] as ChainAddressesObj; + const sbAddress = chainAddresses[Contracts.CCTPSwitchboard]; + const switchboard = ( + await getInstance(Contracts.CCTPSwitchboard, sbAddress) + ).connect(signer); + const remoteChainSlugs = getRemoteChainSlugs(chain); + console.log(chain, " remoteChainSlugs: ", remoteChainSlugs); + + for (const remoteChainSlug of remoteChainSlugs) { + const remoteSwitchboardAddress = + addresses[remoteChainSlug]?.[Contracts.CCTPSwitchboard]; + const currentRemoteEndpoint = await switchboard.remoteEndpoints( + remoteChainSlug + ); + if (currentRemoteEndpoint.remoteAddress == remoteSwitchboardAddress) { + console.log(`Remote endpoint ${remoteChainSlug} already exists`); + continue; + } + + const registerTx = await switchboard.addRemoteEndpoint( + remoteChainSlug, + remoteChainSlug, + remoteSwitchboardAddress, + CCTP_DOMAINS[remoteChainSlug], + { + ...(await overrides(chain)), + } + ); + console.log( + `Adding remote endpoint ${remoteChainSlug} to ${sbAddress}: ${registerTx.hash}` + ); + await registerTx.wait(); + } + } catch (error) { + throw error; + } +}; + +const getRemoteChainSlugs = (chain: number) => { + if (testnetChains.includes(chain)) { + return chains.filter( + (c) => c !== chain && testnetChains.includes(c as ChainSlug) + ); + } + if (mainnetChains.includes(chain)) { + return chains.filter( + (c) => c !== chain && mainnetChains.includes(c as ChainSlug) + ); + } + return chains.filter((c) => c !== chain); +}; + const registerSb = async ( chain: number, sbAddress: string, diff --git a/hardhat-scripts/misc-scripts/getAttestations.ts b/hardhat-scripts/misc-scripts/getAttestations.ts new file mode 100644 index 00000000..dcdcc7be --- /dev/null +++ b/hardhat-scripts/misc-scripts/getAttestations.ts @@ -0,0 +1,95 @@ +import { ethers } from "ethers"; +import axios from "axios"; + +async function getAttestation(messageHash: string): Promise { + try { + const response = await axios.get( + `https://iris-api-sandbox.circle.com/v1/attestations/${messageHash}` + ); + console.log("messageHash", messageHash, "response", response.data); + if (response.data.status === "complete") { + return response.data.attestation; + } + return null; + } catch (error) { + return null; + } +} + +async function main() { + const args = process.argv.slice(2); + if (args.length !== 2) { + console.log("Usage: ts-node getAttestations.ts "); + process.exit(1); + } + + const [txHash, providerUrl] = args; + const provider = new ethers.providers.JsonRpcProvider(providerUrl); + + // Get transaction receipt + const receipt = await provider.getTransactionReceipt(txHash); + + // ABI for MessageSent event + const messageTransmitterInterface = new ethers.utils.Interface([ + "event MessageSent(bytes message)", + ]); + + // Filter logs for MessageSent event + const messageSentLogs = receipt.logs.filter((log) => { + try { + const parsedLog = messageTransmitterInterface.parseLog(log); + return parsedLog.name === "MessageSent"; + } catch { + return false; + } + }); + + if (messageSentLogs.length === 0) { + console.log("No MessageSent events found in transaction"); + process.exit(1); + } + + const messages: string[] = []; + const messageHashes: string[] = []; + const attestations: string[] = []; + + // Get messages and calculate hashes + for (const log of messageSentLogs) { + const parsedLog = messageTransmitterInterface.parseLog(log); + const message = parsedLog.args.message; + const messageHash = ethers.utils.keccak256(message); + + messages.push(message); + messageHashes.push(messageHash); + } + + // Poll for attestations + let complete = false; + while (!complete) { + complete = true; + + for (let i = 0; i < messageHashes.length; i++) { + if (!attestations[i]) { + const attestation = await getAttestation(messageHashes[i]); + if (attestation) { + attestations[i] = attestation; + } else { + complete = false; + } + } + } + + if (!complete) { + console.log("Waiting for attestations..."); + await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5 seconds + } + } + + console.log("\nMessages:", messages); + console.log("\nAttestations:", attestations); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/package.json b/package.json index 9d0b88f1..265df42f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.22", + "version": "1.1.22-test.1", "description": "socket protocol", "scripts": { "build": "yarn abi && tsc --project lib.tsconfig.json", diff --git a/script/supertoken/DeployEVMxSuperTokenApp.s.sol b/script/supertoken/DeployEVMxSuperTokenApp.s.sol new file mode 100644 index 00000000..c09efdcc --- /dev/null +++ b/script/supertoken/DeployEVMxSuperTokenApp.s.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import "../../test/apps/app-gateways/super-token/SuperTokenAppGateway.sol"; +import {CCTP} from "../../contracts/utils/common/Constants.sol"; +// source .env && forge script script/supertoken/deployEVMxSuperTokenApp.s.sol --broadcast --skip-simulation --legacy --gas-price 0 +contract SuperTokenDeploy is Script { + function run() external { + address addressResolver = vm.envAddress("ADDRESS_RESOLVER"); + string memory rpc = vm.envString("EVMX_RPC"); + vm.createSelectFork(rpc); + + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + // Setting fee payment on Arbitrum Sepolia + uint256 fees = 1 ether; + + SuperTokenAppGateway gateway = new SuperTokenAppGateway( + addressResolver, + vm.addr(deployerPrivateKey), + fees, + SuperTokenAppGateway.ConstructorParams({ + name_: "SuperToken", + symbol_: "SUPER", + decimals_: 18, + initialSupplyHolder_: vm.addr(deployerPrivateKey), + initialSupply_: 1000000000000000000000000000 + }) + ); + + console.log("Contracts deployed:"); + console.log("SuperTokenAppGateway:", address(gateway)); + gateway.setSbType(CCTP); + } +} diff --git a/script/supertoken/TransferSuperToken.s.sol b/script/supertoken/TransferSuperToken.s.sol new file mode 100644 index 00000000..c1d39d2a --- /dev/null +++ b/script/supertoken/TransferSuperToken.s.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {SuperTokenAppGateway} from "../../test/apps/app-gateways/super-token/SuperTokenAppGateway.sol"; + +// source .env && forge script script/supertoken/TransferSuperToken.s.sol --broadcast --skip-simulation --legacy --gas-price 0 +contract TransferSuperToken is Script { + function run() external { + string memory socketRPC = vm.envString("EVMX_RPC"); + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + + vm.createSelectFork(socketRPC); + + SuperTokenAppGateway gateway = SuperTokenAppGateway(vm.envAddress("APP_GATEWAY")); + address forwarderArb = gateway.forwarderAddresses(gateway.superToken(), 421614); + address forwarderOpt = gateway.forwarderAddresses(gateway.superToken(), 11155420); + + SuperTokenAppGateway.TransferOrder memory transferOrder = SuperTokenAppGateway + .TransferOrder({ + srcToken: forwarderArb, + dstToken: forwarderOpt, + user: vm.addr(deployerPrivateKey), + srcAmount: 100, + deadline: block.timestamp + 1000000 + }); + + bytes memory encodedOrder = abi.encode(transferOrder); + bytes memory encodedPayload = abi.encodeWithSelector( + bytes4(keccak256("transfer(bytes)")), + encodedOrder + ); + console.logBytes(encodedPayload); + } +} diff --git a/src/enums.ts b/src/enums.ts index f54d3ba8..1c5879c9 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -50,6 +50,7 @@ export enum Contracts { FeesPlug = "FeesPlug", ContractFactoryPlug = "ContractFactoryPlug", FastSwitchboard = "FastSwitchboard", + CCTPSwitchboard = "CCTPSwitchboard", SocketBatcher = "SocketBatcher", SocketFeeManager = "SocketFeeManager", AddressResolver = "AddressResolver", diff --git a/src/types.ts b/src/types.ts index 1a047bcc..ba32e046 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,6 +19,7 @@ export type ChainAddressesObj = { Socket: string; SocketBatcher: string; FastSwitchboard: string; + CCTPSwitchboard: string; ContractFactoryPlug: string; SocketFeesManager?: string; FeesPlug?: string; diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index f1dad31c..3301afda 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -1039,7 +1039,9 @@ contract WatcherSetup is AuctionSetup { return requestHandler.getBatchPayloadIds(currentBatchCount + 1); } - function _getRemoteChainSlugs(bytes32[] memory payloadIds) internal view returns (uint32[] memory) { + function _getRemoteChainSlugs( + bytes32[] memory payloadIds + ) internal view returns (uint32[] memory) { uint32[] memory chainSlugs = new uint32[](payloadIds.length); for (uint i = 0; i < payloadIds.length; i++) { PayloadParams memory params = requestHandler.getPayload(payloadIds[i]); diff --git a/test/mock/CCTPMessageTransmitter.sol b/test/mock/CCTPMessageTransmitter.sol index cb30570e..57eee54a 100644 --- a/test/mock/CCTPMessageTransmitter.sol +++ b/test/mock/CCTPMessageTransmitter.sol @@ -53,8 +53,8 @@ contract CCTPMessageTransmitter is IMessageTransmitter { ) external override returns (bool) { ( uint32 sourceDomain, - bytes32 sender, - , // destinationDomain + bytes32 sender, // destinationDomain + , bytes32 recipient, bytes memory messageBody ) = abi.decode(message, (uint32, bytes32, uint32, bytes32, bytes)); diff --git a/trace.sh b/trace.sh index 3e185bc6..fc1cedf9 100644 --- a/trace.sh +++ b/trace.sh @@ -54,7 +54,7 @@ echo "txHash: $2" echo "rpcUrl: $RPC_URL" npx ts-node hardhat-scripts/misc-scripts/createLabels.ts $1 -cast run --la $2 --rpc-url $RPC_URL +cast run --la $2 --rpc-url $RPC_URL --quick # usage : # yarn trace From aac9e033d840d96a1e1541538d0c61ae8c26e920 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 23 Jun 2025 15:30:22 +0530 Subject: [PATCH 04/12] feat: added cctp constants --- hardhat-scripts/config/config.ts | 29 +++++++----------- hardhat-scripts/deploy/1.deploy.ts | 8 +++-- hardhat-scripts/deploy/3.configureChains.ts | 6 +++- src/cctp.ts | 34 +++++++++++++++++++++ src/index.ts | 1 + 5 files changed, 57 insertions(+), 21 deletions(-) create mode 100644 src/cctp.ts diff --git a/hardhat-scripts/config/config.ts b/hardhat-scripts/config/config.ts index f75bbb56..5d78ae70 100644 --- a/hardhat-scripts/config/config.ts +++ b/hardhat-scripts/config/config.ts @@ -26,9 +26,17 @@ export const logConfig = () => { export const getChains = () => { switch (mode) { case DeploymentMode.LOCAL: - return [ChainSlug.ARBITRUM_SEPOLIA, ChainSlug.OPTIMISM_SEPOLIA]; + return [ + ChainSlug.ARBITRUM_SEPOLIA, + ChainSlug.OPTIMISM_SEPOLIA, + ChainSlug.BASE_SEPOLIA, + ]; case DeploymentMode.DEV: - return [ChainSlug.ARBITRUM_SEPOLIA, ChainSlug.OPTIMISM_SEPOLIA]; + return [ + ChainSlug.ARBITRUM_SEPOLIA, + ChainSlug.OPTIMISM_SEPOLIA, + ChainSlug.BASE_SEPOLIA, + ]; case DeploymentMode.STAGE: return [ ChainSlug.BASE, @@ -95,7 +103,7 @@ export const MAX_MSG_VALUE_LIMIT = ethers.utils.parseEther("0.001"); // Auction parameters export const AUCTION_END_DELAY_SECONDS = 0; export const BID_TIMEOUT = 600; // 10 minutes -export const EXPIRY_TIME = 300; // 5 minutes +export const EXPIRY_TIME = 3600; // 1 hour export const MAX_RE_AUCTION_COUNT = 5; // Fees Pool Funding Amount @@ -116,18 +124,3 @@ export const UPGRADE_VERSION = 1; // Transmitter constants export const TRANSMITTER_CREDIT_THRESHOLD = ethers.utils.parseEther("100"); // 100 ETH threshold export const TRANSMITTER_NATIVE_THRESHOLD = ethers.utils.parseEther("100"); // 100 ETH threshold - -// CCTP -export const MESSAGE_TRANSMITTER: { - [chainSlug: number]: string; -} = { - 421614: "0xaCF1ceeF35caAc005e15888dDb8A3515C41B4872", - 11155420: "0x7865fAfC2db2093669d92c0F33AeEF291086BEFD", -}; - -export const CCTP_DOMAINS: { - [chainSlug: number]: number; -} = { - 421614: 3, - 11155420: 2, -}; diff --git a/hardhat-scripts/deploy/1.deploy.ts b/hardhat-scripts/deploy/1.deploy.ts index 3f088d74..8a7c1317 100644 --- a/hardhat-scripts/deploy/1.deploy.ts +++ b/hardhat-scripts/deploy/1.deploy.ts @@ -2,7 +2,12 @@ import { config } from "dotenv"; import { Contract, utils, Wallet } from "ethers"; import { formatEther } from "ethers/lib/utils"; import { ethers } from "hardhat"; -import { ChainAddressesObj, ChainSlug, Contracts } from "../../src"; +import { + ChainAddressesObj, + ChainSlug, + Contracts, + MESSAGE_TRANSMITTER, +} from "../../src"; import { AUCTION_END_DELAY_SECONDS, BID_TIMEOUT, @@ -13,7 +18,6 @@ import { logConfig, MAX_RE_AUCTION_COUNT, MAX_SCHEDULE_DELAY_SECONDS, - MESSAGE_TRANSMITTER, mode, READ_FEES, SCHEDULE_CALLBACK_FEES, diff --git a/hardhat-scripts/deploy/3.configureChains.ts b/hardhat-scripts/deploy/3.configureChains.ts index b5a568e5..3e196de8 100644 --- a/hardhat-scripts/deploy/3.configureChains.ts +++ b/hardhat-scripts/deploy/3.configureChains.ts @@ -2,9 +2,13 @@ import { config as dotenvConfig } from "dotenv"; dotenvConfig(); import { Contract, Signer, Wallet } from "ethers"; -import { ChainAddressesObj, ChainSlug, Contracts } from "../../src"; import { + ChainAddressesObj, + ChainSlug, + Contracts, CCTP_DOMAINS, +} from "../../src"; +import { chains, EVMX_CHAIN_ID, mainnetChains, diff --git a/src/cctp.ts b/src/cctp.ts new file mode 100644 index 00000000..c0436393 --- /dev/null +++ b/src/cctp.ts @@ -0,0 +1,34 @@ +import { ChainSlug } from "./chain-enums"; + +// CCTP +export const MESSAGE_TRANSMITTER: { + [chainSlug: number]: string; +} = { + [ChainSlug.SEPOLIA]: "0x7865fAfC2db2093669d92c0F33AeEF291086BEFD", + [ChainSlug.OPTIMISM_SEPOLIA]: "0x7865fAfC2db2093669d92c0F33AeEF291086BEFD", + [ChainSlug.ARBITRUM_SEPOLIA]: "0xaCF1ceeF35caAc005e15888dDb8A3515C41B4872", + [ChainSlug.BASE_SEPOLIA]: "0x7865fAfC2db2093669d92c0F33AeEF291086BEFD", + [ChainSlug.POLYGON_AMOY]: "0x7865fAfC2db2093669d92c0F33AeEF291086BEFD", + [ChainSlug.MAINNET]: "0x0a992d191DEeC32aFe36203Ad87D7d289a738F81", + [ChainSlug.AVALANCHE]: "0x8186359aF5F57FbB40c6b14A588d2A59C0C29880", + [ChainSlug.OPTIMISM]: "0x4D41f22c5a0e5c74090899E5a8Fb597a8842b3e8", + [ChainSlug.ARBITRUM]: "0xC30362313FBBA5cf9163F0bb16a0e01f01A896ca", + [ChainSlug.BASE]: "0xAD09780d193884d503182aD4588450C416D6F9D4", + [ChainSlug.POLYGON_MAINNET]: "0xF3be9355363857F3e001be68856A2f96b4C39Ba9", +}; + +export const CCTP_DOMAINS: { + [chainSlug: number]: number; +} = { + [ChainSlug.SEPOLIA]: 0, + [ChainSlug.OPTIMISM_SEPOLIA]: 2, + [ChainSlug.ARBITRUM_SEPOLIA]: 3, + [ChainSlug.BASE_SEPOLIA]: 6, + [ChainSlug.POLYGON_AMOY]: 7, + [ChainSlug.MAINNET]: 0, + [ChainSlug.AVALANCHE]: 1, + [ChainSlug.OPTIMISM]: 2, + [ChainSlug.ARBITRUM]: 3, + [ChainSlug.BASE]: 6, + [ChainSlug.POLYGON_MAINNET]: 7, +}; diff --git a/src/index.ts b/src/index.ts index 39a65132..e58e42d5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,3 +5,4 @@ export * from "./finality"; export * from "./types"; export * from "./constants"; export * from "./signer"; +export * from "./cctp"; From 1223806d70da8bb8aca60eae80d00ac80f063f67 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 23 Jun 2025 15:30:57 +0530 Subject: [PATCH 05/12] fix: tests update plugs, allowing partial deployment --- foundry.toml | 14 +- package.json | 2 +- script/helpers/TransferRemainingCredits.s.sol | 38 ++++ script/helpers/WithdrawRemainingCredits.s.sol | 30 +++ test/SetupTest.t.sol | 45 +++-- test/apps/CCTPSuperToken.t.sol | 179 ------------------ 6 files changed, 108 insertions(+), 200 deletions(-) create mode 100644 script/helpers/TransferRemainingCredits.s.sol create mode 100644 script/helpers/WithdrawRemainingCredits.s.sol delete mode 100644 test/apps/CCTPSuperToken.t.sol diff --git a/foundry.toml b/foundry.toml index 8dca882d..d7f33426 100644 --- a/foundry.toml +++ b/foundry.toml @@ -33,10 +33,10 @@ via_ir = false 0x7726e559A5129A9174f89F7E2029f7212B66dD13 = "WatcherImpl" 0xd8be408E271EEe9d3D0f28305bB9b6003589E1A9 = "WritePrecompile" 0xE24c4b0f67f566Fa558b3FE85f1780CD330f1F4D = "WritePrecompileImpl" -0xf9A93a92c0754084f6320f3fC1D54584C2e0439d = "CCTPSwitchboard" -0xAA78A6c96DF690d30eF161490f6590fCAb8f4406 = "ContractFactoryPlug" -0x696d7d0Af367cFE3d3c56BD61ca16B3A0939618b = "FastSwitchboard" -0xC6Fb338F2009B5AD1e1bbA2dd2c9f52e9dE2C91C = "FeesPlug" -0x4B5718c1f739A83EF5c75f64776f2c9D4D460B1D = "Socket" -0xc9E18b73C1A575D8A5975754a01CD19BE8400037 = "SocketBatcher" -0x86081C993583916720E58788b68292b2BBcDA6f3 = "APP_GATEWAY" +0x74A4aa989515b088A1aC33C5D59897f69cA66B91 = "CCTPSwitchboard" +0x25190648330361f35d00a0D41BD347de8E1B838C = "ContractFactoryPlug" +0x39d21e679312Bf0e3681bd0254c587E3528dd2a3 = "FastSwitchboard" +0xe9BDa44a39F8d29eaF1956EB05442551794871f3 = "FeesPlug" +0x86264607bAD260e9032add9e4E2DA74f71E354E0 = "Socket" +0x5B83E4104E37c7ECDf6Aeb62a4E204E4c63ac8D5 = "SocketBatcher" +0xfC3E610f631c02f7654DaC849B50994944d1FB8B = "APP_GATEWAY" diff --git a/package.json b/package.json index 265df42f..cc9063b6 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.22-test.1", + "version": "1.1.25", "description": "socket protocol", "scripts": { "build": "yarn abi && tsc --project lib.tsconfig.json", diff --git a/script/helpers/TransferRemainingCredits.s.sol b/script/helpers/TransferRemainingCredits.s.sol new file mode 100644 index 00000000..e0ae8cea --- /dev/null +++ b/script/helpers/TransferRemainingCredits.s.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {FeesManager} from "../../contracts/evmx/fees/FeesManager.sol"; +import {IAppGateway} from "../../contracts/evmx/interfaces/IAppGateway.sol"; + +contract TransferRemainingCredits is Script { + function run() external { + string memory rpc = vm.envString("EVMX_RPC"); + vm.createSelectFork(rpc); + + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + FeesManager feesManager = FeesManager(payable(vm.envAddress("FEES_MANAGER"))); + address appGateway = vm.envAddress("APP_GATEWAY"); + address newAppGateway = vm.envAddress("NEW_APP_GATEWAY"); + + (uint256 totalCredits, uint256 blockedCredits) = feesManager.userCredits(appGateway); + console.log("App Gateway:", appGateway); + console.log("New App Gateway:", newAppGateway); + console.log("Fees Manager:", address(feesManager)); + console.log("totalCredits fees:", totalCredits); + console.log("blockedCredits fees:", blockedCredits); + + uint256 availableFees = feesManager.getAvailableCredits(appGateway); + console.log("Available fees:", availableFees); + bytes memory data = abi.encodeWithSignature( + "transferCredits(address,uint256)", + newAppGateway, + availableFees + ); + (bool success, ) = appGateway.call(data); + require(success, "Transfer failed"); + vm.stopBroadcast(); + } +} diff --git a/script/helpers/WithdrawRemainingCredits.s.sol b/script/helpers/WithdrawRemainingCredits.s.sol new file mode 100644 index 00000000..c0b8bd31 --- /dev/null +++ b/script/helpers/WithdrawRemainingCredits.s.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {FeesManager} from "../../contracts/evmx/fees/FeesManager.sol"; + +contract WithdrawRemainingCredits is Script { + function run() external { + string memory rpc = vm.envString("EVMX_RPC"); + vm.createSelectFork(rpc); + + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + FeesManager feesManager = FeesManager(payable(vm.envAddress("FEES_MANAGER"))); + address appGateway = vm.envAddress("APP_GATEWAY"); + + (uint256 totalCredits, uint256 blockedCredits) = feesManager.userCredits(appGateway); + console.log("App Gateway:", appGateway); + console.log("Fees Manager:", address(feesManager)); + console.log("totalCredits fees:", totalCredits); + console.log("blockedCredits fees:", blockedCredits); + + uint256 availableFees = feesManager.getAvailableCredits(appGateway); + console.log("Available fees:", availableFees); + feesManager.transferCredits(appGateway, vm.addr(deployerPrivateKey), availableFees); + + vm.stopBroadcast(); + } +} diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 3301afda..4b6715d2 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -1105,24 +1105,43 @@ contract WatcherSetup is AuctionSetup { IAppGateway appGateway_, bytes32[] memory contractIds_ ) internal { - AppGatewayConfig[] memory configs = new AppGatewayConfig[](contractIds_.length); + // Count valid plugs first. In some cases we might have contractIds such that oly a subset is + // deployed on a chain. for ex, vault on source, and supertoken on destination. + uint256 validPlugCount = 0; + for (uint i = 0; i < contractIds_.length; i++) { + address plug = appGateway_.getOnChainAddress(contractIds_[i], chainSlug_); + if (plug != address(0)) { + validPlugCount++; + } + } + + // Create array with exact size needed + AppGatewayConfig[] memory configs = new AppGatewayConfig[](validPlugCount); + uint256 configIndex = 0; for (uint i = 0; i < contractIds_.length; i++) { address plug = appGateway_.getOnChainAddress(contractIds_[i], chainSlug_); address switchboard = configurations.switchboards(chainSlug_, appGateway_.sbType()); - configs[i] = AppGatewayConfig({ - plug: plug, - chainSlug: chainSlug_, - plugConfig: PlugConfig({ - appGatewayId: encodeAppGatewayId(address(appGateway_)), - switchboard: switchboard - }) - }); + if (plug != address(0)) { + configs[configIndex] = AppGatewayConfig({ + plug: plug, + chainSlug: chainSlug_, + plugConfig: PlugConfig({ + appGatewayId: encodeAppGatewayId(address(appGateway_)), + switchboard: switchboard + }) + }); + configIndex++; + } + } + + // Only call watcher if we have valid configs + if (validPlugCount > 0) { + watcherMultiCall( + address(configurations), + abi.encodeWithSelector(Configurations.setAppGatewayConfigs.selector, configs) + ); } - watcherMultiCall( - address(configurations), - abi.encodeWithSelector(Configurations.setAppGatewayConfigs.selector, configs) - ); } } contract AppGatewayBaseSetup is WatcherSetup { diff --git a/test/apps/CCTPSuperToken.t.sol b/test/apps/CCTPSuperToken.t.sol deleted file mode 100644 index 3c7df780..00000000 --- a/test/apps/CCTPSuperToken.t.sol +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -pragma solidity ^0.8.21; - -import {SuperTokenAppGateway} from "./app-gateways/super-token/SuperTokenAppGateway.sol"; -import {SuperToken} from "./app-gateways/super-token/SuperToken.sol"; -import "../SetupTest.t.sol"; - -/** - * @title SuperToken Test - * @notice Test contract for verifying the functionality of the SuperToken system, which enables - * multi-chain token bridging capabilities. - * @dev Inherits from AppGatewayBaseSetup to utilize multi-chain messaging infrastructure - * - * The test suite validates: - * - Contract deployment across different chains - * - Token transfers between chains - * - Proper balance updates - * - Integration with the delivery and auction system - */ -contract CCTPSuperTokenTest is AppGatewayBaseSetup { - /** - * @notice Groups the main contracts needed for SuperToken functionality - * @param superTokenApp The gateway contract that handles multi-chain token operations - * @param superTokenDeployer Contract responsible for deploying SuperToken instances - * @param superToken Identifier for the SuperToken contract - */ - struct AppContracts { - SuperTokenAppGateway superTokenApp; - bytes32 superToken; - } - address owner = address(uint160(c++)); - uint256 maxFees = 0.01 ether; - - /// @dev Main contracts used throughout the tests - AppContracts appContracts; - /// @dev Array storing contract IDs for deployment (currently only SuperToken) - bytes32[] contractIds = new bytes32[](1); - - /// @dev Test amount for token transfers (0.01 ETH) - uint256 srcAmount = 0.01 ether; - /// @dev Structure holding transfer order details - SuperTokenAppGateway.TransferOrder transferOrder; - - /** - * @notice Sets up the test environment - * @dev Initializes core infrastructure and deploys SuperToken-specific contracts - * Sequence: - * 1. Sets up delivery helper for multi-chain communication - * 2. Deploys SuperToken application - * 3. Initializes contract IDs array - */ - function setUp() public { - deploy(); - - SuperTokenAppGateway superTokenApp = new SuperTokenAppGateway( - address(addressResolver), - owner, - maxFees, - SuperTokenAppGateway.ConstructorParams({ - name_: "SUPER TOKEN", - symbol_: "SUPER", - decimals_: 18, - initialSupplyHolder_: owner, - initialSupply_: 1000000000 ether - }) - ); - vm.prank(owner); - superTokenApp.setSbType(CCTP); - // Enable app gateways to do all operations in the Watcher: Read, Write and Schedule on EVMx - // Watcher sets the limits for apps in this SOCKET protocol version - depositNativeAndCredits(arbChainSlug, 1 ether, 0, address(superTokenApp)); - - appContracts = AppContracts({ - superTokenApp: superTokenApp, - superToken: superTokenApp.superToken() - }); - - contractIds[0] = appContracts.superToken; - } - - /** - * @notice Deploys the SuperToken application and its components - * @dev Creates both the deployer and gateway contracts with initial configuration - * - Sets up SuperToken with "SUPER TOKEN" name and "SUPER" symbol - * - Configures initial supply of 1 billion tokens - * - Sets up fee structure and auction manager integration - */ - function deploySuperToken(uint32 chainSlug) internal { - appContracts.superTokenApp.deployContracts(chainSlug); - executeDeploy(IAppGateway(appContracts.superTokenApp), chainSlug, contractIds); - } - - /** - * @notice Tests the deployment of SuperToken contracts across chains - * @dev Verifies: - * - Correct deployment on target chain (Arbitrum in this case) - * - Proper initialization of token parameters - * - Correct setup of forwarder contracts for multi-chain communication - */ - function testCCTPContractDeployment() public { - deploySuperToken(arbChainSlug); - - (address onChain, address forwarder) = getOnChainAndForwarderAddresses( - arbChainSlug, - appContracts.superToken, - IAppGateway(appContracts.superTokenApp) - ); - - assertEq( - SuperToken(onChain).name(), - "SUPER TOKEN", - "OnChain SuperToken name should be SUPER TOKEN" - ); - - assertEq( - IForwarder(forwarder).getChainSlug(), - arbChainSlug, - "Forwarder SuperToken chainSlug should be arbChainSlug" - ); - - assertEq( - IForwarder(forwarder).getOnChainAddress(), - onChain, - "Forwarder SuperToken onChainAddress should be correct" - ); - assertEq(SuperToken(onChain).owner(), owner, "SuperToken owner should be correct"); - } - - /** - * @notice Tests multi-chain token transfers - * @dev Verifies: - * - Correct token burning on source chain (Arbitrum) - * - Correct token minting on destination chain (Optimism) - * - Accurate balance updates on both chains - * - Proper execution of multi-chain messaging - */ - function testCCTPTransfer() public { - deploySuperToken(arbChainSlug); - deploySuperToken(optChainSlug); - - (address onChainArb, address forwarderArb) = getOnChainAndForwarderAddresses( - arbChainSlug, - appContracts.superToken, - IAppGateway(appContracts.superTokenApp) - ); - - (address onChainOpt, address forwarderOpt) = getOnChainAndForwarderAddresses( - optChainSlug, - appContracts.superToken, - IAppGateway(appContracts.superTokenApp) - ); - - uint256 arbBalanceBefore = SuperToken(onChainArb).balanceOf(owner); - uint256 optBalanceBefore = SuperToken(onChainOpt).balanceOf(owner); - - transferOrder = SuperTokenAppGateway.TransferOrder({ - srcToken: forwarderArb, - dstToken: forwarderOpt, - user: owner, - srcAmount: srcAmount, - deadline: block.timestamp + 1000000 - }); - - bytes memory encodedOrder = abi.encode(transferOrder); - appContracts.superTokenApp.transfer(encodedOrder); - executeRequest(); - - assertEq( - SuperToken(onChainArb).balanceOf(owner), - arbBalanceBefore - srcAmount, - "Arb balance should be decreased by srcAmount" - ); - assertEq( - SuperToken(onChainOpt).balanceOf(owner), - optBalanceBefore + srcAmount, - "Opt balance should be increased by srcAmount" - ); - } -} From 1fe7b30bd6d8462bb52f575a72a009bfc2b3e5e4 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 23 Jun 2025 16:44:19 +0530 Subject: [PATCH 06/12] fix: rename previousDigestsHash -> prevBatchDigestHash --- Errors.md | 205 +++++++++--------- .../protocol/switchboard/CCTPSwitchboard.sol | 16 +- 2 files changed, 111 insertions(+), 110 deletions(-) diff --git a/Errors.md b/Errors.md index f88ea278..58f74337 100644 --- a/Errors.md +++ b/Errors.md @@ -1,155 +1,156 @@ # Custom Error Codes + ## evmx/fees/FeesPool.sol -| Error | Signature | -| ------------------ | ------------ | +| Error | Signature | +|-------|-----------| | `TransferFailed()` | `0x90b8ec18` | ## evmx/helpers/AsyncPromise.sol -| Error | Signature | -| -------------------------- | ------------ | +| Error | Signature | +|-------|-----------| | `PromiseAlreadyResolved()` | `0x56b63537` | -| `OnlyInvoker()` | `0x74ed21f5` | -| `PromiseAlreadySetUp()` | `0x927c53d5` | -| `PromiseRevertFailed()` | `0x0175b9de` | -| `NotLatestPromise()` | `0x39ca95d3` | +| `OnlyInvoker()` | `0x74ed21f5` | +| `PromiseAlreadySetUp()` | `0x927c53d5` | +| `PromiseRevertFailed()` | `0x0175b9de` | +| `NotLatestPromise()` | `0x39ca95d3` | ## evmx/plugs/ContractFactoryPlug.sol -| Error | Signature | -| -------------------------------- | ------------ | -| `DeploymentFailed()` | `0x30116425` | +| Error | Signature | +|-------|-----------| +| `DeploymentFailed()` | `0x30116425` | | `ExecutionFailed(bytes32,bytes)` | `0xd255d8a3` | -| `information(bool,,bytes)` | `0x3a82a1f3` | +| `information(bool,,bytes)` | `0x3a82a1f3` | ## evmx/plugs/FeesPlug.sol -| Error | Signature | -| --------------------------------------------------- | ------------ | +| Error | Signature | +|-------|-----------| | `InsufficientTokenBalance(address,uint256,uint256)` | `0xebd6ced9` | -| `InvalidDepositAmount()` | `0xfe9ba5cd` | -| `TokenNotWhitelisted(address)` | `0xea3bff2e` | +| `InvalidDepositAmount()` | `0xfe9ba5cd` | +| `TokenNotWhitelisted(address)` | `0xea3bff2e` | ## evmx/watcher/RequestHandler.sol -| Error | Signature | -| ----------------------- | ------------ | +| Error | Signature | +|-------|-----------| | `InsufficientMaxFees()` | `0x0e5bc492` | ## protocol/Socket.sol -| Error | Signature | -| ----------------------------------------- | ------------ | +| Error | Signature | +|-------|-----------| | `PayloadAlreadyExecuted(ExecutionStatus)` | `0xf4c54edd` | -| `VerificationFailed()` | `0x439cc0cd` | -| `LowGasLimit()` | `0xd38edae0` | -| `InsufficientMsgValue()` | `0x78f38f76` | +| `VerificationFailed()` | `0x439cc0cd` | +| `LowGasLimit()` | `0xd38edae0` | +| `InsufficientMsgValue()` | `0x78f38f76` | ## protocol/SocketConfig.sol -| Error | Signature | -| ------------------------------- | ------------ | -| `SwitchboardExists()` | `0x2dff8555` | +| Error | Signature | +|-------|-----------| +| `SwitchboardExists()` | `0x2dff8555` | | `SwitchboardExistsOrDisabled()` | `0x1c7d2487` | ## protocol/SocketFeeManager.sol -| Error | Signature | -| -------------------- | ------------ | +| Error | Signature | +|-------|-----------| | `InsufficientFees()` | `0x8d53e553` | -| `FeeTooLow()` | `0x732f9413` | +| `FeeTooLow()` | `0x732f9413` | ## protocol/SocketUtils.sol -| Error | Signature | -| -------------------- | ------------ | -| `OnlyOffChain()` | `0x9cbfe066` | +| Error | Signature | +|-------|-----------| +| `OnlyOffChain()` | `0x9cbfe066` | | `SimulationFailed()` | `0x2fbab3ac` | ## protocol/switchboard/CCTPSwitchboard.sol -| Error | Signature | -| ------------------------------- | ------------ | -| `RemoteExecutionNotFound()` | `0xbd506972` | -| `DigestMismatch()` | `0x582e0907` | -| `PreviousDigestsHashMismatch()` | `0x3e62c52c` | -| `NotAttested()` | `0x99efb890` | -| `NotExecuted()` | `0xec84b1da` | -| `InvalidDomain()` | `0xeb127982` | -| `InvalidSender()` | `0xddb5de5e` | -| `OnlyMessageTransmitter()` | `0x935ac89c` | +| Error | Signature | +|-------|-----------| +| `RemoteExecutionNotFound()` | `0xbd506972` | +| `DigestMismatch()` | `0x582e0907` | +| `PrevBatchDigestHashMismatch()` | `0xc9864e9d` | +| `NotAttested()` | `0x99efb890` | +| `NotExecuted()` | `0xec84b1da` | +| `InvalidDomain()` | `0xeb127982` | +| `InvalidSender()` | `0xddb5de5e` | +| `OnlyMessageTransmitter()` | `0x935ac89c` | ## protocol/switchboard/FastSwitchboard.sol -| Error | Signature | -| ------------------- | ------------ | +| Error | Signature | +|-------|-----------| | `AlreadyAttested()` | `0x35d90805` | | `WatcherNotFound()` | `0xa278e4ad` | ## utils/AccessControl.sol -| Error | Signature | -| ------------------- | ------------ | +| Error | Signature | +|-------|-----------| | `NoPermit(bytes32)` | `0x962f6333` | ## utils/common/Errors.sol -| Error | Signature | -| --------------------------------------------- | ------------ | -| `ZeroAddress()` | `0xd92e233d` | -| `InvalidTransmitter()` | `0x58a70a0a` | -| `InvalidTokenAddress()` | `0x1eb00b06` | -| `InvalidSwitchboard()` | `0xf63c9e4d` | -| `SocketAlreadyInitialized()` | `0xc9500b00` | -| `NotSocket()` | `0xc59f8f7c` | -| `PlugNotFound()` | `0x5f1ac76a` | -| `ResolvingScheduleTooEarly()` | `0x207e8731` | -| `CallFailed()` | `0x3204506f` | -| `InvalidAppGateway()` | `0x82ded261` | -| `AppGatewayAlreadyCalled()` | `0xb224683f` | -| `InvalidCallerTriggered()` | `0x3292d247` | -| `InvalidPromise()` | `0x45f2d176` | -| `InvalidWatcherSignature()` | `0x5029f14f` | -| `NonceUsed()` | `0x1f6d5aef` | -| `AsyncModifierNotSet()` | `0xcae106f9` | -| `WatcherNotSet()` | `0x42d473a7` | -| `InvalidTarget()` | `0x82d5d76a` | -| `InvalidIndex()` | `0x63df8171` | -| `InvalidChainSlug()` | `0xbff6b106` | -| `InvalidPayloadSize()` | `0xfbdf7954` | -| `InvalidOnChainAddress()` | `0xb758c606` | -| `InvalidScheduleDelay()` | `0x9a993219` | -| `AuctionClosed()` | `0x36b6b46d` | -| `AuctionNotOpen()` | `0xf0460077` | -| `BidExceedsMaxFees()` | `0x4c923f3c` | -| `LowerBidAlreadyExists()` | `0xaaa1f709` | -| `RequestCountMismatch()` | `0x98bbcbff` | -| `InvalidAmount()` | `0x2c5211c6` | -| `InsufficientCreditsAvailable()` | `0xe61dc0aa` | -| `InsufficientBalance()` | `0xf4d678b8` | -| `InvalidCaller()` | `0x48f5c3ed` | -| `InvalidGateway()` | `0xfc9dfe85` | -| `RequestAlreadyCancelled()` | `0xc70f47d8` | -| `DeadlineNotPassedForOnChainRevert()` | `0x7006aa10` | -| `InvalidBid()` | `0xc6388ef7` | -| `MaxReAuctionCountReached()` | `0xf2b4388c` | -| `MaxMsgValueLimitExceeded()` | `0x97b4e8ce` | -| `OnlyWatcherAllowed()` | `0xdf7d227c` | -| `InvalidPrecompileData()` | `0x320062c0` | -| `InvalidCallType()` | `0x39d2eb55` | -| `NotRequestHandler()` | `0x8f8cba5b` | -| `NotInvoker()` | `0x8a6353d1` | -| `NotPromiseResolver()` | `0x86d876b2` | -| `RequestPayloadCountLimitExceeded()` | `0xcbef144b` | -| `InsufficientFees()` | `0x8d53e553` | -| `RequestAlreadySettled()` | `0x66fad465` | -| `NoWriteRequest()` | `0x9dcd3065` | -| `AlreadyAssigned()` | `0x9688dc51` | -| `OnlyAppGateway()` | `0xfec944ea` | +| Error | Signature | +|-------|-----------| +| `ZeroAddress()` | `0xd92e233d` | +| `InvalidTransmitter()` | `0x58a70a0a` | +| `InvalidTokenAddress()` | `0x1eb00b06` | +| `InvalidSwitchboard()` | `0xf63c9e4d` | +| `SocketAlreadyInitialized()` | `0xc9500b00` | +| `NotSocket()` | `0xc59f8f7c` | +| `PlugNotFound()` | `0x5f1ac76a` | +| `ResolvingScheduleTooEarly()` | `0x207e8731` | +| `CallFailed()` | `0x3204506f` | +| `InvalidAppGateway()` | `0x82ded261` | +| `AppGatewayAlreadyCalled()` | `0xb224683f` | +| `InvalidCallerTriggered()` | `0x3292d247` | +| `InvalidPromise()` | `0x45f2d176` | +| `InvalidWatcherSignature()` | `0x5029f14f` | +| `NonceUsed()` | `0x1f6d5aef` | +| `AsyncModifierNotSet()` | `0xcae106f9` | +| `WatcherNotSet()` | `0x42d473a7` | +| `InvalidTarget()` | `0x82d5d76a` | +| `InvalidIndex()` | `0x63df8171` | +| `InvalidChainSlug()` | `0xbff6b106` | +| `InvalidPayloadSize()` | `0xfbdf7954` | +| `InvalidOnChainAddress()` | `0xb758c606` | +| `InvalidScheduleDelay()` | `0x9a993219` | +| `AuctionClosed()` | `0x36b6b46d` | +| `AuctionNotOpen()` | `0xf0460077` | +| `BidExceedsMaxFees()` | `0x4c923f3c` | +| `LowerBidAlreadyExists()` | `0xaaa1f709` | +| `RequestCountMismatch()` | `0x98bbcbff` | +| `InvalidAmount()` | `0x2c5211c6` | +| `InsufficientCreditsAvailable()` | `0xe61dc0aa` | +| `InsufficientBalance()` | `0xf4d678b8` | +| `InvalidCaller()` | `0x48f5c3ed` | +| `InvalidGateway()` | `0xfc9dfe85` | +| `RequestAlreadyCancelled()` | `0xc70f47d8` | +| `DeadlineNotPassedForOnChainRevert()` | `0x7006aa10` | +| `InvalidBid()` | `0xc6388ef7` | +| `MaxReAuctionCountReached()` | `0xf2b4388c` | +| `MaxMsgValueLimitExceeded()` | `0x97b4e8ce` | +| `OnlyWatcherAllowed()` | `0xdf7d227c` | +| `InvalidPrecompileData()` | `0x320062c0` | +| `InvalidCallType()` | `0x39d2eb55` | +| `NotRequestHandler()` | `0x8f8cba5b` | +| `NotInvoker()` | `0x8a6353d1` | +| `NotPromiseResolver()` | `0x86d876b2` | +| `RequestPayloadCountLimitExceeded()` | `0xcbef144b` | +| `InsufficientFees()` | `0x8d53e553` | +| `RequestAlreadySettled()` | `0x66fad465` | +| `NoWriteRequest()` | `0x9dcd3065` | +| `AlreadyAssigned()` | `0x9688dc51` | +| `OnlyAppGateway()` | `0xfec944ea` | | `NewMaxFeesLowerThanCurrent(uint256,uint256)` | `0x1345dda1` | -| `InvalidContract()` | `0x6eefed20` | -| `InvalidData()` | `0x5cb045db` | -| `InvalidSignature()` | `0x8baa579f` | -| `DeadlinePassed()` | `0x70f65caa` | +| `InvalidContract()` | `0x6eefed20` | +| `InvalidData()` | `0x5cb045db` | +| `InvalidSignature()` | `0x8baa579f` | +| `DeadlinePassed()` | `0x70f65caa` | diff --git a/contracts/protocol/switchboard/CCTPSwitchboard.sol b/contracts/protocol/switchboard/CCTPSwitchboard.sol index 3be56b01..8c0965d1 100644 --- a/contracts/protocol/switchboard/CCTPSwitchboard.sol +++ b/contracts/protocol/switchboard/CCTPSwitchboard.sol @@ -28,7 +28,7 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { error RemoteExecutionNotFound(); error DigestMismatch(); - error PreviousDigestsHashMismatch(); + error PrevBatchDigestHashMismatch(); error NotAttested(); error NotExecuted(); error InvalidDomain(); @@ -121,18 +121,18 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { bytes calldata transmitterSignature_, ExecuteParams calldata executeParams_ ) external { - // Calculate previousDigestsHash from stored remoteExecutedDigests - bytes32 previousDigestsHash = bytes32(0); + // Calculate prevBatchDigestHash from stored remoteExecutedDigests + bytes32 prevBatchDigestHash = bytes32(0); for (uint256 i = 0; i < previousPayloadIds_.length; i++) { if (remoteExecutedDigests[previousPayloadIds_[i]] == bytes32(0)) revert RemoteExecutionNotFound(); - previousDigestsHash = keccak256( - abi.encodePacked(previousDigestsHash, remoteExecutedDigests[previousPayloadIds_[i]]) + prevBatchDigestHash = keccak256( + abi.encodePacked(prevBatchDigestHash, remoteExecutedDigests[previousPayloadIds_[i]]) ); } - // Check if the calculated previousDigestsHash matches the one in executeParams_ - if (previousDigestsHash != executeParams_.prevBatchDigestHash) - revert PreviousDigestsHashMismatch(); + // Check if the calculated prevBatchDigestHash matches the one in executeParams_ + if (prevBatchDigestHash != executeParams_.prevBatchDigestHash) + revert PrevBatchDigestHashMismatch(); address transmitter = _recoverSigner( keccak256(abi.encode(address(socket__), payloadId_)), From 45ca46616efd7f826c4c212a23f30fb58d1bc395 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 23 Jun 2025 16:44:52 +0530 Subject: [PATCH 07/12] chore: lint --- Errors.md | 203 +++++++++++++++++++++++++++--------------------------- 1 file changed, 101 insertions(+), 102 deletions(-) diff --git a/Errors.md b/Errors.md index 58f74337..a2a0fc42 100644 --- a/Errors.md +++ b/Errors.md @@ -1,156 +1,155 @@ # Custom Error Codes - ## evmx/fees/FeesPool.sol -| Error | Signature | -|-------|-----------| +| Error | Signature | +| ------------------ | ------------ | | `TransferFailed()` | `0x90b8ec18` | ## evmx/helpers/AsyncPromise.sol -| Error | Signature | -|-------|-----------| +| Error | Signature | +| -------------------------- | ------------ | | `PromiseAlreadyResolved()` | `0x56b63537` | -| `OnlyInvoker()` | `0x74ed21f5` | -| `PromiseAlreadySetUp()` | `0x927c53d5` | -| `PromiseRevertFailed()` | `0x0175b9de` | -| `NotLatestPromise()` | `0x39ca95d3` | +| `OnlyInvoker()` | `0x74ed21f5` | +| `PromiseAlreadySetUp()` | `0x927c53d5` | +| `PromiseRevertFailed()` | `0x0175b9de` | +| `NotLatestPromise()` | `0x39ca95d3` | ## evmx/plugs/ContractFactoryPlug.sol -| Error | Signature | -|-------|-----------| -| `DeploymentFailed()` | `0x30116425` | +| Error | Signature | +| -------------------------------- | ------------ | +| `DeploymentFailed()` | `0x30116425` | | `ExecutionFailed(bytes32,bytes)` | `0xd255d8a3` | -| `information(bool,,bytes)` | `0x3a82a1f3` | +| `information(bool,,bytes)` | `0x3a82a1f3` | ## evmx/plugs/FeesPlug.sol -| Error | Signature | -|-------|-----------| +| Error | Signature | +| --------------------------------------------------- | ------------ | | `InsufficientTokenBalance(address,uint256,uint256)` | `0xebd6ced9` | -| `InvalidDepositAmount()` | `0xfe9ba5cd` | -| `TokenNotWhitelisted(address)` | `0xea3bff2e` | +| `InvalidDepositAmount()` | `0xfe9ba5cd` | +| `TokenNotWhitelisted(address)` | `0xea3bff2e` | ## evmx/watcher/RequestHandler.sol -| Error | Signature | -|-------|-----------| +| Error | Signature | +| ----------------------- | ------------ | | `InsufficientMaxFees()` | `0x0e5bc492` | ## protocol/Socket.sol -| Error | Signature | -|-------|-----------| +| Error | Signature | +| ----------------------------------------- | ------------ | | `PayloadAlreadyExecuted(ExecutionStatus)` | `0xf4c54edd` | -| `VerificationFailed()` | `0x439cc0cd` | -| `LowGasLimit()` | `0xd38edae0` | -| `InsufficientMsgValue()` | `0x78f38f76` | +| `VerificationFailed()` | `0x439cc0cd` | +| `LowGasLimit()` | `0xd38edae0` | +| `InsufficientMsgValue()` | `0x78f38f76` | ## protocol/SocketConfig.sol -| Error | Signature | -|-------|-----------| -| `SwitchboardExists()` | `0x2dff8555` | +| Error | Signature | +| ------------------------------- | ------------ | +| `SwitchboardExists()` | `0x2dff8555` | | `SwitchboardExistsOrDisabled()` | `0x1c7d2487` | ## protocol/SocketFeeManager.sol -| Error | Signature | -|-------|-----------| +| Error | Signature | +| -------------------- | ------------ | | `InsufficientFees()` | `0x8d53e553` | -| `FeeTooLow()` | `0x732f9413` | +| `FeeTooLow()` | `0x732f9413` | ## protocol/SocketUtils.sol -| Error | Signature | -|-------|-----------| -| `OnlyOffChain()` | `0x9cbfe066` | +| Error | Signature | +| -------------------- | ------------ | +| `OnlyOffChain()` | `0x9cbfe066` | | `SimulationFailed()` | `0x2fbab3ac` | ## protocol/switchboard/CCTPSwitchboard.sol -| Error | Signature | -|-------|-----------| -| `RemoteExecutionNotFound()` | `0xbd506972` | -| `DigestMismatch()` | `0x582e0907` | +| Error | Signature | +| ------------------------------- | ------------ | +| `RemoteExecutionNotFound()` | `0xbd506972` | +| `DigestMismatch()` | `0x582e0907` | | `PrevBatchDigestHashMismatch()` | `0xc9864e9d` | -| `NotAttested()` | `0x99efb890` | -| `NotExecuted()` | `0xec84b1da` | -| `InvalidDomain()` | `0xeb127982` | -| `InvalidSender()` | `0xddb5de5e` | -| `OnlyMessageTransmitter()` | `0x935ac89c` | +| `NotAttested()` | `0x99efb890` | +| `NotExecuted()` | `0xec84b1da` | +| `InvalidDomain()` | `0xeb127982` | +| `InvalidSender()` | `0xddb5de5e` | +| `OnlyMessageTransmitter()` | `0x935ac89c` | ## protocol/switchboard/FastSwitchboard.sol -| Error | Signature | -|-------|-----------| +| Error | Signature | +| ------------------- | ------------ | | `AlreadyAttested()` | `0x35d90805` | | `WatcherNotFound()` | `0xa278e4ad` | ## utils/AccessControl.sol -| Error | Signature | -|-------|-----------| +| Error | Signature | +| ------------------- | ------------ | | `NoPermit(bytes32)` | `0x962f6333` | ## utils/common/Errors.sol -| Error | Signature | -|-------|-----------| -| `ZeroAddress()` | `0xd92e233d` | -| `InvalidTransmitter()` | `0x58a70a0a` | -| `InvalidTokenAddress()` | `0x1eb00b06` | -| `InvalidSwitchboard()` | `0xf63c9e4d` | -| `SocketAlreadyInitialized()` | `0xc9500b00` | -| `NotSocket()` | `0xc59f8f7c` | -| `PlugNotFound()` | `0x5f1ac76a` | -| `ResolvingScheduleTooEarly()` | `0x207e8731` | -| `CallFailed()` | `0x3204506f` | -| `InvalidAppGateway()` | `0x82ded261` | -| `AppGatewayAlreadyCalled()` | `0xb224683f` | -| `InvalidCallerTriggered()` | `0x3292d247` | -| `InvalidPromise()` | `0x45f2d176` | -| `InvalidWatcherSignature()` | `0x5029f14f` | -| `NonceUsed()` | `0x1f6d5aef` | -| `AsyncModifierNotSet()` | `0xcae106f9` | -| `WatcherNotSet()` | `0x42d473a7` | -| `InvalidTarget()` | `0x82d5d76a` | -| `InvalidIndex()` | `0x63df8171` | -| `InvalidChainSlug()` | `0xbff6b106` | -| `InvalidPayloadSize()` | `0xfbdf7954` | -| `InvalidOnChainAddress()` | `0xb758c606` | -| `InvalidScheduleDelay()` | `0x9a993219` | -| `AuctionClosed()` | `0x36b6b46d` | -| `AuctionNotOpen()` | `0xf0460077` | -| `BidExceedsMaxFees()` | `0x4c923f3c` | -| `LowerBidAlreadyExists()` | `0xaaa1f709` | -| `RequestCountMismatch()` | `0x98bbcbff` | -| `InvalidAmount()` | `0x2c5211c6` | -| `InsufficientCreditsAvailable()` | `0xe61dc0aa` | -| `InsufficientBalance()` | `0xf4d678b8` | -| `InvalidCaller()` | `0x48f5c3ed` | -| `InvalidGateway()` | `0xfc9dfe85` | -| `RequestAlreadyCancelled()` | `0xc70f47d8` | -| `DeadlineNotPassedForOnChainRevert()` | `0x7006aa10` | -| `InvalidBid()` | `0xc6388ef7` | -| `MaxReAuctionCountReached()` | `0xf2b4388c` | -| `MaxMsgValueLimitExceeded()` | `0x97b4e8ce` | -| `OnlyWatcherAllowed()` | `0xdf7d227c` | -| `InvalidPrecompileData()` | `0x320062c0` | -| `InvalidCallType()` | `0x39d2eb55` | -| `NotRequestHandler()` | `0x8f8cba5b` | -| `NotInvoker()` | `0x8a6353d1` | -| `NotPromiseResolver()` | `0x86d876b2` | -| `RequestPayloadCountLimitExceeded()` | `0xcbef144b` | -| `InsufficientFees()` | `0x8d53e553` | -| `RequestAlreadySettled()` | `0x66fad465` | -| `NoWriteRequest()` | `0x9dcd3065` | -| `AlreadyAssigned()` | `0x9688dc51` | -| `OnlyAppGateway()` | `0xfec944ea` | +| Error | Signature | +| --------------------------------------------- | ------------ | +| `ZeroAddress()` | `0xd92e233d` | +| `InvalidTransmitter()` | `0x58a70a0a` | +| `InvalidTokenAddress()` | `0x1eb00b06` | +| `InvalidSwitchboard()` | `0xf63c9e4d` | +| `SocketAlreadyInitialized()` | `0xc9500b00` | +| `NotSocket()` | `0xc59f8f7c` | +| `PlugNotFound()` | `0x5f1ac76a` | +| `ResolvingScheduleTooEarly()` | `0x207e8731` | +| `CallFailed()` | `0x3204506f` | +| `InvalidAppGateway()` | `0x82ded261` | +| `AppGatewayAlreadyCalled()` | `0xb224683f` | +| `InvalidCallerTriggered()` | `0x3292d247` | +| `InvalidPromise()` | `0x45f2d176` | +| `InvalidWatcherSignature()` | `0x5029f14f` | +| `NonceUsed()` | `0x1f6d5aef` | +| `AsyncModifierNotSet()` | `0xcae106f9` | +| `WatcherNotSet()` | `0x42d473a7` | +| `InvalidTarget()` | `0x82d5d76a` | +| `InvalidIndex()` | `0x63df8171` | +| `InvalidChainSlug()` | `0xbff6b106` | +| `InvalidPayloadSize()` | `0xfbdf7954` | +| `InvalidOnChainAddress()` | `0xb758c606` | +| `InvalidScheduleDelay()` | `0x9a993219` | +| `AuctionClosed()` | `0x36b6b46d` | +| `AuctionNotOpen()` | `0xf0460077` | +| `BidExceedsMaxFees()` | `0x4c923f3c` | +| `LowerBidAlreadyExists()` | `0xaaa1f709` | +| `RequestCountMismatch()` | `0x98bbcbff` | +| `InvalidAmount()` | `0x2c5211c6` | +| `InsufficientCreditsAvailable()` | `0xe61dc0aa` | +| `InsufficientBalance()` | `0xf4d678b8` | +| `InvalidCaller()` | `0x48f5c3ed` | +| `InvalidGateway()` | `0xfc9dfe85` | +| `RequestAlreadyCancelled()` | `0xc70f47d8` | +| `DeadlineNotPassedForOnChainRevert()` | `0x7006aa10` | +| `InvalidBid()` | `0xc6388ef7` | +| `MaxReAuctionCountReached()` | `0xf2b4388c` | +| `MaxMsgValueLimitExceeded()` | `0x97b4e8ce` | +| `OnlyWatcherAllowed()` | `0xdf7d227c` | +| `InvalidPrecompileData()` | `0x320062c0` | +| `InvalidCallType()` | `0x39d2eb55` | +| `NotRequestHandler()` | `0x8f8cba5b` | +| `NotInvoker()` | `0x8a6353d1` | +| `NotPromiseResolver()` | `0x86d876b2` | +| `RequestPayloadCountLimitExceeded()` | `0xcbef144b` | +| `InsufficientFees()` | `0x8d53e553` | +| `RequestAlreadySettled()` | `0x66fad465` | +| `NoWriteRequest()` | `0x9dcd3065` | +| `AlreadyAssigned()` | `0x9688dc51` | +| `OnlyAppGateway()` | `0xfec944ea` | | `NewMaxFeesLowerThanCurrent(uint256,uint256)` | `0x1345dda1` | -| `InvalidContract()` | `0x6eefed20` | -| `InvalidData()` | `0x5cb045db` | -| `InvalidSignature()` | `0x8baa579f` | -| `DeadlinePassed()` | `0x70f65caa` | +| `InvalidContract()` | `0x6eefed20` | +| `InvalidData()` | `0x5cb045db` | +| `InvalidSignature()` | `0x8baa579f` | +| `DeadlinePassed()` | `0x70f65caa` | From eab84a580d1029a7073a4e3d6eede51264a80916 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 23 Jun 2025 16:54:47 +0530 Subject: [PATCH 08/12] chore: dev deployment --- deployments/dev_addresses.json | 83 +++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/deployments/dev_addresses.json b/deployments/dev_addresses.json index 6e0444a6..e41f9235 100644 --- a/deployments/dev_addresses.json +++ b/deployments/dev_addresses.json @@ -1,44 +1,55 @@ { + "84532": { + "CCTPSwitchboard": "0xb2B779ab8FC851bCE986d25B2824933B0Cd101d9", + "ContractFactoryPlug": "0x2e0fE75Bd247d7441f279388CD0e3a77FEcddADf", + "FastSwitchboard": "0xaFFfaD81e6DDE509Bd83Ab2024225b2FF537BeA7", + "FeesPlug": "0x1dc20d27F06876cA74ee4e2E9a4724f06a4a5E54", + "Socket": "0x9B06e2Dae351ed8B1112C036e5c306E8fBe4C9c5", + "SocketBatcher": "0xd627BFe7d2fCAC1147c996a6F2CAaB2E1e1bD344", + "startBlock": 27234828 + }, "421614": { - "ContractFactoryPlug": "0x7b9928b01272b915050aDfcba7e0a11b22271BAd", - "FastSwitchboard": "0x2974E94c0d1323D3A24f7B4F924fbdB325Be1aa3", - "FeesPlug": "0xaFD76cADB518E7e5131991Fe4403e00297916957", - "Socket": "0xb7378ae43b135988C8a83dfD1AcD71Ff39381396", - "SocketBatcher": "0x60541d31Fda60163480CAb486be3762b5793B650", - "startBlock": 159641867 + "CCTPSwitchboard": "0x74A4aa989515b088A1aC33C5D59897f69cA66B91", + "ContractFactoryPlug": "0x25190648330361f35d00a0D41BD347de8E1B838C", + "FastSwitchboard": "0x39d21e679312Bf0e3681bd0254c587E3528dd2a3", + "FeesPlug": "0xe9BDa44a39F8d29eaF1956EB05442551794871f3", + "Socket": "0x86264607bAD260e9032add9e4E2DA74f71E354E0", + "SocketBatcher": "0x5B83E4104E37c7ECDf6Aeb62a4E204E4c63ac8D5", + "startBlock": 157984273 }, "7625382": { - "AddressResolver": "0x8161cDBa2d2fCE66307254AAC1d42966D4F5353E", - "AddressResolverImpl": "0x91e548d87768313C03da8405D01171b83912c430", - "AsyncDeployer": "0x025b308371dC1C5e337527f96BE46Ba6A12c774A", - "AsyncDeployerImpl": "0x80CFbD3B6134Fb2D2B7d21FC132a9F7c115e7B72", - "AuctionManager": "0xA40aFA1632328D84226084a4539B3869D2B68e28", - "AuctionManagerImpl": "0x42109F6212765ABeb589f9b2c14Bee4b8DB3e638", - "Configurations": "0x60185198097df249B504D5A164323eBF42B3764d", - "ConfigurationsImpl": "0x0d2646fC08af29A7799Af435c5ABBA1b020C4dC7", - "DeployForwarder": "0xdC51D652B8c3cCB3cAAB9C1E2704fD4D62E76433", - "DeployForwarderImpl": "0xCe95fca954a0BF43c299c79d5152f2c164C02b7A", - "ERC1967Factory": "0xb0364Fd8f158071831ac87E7EE2C792Ab509a524", - "FeesManager": "0x09F824Eae77f71279d73Ae24FEb2163FCe88B25D", - "FeesManagerImpl": "0x5b460B29750648f6D569Ed57139967BE589174F8", - "FeesPool": "0xc20Be67ef742202dc93A78aa741E7C3715eA1DFd", - "PromiseResolver": "0xcfFda1dF8668266E6A77809EcA9CCA8A632ecaF3", - "ReadPrecompile": "0x254Dc9e0623426A79F02D2001E367cd32B50aaaA", - "RequestHandler": "0x1FE7527a8620374B3Fdb101bA1D56eC46EC9a24A", - "RequestHandlerImpl": "0x3d9578B252ed1F5A66348Cc40E482dacc32Ae790", - "SchedulePrecompile": "0x7D6F2A4aDf7e5Cfcf9627CC7FCA1d39fD19C07fc", - "startBlock": 8355289, - "Watcher": "0xD5b30DC89D96ee7303Dc2726491996B46089F693", - "WatcherImpl": "0x872bb254118a2210e3C491918133F2ab4D7Bc362", - "WritePrecompile": "0x10eaDbd1a2787ebbF4Abe9b6D79e669C0c8E8B26", - "WritePrecompileImpl": "0xD3aEb53da0a72788C16eAf5a23a5aBae6708C073" + "AddressResolver": "0x67790E222c41b0E787C278e757b7c40f03Fa5709", + "AddressResolverImpl": "0x89C928379fED43B7117b852931e2968ce39C8380", + "AsyncDeployer": "0x1C70bc3043667e884222B8835E0Ae554eb512810", + "AsyncDeployerImpl": "0x09a762309c63a4e19cd3d822aA340Fe964Ba9C92", + "AuctionManager": "0xC12aDF88dfc116CAF88816d150FE498843dABEEe", + "AuctionManagerImpl": "0x55b76897b3BF6ED04188cbaa7DC21ae14b35D3eE", + "Configurations": "0x377431bD1A3321C401542C8B1EC6E0c23E125042", + "ConfigurationsImpl": "0xDe5DedAe6e17f906D1269D5e84BEfB06F3926310", + "DeployForwarder": "0xd48218b2DafF9063177b0c6Bae229ec6C5f086a9", + "DeployForwarderImpl": "0x8e178161BB3B36a28C15DFBe3142afF8757B8993", + "ERC1967Factory": "0x870fCA8803bEFd119B1317AFB6794F97af7e515e", + "FeesManager": "0x761A9024D267006061ec943d02e3949678906f3E", + "FeesManagerImpl": "0x29C583B64FD2d7b70f8F6253C2a28D60af364Cb5", + "FeesPool": "0x9De353dD1131aB4e502590D3a1832652FA316268", + "PromiseResolver": "0x73b1B3dF6C71e0aa912f9d6933920D4461ae9718", + "ReadPrecompile": "0x58f49313816c1876417EE53De8F5de047359fB2C", + "RequestHandler": "0x63a6D7096b5a2F5c9Ce7D8632A7A2034A85b7F01", + "RequestHandlerImpl": "0x593f4844ceEA828bC6d9D78A0ef7Ce64F42190dC", + "SchedulePrecompile": "0xF77d2059a66026Efac11334D30372429553CAaC3", + "startBlock": 8626651, + "Watcher": "0xe4D1B4B8c0eEE90ac1f5314e758446CBa201BBA8", + "WatcherImpl": "0x7726e559A5129A9174f89F7E2029f7212B66dD13", + "WritePrecompile": "0xd8be408E271EEe9d3D0f28305bB9b6003589E1A9", + "WritePrecompileImpl": "0xE24c4b0f67f566Fa558b3FE85f1780CD330f1F4D" }, "11155420": { - "ContractFactoryPlug": "0x0279A18d5FC235A92fB4ABd5F7e9258e78E27948", - "FastSwitchboard": "0x6b4EF1452265193798bfa3ef6D29421da9e7E222", - "FeesPlug": "0x5E175fD699E066D6536054198d57AF0De88C7c4E", - "Socket": "0xB260A4DD0952e9A5b5F6652019469F05Fb137dC5", - "SocketBatcher": "0xc320FC7b06D4491A9E7e6fa55a3305b12548519e", - "startBlock": 28568337 + "CCTPSwitchboard": "0xf9A93a92c0754084f6320f3fC1D54584C2e0439d", + "ContractFactoryPlug": "0xAA78A6c96DF690d30eF161490f6590fCAb8f4406", + "FastSwitchboard": "0x696d7d0Af367cFE3d3c56BD61ca16B3A0939618b", + "FeesPlug": "0xC6Fb338F2009B5AD1e1bbA2dd2c9f52e9dE2C91C", + "Socket": "0x4B5718c1f739A83EF5c75f64776f2c9D4D460B1D", + "SocketBatcher": "0xc9E18b73C1A575D8A5975754a01CD19BE8400037", + "startBlock": 28356082 } } From 2b98150c6cb64993971306fa83b47c1d338b5fe2 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 23 Jun 2025 23:02:14 +0530 Subject: [PATCH 09/12] fix: PR comments, cleanup --- contracts/protocol/SocketBatcher.sol | 35 +++----- .../protocol/interfaces/ICCTPSwitchboard.sol | 15 ++-- contracts/protocol/interfaces/ISocket.sol | 2 + .../protocol/switchboard/CCTPSwitchboard.sol | 87 +++++-------------- .../protocol/switchboard/FastSwitchboard.sol | 2 +- test/SetupTest.t.sol | 27 +++--- test/mock/MockSocket.sol | 2 + 7 files changed, 59 insertions(+), 111 deletions(-) diff --git a/contracts/protocol/SocketBatcher.sol b/contracts/protocol/SocketBatcher.sol index 7e2384bd..d9f12878 100644 --- a/contracts/protocol/SocketBatcher.sol +++ b/contracts/protocol/SocketBatcher.sol @@ -62,18 +62,17 @@ contract SocketBatcher is ISocketBatcher, Ownable { CCTPBatchParams calldata cctpParams_, address switchboard_ ) external payable returns (bool, bytes memory) { - bytes32 payloadId = _createPayloadId(execParams_.executeParams, switchboard_); - ICCTPSwitchboard(switchboard_).attest(payloadId, execParams_.digest, execParams_.proof); - - ICCTPSwitchboard(switchboard_).verifyAttestations( - cctpParams_.messages, - cctpParams_.attestations + bytes32 payloadId = createPayloadId( + execParams_.executeParams.requestCount, + execParams_.executeParams.batchCount, + execParams_.executeParams.payloadCount, + switchboard_, + socket__.chainSlug() ); - ICCTPSwitchboard(switchboard_).proveRemoteExecutions( - cctpParams_.previousPayloadIds, - payloadId, - execParams_.transmitterSignature, - execParams_.executeParams + ICCTPSwitchboard(switchboard_).attestVerifyAndProveExecutions( + execParams_, + cctpParams_, + payloadId ); (bool success, bytes memory returnData) = socket__.execute{value: msg.value}( execParams_.executeParams, @@ -89,20 +88,6 @@ contract SocketBatcher is ISocketBatcher, Ownable { return (success, returnData); } - function _createPayloadId( - ExecuteParams memory executeParams_, - address switchboard_ - ) internal view returns (bytes32) { - return - createPayloadId( - executeParams_.requestCount, - executeParams_.batchCount, - executeParams_.payloadCount, - switchboard_, - socket__.chainSlug() - ); - } - /** * @notice Rescues funds from the contract * @param token_ The address of the token to rescue diff --git a/contracts/protocol/interfaces/ICCTPSwitchboard.sol b/contracts/protocol/interfaces/ICCTPSwitchboard.sol index ac3bb40e..796ee602 100644 --- a/contracts/protocol/interfaces/ICCTPSwitchboard.sol +++ b/contracts/protocol/interfaces/ICCTPSwitchboard.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.21; import "./ISwitchboard.sol"; -import {ExecuteParams} from "../../utils/common/Structs.sol"; +import {ExecuteParams, CCTPExecutionParams, CCTPBatchParams} from "../../utils/common/Structs.sol"; /** * @title ISwitchboard @@ -10,13 +10,6 @@ import {ExecuteParams} from "../../utils/common/Structs.sol"; * digest is executed. */ interface ICCTPSwitchboard is ISwitchboard { - /** - * @notice Attests a payload - * @param digest_ The digest of the payload - * @param proof_ The proof of the payload - */ - function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) external; - /** * @notice Syncs out a payload to the remote chains * @param payloadId_ The unique identifier for the payload @@ -59,4 +52,10 @@ interface ICCTPSwitchboard is ISwitchboard { bytes[] calldata messages_, bytes[] calldata attestations_ ) external; + + function attestVerifyAndProveExecutions( + CCTPExecutionParams calldata execParams_, + CCTPBatchParams calldata cctpParams_, + bytes32 payloadId_ + ) external; } diff --git a/contracts/protocol/interfaces/ISocket.sol b/contracts/protocol/interfaces/ISocket.sol index e27eaefd..e942ffed 100644 --- a/contracts/protocol/interfaces/ISocket.sol +++ b/contracts/protocol/interfaces/ISocket.sol @@ -80,4 +80,6 @@ interface ISocket { function payloadExecuted(bytes32 payloadId_) external view returns (ExecutionStatus); function chainSlug() external view returns (uint32); + + function payloadIdToDigest(bytes32 payloadId_) external view returns (bytes32); } diff --git a/contracts/protocol/switchboard/CCTPSwitchboard.sol b/contracts/protocol/switchboard/CCTPSwitchboard.sol index 8c0965d1..0a906451 100644 --- a/contracts/protocol/switchboard/CCTPSwitchboard.sol +++ b/contracts/protocol/switchboard/CCTPSwitchboard.sol @@ -3,35 +3,27 @@ pragma solidity ^0.8.21; import "./FastSwitchboard.sol"; import {ISocket} from "../interfaces/ISocket.sol"; -import {ExecuteParams, ExecutionStatus} from "../../utils/common/Structs.sol"; +import {ExecuteParams, ExecutionStatus, CCTPExecutionParams, CCTPBatchParams} from "../../utils/common/Structs.sol"; import {IMessageTransmitter} from "../interfaces/IMessageTransmitter.sol"; import {IMessageHandler} from "../interfaces/IMessageHandler.sol"; contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { struct RemoteEndpoint { - uint256 remoteChainId; - address remoteAddress; + bytes32 remoteAddress; uint32 remoteDomain; } + IMessageTransmitter public immutable messageTransmitter; // remoteChainSlug => remoteEndpoint mapping(uint32 => RemoteEndpoint) public remoteEndpoints; - // remoteDomain => remoteAddress - mapping(uint32 => address) public remoteAddresses; - mapping(bytes32 => bool) public isSyncedOut; - mapping(bytes32 => bytes32) public payloadIdToDigest; mapping(bytes32 => bytes32) public remoteExecutedDigests; mapping(bytes32 => bool) public isRemoteExecuted; - IMessageTransmitter public immutable messageTransmitter; - error RemoteExecutionNotFound(); - error DigestMismatch(); error PrevBatchDigestHashMismatch(); error NotAttested(); error NotExecuted(); - error InvalidDomain(); error InvalidSender(); error OnlyMessageTransmitter(); @@ -44,31 +36,13 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { messageTransmitter = IMessageTransmitter(messageTransmitter_); } - function attest(bytes32 payloadId_, bytes32 digest_, bytes calldata proof_) external { - address watcher = _recoverSigner( - keccak256(abi.encode(address(this), chainSlug, digest_)), - proof_ - ); - - if (isAttested[digest_]) revert AlreadyAttested(); - if (!_hasRole(WATCHER_ROLE, watcher)) revert WatcherNotFound(); - - isAttested[digest_] = true; - payloadIdToDigest[payloadId_] = digest_; - emit Attested(payloadId_, watcher); - } - function allowPacket(bytes32 digest_, bytes32 payloadId_) external view returns (bool) { // digest has enough attestations and is remote executed - return - payloadIdToDigest[payloadId_] == digest_ && - isAttested[digest_] && - isRemoteExecuted[payloadId_]; + return isAttested[digest_] && isRemoteExecuted[payloadId_]; } function syncOut(bytes32 payloadId_, uint32[] calldata remoteChainSlugs_) external { - bytes32 digest = payloadIdToDigest[payloadId_]; - + bytes32 digest = socket__.payloadIdToDigest(payloadId_); // not attested if (digest == bytes32(0) || !isAttested[digest]) revert NotAttested(); @@ -83,13 +57,7 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { bytes memory message = abi.encode(payloadId_, digest); for (uint256 i = 0; i < remoteChainSlugs_.length; i++) { RemoteEndpoint memory endpoint = remoteEndpoints[remoteChainSlugs_[i]]; - if (endpoint.remoteDomain == 0) revert InvalidDomain(); - - messageTransmitter.sendMessage( - endpoint.remoteDomain, - addressToBytes32(endpoint.remoteAddress), - message - ); + messageTransmitter.sendMessage(endpoint.remoteDomain, endpoint.remoteAddress, message); } } @@ -99,17 +67,14 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { bytes calldata messageBody ) external returns (bool) { if (msg.sender != address(messageTransmitter)) revert OnlyMessageTransmitter(); + if (remoteEndpoints[sourceDomain].remoteAddress != sender) revert InvalidSender(); (bytes32 payloadId, bytes32 digest) = abi.decode(messageBody, (bytes32, bytes32)); - if (remoteAddresses[sourceDomain] != bytes32ToAddress(sender)) { - revert InvalidSender(); - } - remoteExecutedDigests[payloadId] = digest; return true; } - function verifyAttestations(bytes[] calldata messages, bytes[] calldata attestations) external { + function verifyAttestations(bytes[] calldata messages, bytes[] calldata attestations) public { for (uint256 i = 0; i < messages.length; i++) { messageTransmitter.receiveMessage(messages[i], attestations[i]); } @@ -120,7 +85,7 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { bytes32 payloadId_, bytes calldata transmitterSignature_, ExecuteParams calldata executeParams_ - ) external { + ) public { // Calculate prevBatchDigestHash from stored remoteExecutedDigests bytes32 prevBatchDigestHash = bytes32(0); for (uint256 i = 0; i < previousPayloadIds_.length; i++) { @@ -148,11 +113,8 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { executeParams_ ); - bytes32 storedDigest = payloadIdToDigest[payloadId_]; // Verify the constructed digest matches the stored one - if (storedDigest == bytes32(0) || !isAttested[storedDigest]) revert NotAttested(); - if (constructedDigest != storedDigest) revert DigestMismatch(); - + if (!isAttested[constructedDigest]) revert NotAttested(); isRemoteExecuted[payloadId_] = true; } @@ -191,28 +153,27 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { function addRemoteEndpoint( uint32 remoteChainSlug_, - uint256 remoteChainId_, - address remoteAddress_, + bytes32 remoteAddress_, uint32 remoteDomain_ ) external onlyOwner { remoteEndpoints[remoteChainSlug_] = RemoteEndpoint({ - remoteChainId: remoteChainId_, remoteAddress: remoteAddress_, remoteDomain: remoteDomain_ }); - remoteAddresses[remoteDomain_] = remoteAddress_; } - function removeRemoteEndpoint(uint32 remoteChainSlug_) external onlyOwner { - uint32 remoteDomain = remoteEndpoints[remoteChainSlug_].remoteDomain; - delete remoteEndpoints[remoteChainSlug_]; - delete remoteAddresses[remoteDomain]; - } - - function addressToBytes32(address addr_) public pure returns (bytes32) { - return bytes32(uint256(uint160(addr_))); - } - function bytes32ToAddress(bytes32 addrBytes32_) public pure returns (address) { - return address(uint160(uint256(addrBytes32_))); + function attestVerifyAndProveExecutions( + CCTPExecutionParams calldata execParams_, + CCTPBatchParams calldata cctpParams_, + bytes32 payloadId_ + ) external { + attest(execParams_.digest, execParams_.proof); + verifyAttestations(cctpParams_.messages, cctpParams_.attestations); + proveRemoteExecutions( + cctpParams_.previousPayloadIds, + payloadId_, + execParams_.transmitterSignature, + execParams_.executeParams + ); } } diff --git a/contracts/protocol/switchboard/FastSwitchboard.sol b/contracts/protocol/switchboard/FastSwitchboard.sol index ee0679a4..8044cc38 100644 --- a/contracts/protocol/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/switchboard/FastSwitchboard.sol @@ -39,7 +39,7 @@ contract FastSwitchboard is SwitchboardBase { * @param proof_ proof from watcher * @notice we are attesting a payload uniquely identified with digest. */ - function attest(bytes32 digest_, bytes calldata proof_) external { + function attest(bytes32 digest_, bytes calldata proof_) public virtual { if (isAttested[digest_]) revert AlreadyAttested(); address watcher = _recoverSigner( diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 4b6715d2..d0cd404f 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -70,7 +70,8 @@ contract SetupStore is Test { uint256 public payloadIdCounter; uint256 public triggerCounter; uint256 public asyncPromiseCounter; - + uint32 public optCCTPDomain = 2; + uint32 public arbCCTPDomain = 3; struct SocketContracts { uint32 chainSlug; Socket socket; @@ -129,15 +130,13 @@ contract DeploySetup is SetupStore { vm.startPrank(socketOwner); arbConfig.cctpSwitchboard.addRemoteEndpoint( optChainSlug, - optChainSlug, - address(optConfig.cctpSwitchboard), - optChainSlug + addressToBytes32(address(optConfig.cctpSwitchboard)), + optCCTPDomain ); optConfig.cctpSwitchboard.addRemoteEndpoint( arbChainSlug, - arbChainSlug, - address(arbConfig.cctpSwitchboard), - arbChainSlug + addressToBytes32(address(arbConfig.cctpSwitchboard)), + arbCCTPDomain ); vm.stopPrank(); // transfer eth to fees pool for native fee payouts @@ -1072,13 +1071,6 @@ contract WatcherSetup is AuctionSetup { return messages; } - function addressToBytes32(address addr_) internal pure returns (bytes32) { - return bytes32(uint256(uint160(addr_))); - } - function bytes32ToAddress(bytes32 addrBytes32_) internal pure returns (address) { - return address(uint160(uint256(addrBytes32_))); - } - function _resolvePromise(PromiseReturnData[] memory promiseReturnData) internal { watcherMultiCall( address(promiseResolver), @@ -1297,3 +1289,10 @@ contract AppGatewayBaseSetup is WatcherSetup { } } } + +function addressToBytes32(address addr_) pure returns (bytes32) { + return bytes32(uint256(uint160(addr_))); +} +function bytes32ToAddress(bytes32 addrBytes32_) pure returns (address) { + return address(uint160(uint256(addrBytes32_))); +} diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 7ae53bfa..65a24240 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -24,6 +24,8 @@ contract MockSocket is ISocket { // plug => (appGateway, switchboard__) mapping(address => PlugConfig) internal _plugConfigs; + mapping(bytes32 => bytes32) public payloadIdToDigest; + function getPlugConfig( address plugAddress_ ) external view returns (bytes32 appGatewayId, address switchboard__) { From d831b3843da54876c03968c58c5084a2c9cecd33 Mon Sep 17 00:00:00 2001 From: Akash Date: Thu, 26 Jun 2025 15:20:35 +0530 Subject: [PATCH 10/12] feat: added RequestSettled, PlugAdded events for indexing --- src/enums.ts | 5 +++++ src/events.ts | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/enums.ts b/src/enums.ts index 1c5879c9..a9bcceac 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -24,10 +24,15 @@ export enum Events { PromiseNotResolved = "PromiseNotResolved", MarkedRevert = "MarkedRevert", + // Configurations + PlugAdded = "PlugAdded", + // RequestHandler RequestSubmitted = "RequestSubmitted", RequestCancelled = "RequestCancelled", FeesIncreased = "FeesIncreased", + RequestSettled = "RequestSettled", + RequestCompletedWithErrors = "RequestCompletedWithErrors", // WritePrecompile WriteProofRequested = "WriteProofRequested", diff --git a/src/events.ts b/src/events.ts index 1d57f1aa..c70e5ffb 100644 --- a/src/events.ts +++ b/src/events.ts @@ -21,8 +21,12 @@ export const requestHandlerEvents = [ Events.RequestSubmitted, Events.FeesIncreased, Events.RequestCancelled, + Events.RequestSettled, + Events.RequestCompletedWithErrors, ]; +export const configurationsEvents = [Events.PlugAdded]; + export const writePrecompileEvents = [ Events.WriteProofRequested, Events.WriteProofUploaded, From 1db7c8bc14a285954c9c3b5572383e943eece54c Mon Sep 17 00:00:00 2001 From: Akash Date: Thu, 26 Jun 2025 15:20:51 +0530 Subject: [PATCH 11/12] fix: cctp remote endpoints --- Errors.md | 2 - FunctionSignatures.md | 62 +++++++++++++++---- .../protocol/switchboard/CCTPSwitchboard.sol | 15 +++-- hardhat-scripts/deploy/3.configureChains.ts | 12 ++-- hardhat-scripts/verify/verify.ts | 2 +- package.json | 2 +- 6 files changed, 70 insertions(+), 25 deletions(-) diff --git a/Errors.md b/Errors.md index a2a0fc42..3ee3e383 100644 --- a/Errors.md +++ b/Errors.md @@ -73,11 +73,9 @@ | Error | Signature | | ------------------------------- | ------------ | | `RemoteExecutionNotFound()` | `0xbd506972` | -| `DigestMismatch()` | `0x582e0907` | | `PrevBatchDigestHashMismatch()` | `0xc9864e9d` | | `NotAttested()` | `0x99efb890` | | `NotExecuted()` | `0xec84b1da` | -| `InvalidDomain()` | `0xeb127982` | | `InvalidSender()` | `0xddb5de5e` | | `OnlyMessageTransmitter()` | `0x935ac89c` | diff --git a/FunctionSignatures.md b/FunctionSignatures.md index c5445dae..3e54fa86 100644 --- a/FunctionSignatures.md +++ b/FunctionSignatures.md @@ -59,7 +59,7 @@ | `chainSlug` | `0xb349ba65` | | `completeOwnershipHandover` | `0xf04e283e` | | `connect` | `0xb3bde1aa` | -| `disableSwitchboard` | `0xe545b261` | +| `disableSwitchboard` | `0xc4d9a820` | | `enableSwitchboard` | `0xf97a498a` | | `execute` | `0xafa8b480` | | `getPlugConfig` | `0xf9778ee0` | @@ -86,18 +86,19 @@ ## SocketBatcher -| Function | Signature | -| ---------------------------- | ------------ | -| `attestAndExecute` | `0x66c7748a` | -| `cancelOwnershipHandover` | `0x54d1f13d` | -| `completeOwnershipHandover` | `0xf04e283e` | -| `owner` | `0x8da5cb5b` | -| `ownershipHandoverExpiresAt` | `0xfee81cf4` | -| `renounceOwnership` | `0x715018a6` | -| `requestOwnershipHandover` | `0x25692962` | -| `rescueFunds` | `0x6ccae054` | -| `socket__` | `0xc6a261d2` | -| `transferOwnership` | `0xf2fde38b` | +| Function | Signature | +| ------------------------------ | ------------ | +| `attestAndExecute` | `0x66c7748a` | +| `attestCCTPAndProveAndExecute` | `0x6c5fd05f` | +| `cancelOwnershipHandover` | `0x54d1f13d` | +| `completeOwnershipHandover` | `0xf04e283e` | +| `owner` | `0x8da5cb5b` | +| `ownershipHandoverExpiresAt` | `0xfee81cf4` | +| `renounceOwnership` | `0x715018a6` | +| `requestOwnershipHandover` | `0x25692962` | +| `rescueFunds` | `0x6ccae054` | +| `socket__` | `0xc6a261d2` | +| `transferOwnership` | `0xf2fde38b` | ## SocketFeeManager @@ -472,6 +473,41 @@ | `watcherMultiCall` | `0x8021e82b` | | `watcher__` | `0x300bb063` | +## CCTPSwitchboard + +| Function | Signature | +| -------------------------------- | ------------ | +| `addRemoteEndpoint` | `0x7d396da5` | +| `allowPacket` | `0x21e9ec80` | +| `allowPayload` | `0x31c23f66` | +| `attest` | `0x63671b60` | +| `attestVerifyAndProveExecutions` | `0x3e9e97e2` | +| `cancelOwnershipHandover` | `0x54d1f13d` | +| `chainSlug` | `0xb349ba65` | +| `chainSlugToRemoteEndpoint` | `0xa4500424` | +| `completeOwnershipHandover` | `0xf04e283e` | +| `domainToRemoteEndpoint` | `0xc24964fe` | +| `grantRole` | `0x2f2ff15d` | +| `handleReceiveMessage` | `0x96abeb70` | +| `hasRole` | `0x91d14854` | +| `isAttested` | `0xc13c2396` | +| `isRemoteExecuted` | `0x0cd97747` | +| `isSyncedOut` | `0x5ae5dfd6` | +| `messageTransmitter` | `0x7b04c181` | +| `owner` | `0x8da5cb5b` | +| `ownershipHandoverExpiresAt` | `0xfee81cf4` | +| `proveRemoteExecutions` | `0xc36f2ca2` | +| `registerSwitchboard` | `0x74f5b1fc` | +| `remoteExecutedDigests` | `0xecbf77d9` | +| `renounceOwnership` | `0x715018a6` | +| `requestOwnershipHandover` | `0x25692962` | +| `rescueFunds` | `0x6ccae054` | +| `revokeRole` | `0xd547741f` | +| `socket__` | `0xc6a261d2` | +| `syncOut` | `0x69a60ff0` | +| `transferOwnership` | `0xf2fde38b` | +| `verifyAttestations` | `0x6f30514c` | + ## FastSwitchboard | Function | Signature | diff --git a/contracts/protocol/switchboard/CCTPSwitchboard.sol b/contracts/protocol/switchboard/CCTPSwitchboard.sol index 0a906451..63b39411 100644 --- a/contracts/protocol/switchboard/CCTPSwitchboard.sol +++ b/contracts/protocol/switchboard/CCTPSwitchboard.sol @@ -15,7 +15,10 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { IMessageTransmitter public immutable messageTransmitter; // remoteChainSlug => remoteEndpoint - mapping(uint32 => RemoteEndpoint) public remoteEndpoints; + mapping(uint32 => RemoteEndpoint) public chainSlugToRemoteEndpoint; + // remoteDomain => remoteEndpoint + mapping(uint32 => RemoteEndpoint) public domainToRemoteEndpoint; + mapping(bytes32 => bool) public isSyncedOut; mapping(bytes32 => bytes32) public remoteExecutedDigests; mapping(bytes32 => bool) public isRemoteExecuted; @@ -56,7 +59,7 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { bytes memory message = abi.encode(payloadId_, digest); for (uint256 i = 0; i < remoteChainSlugs_.length; i++) { - RemoteEndpoint memory endpoint = remoteEndpoints[remoteChainSlugs_[i]]; + RemoteEndpoint memory endpoint = chainSlugToRemoteEndpoint[remoteChainSlugs_[i]]; messageTransmitter.sendMessage(endpoint.remoteDomain, endpoint.remoteAddress, message); } } @@ -67,7 +70,7 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { bytes calldata messageBody ) external returns (bool) { if (msg.sender != address(messageTransmitter)) revert OnlyMessageTransmitter(); - if (remoteEndpoints[sourceDomain].remoteAddress != sender) revert InvalidSender(); + if (domainToRemoteEndpoint[sourceDomain].remoteAddress != sender) revert InvalidSender(); (bytes32 payloadId, bytes32 digest) = abi.decode(messageBody, (bytes32, bytes32)); remoteExecutedDigests[payloadId] = digest; @@ -156,7 +159,11 @@ contract CCTPSwitchboard is FastSwitchboard, IMessageHandler { bytes32 remoteAddress_, uint32 remoteDomain_ ) external onlyOwner { - remoteEndpoints[remoteChainSlug_] = RemoteEndpoint({ + chainSlugToRemoteEndpoint[remoteChainSlug_] = RemoteEndpoint({ + remoteAddress: remoteAddress_, + remoteDomain: remoteDomain_ + }); + domainToRemoteEndpoint[remoteDomain_] = RemoteEndpoint({ remoteAddress: remoteAddress_, remoteDomain: remoteDomain_ }); diff --git a/hardhat-scripts/deploy/3.configureChains.ts b/hardhat-scripts/deploy/3.configureChains.ts index d98ea557..58e3d48c 100644 --- a/hardhat-scripts/deploy/3.configureChains.ts +++ b/hardhat-scripts/deploy/3.configureChains.ts @@ -182,18 +182,22 @@ const addRemoteEndpointsToCCTPSwitchboard = async ( for (const remoteChainSlug of remoteChainSlugs) { const remoteSwitchboardAddress = addresses[remoteChainSlug]?.[Contracts.CCTPSwitchboard]; - const currentRemoteEndpoint = await switchboard.remoteEndpoints( + const currentRemoteEndpoint = await switchboard.chainSlugToRemoteEndpoint( remoteChainSlug ); if (currentRemoteEndpoint.remoteAddress == remoteSwitchboardAddress) { console.log(`Remote endpoint ${remoteChainSlug} already exists`); continue; } - + if (!remoteSwitchboardAddress) { + console.log( + `Remote switchboard address not found for ${remoteChainSlug}` + ); + continue; + } const registerTx = await switchboard.addRemoteEndpoint( remoteChainSlug, - remoteChainSlug, - remoteSwitchboardAddress, + `0x${remoteSwitchboardAddress.slice(2).padStart(64, "0")}`, CCTP_DOMAINS[remoteChainSlug], { ...(await overrides(chain)), diff --git a/hardhat-scripts/verify/verify.ts b/hardhat-scripts/verify/verify.ts index 9f6871ba..eaec585c 100644 --- a/hardhat-scripts/verify/verify.ts +++ b/hardhat-scripts/verify/verify.ts @@ -67,7 +67,7 @@ export const main = async () => { } await storeUnVerifiedParams(unverifiedChainParams, chain, mode); - await new Promise(resolve => setTimeout(resolve, 1000)); + await new Promise((resolve) => setTimeout(resolve, 1000)); retryCount++; if (unverifiedChainParams.length == 0) break; } diff --git a/package.json b/package.json index cc9063b6..644610ff 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.25", + "version": "1.1.28", "description": "socket protocol", "scripts": { "build": "yarn abi && tsc --project lib.tsconfig.json", From 74babcced3daeea3efcceb94929ed3b66efefd7f Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 4 Jul 2025 14:16:14 +0530 Subject: [PATCH 12/12] feat: merged dev into cctp --- contracts/protocol/SocketBatcher.sol | 2 +- test/SetupTest.t.sol | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/protocol/SocketBatcher.sol b/contracts/protocol/SocketBatcher.sol index d9f12878..8268fe65 100644 --- a/contracts/protocol/SocketBatcher.sol +++ b/contracts/protocol/SocketBatcher.sol @@ -66,7 +66,7 @@ contract SocketBatcher is ISocketBatcher, Ownable { execParams_.executeParams.requestCount, execParams_.executeParams.batchCount, execParams_.executeParams.payloadCount, - switchboard_, + bytes32(uint256(uint160(address(switchboard_)))), socket__.chainSlug() ); ICCTPSwitchboard(switchboard_).attestVerifyAndProveExecutions( diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 845aa0a8..32123840 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -984,7 +984,7 @@ contract WatcherSetup is AuctionSetup { refundAddress: transmitterEOA }), cctpBatchParams, - fromBytes32Format(address(getSocketConfig(chainSlug).cctpSwitchboard)) + address(getSocketConfig(chainSlug).cctpSwitchboard) ); } @@ -1106,8 +1106,8 @@ contract WatcherSetup is AuctionSetup { // deployed on a chain. for ex, vault on source, and supertoken on destination. uint256 validPlugCount = 0; for (uint i = 0; i < contractIds_.length; i++) { - address plug = appGateway_.getOnChainAddress(contractIds_[i], chainSlug_); - if (plug != address(0)) { + bytes32 plug = appGateway_.getOnChainAddress(contractIds_[i], chainSlug_); + if (plug != bytes32(0)) { validPlugCount++; } } @@ -1117,15 +1117,15 @@ contract WatcherSetup is AuctionSetup { uint256 configIndex = 0; for (uint i = 0; i < contractIds_.length; i++) { - address plug = appGateway_.getOnChainAddress(contractIds_[i], chainSlug_); - address switchboard = configurations.switchboards(chainSlug_, appGateway_.sbType()); - if (plug != address(0)) { + bytes32 plug = appGateway_.getOnChainAddress(contractIds_[i], chainSlug_); + bytes32 switchboard = configurations.switchboards(chainSlug_, appGateway_.sbType()); + if (plug != bytes32(0)) { configs[configIndex] = AppGatewayConfig({ plug: plug, chainSlug: chainSlug_, plugConfig: PlugConfigGeneric({ appGatewayId: toBytes32Format(address(appGateway_)), - switchboard: toBytes32Format(address(switchboard)) + switchboard: switchboard }) }); configIndex++;